| author | |
| committer | |
| log | b7512c3e5d1734c301b8e5c1cda0385b0a967e0c |
| tree | 76acf9da4580dcc482fedf742cc31711eafd1310 |
| parent | 33ffbb32c9cfea28373b199003f94fa80353ee44 |
| signature |
3 files changed, 144 insertions(+), 1 deletions(-)
lib/std/hash.zig+2| ... | @@ -32,6 +32,8 @@ pub const CityHash64 = cityhash.CityHash64; | ... | @@ -32,6 +32,8 @@ pub const CityHash64 = cityhash.CityHash64; |
| 32 | const wyhash = @import("hash/wyhash.zig"); | 32 | const wyhash = @import("hash/wyhash.zig"); |
| 33 | pub const Wyhash = wyhash.Wyhash; | 33 | pub const Wyhash = wyhash.Wyhash; |
| 34 | 34 | ||
| 35 | pub const RapidHash = @import("hash/RapidHash.zig"); | ||
| 36 | |||
| 35 | const xxhash = @import("hash/xxhash.zig"); | 37 | const xxhash = @import("hash/xxhash.zig"); |
| 36 | pub const XxHash3 = xxhash.XxHash3; | 38 | pub const XxHash3 = xxhash.XxHash3; |
| 37 | pub const XxHash64 = xxhash.XxHash64; | 39 | pub const XxHash64 = xxhash.XxHash64; |
lib/std/hash/RapidHash.zig created+125| ... | @@ -0,0 +1,125 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | |||
| 3 | const readInt = std.mem.readInt; | ||
| 4 | const assert = std.debug.assert; | ||
| 5 | const expect = std.testing.expect; | ||
| 6 | const expectEqual = std.testing.expectEqual; | ||
| 7 | |||
| 8 | const RAPID_SEED: u64 = 0xbdd89aa982704029; | ||
| 9 | const RAPID_SECRET: [3]u64 = .{ 0x2d358dccaa6c78a5, 0x8bb84b93962eacc9, 0x4b33a62ed433d4a3 }; | ||
| 10 | |||
| 11 | pub fn hash(seed: u64, input: []const u8) u64 { | ||
| 12 | const sc = RAPID_SECRET; | ||
| 13 | const len = input.len; | ||
| 14 | var a: u64 = 0; | ||
| 15 | var b: u64 = 0; | ||
| 16 | var k = input; | ||
| 17 | var is: [3]u64 = .{ seed, 0, 0 }; | ||
| 18 | |||
| 19 | is[0] ^= mix(seed ^ sc[0], sc[1]) ^ len; | ||
| 20 | |||
| 21 | if (len <= 16) { | ||
| 22 | if (len >= 4) { | ||
| 23 | const d: u64 = ((len & 24) >> @intCast(len >> 3)); | ||
| 24 | const e = len - 4; | ||
| 25 | a = (r32(k) << 32) | r32(k[e..]); | ||
| 26 | b = ((r32(k[d..]) << 32) | r32(k[(e - d)..])); | ||
| 27 | } else if (len > 0) | ||
| 28 | a = (@as(u64, k[0]) << 56) | (@as(u64, k[len >> 1]) << 32) | @as(u64, k[len - 1]); | ||
| 29 | } else { | ||
| 30 | var remain = len; | ||
| 31 | if (len > 48) { | ||
| 32 | is[1] = is[0]; | ||
| 33 | is[2] = is[0]; | ||
| 34 | while (remain >= 96) { | ||
| 35 | inline for (0..6) |i| { | ||
| 36 | const m1 = r64(k[8 * i * 2 ..]); | ||
| 37 | const m2 = r64(k[8 * (i * 2 + 1) ..]); | ||
| 38 | is[i % 3] = mix(m1 ^ sc[i % 3], m2 ^ is[i % 3]); | ||
| 39 | } | ||
| 40 | k = k[96..]; | ||
| 41 | remain -= 96; | ||
| 42 | } | ||
| 43 | if (remain >= 48) { | ||
| 44 | inline for (0..3) |i| { | ||
| 45 | const m1 = r64(k[8 * i * 2 ..]); | ||
| 46 | const m2 = r64(k[8 * (i * 2 + 1) ..]); | ||
| 47 | is[i] = mix(m1 ^ sc[i], m2 ^ is[i]); | ||
| 48 | } | ||
| 49 | k = k[48..]; | ||
| 50 | remain -= 48; | ||
| 51 | } | ||
| 52 | |||
| 53 | is[0] ^= is[1] ^ is[2]; | ||
| 54 | } | ||
| 55 | |||
| 56 | if (remain > 16) { | ||
| 57 | is[0] = mix(r64(k) ^ sc[2], r64(k[8..]) ^ is[0] ^ sc[1]); | ||
| 58 | if (remain > 32) { | ||
| 59 | is[0] = mix(r64(k[16..]) ^ sc[2], r64(k[24..]) ^ is[0]); | ||
| 60 | } | ||
| 61 | } | ||
| 62 | |||
| 63 | a = r64(input[len - 16 ..]); | ||
| 64 | b = r64(input[len - 8 ..]); | ||
| 65 | } | ||
| 66 | |||
| 67 | a ^= sc[1]; | ||
| 68 | b ^= is[0]; | ||
| 69 | mum(&a, &b); | ||
| 70 | return mix(a ^ sc[0] ^ len, b ^ sc[1]); | ||
| 71 | } | ||
| 72 | |||
| 73 | test "RapidHash.hash" { | ||
| 74 | const bytes: []const u8 = "abcdefgh" ** 128; | ||
| 75 | |||
| 76 | const sizes: [13]u64 = .{ 0, 1, 2, 3, 4, 8, 16, 32, 64, 128, 256, 512, 1024 }; | ||
| 77 | |||
| 78 | const outcomes: [13]u64 = .{ | ||
| 79 | 0x5a6ef77074ebc84b, | ||
| 80 | 0xc11328477bc0f5d1, | ||
| 81 | 0x5644ac035e40d569, | ||
| 82 | 0x347080fbf5fcd81, | ||
| 83 | 0x56b66b8dc802bcc, | ||
| 84 | 0xb6bf9055973aac7c, | ||
| 85 | 0xed56d62eead1e402, | ||
| 86 | 0xc19072d767da8ffb, | ||
| 87 | 0x89bb40a9928a4f0d, | ||
| 88 | 0xe0af7c5e7b6e29fd, | ||
| 89 | 0x9a3ed35fbedfa11a, | ||
| 90 | 0x4c684b2119ca19fb, | ||
| 91 | 0x4b575f5bf25600d6, | ||
| 92 | }; | ||
| 93 | |||
| 94 | var success: bool = true; | ||
| 95 | for (sizes, outcomes) |s, e| { | ||
| 96 | const r = hash(RAPID_SEED, bytes[0..s]); | ||
| 97 | |||
| 98 | expectEqual(e, r) catch |err| { | ||
| 99 | std.debug.print("Failed on {d}: {!}\n", .{ s, err }); | ||
| 100 | success = false; | ||
| 101 | }; | ||
| 102 | } | ||
| 103 | try expect(success); | ||
| 104 | } | ||
| 105 | |||
| 106 | inline fn mum(a: *u64, b: *u64) void { | ||
| 107 | const r = @as(u128, a.*) * b.*; | ||
| 108 | a.* = @truncate(r); | ||
| 109 | b.* = @truncate(r >> 64); | ||
| 110 | } | ||
| 111 | |||
| 112 | inline fn mix(a: u64, b: u64) u64 { | ||
| 113 | var copy_a = a; | ||
| 114 | var copy_b = b; | ||
| 115 | mum(&copy_a, &copy_b); | ||
| 116 | return copy_a ^ copy_b; | ||
| 117 | } | ||
| 118 | |||
| 119 | inline fn r64(p: []const u8) u64 { | ||
| 120 | return readInt(u64, p[0..8], .little); | ||
| 121 | } | ||
| 122 | |||
| 123 | inline fn r32(p: []const u8) u64 { | ||
| 124 | return readInt(u32, p[0..4], .little); | ||
| 125 | } | ||
lib/std/hash/benchmark.zig+17-1| ... | @@ -59,6 +59,12 @@ const hashes = [_]Hash{ | ... | @@ -59,6 +59,12 @@ const hashes = [_]Hash{ |
| 59 | .ty = hash.crc.Crc32, | 59 | .ty = hash.crc.Crc32, |
| 60 | .name = "crc32", | 60 | .name = "crc32", |
| 61 | }, | 61 | }, |
| 62 | Hash{ | ||
| 63 | .ty = hash.RapidHash, | ||
| 64 | .name = "rapidhash", | ||
| 65 | .has_iterative_api = false, | ||
| 66 | .init_u64 = 0, | ||
| 67 | }, | ||
| 62 | Hash{ | 68 | Hash{ |
| 63 | .ty = hash.CityHash32, | 69 | .ty = hash.CityHash32, |
| 64 | .name = "cityhash-32", | 70 | .name = "cityhash-32", |
| ... | @@ -329,6 +335,7 @@ fn usage() void { | ... | @@ -329,6 +335,7 @@ fn usage() void { |
| 329 | \\ --count [int] | 335 | \\ --count [int] |
| 330 | \\ --key-size [int] | 336 | \\ --key-size [int] |
| 331 | \\ --iterative-only | 337 | \\ --iterative-only |
| 338 | \\ --small-key-only | ||
| 332 | \\ --help | 339 | \\ --help |
| 333 | \\ | 340 | \\ |
| 334 | , .{}); | 341 | , .{}); |
| ... | @@ -349,6 +356,7 @@ pub fn main() !void { | ... | @@ -349,6 +356,7 @@ pub fn main() !void { |
| 349 | var count: usize = mode(128 * MiB); | 356 | var count: usize = mode(128 * MiB); |
| 350 | var key_size: ?usize = null; | 357 | var key_size: ?usize = null; |
| 351 | var seed: u32 = 0; | 358 | var seed: u32 = 0; |
| 359 | var test_small_key_only = false; | ||
| 352 | var test_iterative_only = false; | 360 | var test_iterative_only = false; |
| 353 | var test_arrays = false; | 361 | var test_arrays = false; |
| 354 | 362 | ||
| ... | @@ -399,6 +407,8 @@ pub fn main() !void { | ... | @@ -399,6 +407,8 @@ pub fn main() !void { |
| 399 | } | 407 | } |
| 400 | } else if (std.mem.eql(u8, args[i], "--iterative-only")) { | 408 | } else if (std.mem.eql(u8, args[i], "--iterative-only")) { |
| 401 | test_iterative_only = true; | 409 | test_iterative_only = true; |
| 410 | } else if (std.mem.eql(u8, args[i], "--small-key-only")) { | ||
| 411 | test_small_key_only = true; | ||
| 402 | } else if (std.mem.eql(u8, args[i], "--include-array")) { | 412 | } else if (std.mem.eql(u8, args[i], "--include-array")) { |
| 403 | test_arrays = true; | 413 | test_arrays = true; |
| 404 | } else if (std.mem.eql(u8, args[i], "--help")) { | 414 | } else if (std.mem.eql(u8, args[i], "--help")) { |
| ... | @@ -410,6 +420,12 @@ pub fn main() !void { | ... | @@ -410,6 +420,12 @@ pub fn main() !void { |
| 410 | } | 420 | } |
| 411 | } | 421 | } |
| 412 | 422 | ||
| 423 | if (test_iterative_only and test_small_key_only) { | ||
| 424 | try stdout.print("Cannot use iterative-only and small-key-only together!\n", .{}); | ||
| 425 | usage(); | ||
| 426 | std.process.exit(1); | ||
| 427 | } | ||
| 428 | |||
| 413 | var gpa: std.heap.GeneralPurposeAllocator(.{}) = .init; | 429 | var gpa: std.heap.GeneralPurposeAllocator(.{}) = .init; |
| 414 | defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak"); | 430 | defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak"); |
| 415 | const allocator = gpa.allocator(); | 431 | const allocator = gpa.allocator(); |
| ... | @@ -421,7 +437,7 @@ pub fn main() !void { | ... | @@ -421,7 +437,7 @@ pub fn main() !void { |
| 421 | 437 | ||
| 422 | // Always reseed prior to every call so we are hashing the same buffer contents. | 438 | // Always reseed prior to every call so we are hashing the same buffer contents. |
| 423 | // This allows easier comparison between different implementations. | 439 | // This allows easier comparison between different implementations. |
| 424 | if (H.has_iterative_api) { | 440 | if (H.has_iterative_api and !test_small_key_only) { |
| 425 | prng.seed(seed); | 441 | prng.seed(seed); |
| 426 | const result = try benchmarkHash(H, count, allocator); | 442 | const result = try benchmarkHash(H, count, allocator); |
| 427 | try stdout.print(" iterative: {:5} MiB/s [{x:0<16}]\n", .{ result.throughput / (1 * MiB), result.hash }); | 443 | try stdout.print(" iterative: {:5} MiB/s [{x:0<16}]\n", .{ result.throughput / (1 * MiB), result.hash }); |