diff --git a/lib/std/crypto/benchmark.zig b/lib/std/crypto/benchmark.zig index 696a9c3107..26f5d50979 100644 --- a/lib/std/crypto/benchmark.zig +++ b/lib/std/crypto/benchmark.zig @@ -34,19 +34,24 @@ const hashes = [_]Crypto{ Crypto{ .ty = crypto.hash.Blake3, .name = "blake3" }, }; +const block_size: usize = 8 * 8192; + pub fn benchmarkHash(comptime Hash: anytype, comptime bytes: comptime_int) !u64 { + const blocks_count = bytes / block_size; + var block: [block_size]u8 = undefined; + random.bytes(&block); + var h = Hash.init(.{}); - var block: [Hash.digest_length]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) |_| { + h.update(&block); } - mem.doNotOptimizeAway(&h); + var final: [Hash.digest_length]u8 = undefined; + h.final(&final); + std.mem.doNotOptimizeAway(final); + const end = timer.read(); const elapsed_s = @intToFloat(f64, end - start) / time.ns_per_s; diff --git a/lib/std/crypto/siphash.zig b/lib/std/crypto/siphash.zig index 1303970b22..37d219f868 100644 --- a/lib/std/crypto/siphash.zig +++ b/lib/std/crypto/siphash.zig @@ -47,7 +47,6 @@ fn SipHashStateless(comptime T: type, comptime c_rounds: usize, comptime d_round return struct { const Self = @This(); const block_length = 64; - const digest_length = 64; const key_length = 16; v0: u64, @@ -56,7 +55,7 @@ fn SipHashStateless(comptime T: type, comptime c_rounds: usize, comptime d_round v3: u64, msg_len: u8, - pub fn init(key: *const [key_length]u8) Self { + fn init(key: *const [key_length]u8) Self { const k0 = mem.readIntLittle(u64, key[0..8]); const k1 = mem.readIntLittle(u64, key[8..16]); @@ -75,7 +74,7 @@ fn SipHashStateless(comptime T: type, comptime c_rounds: usize, comptime d_round return d; } - pub fn update(self: *Self, b: []const u8) void { + fn update(self: *Self, b: []const u8) void { std.debug.assert(b.len % 8 == 0); var off: usize = 0; @@ -87,12 +86,7 @@ fn SipHashStateless(comptime T: type, comptime c_rounds: usize, comptime d_round self.msg_len +%= @truncate(u8, b.len); } - pub fn peek(self: Self) [digest_length]u8 { - var copy = self; - return copy.finalResult(); - } - - pub fn final(self: *Self, b: []const u8) T { + fn final(self: *Self, b: []const u8) T { std.debug.assert(b.len < 8); self.msg_len +%= @truncate(u8, b.len); @@ -129,14 +123,8 @@ fn SipHashStateless(comptime T: type, comptime c_rounds: usize, comptime d_round return (@as(u128, b2) << 64) | b1; } - pub fn finalResult(self: *Self) [digest_length]u8 { - var result: [digest_length]u8 = undefined; - self.final(&result); - return result; - } - fn round(self: *Self, b: [8]u8) void { - const m = mem.readIntLittle(u64, b[0..8]); + const m = mem.readIntLittle(u64, &b); self.v3 ^= m; comptime var i: usize = 0; @@ -164,7 +152,7 @@ fn SipHashStateless(comptime T: type, comptime c_rounds: usize, comptime d_round d.v2 = math.rotl(u64, d.v2, @as(u64, 32)); } - pub fn hash(msg: []const u8, key: *const [key_length]u8) T { + fn hash(msg: []const u8, key: *const [key_length]u8) T { const aligned_len = msg.len - (msg.len % 8); var c = Self.init(key); @call(.always_inline, update, .{ &c, msg[0..aligned_len] }); 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,11 +17,22 @@ 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", @@ -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 { - const key_count = bytes / key_size; - var block: [block_size]u8 = undefined; - random.bytes(block[0..]); +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 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 }); } } diff --git a/lib/std/hash/xxhash.zig b/lib/std/hash/xxhash.zig index 88db1dc1bd..3122406488 100644 --- a/lib/std/hash/xxhash.zig +++ b/lib/std/hash/xxhash.zig @@ -126,8 +126,8 @@ pub const XxHash64 = struct { return b +% prime_4; } - pub fn hash(input: []const u8) u64 { - var hasher = XxHash64.init(0); + pub fn hash(seed: u64, input: []const u8) u64 { + var hasher = XxHash64.init(seed); hasher.update(input); return hasher.final(); } @@ -236,8 +236,8 @@ pub const XxHash32 = struct { return acc; } - pub fn hash(input: []const u8) u32 { - var hasher = XxHash32.init(0); + pub fn hash(seed: u32, input: []const u8) u32 { + var hasher = XxHash32.init(seed); hasher.update(input); return hasher.final(); } @@ -246,23 +246,23 @@ pub const XxHash32 = struct { test "xxhash64" { const hash = XxHash64.hash; - try expectEqual(hash(""), 0xef46db3751d8e999); - try expectEqual(hash("a"), 0xd24ec4f1a98c6e5b); - try expectEqual(hash("abc"), 0x44bc2cf5ad770999); - try expectEqual(hash("message digest"), 0x066ed728fceeb3be); - try expectEqual(hash("abcdefghijklmnopqrstuvwxyz"), 0xcfe1f278fa89835c); - try expectEqual(hash("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"), 0xaaa46907d3047814); - try expectEqual(hash("12345678901234567890123456789012345678901234567890123456789012345678901234567890"), 0xe04a477f19ee145d); + try expectEqual(hash(0, ""), 0xef46db3751d8e999); + try expectEqual(hash(0, "a"), 0xd24ec4f1a98c6e5b); + try expectEqual(hash(0, "abc"), 0x44bc2cf5ad770999); + try expectEqual(hash(0, "message digest"), 0x066ed728fceeb3be); + try expectEqual(hash(0, "abcdefghijklmnopqrstuvwxyz"), 0xcfe1f278fa89835c); + try expectEqual(hash(0, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"), 0xaaa46907d3047814); + try expectEqual(hash(0, "12345678901234567890123456789012345678901234567890123456789012345678901234567890"), 0xe04a477f19ee145d); } test "xxhash32" { const hash = XxHash32.hash; - try expectEqual(hash(""), 0x02cc5d05); - try expectEqual(hash("a"), 0x550d7456); - try expectEqual(hash("abc"), 0x32d153ff); - try expectEqual(hash("message digest"), 0x7c948494); - try expectEqual(hash("abcdefghijklmnopqrstuvwxyz"), 0x63a14d5f); - try expectEqual(hash("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"), 0x9c285e64); - try expectEqual(hash("12345678901234567890123456789012345678901234567890123456789012345678901234567890"), 0x9c05f475); + try expectEqual(hash(0, ""), 0x02cc5d05); + try expectEqual(hash(0, "a"), 0x550d7456); + try expectEqual(hash(0, "abc"), 0x32d153ff); + try expectEqual(hash(0, "message digest"), 0x7c948494); + try expectEqual(hash(0, "abcdefghijklmnopqrstuvwxyz"), 0x63a14d5f); + try expectEqual(hash(0, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"), 0x9c285e64); + try expectEqual(hash(0, "12345678901234567890123456789012345678901234567890123456789012345678901234567890"), 0x9c05f475); }