Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.94.2) (envelope-from ) id 1uSeet-00BEvQ-4j for pgsql-hackers@arkaria.postgresql.org; Fri, 20 Jun 2025 16:32:07 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.94.2) (envelope-from ) id 1uSeeq-002vmH-UV for pgsql-hackers@arkaria.postgresql.org; Fri, 20 Jun 2025 16:32:05 +0000 Received: from makus.postgresql.org ([2001:4800:3e1:1::229]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.94.2) (envelope-from ) id 1uSeeq-002vm9-Hx for pgsql-hackers@lists.postgresql.org; Fri, 20 Jun 2025 16:32:05 +0000 Received: from siberian.tulip.relay.mailchannels.net ([23.83.218.246]) by makus.postgresql.org with smtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1uSeeo-0035Om-2m for pgsql-hackers@postgresql.org; Fri, 20 Jun 2025 16:32:03 +0000 X-Sender-Id: dreamhost|x-authsender|nico@cryptonector.com Received: from relay.mailchannels.net (localhost [127.0.0.1]) by relay.mailchannels.net (Postfix) with ESMTP id 49103784CBC; Fri, 20 Jun 2025 16:32:01 +0000 (UTC) Received: from pdx1-sub0-mail-a237.dreamhost.com (100-99-178-123.trex-nlb.outbound.svc.cluster.local [100.99.178.123]) (Authenticated sender: dreamhost) by relay.mailchannels.net (Postfix) with ESMTPA id AFF727834BF; Fri, 20 Jun 2025 16:31:52 +0000 (UTC) ARC-Seal: i=1; s=arc-2022; d=mailchannels.net; t=1750437119; a=rsa-sha256; cv=none; b=+H/n8aQae+UpPnAWv79qGx3H/eLfe5eQudnzJh4Ktzo4LxmxkMncnKI4yaSMNZrVcu1s8P H9qqx2FkeNWeQUGjaGyxMt8Vo8rYi1efFAeSNZem9o5eERQO5Hb9fgui8+RV2mfKZ+PZkf KVzx7NzGIGaHncv67UBWlbH/u27hW3EXrpRaxxgpyV4dqM/lJumd89uPCV7C7I2MdwLDwy bZHL4AF/3t9MgHWAmcRkOwz7VRkKLlM88soNg2bQZcOK5XzYO2wmTBkrapfFEKXdGxTCBO +NZtsGTPkR8zPxAxMHrSMlOFb08aOOFZpEZWpFe+IA82lCYn3hh2XPDiOz5iWw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=mailchannels.net; s=arc-2022; t=1750437119; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: in-reply-to:in-reply-to:references:references:dkim-signature; bh=lMuxsZOy3+gc60J/ttn7VnojR1cCxOOc+GseYrMYZ1I=; b=xy61HL9HxwepmpZJQmWBVB17wgPbzSAX0zR4j8tFRErhYDAYS8O4/HIPrJ3e5ZXg2I7tE/ 9mFtc59OW3BuLE0coZmudMnhm+u5wpLMxa+0meKXjlmGU0D91XYgEYCZQh9TfiYWrHroMX eWA4pwXyuR2p7Qu7mGMSue++YY0657eTaZXr/zg9ytLKvxLf3WFJl+B6pCymNYALcEC74u 8RT2j0rSSnzh9DasVK1hoZq1bJDFx8OkAhV+u6xgZPbp8yXBhjkjthrLy59pQIm4/B21vJ cuOtLsNK6DpMSFMW2GTsljePSM5rtxmJ9gqiXKXxTdY9ifWjgYn81DrPpxHxfw== ARC-Authentication-Results: i=1; rspamd-679c59f89-bqc5r; auth=pass smtp.auth=dreamhost smtp.mailfrom=nico@cryptonector.com X-Sender-Id: dreamhost|x-authsender|nico@cryptonector.com X-MC-Relay: Neutral X-MailChannels-SenderId: dreamhost|x-authsender|nico@cryptonector.com X-MailChannels-Auth-Id: dreamhost X-Illegal-Spot: 23fcfe771ec1a87f_1750437121147_1594831789 X-MC-Loop-Signature: 1750437121147:2832415827 X-MC-Ingress-Time: 1750437121147 Received: from pdx1-sub0-mail-a237.dreamhost.com (pop.dreamhost.com [64.90.62.162]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384) by 100.99.178.123 (trex/7.0.3); Fri, 20 Jun 2025 16:32:01 +0000 Received: from ubby (syn-075-081-095-064.res.spectrum.com [75.81.95.64]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange ECDHE (P-256) server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) (Authenticated sender: nico@cryptonector.com) by pdx1-sub0-mail-a237.dreamhost.com (Postfix) with ESMTPSA id 4bP3074S3Dz8y; Fri, 20 Jun 2025 09:31:51 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=cryptonector.com; s=dreamhost; t=1750437112; bh=lMuxsZOy3+gc60J/ttn7VnojR1cCxOOc+GseYrMYZ1I=; h=Date:From:To:Cc:Subject:Content-Type; b=kWfiKU96owe/nUOic8phjM4bs0vSOpGWLeLRDAFvgq3+jfUz4KDBfrYgDRSe8RwKP sXrp/bW+u0W5zvN41haNG7Tlhx38Gd04NvAqox4drEq4Uk5nBy2k7CLvzvwDTPioiW 3hq7M8EGnRrpaY923FCgPwGzFHIZsCKkfL5kmvF9jjYDn8109cDk0yB8aC37Rk5jon rtGKuByFItJQOuHtGIVP+5oS3/MuYw31m9/9gzKVQx/c8mpGTtIrPivWwyOyOh8OY0 vmg/XICw13N3tpj0xvzizuGinLPZR1mSlBuIimbSZro0y7l6cGVxpIUYHb+ZZKzKyu KaMVbJKlZzeCw== Date: Fri, 20 Jun 2025 11:31:49 -0500 From: Nico Williams To: Jeff Davis Cc: Alexander Borisov , PostgreSQL Hackers Subject: Re: Improve the performance of Unicode Normalization Forms. Message-ID: References: <844d3dd7-2955-4794-95d1-7f4c13cb89fc@gmail.com> <4211ffd7fe154c4af693b98d78f4a3689ce8cc30.camel@j-davis.com> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: <4211ffd7fe154c4af693b98d78f4a3689ce8cc30.camel@j-davis.com> List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk On Thu, Jun 19, 2025 at 10:41:57AM -0700, Jeff Davis wrote: > In addition to the lookups themselves, there are other opportunities > for optimization as well, such as: > > * reducing the need for palloc and extra buffers, perhaps by using > buffers on the stack for small strings > > * operate more directly on UTF-8 data rather than decoding and re- > encoding the entire string Not sure how relevant it is here, but in ZFS in the 00s we implement form-insensitive, from-preserving functionality with an interesting optimization that greatly reduced allocations and which had a decent fast path for ASCII-mostly strings. It works by doing normalization only in a) string comparison functions, b) string hashing functions (because in ZFS directories are hash tables). For b-trees one has to normalize the whole string, so perhaps this scheme is just not that relevant to databases that use b-trees, but then depending on how the b-tree key is smeared over internal nodes it might be. The ASCII-mostly fast path was that when comparing strings you want a "cursor" through each string, and whenever the current and next bytes are both ASCII you compare the current one the fast way (as ASCII), and you only take the slow path of having to check if the current _character_ (as opposed to _byte_) requires normalization when either the current or next bytes are not ASCII. In the slow path you only normalize the _current character_, so you only need enough buffer space for that. In principle a Unicode character could consist of an unbounded number of codepoints (zalgo), I think, but in practice it will be no more than a half a dozen or so, so the buffer can be small and stack allocated. This same concept applies to string hashing: you walk a cursor over the string and hash each ASCII _character_ and normalize all non-ASCII characters, all one character at a time. The really nice thing about form-insensitive/form-preserving functionality is that it is form-preserving rather than normalizing on create and lookup, and that makes the fast-path described above feasible. Whereas if you normalize on create and lookup you have to heap allocate enough space for each string normalized. The other nice thing is that f-i/f-p behavior is a lot less surprising to users -- the input methods they use don't matter. What motivated this f-i/f-p behavior was that HFS+ used NFD (well, something very close to NFD) but input methods (even on OS X) invariably produce NFC (well, something close to it), at least for Latin scripts. This means that on OS X if you use filenames from directory listings those will be NFD while user inputs will be NFC, and so you can't just memcmp()/strcmp() those -- you have to normalize _or_ use form- insensitive string comparison, but nothing did that 20 years ago. Thus doing the form-insensitivity in the filesystem seemed best, and if you do that you can be form-preserving to enable the optimization described above. My apologies if the above is not relevant to PG and thus noise, Nico --