]> granicus.if.org Git - postgresql/commitdiff
Improve hash method for bitmapsets: some examination of actual outputs
authorTom Lane <tgl@sss.pgh.pa.us>
Wed, 15 Jun 2005 16:24:07 +0000 (16:24 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Wed, 15 Jun 2005 16:24:07 +0000 (16:24 +0000)
shows that adding a circular shift between words greatly improves the
distribution of hash outputs.

src/backend/nodes/bitmapset.c

index 5f4ca9a779b26bc8974601735ded4044d5b458d2..d74ba6189ed78264988a09191da43300d8169627 100644 (file)
@@ -14,7 +14,7 @@
  * Copyright (c) 2003-2005, PostgreSQL Global Development Group
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/nodes/bitmapset.c,v 1.8 2005/06/08 23:02:04 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/nodes/bitmapset.c,v 1.9 2005/06/15 16:24:07 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -769,22 +769,36 @@ bms_first_member(Bitmapset *a)
  *
  * Note: we must ensure that any two bitmapsets that are bms_equal() will
  * hash to the same value; in practice this means that trailing all-zero
- * words cannot affect the result.  Longitudinal XOR provides a reasonable
- * hash value that has this property.
+ * words cannot affect the result.  The circular-shift-and-XOR hash method
+ * used here has this property, so long as we work from back to front.
+ *
+ * Note: you might wonder why we bother with the circular shift; at first
+ * glance a straight longitudinal XOR seems as good and much simpler.  The
+ * reason is empirical: this gives a better distribution of hash values on
+ * the bitmapsets actually generated by the planner.  A common way to have
+ * multiword bitmapsets is "a JOIN b JOIN c JOIN d ...", which gives rise
+ * to rangetables in which base tables and JOIN nodes alternate; so
+ * bitmapsets of base table RT indexes tend to use only odd-numbered or only
+ * even-numbered bits.  A straight longitudinal XOR would preserve this
+ * property, leading to a much smaller set of possible outputs than if
+ * we include a shift.
  */
 uint32
 bms_hash_value(const Bitmapset *a)
 {
        bitmapword      result = 0;
-       int                     nwords;
        int                     wordnum;
 
-       if (a == NULL)
+       if (a == NULL || a->nwords <= 0)
                return 0;                               /* All empty sets hash to 0 */
-       nwords = a->nwords;
-       for (wordnum = 0; wordnum < nwords; wordnum++)
+       for (wordnum = a->nwords; --wordnum > 0; )
        {
                result ^= a->words[wordnum];
+               if (result & ((bitmapword) 1 << (BITS_PER_BITMAPWORD - 1)))
+                       result = (result << 1) | 1;
+               else
+                       result = (result << 1);
        }
+       result ^= a->words[0];
        return (uint32) result;
 }