Commit 2d4d4baa42

Sahnvour <sahnvour@pm.me>
2021-05-12 19:39:36
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 parent 8b4e91e
Changed files (1)
lib
lib/std/hash_map.zig
@@ -304,29 +304,32 @@ pub fn HashMapUnmanaged(
         /// Metadata for a slot. It can be in three states: empty, used or
         /// tombstone. Tombstones indicate that an entry was previously used,
         /// they are a simple way to handle removal.
-        /// To this state, we add 6 bits from the slot's key hash. These are
+        /// To this state, we add 7 bits from the slot's key hash. These are
         /// used as a fast way to disambiguate between entries without
         /// having to use the equality function. If two fingerprints are
         /// different, we know that we don't have to compare the keys at all.
-        /// The 6 bits are the highest ones from a 64 bit hash. This way, not
+        /// The 7 bits are the highest ones from a 64 bit hash. This way, not
         /// only we use the `log2(capacity)` lowest bits from the hash to determine
-        /// a slot index, but we use 6 more bits to quickly resolve collisions
-        /// when multiple elements with different hashes end up wanting to be in / the same slot.
+        /// a slot index, but we use 7 more bits to quickly resolve collisions
+        /// when multiple elements with different hashes end up wanting to be in the same slot.
         /// Not using the equality function means we don't have to read into
-        /// the entries array, avoiding a likely cache miss.
+        /// the entries array, likely avoiding a cache miss and a potentially
+        /// costly function call.
         const Metadata = packed struct {
-            const FingerPrint = u6;
+            const FingerPrint = u7;
 
+            const free: FingerPrint = 0;
+            const tombstone: FingerPrint = 1;
+
+            fingerprint: FingerPrint = free,
             used: u1 = 0,
-            tombstone: u1 = 0,
-            fingerprint: FingerPrint = 0,
 
             pub fn isUsed(self: Metadata) bool {
                 return self.used == 1;
             }
 
             pub fn isTombstone(self: Metadata) bool {
-                return self.tombstone == 1;
+                return !self.isUsed() and self.fingerprint == tombstone;
             }
 
             pub fn takeFingerprint(hash: Hash) FingerPrint {
@@ -337,14 +340,12 @@ pub fn HashMapUnmanaged(
 
             pub fn fill(self: *Metadata, fp: FingerPrint) void {
                 self.used = 1;
-                self.tombstone = 0;
                 self.fingerprint = fp;
             }
 
             pub fn remove(self: *Metadata) void {
                 self.used = 0;
-                self.tombstone = 1;
-                self.fingerprint = 0;
+                self.fingerprint = tombstone;
             }
         };