authorgravatar for sentientwaffle@gmail.comdjg <sentientwaffle@gmail.com> 2022-01-10 20:54:45-08:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-01-10 23:54:45-05:00
log4731a6e5d57d5fe6c17c42028aebd9fce3682ddb
tree31ae73c4f9a63387c4ab2079f8f74b27015e95da
parentada8e171373017cfd2a92267797c473c02aba130
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

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.

1 files changed, 10 insertions(+), 3 deletions(-)

lib/std/hash_map.zig+10-3
...@@ -750,12 +750,19 @@ pub fn HashMapUnmanaged(...@@ -750,12 +750,19 @@ pub fn HashMapUnmanaged(
750 fingerprint: FingerPrint = free,750 fingerprint: FingerPrint = free,
751 used: u1 = 0,751 used: u1 = 0,
752752
753 const slot_free = @bitCast(u8, Metadata{ .fingerprint = free });
754 const slot_tombstone = @bitCast(u8, Metadata{ .fingerprint = tombstone });
755
753 pub fn isUsed(self: Metadata) bool {756 pub fn isUsed(self: Metadata) bool {
754 return self.used == 1;757 return self.used == 1;
755 }758 }
756759
757 pub fn isTombstone(self: Metadata) bool {760 pub fn isTombstone(self: Metadata) bool {
758 return !self.isUsed() and self.fingerprint == tombstone;761 return @bitCast(u8, self) == slot_tombstone;
762 }
763
764 pub fn isFree(self: Metadata) bool {
765 return @bitCast(u8, self) == slot_free;
759 }766 }
760767
761 pub fn takeFingerprint(hash: Hash) FingerPrint {768 pub fn takeFingerprint(hash: Hash) FingerPrint {
...@@ -1115,7 +1122,7 @@ pub fn HashMapUnmanaged(...@@ -1115,7 +1122,7 @@ pub fn HashMapUnmanaged(
1115 var idx = @truncate(usize, hash & mask);1122 var idx = @truncate(usize, hash & mask);
11161123
1117 var metadata = self.metadata.? + idx;1124 var metadata = self.metadata.? + idx;
1118 while ((metadata[0].isUsed() or metadata[0].isTombstone()) and limit != 0) {1125 while (!metadata[0].isFree() and limit != 0) {
1119 if (metadata[0].isUsed() and metadata[0].fingerprint == fingerprint) {1126 if (metadata[0].isUsed() and metadata[0].fingerprint == fingerprint) {
1120 const test_key = &self.keys()[idx];1127 const test_key = &self.keys()[idx];
1121 // If you get a compile error on this line, it means that your generic eql1128 // If you get a compile error on this line, it means that your generic eql
...@@ -1294,7 +1301,7 @@ pub fn HashMapUnmanaged(...@@ -1294,7 +1301,7 @@ pub fn HashMapUnmanaged(
12941301
1295 var first_tombstone_idx: usize = self.capacity(); // invalid index1302 var first_tombstone_idx: usize = self.capacity(); // invalid index
1296 var metadata = self.metadata.? + idx;1303 var metadata = self.metadata.? + idx;
1297 while ((metadata[0].isUsed() or metadata[0].isTombstone()) and limit != 0) {1304 while (!metadata[0].isFree() and limit != 0) {
1298 if (metadata[0].isUsed() and metadata[0].fingerprint == fingerprint) {1305 if (metadata[0].isUsed() and metadata[0].fingerprint == fingerprint) {
1299 const test_key = &self.keys()[idx];1306 const test_key = &self.keys()[idx];
1300 // If you get a compile error on this line, it means that your generic eql1307 // If you get a compile error on this line, it means that your generic eql