diff options
| author | Frank Denis <124872+jedisct1@users.noreply.github.com> | 2023-06-02 20:08:28 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-06-02 20:08:28 +0200 |
| commit | 879f0b9cee9b409160edf10d8b52f73be2bddb4f (patch) | |
| tree | 4d875ad83f648069cc342aa680eb81d49b9b98f2 /lib/std/hash/benchmark.zig | |
| parent | 3faf376b081caa47a98a172fe7c7f63f82b150e4 (diff) | |
| download | zig-879f0b9cee9b409160edf10d8b52f73be2bddb4f.tar.gz zig-879f0b9cee9b409160edf10d8b52f73be2bddb4f.zip | |
Fix std.hash benchmarks (#15917)
Diffstat (limited to 'lib/std/hash/benchmark.zig')
| -rw-r--r-- | lib/std/hash/benchmark.zig | 78 |
1 files changed, 59 insertions, 19 deletions
diff --git a/lib/std/hash/benchmark.zig b/lib/std/hash/benchmark.zig index a7c0410b53..cf2f18d22f 100644 --- a/lib/std/hash/benchmark.zig +++ b/lib/std/hash/benchmark.zig @@ -17,12 +17,23 @@ const Hash = struct { ty: type, name: []const u8, has_iterative_api: bool = true, + has_crypto_api: bool = false, init_u8s: ?[]const u8 = null, init_u64: ?u64 = null, }; const hashes = [_]Hash{ Hash{ + .ty = hash.XxHash64, + .name = "xxhash64", + .init_u64 = 0, + }, + Hash{ + .ty = hash.XxHash32, + .name = "xxhash32", + .init_u64 = 0, + }, + Hash{ .ty = hash.Wyhash, .name = "wyhash", .init_u64 = 0, @@ -68,6 +79,18 @@ const hashes = [_]Hash{ .name = "murmur3-32", .has_iterative_api = false, }, + Hash{ + .ty = hash.SipHash64(1, 3), + .name = "siphash64", + .has_crypto_api = true, + .init_u8s = &[_]u8{0} ** 16, + }, + Hash{ + .ty = hash.SipHash128(1, 3), + .name = "siphash128", + .has_crypto_api = true, + .init_u8s = &[_]u8{0} ** 16, + }, }; const Result = struct { @@ -76,11 +99,17 @@ const Result = struct { }; const block_size: usize = 8 * 8192; +const alignment: usize = 64; + +pub fn benchmarkHash(comptime H: anytype, bytes: usize, allocator: std.mem.Allocator) !Result { + const blocks_count = bytes / block_size; + var blocks = try allocator.alloc(u8, block_size + alignment * (blocks_count - 1)); + defer allocator.free(blocks); + random.bytes(blocks); -pub fn benchmarkHash(comptime H: anytype, bytes: usize) !Result { var h = blk: { if (H.init_u8s) |init| { - break :blk H.ty.init(init); + break :blk H.ty.init(init[0..H.ty.key_length]); } if (H.init_u64) |init| { break :blk H.ty.init(init); @@ -88,53 +117,60 @@ pub fn benchmarkHash(comptime H: anytype, bytes: usize) !Result { break :blk H.ty.init(); }; - var block: [block_size]u8 = undefined; - random.bytes(block[0..]); - - var offset: usize = 0; var timer = try Timer.start(); const start = timer.lap(); - while (offset < bytes) : (offset += block.len) { - h.update(block[0..]); + for (0..blocks_count) |i| { + h.update(blocks[i * alignment ..][0..block_size]); } + const final = if (H.has_crypto_api) @truncate(u64, h.finalInt()) else h.final(); + std.mem.doNotOptimizeAway(final); + const end = timer.read(); const elapsed_s = @intToFloat(f64, end - start) / time.ns_per_s; const throughput = @floatToInt(u64, @intToFloat(f64, bytes) / elapsed_s); return Result{ - .hash = h.final(), + .hash = final, .throughput = throughput, }; } -pub fn benchmarkHashSmallKeys(comptime H: anytype, key_size: usize, bytes: usize) !Result { +pub fn benchmarkHashSmallKeys(comptime H: anytype, key_size: usize, bytes: usize, allocator: std.mem.Allocator) !Result { + var blocks = try allocator.alloc(u8, bytes); + defer allocator.free(blocks); + random.bytes(blocks); + const key_count = bytes / key_size; - var block: [block_size]u8 = undefined; - random.bytes(block[0..]); - var i: usize = 0; var timer = try Timer.start(); const start = timer.lap(); var sum: u64 = 0; - while (i < key_count) : (i += 1) { - const small_key = block[0..key_size]; - sum +%= blk: { + for (0..key_count) |i| { + const small_key = blocks[i * key_size ..][0..key_size]; + const final = blk: { if (H.init_u8s) |init| { - break :blk H.ty.hash(init, small_key); + if (H.has_crypto_api) { + break :blk @truncate(u64, H.ty.toInt(small_key, init[0..H.ty.key_length])); + } else { + break :blk H.ty.hash(init, small_key); + } } if (H.init_u64) |init| { break :blk H.ty.hash(init, small_key); } break :blk H.ty.hash(small_key); }; + sum +%= final; } const end = timer.read(); const elapsed_s = @intToFloat(f64, end - start) / time.ns_per_s; const throughput = @floatToInt(u64, @intToFloat(f64, bytes) / elapsed_s); + std.mem.doNotOptimizeAway(sum); + return Result{ .hash = sum, .throughput = throughput, @@ -227,6 +263,10 @@ pub fn main() !void { } } + var gpa = std.heap.GeneralPurposeAllocator(.{}){}; + defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak"); + const allocator = gpa.allocator(); + inline for (hashes) |H| { if (filter == null or std.mem.indexOf(u8, H.name, filter.?) != null) { if (!test_iterative_only or H.has_iterative_api) { @@ -236,13 +276,13 @@ pub fn main() !void { // This allows easier comparison between different implementations. if (H.has_iterative_api) { prng.seed(seed); - const result = try benchmarkHash(H, count); + const result = try benchmarkHash(H, count, allocator); try stdout.print(" iterative: {:5} MiB/s [{x:0<16}]\n", .{ result.throughput / (1 * MiB), result.hash }); } if (!test_iterative_only) { prng.seed(seed); - const result_small = try benchmarkHashSmallKeys(H, key_size, count); + const result_small = try benchmarkHashSmallKeys(H, key_size, count, allocator); try stdout.print(" small keys: {:5} MiB/s [{x:0<16}]\n", .{ result_small.throughput / (1 * MiB), result_small.hash }); } } |
