Skip to content

Commit 0e75fef

Browse files
authored
Merge pull request #3106 from ziglang/hash-tooling-changes
Hash tooling changes
2 parents ec7d7a5 + 16fa255 commit 0e75fef

File tree

8 files changed

+306
-176
lines changed

8 files changed

+306
-176
lines changed

std/crypto/throughput_test.zig renamed to std/crypto/benchmark.zig

+12-7
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1+
// zig run benchmark.zig --release-fast --override-std-dir ..
2+
13
const builtin = @import("builtin");
2-
const std = @import("std");
4+
const std = @import("../std.zig");
35
const time = std.time;
46
const Timer = time.Timer;
5-
const crypto = @import("../crypto.zig");
7+
const crypto = std.crypto;
68

79
const KiB = 1024;
810
const MiB = 1024 * KiB;
@@ -14,7 +16,7 @@ const Crypto = struct {
1416
name: []const u8,
1517
};
1618

17-
const hashes = []Crypto{
19+
const hashes = [_]Crypto{
1820
Crypto{ .ty = crypto.Md5, .name = "md5" },
1921
Crypto{ .ty = crypto.Sha1, .name = "sha1" },
2022
Crypto{ .ty = crypto.Sha256, .name = "sha256" },
@@ -45,7 +47,7 @@ pub fn benchmarkHash(comptime Hash: var, comptime bytes: comptime_int) !u64 {
4547
return throughput;
4648
}
4749

48-
const macs = []Crypto{
50+
const macs = [_]Crypto{
4951
Crypto{ .ty = crypto.Poly1305, .name = "poly1305" },
5052
Crypto{ .ty = crypto.HmacMd5, .name = "hmac-md5" },
5153
Crypto{ .ty = crypto.HmacSha1, .name = "hmac-sha1" },
@@ -75,7 +77,7 @@ pub fn benchmarkMac(comptime Mac: var, comptime bytes: comptime_int) !u64 {
7577
return throughput;
7678
}
7779

78-
const exchanges = []Crypto{Crypto{ .ty = crypto.X25519, .name = "x25519" }};
80+
const exchanges = [_]Crypto{Crypto{ .ty = crypto.X25519, .name = "x25519" }};
7981

8082
pub fn benchmarkKeyExchange(comptime DhKeyExchange: var, comptime exchange_count: comptime_int) !u64 {
8183
std.debug.assert(DhKeyExchange.minimum_key_length >= DhKeyExchange.secret_length);
@@ -135,13 +137,16 @@ pub fn main() !void {
135137

136138
var buffer: [1024]u8 = undefined;
137139
var fixed = std.heap.FixedBufferAllocator.init(buffer[0..]);
138-
const args = try std.os.argsAlloc(&fixed.allocator);
140+
const args = try std.process.argsAlloc(&fixed.allocator);
139141

140142
var filter: ?[]u8 = "";
141143

142144
var i: usize = 1;
143145
while (i < args.len) : (i += 1) {
144-
if (std.mem.eql(u8, args[i], "--seed")) {
146+
if (std.mem.eql(u8, args[i], "--mode")) {
147+
try stdout.print("{}\n", builtin.mode);
148+
return;
149+
} else if (std.mem.eql(u8, args[i], "--seed")) {
145150
i += 1;
146151
if (i == args.len) {
147152
usage();

std/crypto/blake2.zig

+2-2
Original file line numberDiff line numberDiff line change
@@ -269,8 +269,8 @@ pub const Blake2b512 = Blake2b(512);
269269
fn Blake2b(comptime out_len: usize) type {
270270
return struct {
271271
const Self = @This();
272-
const block_length = 128;
273-
const digest_length = out_len / 8;
272+
pub const block_length = 128;
273+
pub const digest_length = out_len / 8;
274274

275275
const iv = [8]u64{
276276
0x6a09e667f3bcc908,

std/crypto/sha2.zig

+2-2
Original file line numberDiff line numberDiff line change
@@ -420,8 +420,8 @@ pub const Sha512 = Sha2_64(Sha512Params);
420420
fn Sha2_64(comptime params: Sha2Params64) type {
421421
return struct {
422422
const Self = @This();
423-
const block_length = 128;
424-
const digest_length = params.out_len / 8;
423+
pub const block_length = 128;
424+
pub const digest_length = params.out_len / 8;
425425

426426
s: [8]u64,
427427
// Streaming Cache

std/hash/benchmark.zig

+273
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,273 @@
1+
// zig run benchmark.zig --release-fast --override-std-dir ..
2+
3+
const builtin = @import("builtin");
4+
const std = @import("std");
5+
const time = std.time;
6+
const Timer = time.Timer;
7+
const hash = std.hash;
8+
9+
const KiB = 1024;
10+
const MiB = 1024 * KiB;
11+
const GiB = 1024 * MiB;
12+
13+
var prng = std.rand.DefaultPrng.init(0);
14+
15+
const Hash = struct {
16+
ty: type,
17+
name: []const u8,
18+
has_iterative_api: bool = true,
19+
init_u8s: ?[]const u8 = null,
20+
init_u64: ?u64 = null,
21+
};
22+
23+
const siphash_key = "0123456789abcdef";
24+
25+
const hashes = [_]Hash{
26+
Hash{
27+
.ty = hash.Wyhash,
28+
.name = "wyhash",
29+
.init_u64 = 0,
30+
},
31+
Hash{
32+
.ty = hash.SipHash64(1, 3),
33+
.name = "siphash(1,3)",
34+
.init_u8s = siphash_key,
35+
},
36+
Hash{
37+
.ty = hash.SipHash64(2, 4),
38+
.name = "siphash(2,4)",
39+
.init_u8s = siphash_key,
40+
},
41+
Hash{
42+
.ty = hash.Fnv1a_64,
43+
.name = "fnv1a",
44+
},
45+
Hash{
46+
.ty = hash.Adler32,
47+
.name = "adler32",
48+
},
49+
Hash{
50+
.ty = hash.crc.Crc32WithPoly(.IEEE),
51+
.name = "crc32-slicing-by-8",
52+
},
53+
Hash{
54+
.ty = hash.crc.Crc32SmallWithPoly(.IEEE),
55+
.name = "crc32-half-byte-lookup",
56+
},
57+
Hash{
58+
.ty = hash.CityHash32,
59+
.name = "cityhash-32",
60+
.has_iterative_api = false,
61+
},
62+
Hash{
63+
.ty = hash.CityHash64,
64+
.name = "cityhash-64",
65+
.has_iterative_api = false,
66+
},
67+
Hash{
68+
.ty = hash.Murmur2_32,
69+
.name = "murmur2-32",
70+
.has_iterative_api = false,
71+
},
72+
Hash{
73+
.ty = hash.Murmur2_64,
74+
.name = "murmur2-64",
75+
.has_iterative_api = false,
76+
},
77+
Hash{
78+
.ty = hash.Murmur3_32,
79+
.name = "murmur3-32",
80+
.has_iterative_api = false,
81+
},
82+
};
83+
84+
const Result = struct {
85+
hash: u64,
86+
throughput: u64,
87+
};
88+
89+
const block_size: usize = 8192;
90+
91+
pub fn benchmarkHash(comptime H: var, bytes: usize) !Result {
92+
var h = blk: {
93+
if (H.init_u8s) |init| {
94+
break :blk H.ty.init(init);
95+
}
96+
if (H.init_u64) |init| {
97+
break :blk H.ty.init(init);
98+
}
99+
break :blk H.ty.init();
100+
};
101+
102+
var block: [block_size]u8 = undefined;
103+
prng.random.bytes(block[0..]);
104+
105+
var offset: usize = 0;
106+
var timer = try Timer.start();
107+
const start = timer.lap();
108+
while (offset < bytes) : (offset += block.len) {
109+
h.update(block[0..]);
110+
}
111+
const end = timer.read();
112+
113+
const elapsed_s = @intToFloat(f64, end - start) / time.ns_per_s;
114+
const throughput = @floatToInt(u64, @intToFloat(f64, bytes) / elapsed_s);
115+
116+
return Result{
117+
.hash = h.final(),
118+
.throughput = throughput,
119+
};
120+
}
121+
122+
pub fn benchmarkHashSmallKeys(comptime H: var, key_size: usize, bytes: usize) !Result {
123+
const key_count = bytes / key_size;
124+
var block: [block_size]u8 = undefined;
125+
prng.random.bytes(block[0..]);
126+
127+
var i: usize = 0;
128+
var timer = try Timer.start();
129+
const start = timer.lap();
130+
131+
var sum: u64 = 0;
132+
while (i < key_count) : (i += 1) {
133+
const small_key = block[0..key_size];
134+
sum +%= blk: {
135+
if (H.init_u8s) |init| {
136+
break :blk H.ty.hash(init, small_key);
137+
}
138+
if (H.init_u64) |init| {
139+
break :blk H.ty.hash(init, small_key);
140+
}
141+
break :blk H.ty.hash(small_key);
142+
};
143+
}
144+
const end = timer.read();
145+
146+
const elapsed_s = @intToFloat(f64, end - start) / time.ns_per_s;
147+
const throughput = @floatToInt(u64, @intToFloat(f64, bytes) / elapsed_s);
148+
149+
return Result{
150+
.hash = sum,
151+
.throughput = throughput,
152+
};
153+
}
154+
155+
fn usage() void {
156+
std.debug.warn(
157+
\\throughput_test [options]
158+
\\
159+
\\Options:
160+
\\ --filter [test-name]
161+
\\ --seed [int]
162+
\\ --count [int]
163+
\\ --key-size [int]
164+
\\ --iterative-only
165+
\\ --help
166+
\\
167+
);
168+
}
169+
170+
fn mode(comptime x: comptime_int) comptime_int {
171+
return if (builtin.mode == builtin.Mode.Debug) x / 64 else x;
172+
}
173+
174+
// TODO(#1358): Replace with builtin formatted padding when available.
175+
fn printPad(stdout: var, s: []const u8) !void {
176+
var i: usize = 0;
177+
while (i < 12 - s.len) : (i += 1) {
178+
try stdout.print(" ");
179+
}
180+
try stdout.print("{}", s);
181+
}
182+
183+
pub fn main() !void {
184+
var stdout_file = try std.io.getStdOut();
185+
var stdout_out_stream = stdout_file.outStream();
186+
const stdout = &stdout_out_stream.stream;
187+
188+
var buffer: [1024]u8 = undefined;
189+
var fixed = std.heap.FixedBufferAllocator.init(buffer[0..]);
190+
const args = try std.process.argsAlloc(&fixed.allocator);
191+
192+
var filter: ?[]u8 = "";
193+
var count: usize = mode(128 * MiB);
194+
var key_size: usize = 32;
195+
var seed: u32 = 0;
196+
var test_iterative_only = false;
197+
198+
var i: usize = 1;
199+
while (i < args.len) : (i += 1) {
200+
if (std.mem.eql(u8, args[i], "--mode")) {
201+
try stdout.print("{}\n", builtin.mode);
202+
return;
203+
} else if (std.mem.eql(u8, args[i], "--seed")) {
204+
i += 1;
205+
if (i == args.len) {
206+
usage();
207+
std.os.exit(1);
208+
}
209+
210+
seed = try std.fmt.parseUnsigned(u32, args[i], 10);
211+
// we seed later
212+
} else if (std.mem.eql(u8, args[i], "--filter")) {
213+
i += 1;
214+
if (i == args.len) {
215+
usage();
216+
std.os.exit(1);
217+
}
218+
219+
filter = args[i];
220+
} else if (std.mem.eql(u8, args[i], "--count")) {
221+
i += 1;
222+
if (i == args.len) {
223+
usage();
224+
std.os.exit(1);
225+
}
226+
227+
const c = try std.fmt.parseUnsigned(usize, args[i], 10);
228+
count = c * MiB;
229+
} else if (std.mem.eql(u8, args[i], "--key-size")) {
230+
i += 1;
231+
if (i == args.len) {
232+
usage();
233+
std.os.exit(1);
234+
}
235+
236+
key_size = try std.fmt.parseUnsigned(usize, args[i], 10);
237+
if (key_size > block_size) {
238+
try stdout.print("key_size cannot exceed block size of {}\n", block_size);
239+
std.os.exit(1);
240+
}
241+
} else if (std.mem.eql(u8, args[i], "--iterative-only")) {
242+
test_iterative_only = true;
243+
} else if (std.mem.eql(u8, args[i], "--help")) {
244+
usage();
245+
return;
246+
} else {
247+
usage();
248+
std.os.exit(1);
249+
}
250+
}
251+
252+
inline for (hashes) |H| {
253+
if (filter == null or std.mem.indexOf(u8, H.name, filter.?) != null) {
254+
if (!test_iterative_only or H.has_iterative_api) {
255+
try stdout.print("{}\n", H.name);
256+
257+
// Always reseed prior to every call so we are hashing the same buffer contents.
258+
// This allows easier comparison between different implementations.
259+
if (H.has_iterative_api) {
260+
prng.seed(seed);
261+
const result = try benchmarkHash(H, count);
262+
try stdout.print(" iterative: {:4} MiB/s [{x:0<16}]\n", result.throughput / (1 * MiB), result.hash);
263+
}
264+
265+
if (!test_iterative_only) {
266+
prng.seed(seed);
267+
const result_small = try benchmarkHashSmallKeys(H, key_size, count);
268+
try stdout.print(" small keys: {:4} MiB/s [{x:0<16}]\n", result_small.throughput / (1 * MiB), result_small.hash);
269+
}
270+
}
271+
}
272+
}
273+
}

0 commit comments

Comments
 (0)