Fix performance problems in multi-batch hash joins by ensuring that we select
authorTom Lane <[email protected]>
Fri, 1 Jun 2007 15:58:02 +0000 (15:58 +0000)
committerTom Lane <[email protected]>
Fri, 1 Jun 2007 15:58:02 +0000 (15:58 +0000)
a well-randomized batch number even when given a poorly-randomized hash value.
This is a bit inefficient but seems the only practical solution given the
constraint that we can't change the hash functions in released branches.
Per report from Joseph Shraibman.

Applied to 8.1 and 8.2 only --- HEAD is getting a cleaner fix, and 8.0 and
before use different coding that seems less vulnerable.

src/backend/access/hash/hashfunc.c
src/backend/executor/nodeHash.c
src/include/access/hash.h

index 0959ef052b03939fd799d145ea1c584aad80da28..79d503639049c80d208f67019d5702c7a8c91216 100644 (file)
@@ -267,6 +267,31 @@ hash_any(register const unsigned char *k, register int keylen)
                        /* case 0: nothing left to add */
        }
        mix(a, b, c);
+
+       /* report the result */
+       return UInt32GetDatum(c);
+}
+
+/*
+ * hash_uint32() -- hash a 32-bit value
+ *
+ * This has the same result (at least on little-endian machines) as
+ *             hash_any(&k, sizeof(uint32))
+ * but is faster and doesn't force the caller to store k into memory.
+ */
+Datum
+hash_uint32(uint32 k)
+{
+       register uint32 a,
+                               b,
+                               c;
+
+       a = 0x9e3779b9 + k;
+       b = 0x9e3779b9;
+       c = 3923095 + (uint32) sizeof(uint32);
+
+       mix(a, b, c);
+
        /* report the result */
        return UInt32GetDatum(c);
 }
index 08bd516bbfdf05051821750b6e0305f26ddca56e..5e41a5dcaa09d66219b1d8ced359e026a37d619f 100644 (file)
@@ -24,6 +24,7 @@
 #include <math.h>
 #include <limits.h>
 
+#include "access/hash.h"
 #include "executor/execdebug.h"
 #include "executor/hashjoin.h"
 #include "executor/instrument.h"
@@ -719,9 +720,11 @@ ExecHashGetHashValue(HashJoinTable hashtable,
  * chains), and must only cause the batch number to remain the same or
  * increase.  Our algorithm is
  *             bucketno = hashvalue MOD nbuckets
- *             batchno = (hashvalue DIV nbuckets) MOD nbatch
- * where nbuckets should preferably be prime so that all bits of the
- * hash value can affect both bucketno and batchno.
+ *             batchno = hash_uint32(hashvalue) MOD nbatch
+ * which gives reasonably independent bucket and batch numbers in the face
+ * of some rather poorly-implemented hash functions in hashfunc.c.  (This
+ * will change in PG 8.3.)
+ *
  * nbuckets doesn't change over the course of the join.
  *
  * nbatch is always a power of 2; we increase it only by doubling it.  This
@@ -740,7 +743,7 @@ ExecHashGetBucketAndBatch(HashJoinTable hashtable,
        {
                *bucketno = hashvalue % nbuckets;
                /* since nbatch is a power of 2, can do MOD by masking */
-               *batchno = (hashvalue / nbuckets) & (nbatch - 1);
+               *batchno = hash_uint32(hashvalue) & (nbatch - 1);
        }
        else
        {
index f6379dc28e7dccc448f6c58a6e2cd279e4c072c2..47b1dc611de3c7f470dd87faa9a48d9a6b9cb67b 100644 (file)
@@ -262,6 +262,7 @@ extern Datum hashname(PG_FUNCTION_ARGS);
 extern Datum hashtext(PG_FUNCTION_ARGS);
 extern Datum hashvarlena(PG_FUNCTION_ARGS);
 extern Datum hash_any(register const unsigned char *k, register int keylen);
+extern Datum hash_uint32(uint32 k);
 
 /* private routines */