authorgravatar for sahnvour@pm.meSahnvour <sahnvour@pm.me> 2021-05-12 19:39:36+02:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-05-14 15:15:55-04:00
log2d4d4baa42590803cff8581add325ea3f102ac2a
tree9f14b2d55c76a71fa7b9ec0c565cfe64ed3be103
parent8b4e91e18ce425902bef726ec3731d266867a6f7

std.hash_map: use 7 bits of metadata instead of 6

we only effectively need 1 control bit to represent 2 special states for the metadata (free and tombstone) this should reduce the number of actual element equality tests, but since it's very low already, the impact is negligible

1 files changed, 13 insertions(+), 12 deletions(-)

lib/std/hash_map.zig+13-12
...@@ -304,29 +304,32 @@ pub fn HashMapUnmanaged(...@@ -304,29 +304,32 @@ pub fn HashMapUnmanaged(
304 /// Metadata for a slot. It can be in three states: empty, used or304 /// Metadata for a slot. It can be in three states: empty, used or
305 /// tombstone. Tombstones indicate that an entry was previously used,305 /// tombstone. Tombstones indicate that an entry was previously used,
306 /// they are a simple way to handle removal.306 /// they are a simple way to handle removal.
307 /// To this state, we add 6 bits from the slot's key hash. These are307 /// To this state, we add 7 bits from the slot's key hash. These are
308 /// used as a fast way to disambiguate between entries without308 /// used as a fast way to disambiguate between entries without
309 /// having to use the equality function. If two fingerprints are309 /// having to use the equality function. If two fingerprints are
310 /// different, we know that we don't have to compare the keys at all.310 /// different, we know that we don't have to compare the keys at all.
311 /// The 6 bits are the highest ones from a 64 bit hash. This way, not311 /// The 7 bits are the highest ones from a 64 bit hash. This way, not
312 /// only we use the `log2(capacity)` lowest bits from the hash to determine312 /// only we use the `log2(capacity)` lowest bits from the hash to determine
313 /// a slot index, but we use 6 more bits to quickly resolve collisions313 /// a slot index, but we use 7 more bits to quickly resolve collisions
314 /// when multiple elements with different hashes end up wanting to be in / the same slot.314 /// when multiple elements with different hashes end up wanting to be in the same slot.
315 /// Not using the equality function means we don't have to read into315 /// Not using the equality function means we don't have to read into
316 /// the entries array, avoiding a likely cache miss.316 /// the entries array, likely avoiding a cache miss and a potentially
317 /// costly function call.
317 const Metadata = packed struct {318 const Metadata = packed struct {
318 const FingerPrint = u6;319 const FingerPrint = u7;
319320
321 const free: FingerPrint = 0;
322 const tombstone: FingerPrint = 1;
323
324 fingerprint: FingerPrint = free,
320 used: u1 = 0,325 used: u1 = 0,
321 tombstone: u1 = 0,
322 fingerprint: FingerPrint = 0,
323326
324 pub fn isUsed(self: Metadata) bool {327 pub fn isUsed(self: Metadata) bool {
325 return self.used == 1;328 return self.used == 1;
326 }329 }
327330
328 pub fn isTombstone(self: Metadata) bool {331 pub fn isTombstone(self: Metadata) bool {
329 return self.tombstone == 1;332 return !self.isUsed() and self.fingerprint == tombstone;
330 }333 }
331334
332 pub fn takeFingerprint(hash: Hash) FingerPrint {335 pub fn takeFingerprint(hash: Hash) FingerPrint {
...@@ -337,14 +340,12 @@ pub fn HashMapUnmanaged(...@@ -337,14 +340,12 @@ pub fn HashMapUnmanaged(
337340
338 pub fn fill(self: *Metadata, fp: FingerPrint) void {341 pub fn fill(self: *Metadata, fp: FingerPrint) void {
339 self.used = 1;342 self.used = 1;
340 self.tombstone = 0;
341 self.fingerprint = fp;343 self.fingerprint = fp;
342 }344 }
343345
344 pub fn remove(self: *Metadata) void {346 pub fn remove(self: *Metadata) void {
345 self.used = 0;347 self.used = 0;
346 self.tombstone = 1;348 self.fingerprint = tombstone;
347 self.fingerprint = 0;
348 }349 }
349 };350 };
350351