| author | |
| committer | |
| log | 879f0b9cee9b409160edf10d8b52f73be2bddb4f |
| tree | 4d875ad83f648069cc342aa680eb81d49b9b98f2 |
| parent | 3faf376b081caa47a98a172fe7c7f63f82b150e4 |
| signature |
4 files changed, 94 insertions(+), 61 deletions(-)
lib/std/crypto/benchmark.zig+12-7| ... | @@ -34,19 +34,24 @@ const hashes = [_]Crypto{ | ... | @@ -34,19 +34,24 @@ const hashes = [_]Crypto{ |
| 34 | Crypto{ .ty = crypto.hash.Blake3, .name = "blake3" }, | 34 | Crypto{ .ty = crypto.hash.Blake3, .name = "blake3" }, |
| 35 | }; | 35 | }; |
| 36 | 36 | ||
| 37 | const block_size: usize = 8 * 8192; | ||
| 38 | |||
| 37 | pub fn benchmarkHash(comptime Hash: anytype, comptime bytes: comptime_int) !u64 { | 39 | pub fn benchmarkHash(comptime Hash: anytype, comptime bytes: comptime_int) !u64 { |
| 38 | var h = Hash.init(.{}); | 40 | const blocks_count = bytes / block_size; |
| 41 | var block: [block_size]u8 = undefined; | ||
| 42 | random.bytes(&block); | ||
| 39 | 43 | ||
| 40 | var block: [Hash.digest_length]u8 = undefined; | 44 | var h = Hash.init(.{}); |
| 41 | random.bytes(block[0..]); | ||
| 42 | 45 | ||
| 43 | var offset: usize = 0; | ||
| 44 | var timer = try Timer.start(); | 46 | var timer = try Timer.start(); |
| 45 | const start = timer.lap(); | 47 | const start = timer.lap(); |
| 46 | while (offset < bytes) : (offset += block.len) { | 48 | for (0..blocks_count) |_| { |
| 47 | h.update(block[0..]); | 49 | h.update(&block); |
| 48 | } | 50 | } |
| 49 | mem.doNotOptimizeAway(&h); | 51 | var final: [Hash.digest_length]u8 = undefined; |
| 52 | h.final(&final); | ||
| 53 | std.mem.doNotOptimizeAway(final); | ||
| 54 | |||
| 50 | const end = timer.read(); | 55 | const end = timer.read(); |
| 51 | 56 | ||
| 52 | const elapsed_s = @intToFloat(f64, end - start) / time.ns_per_s; | 57 | const elapsed_s = @intToFloat(f64, end - start) / time.ns_per_s; |
lib/std/crypto/siphash.zig+5-17| ... | @@ -47,7 +47,6 @@ fn SipHashStateless(comptime T: type, comptime c_rounds: usize, comptime d_round | ... | @@ -47,7 +47,6 @@ fn SipHashStateless(comptime T: type, comptime c_rounds: usize, comptime d_round |
| 47 | return struct { | 47 | return struct { |
| 48 | const Self = @This(); | 48 | const Self = @This(); |
| 49 | const block_length = 64; | 49 | const block_length = 64; |
| 50 | const digest_length = 64; | ||
| 51 | const key_length = 16; | 50 | const key_length = 16; |
| 52 | 51 | ||
| 53 | v0: u64, | 52 | v0: u64, |
| ... | @@ -56,7 +55,7 @@ fn SipHashStateless(comptime T: type, comptime c_rounds: usize, comptime d_round | ... | @@ -56,7 +55,7 @@ fn SipHashStateless(comptime T: type, comptime c_rounds: usize, comptime d_round |
| 56 | v3: u64, | 55 | v3: u64, |
| 57 | msg_len: u8, | 56 | msg_len: u8, |
| 58 | 57 | ||
| 59 | pub fn init(key: *const [key_length]u8) Self { | 58 | fn init(key: *const [key_length]u8) Self { |
| 60 | const k0 = mem.readIntLittle(u64, key[0..8]); | 59 | const k0 = mem.readIntLittle(u64, key[0..8]); |
| 61 | const k1 = mem.readIntLittle(u64, key[8..16]); | 60 | const k1 = mem.readIntLittle(u64, key[8..16]); |
| 62 | 61 | ||
| ... | @@ -75,7 +74,7 @@ fn SipHashStateless(comptime T: type, comptime c_rounds: usize, comptime d_round | ... | @@ -75,7 +74,7 @@ fn SipHashStateless(comptime T: type, comptime c_rounds: usize, comptime d_round |
| 75 | return d; | 74 | return d; |
| 76 | } | 75 | } |
| 77 | 76 | ||
| 78 | pub fn update(self: *Self, b: []const u8) void { | 77 | fn update(self: *Self, b: []const u8) void { |
| 79 | std.debug.assert(b.len % 8 == 0); | 78 | std.debug.assert(b.len % 8 == 0); |
| 80 | 79 | ||
| 81 | var off: usize = 0; | 80 | var off: usize = 0; |
| ... | @@ -87,12 +86,7 @@ fn SipHashStateless(comptime T: type, comptime c_rounds: usize, comptime d_round | ... | @@ -87,12 +86,7 @@ fn SipHashStateless(comptime T: type, comptime c_rounds: usize, comptime d_round |
| 87 | self.msg_len +%= @truncate(u8, b.len); | 86 | self.msg_len +%= @truncate(u8, b.len); |
| 88 | } | 87 | } |
| 89 | 88 | ||
| 90 | pub fn peek(self: Self) [digest_length]u8 { | 89 | fn final(self: *Self, b: []const u8) T { |
| 91 | var copy = self; | ||
| 92 | return copy.finalResult(); | ||
| 93 | } | ||
| 94 | |||
| 95 | pub fn final(self: *Self, b: []const u8) T { | ||
| 96 | std.debug.assert(b.len < 8); | 90 | std.debug.assert(b.len < 8); |
| 97 | 91 | ||
| 98 | self.msg_len +%= @truncate(u8, b.len); | 92 | self.msg_len +%= @truncate(u8, b.len); |
| ... | @@ -129,14 +123,8 @@ fn SipHashStateless(comptime T: type, comptime c_rounds: usize, comptime d_round | ... | @@ -129,14 +123,8 @@ fn SipHashStateless(comptime T: type, comptime c_rounds: usize, comptime d_round |
| 129 | return (@as(u128, b2) << 64) | b1; | 123 | return (@as(u128, b2) << 64) | b1; |
| 130 | } | 124 | } |
| 131 | 125 | ||
| 132 | pub fn finalResult(self: *Self) [digest_length]u8 { | ||
| 133 | var result: [digest_length]u8 = undefined; | ||
| 134 | self.final(&result); | ||
| 135 | return result; | ||
| 136 | } | ||
| 137 | |||
| 138 | fn round(self: *Self, b: [8]u8) void { | 126 | fn round(self: *Self, b: [8]u8) void { |
| 139 | const m = mem.readIntLittle(u64, b[0..8]); | 127 | const m = mem.readIntLittle(u64, &b); |
| 140 | self.v3 ^= m; | 128 | self.v3 ^= m; |
| 141 | 129 | ||
| 142 | comptime var i: usize = 0; | 130 | comptime var i: usize = 0; |
| ... | @@ -164,7 +152,7 @@ fn SipHashStateless(comptime T: type, comptime c_rounds: usize, comptime d_round | ... | @@ -164,7 +152,7 @@ fn SipHashStateless(comptime T: type, comptime c_rounds: usize, comptime d_round |
| 164 | d.v2 = math.rotl(u64, d.v2, @as(u64, 32)); | 152 | d.v2 = math.rotl(u64, d.v2, @as(u64, 32)); |
| 165 | } | 153 | } |
| 166 | 154 | ||
| 167 | pub fn hash(msg: []const u8, key: *const [key_length]u8) T { | 155 | fn hash(msg: []const u8, key: *const [key_length]u8) T { |
| 168 | const aligned_len = msg.len - (msg.len % 8); | 156 | const aligned_len = msg.len - (msg.len % 8); |
| 169 | var c = Self.init(key); | 157 | var c = Self.init(key); |
| 170 | @call(.always_inline, update, .{ &c, msg[0..aligned_len] }); | 158 | @call(.always_inline, update, .{ &c, msg[0..aligned_len] }); |
lib/std/hash/benchmark.zig+59-19| ... | @@ -17,11 +17,22 @@ const Hash = struct { | ... | @@ -17,11 +17,22 @@ const Hash = struct { |
| 17 | ty: type, | 17 | ty: type, |
| 18 | name: []const u8, | 18 | name: []const u8, |
| 19 | has_iterative_api: bool = true, | 19 | has_iterative_api: bool = true, |
| 20 | has_crypto_api: bool = false, | ||
| 20 | init_u8s: ?[]const u8 = null, | 21 | init_u8s: ?[]const u8 = null, |
| 21 | init_u64: ?u64 = null, | 22 | init_u64: ?u64 = null, |
| 22 | }; | 23 | }; |
| 23 | 24 | ||
| 24 | const hashes = [_]Hash{ | 25 | const hashes = [_]Hash{ |
| 26 | Hash{ | ||
| 27 | .ty = hash.XxHash64, | ||
| 28 | .name = "xxhash64", | ||
| 29 | .init_u64 = 0, | ||
| 30 | }, | ||
| 31 | Hash{ | ||
| 32 | .ty = hash.XxHash32, | ||
| 33 | .name = "xxhash32", | ||
| 34 | .init_u64 = 0, | ||
| 35 | }, | ||
| 25 | Hash{ | 36 | Hash{ |
| 26 | .ty = hash.Wyhash, | 37 | .ty = hash.Wyhash, |
| 27 | .name = "wyhash", | 38 | .name = "wyhash", |
| ... | @@ -68,6 +79,18 @@ const hashes = [_]Hash{ | ... | @@ -68,6 +79,18 @@ const hashes = [_]Hash{ |
| 68 | .name = "murmur3-32", | 79 | .name = "murmur3-32", |
| 69 | .has_iterative_api = false, | 80 | .has_iterative_api = false, |
| 70 | }, | 81 | }, |
| 82 | Hash{ | ||
| 83 | .ty = hash.SipHash64(1, 3), | ||
| 84 | .name = "siphash64", | ||
| 85 | .has_crypto_api = true, | ||
| 86 | .init_u8s = &[_]u8{0} ** 16, | ||
| 87 | }, | ||
| 88 | Hash{ | ||
| 89 | .ty = hash.SipHash128(1, 3), | ||
| 90 | .name = "siphash128", | ||
| 91 | .has_crypto_api = true, | ||
| 92 | .init_u8s = &[_]u8{0} ** 16, | ||
| 93 | }, | ||
| 71 | }; | 94 | }; |
| 72 | 95 | ||
| 73 | const Result = struct { | 96 | const Result = struct { |
| ... | @@ -76,11 +99,17 @@ const Result = struct { | ... | @@ -76,11 +99,17 @@ const Result = struct { |
| 76 | }; | 99 | }; |
| 77 | 100 | ||
| 78 | const block_size: usize = 8 * 8192; | 101 | const block_size: usize = 8 * 8192; |
| 102 | const alignment: usize = 64; | ||
| 103 | |||
| 104 | pub fn benchmarkHash(comptime H: anytype, bytes: usize, allocator: std.mem.Allocator) !Result { | ||
| 105 | const blocks_count = bytes / block_size; | ||
| 106 | var blocks = try allocator.alloc(u8, block_size + alignment * (blocks_count - 1)); | ||
| 107 | defer allocator.free(blocks); | ||
| 108 | random.bytes(blocks); | ||
| 79 | 109 | ||
| 80 | pub fn benchmarkHash(comptime H: anytype, bytes: usize) !Result { | ||
| 81 | var h = blk: { | 110 | var h = blk: { |
| 82 | if (H.init_u8s) |init| { | 111 | if (H.init_u8s) |init| { |
| 83 | break :blk H.ty.init(init); | 112 | break :blk H.ty.init(init[0..H.ty.key_length]); |
| 84 | } | 113 | } |
| 85 | if (H.init_u64) |init| { | 114 | if (H.init_u64) |init| { |
| 86 | break :blk H.ty.init(init); | 115 | break :blk H.ty.init(init); |
| ... | @@ -88,53 +117,60 @@ pub fn benchmarkHash(comptime H: anytype, bytes: usize) !Result { | ... | @@ -88,53 +117,60 @@ pub fn benchmarkHash(comptime H: anytype, bytes: usize) !Result { |
| 88 | break :blk H.ty.init(); | 117 | break :blk H.ty.init(); |
| 89 | }; | 118 | }; |
| 90 | 119 | ||
| 91 | var block: [block_size]u8 = undefined; | ||
| 92 | random.bytes(block[0..]); | ||
| 93 | |||
| 94 | var offset: usize = 0; | ||
| 95 | var timer = try Timer.start(); | 120 | var timer = try Timer.start(); |
| 96 | const start = timer.lap(); | 121 | const start = timer.lap(); |
| 97 | while (offset < bytes) : (offset += block.len) { | 122 | for (0..blocks_count) |i| { |
| 98 | h.update(block[0..]); | 123 | h.update(blocks[i * alignment ..][0..block_size]); |
| 99 | } | 124 | } |
| 125 | const final = if (H.has_crypto_api) @truncate(u64, h.finalInt()) else h.final(); | ||
| 126 | std.mem.doNotOptimizeAway(final); | ||
| 127 | |||
| 100 | const end = timer.read(); | 128 | const end = timer.read(); |
| 101 | 129 | ||
| 102 | const elapsed_s = @intToFloat(f64, end - start) / time.ns_per_s; | 130 | const elapsed_s = @intToFloat(f64, end - start) / time.ns_per_s; |
| 103 | const throughput = @floatToInt(u64, @intToFloat(f64, bytes) / elapsed_s); | 131 | const throughput = @floatToInt(u64, @intToFloat(f64, bytes) / elapsed_s); |
| 104 | 132 | ||
| 105 | return Result{ | 133 | return Result{ |
| 106 | .hash = h.final(), | 134 | .hash = final, |
| 107 | .throughput = throughput, | 135 | .throughput = throughput, |
| 108 | }; | 136 | }; |
| 109 | } | 137 | } |
| 110 | 138 | ||
| 111 | pub fn benchmarkHashSmallKeys(comptime H: anytype, key_size: usize, bytes: usize) !Result { | 139 | pub fn benchmarkHashSmallKeys(comptime H: anytype, key_size: usize, bytes: usize, allocator: std.mem.Allocator) !Result { |
| 140 | var blocks = try allocator.alloc(u8, bytes); | ||
| 141 | defer allocator.free(blocks); | ||
| 142 | random.bytes(blocks); | ||
| 143 | |||
| 112 | const key_count = bytes / key_size; | 144 | const key_count = bytes / key_size; |
| 113 | var block: [block_size]u8 = undefined; | ||
| 114 | random.bytes(block[0..]); | ||
| 115 | 145 | ||
| 116 | var i: usize = 0; | ||
| 117 | var timer = try Timer.start(); | 146 | var timer = try Timer.start(); |
| 118 | const start = timer.lap(); | 147 | const start = timer.lap(); |
| 119 | 148 | ||
| 120 | var sum: u64 = 0; | 149 | var sum: u64 = 0; |
| 121 | while (i < key_count) : (i += 1) { | 150 | for (0..key_count) |i| { |
| 122 | const small_key = block[0..key_size]; | 151 | const small_key = blocks[i * key_size ..][0..key_size]; |
| 123 | sum +%= blk: { | 152 | const final = blk: { |
| 124 | if (H.init_u8s) |init| { | 153 | if (H.init_u8s) |init| { |
| 125 | break :blk H.ty.hash(init, small_key); | 154 | if (H.has_crypto_api) { |
| 155 | break :blk @truncate(u64, H.ty.toInt(small_key, init[0..H.ty.key_length])); | ||
| 156 | } else { | ||
| 157 | break :blk H.ty.hash(init, small_key); | ||
| 158 | } | ||
| 126 | } | 159 | } |
| 127 | if (H.init_u64) |init| { | 160 | if (H.init_u64) |init| { |
| 128 | break :blk H.ty.hash(init, small_key); | 161 | break :blk H.ty.hash(init, small_key); |
| 129 | } | 162 | } |
| 130 | break :blk H.ty.hash(small_key); | 163 | break :blk H.ty.hash(small_key); |
| 131 | }; | 164 | }; |
| 165 | sum +%= final; | ||
| 132 | } | 166 | } |
| 133 | const end = timer.read(); | 167 | const end = timer.read(); |
| 134 | 168 | ||
| 135 | const elapsed_s = @intToFloat(f64, end - start) / time.ns_per_s; | 169 | const elapsed_s = @intToFloat(f64, end - start) / time.ns_per_s; |
| 136 | const throughput = @floatToInt(u64, @intToFloat(f64, bytes) / elapsed_s); | 170 | const throughput = @floatToInt(u64, @intToFloat(f64, bytes) / elapsed_s); |
| 137 | 171 | ||
| 172 | std.mem.doNotOptimizeAway(sum); | ||
| 173 | |||
| 138 | return Result{ | 174 | return Result{ |
| 139 | .hash = sum, | 175 | .hash = sum, |
| 140 | .throughput = throughput, | 176 | .throughput = throughput, |
| ... | @@ -227,6 +263,10 @@ pub fn main() !void { | ... | @@ -227,6 +263,10 @@ pub fn main() !void { |
| 227 | } | 263 | } |
| 228 | } | 264 | } |
| 229 | 265 | ||
| 266 | var gpa = std.heap.GeneralPurposeAllocator(.{}){}; | ||
| 267 | defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak"); | ||
| 268 | const allocator = gpa.allocator(); | ||
| 269 | |||
| 230 | inline for (hashes) |H| { | 270 | inline for (hashes) |H| { |
| 231 | if (filter == null or std.mem.indexOf(u8, H.name, filter.?) != null) { | 271 | if (filter == null or std.mem.indexOf(u8, H.name, filter.?) != null) { |
| 232 | if (!test_iterative_only or H.has_iterative_api) { | 272 | if (!test_iterative_only or H.has_iterative_api) { |
| ... | @@ -236,13 +276,13 @@ pub fn main() !void { | ... | @@ -236,13 +276,13 @@ pub fn main() !void { |
| 236 | // This allows easier comparison between different implementations. | 276 | // This allows easier comparison between different implementations. |
| 237 | if (H.has_iterative_api) { | 277 | if (H.has_iterative_api) { |
| 238 | prng.seed(seed); | 278 | prng.seed(seed); |
| 239 | const result = try benchmarkHash(H, count); | 279 | const result = try benchmarkHash(H, count, allocator); |
| 240 | try stdout.print(" iterative: {:5} MiB/s [{x:0<16}]\n", .{ result.throughput / (1 * MiB), result.hash }); | 280 | try stdout.print(" iterative: {:5} MiB/s [{x:0<16}]\n", .{ result.throughput / (1 * MiB), result.hash }); |
| 241 | } | 281 | } |
| 242 | 282 | ||
| 243 | if (!test_iterative_only) { | 283 | if (!test_iterative_only) { |
| 244 | prng.seed(seed); | 284 | prng.seed(seed); |
| 245 | const result_small = try benchmarkHashSmallKeys(H, key_size, count); | 285 | const result_small = try benchmarkHashSmallKeys(H, key_size, count, allocator); |
| 246 | try stdout.print(" small keys: {:5} MiB/s [{x:0<16}]\n", .{ result_small.throughput / (1 * MiB), result_small.hash }); | 286 | try stdout.print(" small keys: {:5} MiB/s [{x:0<16}]\n", .{ result_small.throughput / (1 * MiB), result_small.hash }); |
| 247 | } | 287 | } |
| 248 | } | 288 | } |
lib/std/hash/xxhash.zig+18-18| ... | @@ -126,8 +126,8 @@ pub const XxHash64 = struct { | ... | @@ -126,8 +126,8 @@ pub const XxHash64 = struct { |
| 126 | return b +% prime_4; | 126 | return b +% prime_4; |
| 127 | } | 127 | } |
| 128 | 128 | ||
| 129 | pub fn hash(input: []const u8) u64 { | 129 | pub fn hash(seed: u64, input: []const u8) u64 { |
| 130 | var hasher = XxHash64.init(0); | 130 | var hasher = XxHash64.init(seed); |
| 131 | hasher.update(input); | 131 | hasher.update(input); |
| 132 | return hasher.final(); | 132 | return hasher.final(); |
| 133 | } | 133 | } |
| ... | @@ -236,8 +236,8 @@ pub const XxHash32 = struct { | ... | @@ -236,8 +236,8 @@ pub const XxHash32 = struct { |
| 236 | return acc; | 236 | return acc; |
| 237 | } | 237 | } |
| 238 | 238 | ||
| 239 | pub fn hash(input: []const u8) u32 { | 239 | pub fn hash(seed: u32, input: []const u8) u32 { |
| 240 | var hasher = XxHash32.init(0); | 240 | var hasher = XxHash32.init(seed); |
| 241 | hasher.update(input); | 241 | hasher.update(input); |
| 242 | return hasher.final(); | 242 | return hasher.final(); |
| 243 | } | 243 | } |
| ... | @@ -246,23 +246,23 @@ pub const XxHash32 = struct { | ... | @@ -246,23 +246,23 @@ pub const XxHash32 = struct { |
| 246 | test "xxhash64" { | 246 | test "xxhash64" { |
| 247 | const hash = XxHash64.hash; | 247 | const hash = XxHash64.hash; |
| 248 | 248 | ||
| 249 | try expectEqual(hash(""), 0xef46db3751d8e999); | 249 | try expectEqual(hash(0, ""), 0xef46db3751d8e999); |
| 250 | try expectEqual(hash("a"), 0xd24ec4f1a98c6e5b); | 250 | try expectEqual(hash(0, "a"), 0xd24ec4f1a98c6e5b); |
| 251 | try expectEqual(hash("abc"), 0x44bc2cf5ad770999); | 251 | try expectEqual(hash(0, "abc"), 0x44bc2cf5ad770999); |
| 252 | try expectEqual(hash("message digest"), 0x066ed728fceeb3be); | 252 | try expectEqual(hash(0, "message digest"), 0x066ed728fceeb3be); |
| 253 | try expectEqual(hash("abcdefghijklmnopqrstuvwxyz"), 0xcfe1f278fa89835c); | 253 | try expectEqual(hash(0, "abcdefghijklmnopqrstuvwxyz"), 0xcfe1f278fa89835c); |
| 254 | try expectEqual(hash("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"), 0xaaa46907d3047814); | 254 | try expectEqual(hash(0, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"), 0xaaa46907d3047814); |
| 255 | try expectEqual(hash("12345678901234567890123456789012345678901234567890123456789012345678901234567890"), 0xe04a477f19ee145d); | 255 | try expectEqual(hash(0, "12345678901234567890123456789012345678901234567890123456789012345678901234567890"), 0xe04a477f19ee145d); |
| 256 | } | 256 | } |
| 257 | 257 | ||
| 258 | test "xxhash32" { | 258 | test "xxhash32" { |
| 259 | const hash = XxHash32.hash; | 259 | const hash = XxHash32.hash; |
| 260 | 260 | ||
| 261 | try expectEqual(hash(""), 0x02cc5d05); | 261 | try expectEqual(hash(0, ""), 0x02cc5d05); |
| 262 | try expectEqual(hash("a"), 0x550d7456); | 262 | try expectEqual(hash(0, "a"), 0x550d7456); |
| 263 | try expectEqual(hash("abc"), 0x32d153ff); | 263 | try expectEqual(hash(0, "abc"), 0x32d153ff); |
| 264 | try expectEqual(hash("message digest"), 0x7c948494); | 264 | try expectEqual(hash(0, "message digest"), 0x7c948494); |
| 265 | try expectEqual(hash("abcdefghijklmnopqrstuvwxyz"), 0x63a14d5f); | 265 | try expectEqual(hash(0, "abcdefghijklmnopqrstuvwxyz"), 0x63a14d5f); |
| 266 | try expectEqual(hash("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"), 0x9c285e64); | 266 | try expectEqual(hash(0, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"), 0x9c285e64); |
| 267 | try expectEqual(hash("12345678901234567890123456789012345678901234567890123456789012345678901234567890"), 0x9c05f475); | 267 | try expectEqual(hash(0, "12345678901234567890123456789012345678901234567890123456789012345678901234567890"), 0x9c05f475); |
| 268 | } | 268 | } |