authorgravatar for 124872+jedisct1@users.noreply.github.comFrank Denis <124872+jedisct1@users.noreply.github.com> 2020-08-25 16:20:57+02:00
committergravatar for 124872+jedisct1@users.noreply.github.comFrank Denis <124872+jedisct1@users.noreply.github.com> 2020-08-26 10:50:34+02:00
logb8729ca1a07863a0413b90206b82c1a0794abbd5
tree87f3aacf2b015c545367135fe4b4cc3326cad457
parent3abf9e1457ed9332474ab211eb8d996d594a33c3

Improve crypto benchmarks

- 1MiB objects on the stack doesn't play well with wasmtime. Reduce these to 512KiB so that the webassembly benchmarks can run. - Pass expected results to a blackBox() function. Without this, in release-fast mode, the compiler could detected unused return values, and would produce results that didn't make sense for siphash. - Add AEAD constructions to the benchmarks. - Inline chacha20Core() makes it 4 times faster. - benchmarkSignatures() -> benchmarkSignature() for consistency.

3 files changed, 74 insertions(+), 17 deletions(-)

lib/std/crypto/benchmark.zig+66-13
...@@ -21,6 +21,14 @@ const Crypto = struct {...@@ -21,6 +21,14 @@ const Crypto = struct {
21 name: []const u8,21 name: []const u8,
22};22};
2323
24fn blackBox(x: anytype) void {
25 asm volatile (""
26 :
27 : [x] "rm" (x)
28 : "memory"
29 );
30}
31
24const hashes = [_]Crypto{32const hashes = [_]Crypto{
25 Crypto{ .ty = crypto.hash.Md5, .name = "md5" },33 Crypto{ .ty = crypto.hash.Md5, .name = "md5" },
26 Crypto{ .ty = crypto.hash.Sha1, .name = "sha1" },34 Crypto{ .ty = crypto.hash.Sha1, .name = "sha1" },
...@@ -46,6 +54,7 @@ pub fn benchmarkHash(comptime Hash: anytype, comptime bytes: comptime_int) !u64...@@ -46,6 +54,7 @@ pub fn benchmarkHash(comptime Hash: anytype, comptime bytes: comptime_int) !u64
46 while (offset < bytes) : (offset += block.len) {54 while (offset < bytes) : (offset += block.len) {
47 h.update(block[0..]);55 h.update(block[0..]);
48 }56 }
57 blackBox(&h);
49 const end = timer.read();58 const end = timer.read();
5059
51 const elapsed_s = @intToFloat(f64, end - start) / time.ns_per_s;60 const elapsed_s = @intToFloat(f64, end - start) / time.ns_per_s;
...@@ -67,19 +76,20 @@ const macs = [_]Crypto{...@@ -67,19 +76,20 @@ const macs = [_]Crypto{
67};76};
6877
69pub fn benchmarkMac(comptime Mac: anytype, comptime bytes: comptime_int) !u64 {78pub fn benchmarkMac(comptime Mac: anytype, comptime bytes: comptime_int) !u64 {
70 std.debug.assert(64 >= Mac.mac_length and 32 >= Mac.minimum_key_length);79 var in: [512 * KiB]u8 = undefined;
71
72 var in: [1 * MiB]u8 = undefined;
73 prng.random.bytes(in[0..]);80 prng.random.bytes(in[0..]);
7481
75 var key: [64]u8 = undefined;82 const key_length = if (Mac.minimum_key_length == 0) 32 else Mac.minimum_key_length;
83 var key: [key_length]u8 = undefined;
76 prng.random.bytes(key[0..]);84 prng.random.bytes(key[0..]);
7785
86 var mac: [Mac.mac_length]u8 = undefined;
78 var offset: usize = 0;87 var offset: usize = 0;
79 var timer = try Timer.start();88 var timer = try Timer.start();
80 const start = timer.lap();89 const start = timer.lap();
81 while (offset < bytes) : (offset += in.len) {90 while (offset < bytes) : (offset += in.len) {
82 Mac.create(key[0..], in[0..], key[0..]);91 Mac.create(mac[0..], in[0..], key[0..]);
92 blackBox(&mac);
83 }93 }
84 const end = timer.read();94 const end = timer.read();
8595
...@@ -106,6 +116,7 @@ pub fn benchmarkKeyExchange(comptime DhKeyExchange: anytype, comptime exchange_c...@@ -106,6 +116,7 @@ pub fn benchmarkKeyExchange(comptime DhKeyExchange: anytype, comptime exchange_c
106 var i: usize = 0;116 var i: usize = 0;
107 while (i < exchange_count) : (i += 1) {117 while (i < exchange_count) : (i += 1) {
108 _ = DhKeyExchange.create(out[0..], out[0..], in[0..]);118 _ = DhKeyExchange.create(out[0..], out[0..], in[0..]);
119 blackBox(&out);
109 }120 }
110 }121 }
111 const end = timer.read();122 const end = timer.read();
...@@ -118,7 +129,7 @@ pub fn benchmarkKeyExchange(comptime DhKeyExchange: anytype, comptime exchange_c...@@ -118,7 +129,7 @@ pub fn benchmarkKeyExchange(comptime DhKeyExchange: anytype, comptime exchange_c
118129
119const signatures = [_]Crypto{Crypto{ .ty = crypto.sign.Ed25519, .name = "ed25519" }};130const signatures = [_]Crypto{Crypto{ .ty = crypto.sign.Ed25519, .name = "ed25519" }};
120131
121pub fn benchmarkSignatures(comptime Signature: anytype, comptime signatures_count: comptime_int) !u64 {132pub fn benchmarkSignature(comptime Signature: anytype, comptime signatures_count: comptime_int) !u64 {
122 var seed: [Signature.seed_length]u8 = undefined;133 var seed: [Signature.seed_length]u8 = undefined;
123 prng.random.bytes(seed[0..]);134 prng.random.bytes(seed[0..]);
124 const msg = [_]u8{0} ** 64;135 const msg = [_]u8{0} ** 64;
...@@ -129,7 +140,8 @@ pub fn benchmarkSignatures(comptime Signature: anytype, comptime signatures_coun...@@ -129,7 +140,8 @@ pub fn benchmarkSignatures(comptime Signature: anytype, comptime signatures_coun
129 {140 {
130 var i: usize = 0;141 var i: usize = 0;
131 while (i < signatures_count) : (i += 1) {142 while (i < signatures_count) : (i += 1) {
132 _ = try Signature.sign(&msg, key_pair, null);143 const s = try Signature.sign(&msg, key_pair, null);
144 blackBox(&s);
133 }145 }
134 }146 }
135 const end = timer.read();147 const end = timer.read();
...@@ -140,6 +152,40 @@ pub fn benchmarkSignatures(comptime Signature: anytype, comptime signatures_coun...@@ -140,6 +152,40 @@ pub fn benchmarkSignatures(comptime Signature: anytype, comptime signatures_coun
140 return throughput;152 return throughput;
141}153}
142154
155const aeads = [_]Crypto{
156 Crypto{ .ty = crypto.aead.ChaCha20Poly1305, .name = "chacha20Poly1305" },
157 Crypto{ .ty = crypto.aead.XChaCha20Poly1305, .name = "xchacha20Poly1305" },
158 Crypto{ .ty = crypto.aead.Gimli, .name = "gimli-aead" },
159};
160
161pub fn benchmarkAead(comptime Aead: anytype, comptime bytes: comptime_int) !u64 {
162 var in: [512 * KiB]u8 = undefined;
163 prng.random.bytes(in[0..]);
164
165 var tag: [Aead.tag_length]u8 = undefined;
166
167 var key: [Aead.key_length]u8 = undefined;
168 prng.random.bytes(key[0..]);
169
170 var nonce: [Aead.nonce_length]u8 = undefined;
171 prng.random.bytes(nonce[0..]);
172
173 var offset: usize = 0;
174 var timer = try Timer.start();
175 const start = timer.lap();
176 while (offset < bytes) : (offset += in.len) {
177 Aead.encrypt(in[0..], tag[0..], in[0..], &[_]u8{}, nonce, key);
178 Aead.decrypt(in[0..], in[0..], tag, &[_]u8{}, nonce, key) catch unreachable;
179 }
180 blackBox(&in);
181 const end = timer.read();
182
183 const elapsed_s = @intToFloat(f64, end - start) / time.ns_per_s;
184 const throughput = @floatToInt(u64, 2 * bytes / elapsed_s);
185
186 return throughput;
187}
188
143fn usage() void {189fn usage() void {
144 std.debug.warn(190 std.debug.warn(
145 \\throughput_test [options]191 \\throughput_test [options]
...@@ -198,29 +244,36 @@ pub fn main() !void {...@@ -198,29 +244,36 @@ pub fn main() !void {
198244
199 inline for (hashes) |H| {245 inline for (hashes) |H| {
200 if (filter == null or std.mem.indexOf(u8, H.name, filter.?) != null) {246 if (filter == null or std.mem.indexOf(u8, H.name, filter.?) != null) {
201 const throughput = try benchmarkHash(H.ty, mode(32 * MiB));247 const throughput = try benchmarkHash(H.ty, mode(128 * MiB));
202 try stdout.print("{:>11}: {:5} MiB/s\n", .{ H.name, throughput / (1 * MiB) });248 try stdout.print("{:>17}: {:7} MiB/s\n", .{ H.name, throughput / (1 * MiB) });
203 }249 }
204 }250 }
205251
206 inline for (macs) |M| {252 inline for (macs) |M| {
207 if (filter == null or std.mem.indexOf(u8, M.name, filter.?) != null) {253 if (filter == null or std.mem.indexOf(u8, M.name, filter.?) != null) {
208 const throughput = try benchmarkMac(M.ty, mode(128 * MiB));254 const throughput = try benchmarkMac(M.ty, mode(128 * MiB));
209 try stdout.print("{:>11}: {:5} MiB/s\n", .{ M.name, throughput / (1 * MiB) });255 try stdout.print("{:>17}: {:7} MiB/s\n", .{ M.name, throughput / (1 * MiB) });
210 }256 }
211 }257 }
212258
213 inline for (exchanges) |E| {259 inline for (exchanges) |E| {
214 if (filter == null or std.mem.indexOf(u8, E.name, filter.?) != null) {260 if (filter == null or std.mem.indexOf(u8, E.name, filter.?) != null) {
215 const throughput = try benchmarkKeyExchange(E.ty, mode(1000));261 const throughput = try benchmarkKeyExchange(E.ty, mode(1000));
216 try stdout.print("{:>11}: {:5} exchanges/s\n", .{ E.name, throughput });262 try stdout.print("{:>17}: {:7} exchanges/s\n", .{ E.name, throughput });
217 }263 }
218 }264 }
219265
220 inline for (signatures) |E| {266 inline for (signatures) |E| {
221 if (filter == null or std.mem.indexOf(u8, E.name, filter.?) != null) {267 if (filter == null or std.mem.indexOf(u8, E.name, filter.?) != null) {
222 const throughput = try benchmarkSignatures(E.ty, mode(1000));268 const throughput = try benchmarkSignature(E.ty, mode(1000));
223 try stdout.print("{:>11}: {:5} signatures/s\n", .{ E.name, throughput });269 try stdout.print("{:>17}: {:7} signatures/s\n", .{ E.name, throughput });
270 }
271 }
272
273 inline for (aeads) |E| {
274 if (filter == null or std.mem.indexOf(u8, E.name, filter.?) != null) {
275 const throughput = try benchmarkAead(E.ty, mode(128 * MiB));
276 try stdout.print("{:>17}: {:7} MiB/s\n", .{ E.name, throughput / (1 * MiB) });
224 }277 }
225 }278 }
226}279}
lib/std/crypto/chacha20.zig+1-1
...@@ -47,7 +47,7 @@ fn initContext(key: [8]u32, d: [4]u32) [16]u32 {...@@ -47,7 +47,7 @@ fn initContext(key: [8]u32, d: [4]u32) [16]u32 {
47}47}
4848
49// The chacha family of ciphers are based on the salsa family.49// The chacha family of ciphers are based on the salsa family.
50fn chacha20Core(x: []u32, input: [16]u32) void {50inline fn chacha20Core(x: []u32, input: [16]u32) void {
51 for (x) |_, i|51 for (x) |_, i|
52 x[i] = input[i];52 x[i] = input[i];
5353
lib/std/crypto/gimli.zig+7-3
...@@ -180,10 +180,14 @@ test "hash" {...@@ -180,10 +180,14 @@ test "hash" {
180}180}
181181
182pub const Aead = struct {182pub const Aead = struct {
183 pub const tag_length = State.RATE;
184 pub const nonce_length = 16;
185 pub const key_length = 32;
186
183 /// ad: Associated Data187 /// ad: Associated Data
184 /// npub: public nonce188 /// npub: public nonce
185 /// k: private key189 /// k: private key
186 fn init(ad: []const u8, npub: [16]u8, k: [32]u8) State {190 fn init(ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) State {
187 var state = State{191 var state = State{
188 .data = undefined,192 .data = undefined,
189 };193 };
...@@ -229,7 +233,7 @@ pub const Aead = struct {...@@ -229,7 +233,7 @@ pub const Aead = struct {
229 /// ad: Associated Data233 /// ad: Associated Data
230 /// npub: public nonce234 /// npub: public nonce
231 /// k: private key235 /// k: private key
232 pub fn encrypt(c: []u8, at: *[State.RATE]u8, m: []const u8, ad: []const u8, npub: [16]u8, k: [32]u8) void {236 pub fn encrypt(c: []u8, at: *[tag_length]u8, m: []const u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) void {
233 assert(c.len == m.len);237 assert(c.len == m.len);
234238
235 var state = Aead.init(ad, npub, k);239 var state = Aead.init(ad, npub, k);
...@@ -275,7 +279,7 @@ pub const Aead = struct {...@@ -275,7 +279,7 @@ pub const Aead = struct {
275 /// npub: public nonce279 /// npub: public nonce
276 /// k: private key280 /// k: private key
277 /// NOTE: the check of the authentication tag is currently not done in constant time281 /// NOTE: the check of the authentication tag is currently not done in constant time
278 pub fn decrypt(m: []u8, c: []const u8, at: [State.RATE]u8, ad: []const u8, npub: [16]u8, k: [32]u8) !void {282 pub fn decrypt(m: []u8, c: []const u8, at: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) !void {
279 assert(c.len == m.len);283 assert(c.len == m.len);
280284
281 var state = Aead.init(ad, npub, k);285 var state = Aead.init(ad, npub, k);