diff options
| author | Ryan Liptak <squeek502@hotmail.com> | 2019-06-05 23:26:48 -0700 |
|---|---|---|
| committer | Ryan Liptak <squeek502@hotmail.com> | 2019-06-05 23:26:48 -0700 |
| commit | 656ac43735094f7cc6b4616cd95e0d427b0174a6 (patch) | |
| tree | a38992b496f6bb148f3010e5f1525d5a3d402d79 /std | |
| parent | a0d66fa1e6e8b9620e4990a33c32b0db11469aec (diff) | |
| download | zig-656ac43735094f7cc6b4616cd95e0d427b0174a6.tar.gz zig-656ac43735094f7cc6b4616cd95e0d427b0174a6.zip | |
std.HashMap: optimize indexing by avoiding modulo operator
x % y can be optimized if y is a power of two by doing x & (y-1) instead. HashMap already enforces power of two capacity, so we can take advantage of this optimization.
Diffstat (limited to 'std')
| -rw-r--r-- | std/hash_map.zig | 18 |
1 files changed, 13 insertions, 5 deletions
diff --git a/std/hash_map.zig b/std/hash_map.zig index 8cbff7be9d..af58ae076a 100644 --- a/std/hash_map.zig +++ b/std/hash_map.zig @@ -155,6 +155,8 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3 /// capacity is greater than the current capacity. /// New capacity must be a power of two. fn ensureCapacityExact(self: *Self, new_capacity: usize) !void { + // capacity must always be a power of two to allow for modulo + // optimization in the constrainIndex fn const is_power_of_two = new_capacity & (new_capacity - 1) == 0; assert(is_power_of_two); @@ -209,7 +211,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3 { var roll_over: usize = 0; while (roll_over <= hm.max_distance_from_start_index) : (roll_over += 1) { - const index = (start_index + roll_over) % hm.entries.len; + const index = hm.constrainIndex(start_index + roll_over); var entry = &hm.entries[index]; if (!entry.used) return null; @@ -218,7 +220,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3 const removed_kv = entry.kv; while (roll_over < hm.entries.len) : (roll_over += 1) { - const next_index = (start_index + roll_over + 1) % hm.entries.len; + const next_index = hm.constrainIndex(start_index + roll_over + 1); const next_entry = &hm.entries[next_index]; if (!next_entry.used or next_entry.distance_from_start_index == 0) { entry.used = false; @@ -301,7 +303,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3 roll_over += 1; distance_from_start_index += 1; }) { - const index = (start_index + roll_over) % self.entries.len; + const index = self.constrainIndex(start_index + roll_over); const entry = &self.entries[index]; if (entry.used and !eql(entry.kv.key, key)) { @@ -358,7 +360,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3 { var roll_over: usize = 0; while (roll_over <= hm.max_distance_from_start_index) : (roll_over += 1) { - const index = (start_index + roll_over) % hm.entries.len; + const index = hm.constrainIndex(start_index + roll_over); const entry = &hm.entries[index]; if (!entry.used) return null; @@ -369,7 +371,13 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3 } fn keyToIndex(hm: Self, key: K) usize { - return usize(hash(key)) % hm.entries.len; + return hm.constrainIndex(usize(hash(key))); + } + + fn constrainIndex(hm: Self, i: usize) usize { + // this is an optimization for modulo of power of two integers; + // it requires hm.entries.len to always be a power of two + return i & (hm.entries.len - 1); } }; } |
