Fix horrendously bad bit hashing function.
hashBits() is used to generate a hash code from the same 64 bits used to represent a Wren number as a double. When building a map containing a large number of integer keys, it's important for this to do a good job scattering the bits across the 32-bit key space. Alas, it does not. Worse, the benchmark to test this happens to stop just before the performance falls off a cliff, so this was easy to overlook. This replaces it with the hash function V8 uses, which has much better performance across the numeric range.
This commit is contained in:
+13
-21
@@ -356,24 +356,18 @@ ObjMap* wrenNewMap(WrenVM* vm)
|
||||
return map;
|
||||
}
|
||||
|
||||
static inline uint32_t hashBits(DoubleBits bits)
|
||||
static inline uint32_t hashBits(uint64_t hash)
|
||||
{
|
||||
uint32_t result = bits.bits32[0] ^ bits.bits32[1];
|
||||
|
||||
// Slosh the bits around some. Due to the way doubles are represented, small
|
||||
// integers will have most of low bits of the double respresentation set to
|
||||
// zero. For example, the above result for 5 is 43d00600.
|
||||
//
|
||||
// We map that to an entry index by masking off the high bits which means
|
||||
// most small integers would all end up in entry zero. That's bad. To avoid
|
||||
// that, push a bunch of the high bits lower down so they affect the lower
|
||||
// bits too.
|
||||
//
|
||||
// The specific mixing function here was pulled from Java's HashMap
|
||||
// implementation.
|
||||
result ^= (result >> 20) ^ (result >> 12);
|
||||
result ^= (result >> 7) ^ (result >> 4);
|
||||
return result;
|
||||
// From v8's ComputeLongHash() which in turn cites:
|
||||
// Thomas Wang, Integer Hash Functions.
|
||||
// http://www.concentric.net/~Ttwang/tech/inthash.htm
|
||||
hash = ~hash + (hash << 18); // hash = (hash << 18) - hash - 1;
|
||||
hash = hash ^ (hash >> 31);
|
||||
hash = hash * 21; // hash = (hash + (hash << 2)) + (hash << 4);
|
||||
hash = hash ^ (hash >> 11);
|
||||
hash = hash + (hash << 6);
|
||||
hash = hash ^ (hash >> 22);
|
||||
return (uint32_t)(hash & 0x3fffffff);
|
||||
}
|
||||
|
||||
// Generates a hash code for [num].
|
||||
@@ -382,7 +376,7 @@ static inline uint32_t hashNumber(double num)
|
||||
// Hash the raw bits of the value.
|
||||
DoubleBits bits;
|
||||
bits.num = num;
|
||||
return hashBits(bits);
|
||||
return hashBits(bits.bits64);
|
||||
}
|
||||
|
||||
// Generates a hash code for [object].
|
||||
@@ -429,9 +423,7 @@ static uint32_t hashValue(Value value)
|
||||
if (IS_OBJ(value)) return hashObject(AS_OBJ(value));
|
||||
|
||||
// Hash the raw bits of the unboxed value.
|
||||
DoubleBits bits;
|
||||
bits.bits64 = value;
|
||||
return hashBits(bits);
|
||||
return hashBits(value);
|
||||
#else
|
||||
switch (value.type)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user