From 4731a6e5d57d5fe6c17c42028aebd9fce3682ddb Mon Sep 17 00:00:00 2001 From: djg Date: Mon, 10 Jan 2022 20:54:45 -0800 Subject: [PATCH] std: hash_map: optimize isFree/isTombstone (#10562) - Add an `Metadata.isFree` helper method. - Implement `Metadata.isTombstone` and `Metadata.isFree` with `@bitCast` then comparing to a constant. I assume `@bitCast`-then-compare is faster than the old method because it only involves one comparison, and doesn't require bitmasking. - Summary of benchmarked changes (`gotta-go-fast`, run locally, compared to master): - 3/4 of the hash map benchmarks used ~10% fewer cycles - The last one (project Euler) shows 4% fewer cycles. --- lib/std/hash_map.zig | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/lib/std/hash_map.zig b/lib/std/hash_map.zig index 851df83f84..15b62e0d40 100644 --- a/lib/std/hash_map.zig +++ b/lib/std/hash_map.zig @@ -750,12 +750,19 @@ pub fn HashMapUnmanaged( fingerprint: FingerPrint = free, used: u1 = 0, + const slot_free = @bitCast(u8, Metadata{ .fingerprint = free }); + const slot_tombstone = @bitCast(u8, Metadata{ .fingerprint = tombstone }); + pub fn isUsed(self: Metadata) bool { return self.used == 1; } pub fn isTombstone(self: Metadata) bool { - return !self.isUsed() and self.fingerprint == tombstone; + return @bitCast(u8, self) == slot_tombstone; + } + + pub fn isFree(self: Metadata) bool { + return @bitCast(u8, self) == slot_free; } pub fn takeFingerprint(hash: Hash) FingerPrint { @@ -1115,7 +1122,7 @@ pub fn HashMapUnmanaged( var idx = @truncate(usize, hash & mask); var metadata = self.metadata.? + idx; - while ((metadata[0].isUsed() or metadata[0].isTombstone()) and limit != 0) { + while (!metadata[0].isFree() and limit != 0) { if (metadata[0].isUsed() and metadata[0].fingerprint == fingerprint) { const test_key = &self.keys()[idx]; // If you get a compile error on this line, it means that your generic eql @@ -1294,7 +1301,7 @@ pub fn HashMapUnmanaged( var first_tombstone_idx: usize = self.capacity(); // invalid index var metadata = self.metadata.? + idx; - while ((metadata[0].isUsed() or metadata[0].isTombstone()) and limit != 0) { + while (!metadata[0].isFree() and limit != 0) { if (metadata[0].isUsed() and metadata[0].fingerprint == fingerprint) { const test_key = &self.keys()[idx]; // If you get a compile error on this line, it means that your generic eql