Skip to content

Commit 1dd9853

Browse files
committed
Speed up the constant hash function
1 parent 390bdaa commit 1dd9853

1 file changed

Lines changed: 43 additions & 7 deletions

File tree

src/util/pm_constant_pool.c

Lines changed: 43 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -70,19 +70,55 @@ pm_constant_id_list_includes(pm_constant_id_list_t *list, pm_constant_id_t id) {
7070
}
7171

7272
/**
73-
* A relatively simple hash function (djb2) that is used to hash strings. We are
74-
* optimizing here for simplicity and speed.
73+
* A multiply-xorshift hash that processes input a word at a time. This is
74+
* significantly faster than the byte-at-a-time djb2 hash for the short strings
75+
* typical in Ruby source (~15 bytes average). Each word is mixed into the hash
76+
* by XOR followed by multiplication by a large odd constant, which spreads
77+
* entropy across all bits. A final xorshift fold produces the 32-bit result.
7578
*/
7679
static inline uint32_t
7780
pm_constant_pool_hash(const uint8_t *start, size_t length) {
78-
// This is a prime number used as the initial value for the hash function.
79-
uint32_t value = 5381;
81+
// This constant is borrowed from wyhash. It is a 64-bit odd integer with
82+
// roughly equal 0/1 bits, chosen for good avalanche behavior when used in
83+
// multiply-xorshift sequences.
84+
static const uint64_t secret = 0x517cc1b727220a95ULL;
85+
uint64_t hash = (uint64_t) length;
86+
87+
const uint8_t *ptr = start;
88+
size_t remaining = length;
89+
90+
while (remaining >= 8) {
91+
uint64_t word;
92+
memcpy(&word, ptr, 8);
93+
hash ^= word;
94+
hash *= secret;
95+
ptr += 8;
96+
remaining -= 8;
97+
}
98+
99+
if (remaining >= 4) {
100+
uint32_t word;
101+
memcpy(&word, ptr, 4);
102+
hash ^= (uint64_t) word;
103+
hash *= secret;
104+
ptr += 4;
105+
remaining -= 4;
106+
}
107+
108+
if (remaining >= 2) {
109+
hash ^= (uint64_t) ptr[0] | ((uint64_t) ptr[1] << 8);
110+
hash *= secret;
111+
ptr += 2;
112+
remaining -= 2;
113+
}
80114

81-
for (size_t index = 0; index < length; index++) {
82-
value = ((value << 5) + value) + start[index];
115+
if (remaining >= 1) {
116+
hash ^= (uint64_t) ptr[0];
117+
hash *= secret;
83118
}
84119

85-
return value;
120+
hash ^= hash >> 32;
121+
return (uint32_t) hash;
86122
}
87123

88124
/**

0 commit comments

Comments
 (0)