authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-06-06 12:26:27-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2019-06-06 12:26:27-04:00
loga682b0acb6e74348117eddffe6b966e7720efa61
tree315dcc330cb59721b65b777d4a51a1b4aab070ab
parentddfab40e30e04e1874bff95e5cae54eaaca5542f
parent8f4229a61c311c0fc990c34b280ec91010595bc9
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #2630 from squeek502/hashmap-pow2

std.HashMap: optimize by taking advantage of power of two capacity

1 files changed, 16 insertions(+), 8 deletions(-)

std/hash_map.zig+16-8
...@@ -139,9 +139,9 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3...@@ -139,9 +139,9 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3
139 // ensure that the hash map will be at most 60% full if139 // ensure that the hash map will be at most 60% full if
140 // expected_count items are put into it140 // expected_count items are put into it
141 var optimized_capacity = expected_count * 5 / 3;141 var optimized_capacity = expected_count * 5 / 3;
142 // round capacity to the next power of two142 // an overflow here would mean the amount of memory required would not
143 const pow = math.log2_int_ceil(usize, optimized_capacity);143 // be representable in the address space
144 return math.pow(usize, 2, pow);144 return math.ceilPowerOfTwo(usize, optimized_capacity) catch unreachable;
145 }145 }
146146
147 /// Increases capacity so that the hash map will be at most147 /// Increases capacity so that the hash map will be at most
...@@ -155,6 +155,8 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3...@@ -155,6 +155,8 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3
155 /// capacity is greater than the current capacity.155 /// capacity is greater than the current capacity.
156 /// New capacity must be a power of two.156 /// New capacity must be a power of two.
157 fn ensureCapacityExact(self: *Self, new_capacity: usize) !void {157 fn ensureCapacityExact(self: *Self, new_capacity: usize) !void {
158 // capacity must always be a power of two to allow for modulo
159 // optimization in the constrainIndex fn
158 const is_power_of_two = new_capacity & (new_capacity - 1) == 0;160 const is_power_of_two = new_capacity & (new_capacity - 1) == 0;
159 assert(is_power_of_two);161 assert(is_power_of_two);
160162
...@@ -209,7 +211,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3...@@ -209,7 +211,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3
209 {211 {
210 var roll_over: usize = 0;212 var roll_over: usize = 0;
211 while (roll_over <= hm.max_distance_from_start_index) : (roll_over += 1) {213 while (roll_over <= hm.max_distance_from_start_index) : (roll_over += 1) {
212 const index = (start_index + roll_over) % hm.entries.len;214 const index = hm.constrainIndex(start_index + roll_over);
213 var entry = &hm.entries[index];215 var entry = &hm.entries[index];
214216
215 if (!entry.used) return null;217 if (!entry.used) return null;
...@@ -218,7 +220,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3...@@ -218,7 +220,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3
218220
219 const removed_kv = entry.kv;221 const removed_kv = entry.kv;
220 while (roll_over < hm.entries.len) : (roll_over += 1) {222 while (roll_over < hm.entries.len) : (roll_over += 1) {
221 const next_index = (start_index + roll_over + 1) % hm.entries.len;223 const next_index = hm.constrainIndex(start_index + roll_over + 1);
222 const next_entry = &hm.entries[next_index];224 const next_entry = &hm.entries[next_index];
223 if (!next_entry.used or next_entry.distance_from_start_index == 0) {225 if (!next_entry.used or next_entry.distance_from_start_index == 0) {
224 entry.used = false;226 entry.used = false;
...@@ -301,7 +303,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3...@@ -301,7 +303,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3
301 roll_over += 1;303 roll_over += 1;
302 distance_from_start_index += 1;304 distance_from_start_index += 1;
303 }) {305 }) {
304 const index = (start_index + roll_over) % self.entries.len;306 const index = self.constrainIndex(start_index + roll_over);
305 const entry = &self.entries[index];307 const entry = &self.entries[index];
306308
307 if (entry.used and !eql(entry.kv.key, key)) {309 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...@@ -358,7 +360,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3
358 {360 {
359 var roll_over: usize = 0;361 var roll_over: usize = 0;
360 while (roll_over <= hm.max_distance_from_start_index) : (roll_over += 1) {362 while (roll_over <= hm.max_distance_from_start_index) : (roll_over += 1) {
361 const index = (start_index + roll_over) % hm.entries.len;363 const index = hm.constrainIndex(start_index + roll_over);
362 const entry = &hm.entries[index];364 const entry = &hm.entries[index];
363365
364 if (!entry.used) return null;366 if (!entry.used) return null;
...@@ -369,7 +371,13 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3...@@ -369,7 +371,13 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3
369 }371 }
370372
371 fn keyToIndex(hm: Self, key: K) usize {373 fn keyToIndex(hm: Self, key: K) usize {
372 return usize(hash(key)) % hm.entries.len;374 return hm.constrainIndex(usize(hash(key)));
375 }
376
377 fn constrainIndex(hm: Self, i: usize) usize {
378 // this is an optimization for modulo of power of two integers;
379 // it requires hm.entries.len to always be a power of two
380 return i & (hm.entries.len - 1);
373 }381 }
374 };382 };
375}383}