On Mon, 2023-12-18 at 13:39 +0700, John Naylor wrote:
> For now just two:
> v10-0002 is Jeff's change to the search path cache, but with the
> chunked interface that I found to be faster.

Did you consider specializing for the case of an aligned pointer? If
it's a string (c string or byte string) it's almost always going to be
aligned, right?

I hacked up a patch (attached). I lost track of which benchmark we're
using to test the performance, but when I test in a loop it seems
substantially faster.

It reads past the NUL byte, but only to the next alignment boundary,
which I think is OK (though I think I'd need to fix the patch for when
maxalign < 8).

Regards,
        Jeff Davis

From 055d5cc24404584fd98109fabdcf83348e5c49b4 Mon Sep 17 00:00:00 2001
From: Jeff Davis <j...@j-davis.com>
Date: Mon, 18 Dec 2023 16:44:27 -0800
Subject: [PATCH v10jd] Optimize hash function further.

---
 src/backend/catalog/namespace.c      | 46 +++++++++++++++++++++++++---
 src/include/common/hashfn_unstable.h |  9 ++++++
 2 files changed, 51 insertions(+), 4 deletions(-)

diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c
index c23f46aca3..368a7fabec 100644
--- a/src/backend/catalog/namespace.c
+++ b/src/backend/catalog/namespace.c
@@ -245,15 +245,44 @@ static bool MatchNamedCall(HeapTuple proctup, int nargs, List *argnames,
  * offers a more convenient API.
  */
 
+/* From: https://graphics.stanford.edu/~seander/bithacks.html#ZeroInWord */
+#define haszero64(v) \
+	(((v) - 0x0101010101010101UL) & ~(v) & 0x8080808080808080UL)
+
 static inline uint32
-spcachekey_hash(SearchPathCacheKey key)
+cstring_hash_aligned(const char *str, uint64 seed)
+{
+	const char *const start = str;
+	const char *buf = start;
+	int chunk_len = 0;
+	fasthash_state hs;
+
+	fasthash_init(&hs, FH_UNKNOWN_LENGTH, seed);
+
+	Assert(PointerIsAligned(start, uint64));
+	while (!haszero64(*(uint64 *)buf))
+	{
+		fasthash_accum64(&hs, buf);
+		buf += sizeof(uint64);
+	}
+
+	while (buf[chunk_len] != '\0')
+		chunk_len++;
+	fasthash_accum(&hs, buf, chunk_len);
+	buf += chunk_len;
+
+	return fasthash_final32(&hs, buf - start);
+}
+
+static inline uint32
+cstring_hash_unaligned(const char *str, uint64 seed)
 {
-	const char *const start = key.searchPath;
-	const char *buf = key.searchPath;
+	const char *const start = str;
+	const char *buf = str;
 	fasthash_state hs;
 
 	/* WIP: maybe roleid should be mixed in normally */
-	fasthash_init(&hs, FH_UNKNOWN_LENGTH, key.roleid);
+	fasthash_init(&hs, FH_UNKNOWN_LENGTH, seed);
 	while (*buf)
 	{
 		int			chunk_len = 0;
@@ -269,6 +298,15 @@ spcachekey_hash(SearchPathCacheKey key)
 	return fasthash_final32(&hs, buf - start);
 }
 
+static inline uint32
+spcachekey_hash(SearchPathCacheKey key)
+{
+	if (PointerIsAligned(key.searchPath, uint64))
+		return cstring_hash_aligned(key.searchPath, key.roleid);
+	else
+		return cstring_hash_unaligned(key.searchPath, key.roleid);
+}
+
 static inline bool
 spcachekey_equal(SearchPathCacheKey a, SearchPathCacheKey b)
 {
diff --git a/src/include/common/hashfn_unstable.h b/src/include/common/hashfn_unstable.h
index bf1dbee28d..553fab0415 100644
--- a/src/include/common/hashfn_unstable.h
+++ b/src/include/common/hashfn_unstable.h
@@ -105,6 +105,15 @@ fasthash_combine(fasthash_state *hs)
 	hs->accum = 0;
 }
 
+/* Accumulate 8 bytes from aligned pointer and combine it into the hash */
+static inline void
+fasthash_accum64(fasthash_state *hs, const char *ptr)
+{
+	Assert(PointerIsAligned(ptr, uint64));
+	hs->accum = *(uint64 *)ptr;
+	fasthash_combine(hs);
+}
+
 /* Accumulate up to 8 bytes of input and combine it into the hash */
 static inline void
 fasthash_accum(fasthash_state *hs, const char *k, int len)
-- 
2.34.1

Reply via email to