On Wed, 2025-03-12 at 19:55 +0300, Alexander Borisov wrote:
> 1. Added static for casemap() function. Otherwise the compiler could
> not
> optimize the code and the performance dropped significantly.

Oops, it was static, but I made it external just to see what code it
generated. I didn't intend to publish it as an external function --
thank you for catching that!

> 2. Added a fast path for codepoint < 0x80.
> 
> v3j-0002:
> In the fast path for codepoints < 0x80, I added a premature return.
> This avoided additional insertions, which increased performance.

What do you mean "additional insertions"?

Also, should we just compute the results in the fast path? We don't
even need a table. Rough patch attached to go on top of v4-0001.

Should we properly return CASEMAP_SELF when *simple == u1, or is it ok
to return CASEMAP_SIMPLE? It probably doesn't matter performance-wise,
but it feels more correct to return CASEMAP_SELF.

> 
> Perhaps for general
> beauty it should be made static inline, I don't have a rigid position
> here.

We ordinarily use "static inline" if it's in a header file, and
"static" if it's in a .c file, so I'll do it that way.

> I was purely based on existing approaches in Postgres, the
> Normalization Forms have them separated into different headers. Just
> trying to be consistent with existing approaches.

I think that was done for normalization primarily because it's not used
#ifndef FRONTEND (see unicode_norm.c), and perhaps also because it's
just a more complex function worthy of its own file.

I looked into the history, and commit 783f0cc64d explains why perfect
hashing is not used in the frontend:

"The decomposition table remains the same, getting used for the binary
search in the frontend code, where we care more about the size of the
libraries like libpq over performance..."

> 
Regards,
        Jeff Davis

From ed4d2803aa32add7c05726286b94e78e49bb1257 Mon Sep 17 00:00:00 2001
From: Jeff Davis <j...@j-davis.com>
Date: Wed, 12 Mar 2025 11:56:59 -0700
Subject: [PATCH vtmp] fastpath

---
 src/common/unicode_case.c | 34 ++++++++++++++++++++++++++++++++--
 1 file changed, 32 insertions(+), 2 deletions(-)

diff --git a/src/common/unicode_case.c b/src/common/unicode_case.c
index 2b3b4cdc2e7..b1fc1651043 100644
--- a/src/common/unicode_case.c
+++ b/src/common/unicode_case.c
@@ -390,11 +390,41 @@ casemap(pg_wchar u1, CaseKind casekind, bool full,
 {
 	const pg_case_map *map;
 
+	/*
+	 * Fast path for early codepoints. The only interesting characters are
+	 * [a-zA-Z].
+	 */
 	if (u1 < 0x80)
 	{
-		*simple = case_map[u1].simplemap[casekind];
+		/* fast-path codepoints do not have special casing */
+		Assert(find_case_map(u1)->special_case == NULL);
 
-		return CASEMAP_SIMPLE;
+		switch (casekind)
+		{
+			case CaseLower:
+			case CaseFold:
+				if (u1 >= 'A' && u1 <= 'Z')
+				{
+					*simple = u1 + 0x20;
+					Assert(case_map[u1].simplemap[casekind] == *simple);
+					return CASEMAP_SIMPLE;
+				}
+				break;
+			case CaseTitle:
+			case CaseUpper:
+				if (u1 >= 'a' && u1 <= 'z')
+				{
+					*simple = u1 - 0x20;
+					Assert(case_map[u1].simplemap[casekind] == *simple);
+					return CASEMAP_SIMPLE;
+				}
+				break;
+			default:
+				Assert(false);
+		}
+
+		Assert(case_map[u1].simplemap[casekind] == u1);
+		return CASEMAP_SELF;
 	}
 
 	map = find_case_map(u1);
-- 
2.34.1

Reply via email to