Re: [PATCHES] updated hash functions for postgresql v1
Jeff Davis <[email protected]> Fri, 09 Jan 2009 12:04:15 -0800
| Newsgroups | gmane.comp.db.postgresql.devel.general,gmane.comp.db.postgresql.devel.patches |
|---|---|
| Message-ID | <1231531455.25019.75.camel@jdavis> |
--=-egjKVmcXe/J+FTPaKUFa
Content-Type: text/plain
Content-Transfer-Encoding: 7bit
On Mon, 2008-12-22 at 13:47 -0600, Kenneth Marshall wrote:
> Dear PostgreSQL developers,
>
> I am re-sending this to keep this last change to the
> internal hash function on the radar.
>
Hi Ken,
A few comments:
1. New patch with very minor changes attached.
2. I reverted the change you made to indices.sgml. We still don't use
WAL for hash indexes, and in my opinion we should continue to discourage
their use until we do use WAL. We can add back in the comment that hash
indexes are suitable for large keys if we have some results to show
that.
3. There was a regression test failure in union.sql because the ordering
of the results was different. I updated the regression test.
4. Hash functions affect a lot more than hash indexes, so I ran through
a variety of tests that use a HashAggregate plan. Test setup and results
are attached. These results show no difference between the old and the
new code (about 0.1% better).
5. The hash index build time shows some improvement. The new code won in
every instance in which a there were a lot of duplicates in the table
(100 distinct values, 50K of each) by around 5%.
The new code appeared to be the same or slightly worse in the case of
hash index builds with few duplicates (1000000 distinct values, 5 of
each). The difference was about 1% worse, which is probably just noise.
Note: I'm no expert on hash functions. Take all of my tests with a grain
of salt.
I would feel a little better if I saw at least one test that showed
better performance of the new code on a reasonable-looking distribution
of data. The hash index build that you showed only took a second or two
-- it would be nice to see a test that lasted at least a minute.
Regards,
Jeff Davis
--=-egjKVmcXe/J+FTPaKUFa
Content-Disposition: attachment; filename=test_results.tar.gz
Content-Type: application/x-compressed-tar; name=test_results.tar.gz
Content-Transfer-Encoding: base64
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
--=-egjKVmcXe/J+FTPaKUFa
Content-Disposition: attachment; filename=hash.20090109.patch
Content-Type: text/x-patch; name=hash.20090109.patch; charset=utf-8
Content-Transfer-Encoding: 7bit
diff --git a/src/backend/access/hash/hashfunc.c b/src/backend/access/hash/hashfunc.c
index 96d5643..8a236b5 100644
--- a/src/backend/access/hash/hashfunc.c
+++ b/src/backend/access/hash/hashfunc.c
@@ -200,39 +200,94 @@ hashvarlena(PG_FUNCTION_ARGS)
* hash function, see http://burtleburtle.net/bob/hash/doobs.html,
* or Bob's article in Dr. Dobb's Journal, Sept. 1997.
*
- * In the current code, we have adopted an idea from Bob's 2006 update
- * of his hash function, which is to fetch the data a word at a time when
- * it is suitably aligned. This makes for a useful speedup, at the cost
- * of having to maintain four code paths (aligned vs unaligned, and
- * little-endian vs big-endian). Note that we have NOT adopted his newer
- * mix() function, which is faster but may sacrifice some randomness.
+ * In the current code, we have adopted Bob's 2006 update of his hash
+ * which fetches the data a word at a time when it is suitably aligned.
+ * This makes for a useful speedup, at the cost of having to maintain
+ * four code paths (aligned vs unaligned, and little-endian vs big-endian).
+ * It also two separate mixing functions mix() and final(), instead
+ * of a slower multi-purpose function.
*/
/* Get a bit mask of the bits set in non-uint32 aligned addresses */
#define UINT32_ALIGN_MASK (sizeof(uint32) - 1)
+#define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k))))
/*----------
* mix -- mix 3 32-bit values reversibly.
- * For every delta with one or two bits set, and the deltas of all three
- * high bits or all three low bits, whether the original value of a,b,c
- * is almost all zero or is uniformly distributed,
- * - If mix() is run forward or backward, at least 32 bits in a,b,c
- * have at least 1/4 probability of changing.
- * - If mix() is run forward, every bit of c will change between 1/3 and
- * 2/3 of the time. (Well, 22/100 and 78/100 for some 2-bit deltas.)
+ *
+ * This is reversible, so any information in (a,b,c) before mix() is
+ * still in (a,b,c) after mix().
+ *
+ * If four pairs of (a,b,c) inputs are run through mix(), or through
+ * mix() in reverse, there are at least 32 bits of the output that
+ * are sometimes the same for one pair and different for another pair.
+ * This was tested for:
+ * * pairs that differed by one bit, by two bits, in any combination
+ * of top bits of (a,b,c), or in any combination of bottom bits of
+ * (a,b,c).
+ * * "differ" is defined as +, -, ^, or ~^. For + and -, I transformed
+ * the output delta to a Gray code (a^(a>>1)) so a string of 1's (as
+ * is commonly produced by subtraction) look like a single 1-bit
+ * difference.
+ * * the base values were pseudorandom, all zero but one bit set, or
+ * all zero plus a counter that starts at zero.
+ *
+ * This does not achieve avalanche. There are input bits of (a,b,c)
+ * that fail to affect some output bits of (a,b,c), especially of a. The
+ * most thoroughly mixed value is c, but it doesn't really even achieve
+ * avalanche in c.
+ *
+ * This allows some parallelism. Read-after-writes are good at doubling
+ * the number of bits affected, so the goal of mixing pulls in the opposite
+ * direction as the goal of parallelism. I did what I could. Rotates
+ * seem to cost as much as shifts on every machine I could lay my hands
+ * on, and rotates are much kinder to the top and bottom bits, so I used
+ * rotates.
*----------
*/
#define mix(a,b,c) \
{ \
- a -= b; a -= c; a ^= ((c)>>13); \
- b -= c; b -= a; b ^= ((a)<<8); \
- c -= a; c -= b; c ^= ((b)>>13); \
- a -= b; a -= c; a ^= ((c)>>12); \
- b -= c; b -= a; b ^= ((a)<<16); \
- c -= a; c -= b; c ^= ((b)>>5); \
- a -= b; a -= c; a ^= ((c)>>3); \
- b -= c; b -= a; b ^= ((a)<<10); \
- c -= a; c -= b; c ^= ((b)>>15); \
+ a -= c; a ^= rot(c, 4); c += b; \
+ b -= a; b ^= rot(a, 6); a += c; \
+ c -= b; c ^= rot(b, 8); b += a; \
+ a -= c; a ^= rot(c,16); c += b; \
+ b -= a; b ^= rot(a,19); a += c; \
+ c -= b; c ^= rot(b, 4); b += a; \
+}
+
+/*----------
+ * final -- final mixing of 3 32-bit values (a,b,c) into c
+ *
+ * Pairs of (a,b,c) values differing in only a few bits will usually
+ * produce values of c that look totally different. This was tested for
+ * * pairs that differed by one bit, by two bits, in any combination
+ * of top bits of (a,b,c), or in any combination of bottom bits of
+ * (a,b,c).
+ * * "differ" is defined as +, -, ^, or ~^. For + and -, I transformed
+ * the output delta to a Gray code (a^(a>>1)) so a string of 1's (as
+ * is commonly produced by subtraction) look like a single 1-bit
+ * difference.
+ * * the base values were pseudorandom, all zero but one bit set, or
+ * all zero plus a counter that starts at zero.
+ *
+ * The use of separate functions for mix() and final() allow for a
+ * substantial performance increase since final() does not need to
+ * do well in reverse, but is does need to affect all output bits.
+ * mix(), on the other hand, does not need to affect all output
+ * bits (affecting 32 bits is enough).The original hash function had
+ * a single mixing operation that had to satisfy both sets of requirements
+ * and was slower as a result.
+ *----------
+ */
+#define final(a,b,c) \
+{ \
+ c ^= b; c -= rot(b,14); \
+ a ^= c; a -= rot(c,11); \
+ b ^= a; b -= rot(a,25); \
+ c ^= b; c -= rot(b,16); \
+ a ^= c; a -= rot(c,4); \
+ b ^= a; b -= rot(a,14); \
+ c ^= b; c -= rot(b,24); \
}
/*
@@ -260,8 +315,7 @@ hash_any(register const unsigned char *k, register int keylen)
/* Set up the internal state */
len = keylen;
- a = b = 0x9e3779b9; /* the golden ratio; an arbitrary value */
- c = 3923095; /* initialize with an arbitrary value */
+ a = b = c = 0x9e3779b9 + len + 3923095;
/* If the source pointer is word-aligned, we use word-wide fetches */
if (((long) k & UINT32_ALIGN_MASK) == 0)
@@ -445,7 +499,7 @@ hash_any(register const unsigned char *k, register int keylen)
#endif /* WORDS_BIGENDIAN */
}
- mix(a, b, c);
+ final(a, b, c);
/* report the result */
return UInt32GetDatum(c);
@@ -465,11 +519,10 @@ hash_uint32(uint32 k)
b,
c;
- a = 0x9e3779b9 + k;
- b = 0x9e3779b9;
- c = 3923095 + (uint32) sizeof(uint32);
+ a = b = c = 0x9e3779b9 + (uint32) sizeof(uint32) + 3923095;
+ a += k;
- mix(a, b, c);
+ final(a, b, c);
/* report the result */
return UInt32GetDatum(c);
diff --git a/src/test/regress/expected/union.out b/src/test/regress/expected/union.out
index 295dace..be046a9 100644
--- a/src/test/regress/expected/union.out
+++ b/src/test/regress/expected/union.out
@@ -263,16 +263,16 @@ ORDER BY 1;
SELECT q2 FROM int8_tbl INTERSECT SELECT q1 FROM int8_tbl;
q2
------------------
- 123
4567890123456789
+ 123
(2 rows)
SELECT q2 FROM int8_tbl INTERSECT ALL SELECT q1 FROM int8_tbl;
q2
------------------
- 123
4567890123456789
4567890123456789
+ 123
(3 rows)
SELECT q2 FROM int8_tbl EXCEPT SELECT q1 FROM int8_tbl ORDER BY 1;
@@ -305,16 +305,16 @@ SELECT q1 FROM int8_tbl EXCEPT SELECT q2 FROM int8_tbl;
SELECT q1 FROM int8_tbl EXCEPT ALL SELECT q2 FROM int8_tbl;
q1
------------------
- 123
4567890123456789
+ 123
(2 rows)
SELECT q1 FROM int8_tbl EXCEPT ALL SELECT DISTINCT q2 FROM int8_tbl;
q1
------------------
- 123
4567890123456789
4567890123456789
+ 123
(3 rows)
--
@@ -341,8 +341,8 @@ SELECT f1 FROM float8_tbl EXCEPT SELECT f1 FROM int4_tbl ORDER BY 1;
SELECT q1 FROM int8_tbl INTERSECT SELECT q2 FROM int8_tbl UNION ALL SELECT q2 FROM int8_tbl;
q1
-------------------
- 123
4567890123456789
+ 123
456
4567890123456789
123
@@ -353,15 +353,15 @@ SELECT q1 FROM int8_tbl INTERSECT SELECT q2 FROM int8_tbl UNION ALL SELECT q2 FR
SELECT q1 FROM int8_tbl INTERSECT (((SELECT q2 FROM int8_tbl UNION ALL SELECT q2 FROM int8_tbl)));
q1
------------------
- 123
4567890123456789
+ 123
(2 rows)
(((SELECT q1 FROM int8_tbl INTERSECT SELECT q2 FROM int8_tbl))) UNION ALL SELECT q2 FROM int8_tbl;
q1
-------------------
- 123
4567890123456789
+ 123
456
4567890123456789
123
@@ -416,8 +416,8 @@ LINE 1: ... int8_tbl EXCEPT SELECT q2 FROM int8_tbl ORDER BY q2 LIMIT 1...
SELECT q1 FROM int8_tbl EXCEPT (((SELECT q2 FROM int8_tbl ORDER BY q2 LIMIT 1)));
q1
------------------
- 123
4567890123456789
+ 123
(2 rows)
--
--=-egjKVmcXe/J+FTPaKUFa
Content-Type: text/plain
Content-Disposition: inline
MIME-Version: 1.0
Content-Transfer-Encoding: quoted-printable
--=20
Sent via pgsql-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
--=-egjKVmcXe/J+FTPaKUFa--