From 8d64fd04ca902f6760aafa5ec2ec14cfcfdddbec Mon Sep 17 00:00:00 2001 From: David Rowley Date: Wed, 26 Mar 2025 10:35:42 +1300 Subject: [PATCH v9 2/2] Optimize Query jumble A recent change adjusted query jumbling so it no longer ignores NULL nodes during the jumble. This added some overhead. Here we tune a few things to make jumbling faster again. This makes jumbling perform similar or slightly faster than prior to that change. Author: David Rowley Discussion: https://postgr.es/m/CAApHDvreP04nhTKuYsPw0F-YN+4nr4f=L72SPeFb81jfv+2c7w@mail.gmail.com --- src/backend/nodes/queryjumblefuncs.c | 130 ++++++++++++++++++++++++--- 1 file changed, 117 insertions(+), 13 deletions(-) diff --git a/src/backend/nodes/queryjumblefuncs.c b/src/backend/nodes/queryjumblefuncs.c index 9d206afef15..a8c426b2483 100644 --- a/src/backend/nodes/queryjumblefuncs.c +++ b/src/backend/nodes/queryjumblefuncs.c @@ -214,25 +214,50 @@ DoJumble(JumbleState *jstate, Node *node) } /* - * AppendJumble: Append a value that is substantive in a given query to - * the current jumble. + * AppendJumbleInternal: Append a value that is substantive in a given query + * to the current jumble. + * + * Note: Callers must ensure that jstate->pending_nulls is zero first by + * calling FlushPendingNulls() when required. Callers must also ensure that + * size > 0. */ -static void -AppendJumble(JumbleState *jstate, const unsigned char *item, Size size) +static pg_attribute_always_inline void +AppendJumbleInternal(JumbleState *jstate, const unsigned char *item, + Size size) { unsigned char *jumble = jstate->jumble; Size jumble_len = jstate->jumble_len; + /* Ensure the caller didn't mess up */ + Assert(size > 0); + + /* + * Fast path for when there's enough space left in the buffer. This is + * worthwhile as means the memcpy can be inlined into very efficient code + * when 'size' is a compile-time constant. + */ + if (likely(size <= JUMBLE_SIZE - jumble_len)) + { + memcpy(jumble + jumble_len, item, size); + jstate->jumble_len += size; + +#ifdef USE_ASSERT_CHECKING + jstate->total_jumble_len += size; +#endif + + return; + } + /* * Whenever the jumble buffer is full, we hash the current contents and * reset the buffer to contain just that hash value, thus relying on the * hash to summarize everything so far. */ - while (size > 0) + do { Size part_size; - if (jumble_len >= JUMBLE_SIZE) + if (unlikely(jumble_len >= JUMBLE_SIZE)) { uint64 start_hash; @@ -250,7 +275,7 @@ AppendJumble(JumbleState *jstate, const unsigned char *item, Size size) #ifdef USE_ASSERT_CHECKING jstate->total_jumble_len += part_size; #endif - } + } while (size > 0); jstate->jumble_len = jumble_len; } @@ -265,6 +290,74 @@ AppendJumbleNull(JumbleState *jstate) jstate->pending_nulls++; } +/* + * AppendJumble + * Add 'size' bytes of the given jumble 'value' to the jumble state + */ +static pg_noinline void +AppendJumble(JumbleState *jstate, const unsigned char *value, Size size) +{ + if (jstate->pending_nulls > 0) + FlushPendingNulls(jstate); + + AppendJumbleInternal(jstate, value, size); +} + +/* + * AppendJumble8 + * Add the first byte from the given 'value' pointer to the jumble state + */ +static pg_noinline void +AppendJumble8(JumbleState *jstate, const unsigned char *value) +{ + if (jstate->pending_nulls > 0) + FlushPendingNulls(jstate); + + AppendJumbleInternal(jstate, value, 1); +} + +/* + * AppendJumble16 + * Add the first 2 bytes from the given 'value' pointer to the jumble + * state. + */ +static pg_noinline void +AppendJumble16(JumbleState *jstate, const unsigned char *value) +{ + if (jstate->pending_nulls > 0) + FlushPendingNulls(jstate); + + AppendJumbleInternal(jstate, value, 2); +} + +/* + * AppendJumble32 + * Add the first 4 bytes from the given 'value' pointer to the jumble + * state. + */ +static pg_noinline void +AppendJumble32(JumbleState *jstate, const unsigned char *value) +{ + if (jstate->pending_nulls > 0) + FlushPendingNulls(jstate); + + AppendJumbleInternal(jstate, value, 4); +} + +/* + * AppendJumble64 + * Add the first 8 bytes from the given 'value' pointer to the jumble + * state. + */ +static pg_noinline void +AppendJumble64(JumbleState *jstate, const unsigned char *value) +{ + if (jstate->pending_nulls > 0) + FlushPendingNulls(jstate); + + AppendJumbleInternal(jstate, value, 8); +} + /* * FlushPendingNulls * Incorporate the pending_null value into the jumble buffer. @@ -276,8 +369,8 @@ FlushPendingNulls(JumbleState *jstate) { Assert(jstate->pending_nulls > 0); - AppendJumble(jstate, - (const unsigned char *) &jstate->pending_nulls, 4); + AppendJumbleInternal(jstate, + (const unsigned char *) &jstate->pending_nulls, 4); jstate->pending_nulls = 0; } @@ -401,7 +494,18 @@ IsSquashableConstList(List *elements, Node **firstExpr, Node **lastExpr) #define JUMBLE_LOCATION(location) \ RecordConstLocation(jstate, expr->location, false) #define JUMBLE_FIELD(item) \ - AppendJumble(jstate, (const unsigned char *) &(expr->item), sizeof(expr->item)) +do { \ + if (sizeof(expr->item) == 8) \ + AppendJumble64(jstate, (const unsigned char *) &(expr->item)); \ + else if (sizeof(expr->item) == 4) \ + AppendJumble32(jstate, (const unsigned char *) &(expr->item)); \ + else if (sizeof(expr->item) == 2) \ + AppendJumble16(jstate, (const unsigned char *) &(expr->item)); \ + else if (sizeof(expr->item) == 1) \ + AppendJumble8(jstate, (const unsigned char *) &(expr->item)); \ + else \ + AppendJumble(jstate, (const unsigned char *) &(expr->item), sizeof(expr->item)); \ +} while (0) #define JUMBLE_FIELD_SINGLE(item) \ AppendJumble(jstate, (const unsigned char *) &(item), sizeof(item)) #define JUMBLE_STRING(str) \ @@ -535,15 +639,15 @@ _jumbleList(JumbleState *jstate, Node *node) break; case T_IntList: foreach(l, expr) - JUMBLE_FIELD_SINGLE(lfirst_int(l)); + AppendJumble32(jstate, (const unsigned char *) &lfirst_int(l)); break; case T_OidList: foreach(l, expr) - JUMBLE_FIELD_SINGLE(lfirst_oid(l)); + AppendJumble32(jstate, (const unsigned char *) &lfirst_oid(l)); break; case T_XidList: foreach(l, expr) - JUMBLE_FIELD_SINGLE(lfirst_xid(l)); + AppendJumble32(jstate, (const unsigned char *) &lfirst_xid(l)); break; default: elog(ERROR, "unrecognized list node type: %d", -- 2.43.0