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.
This commit is contained in:
@@ -21,6 +21,14 @@ const Crypto = struct {
|
||||
name: []const u8,
|
||||
};
|
||||
|
||||
fn blackBox(x: anytype) void {
|
||||
asm volatile (""
|
||||
:
|
||||
: [x] "rm" (x)
|
||||
: "memory"
|
||||
);
|
||||
}
|
||||
|
||||
const hashes = [_]Crypto{
|
||||
Crypto{ .ty = crypto.hash.Md5, .name = "md5" },
|
||||
Crypto{ .ty = crypto.hash.Sha1, .name = "sha1" },
|
||||
@@ -46,6 +54,7 @@ pub fn benchmarkHash(comptime Hash: anytype, comptime bytes: comptime_int) !u64
|
||||
while (offset < bytes) : (offset += block.len) {
|
||||
h.update(block[0..]);
|
||||
}
|
||||
blackBox(&h);
|
||||
const end = timer.read();
|
||||
|
||||
const elapsed_s = @intToFloat(f64, end - start) / time.ns_per_s;
|
||||
@@ -67,19 +76,20 @@ const macs = [_]Crypto{
|
||||
};
|
||||
|
||||
pub fn benchmarkMac(comptime Mac: anytype, comptime bytes: comptime_int) !u64 {
|
||||
std.debug.assert(64 >= Mac.mac_length and 32 >= Mac.minimum_key_length);
|
||||
|
||||
var in: [1 * MiB]u8 = undefined;
|
||||
var in: [512 * KiB]u8 = undefined;
|
||||
prng.random.bytes(in[0..]);
|
||||
|
||||
var key: [64]u8 = undefined;
|
||||
const key_length = if (Mac.minimum_key_length == 0) 32 else Mac.minimum_key_length;
|
||||
var key: [key_length]u8 = undefined;
|
||||
prng.random.bytes(key[0..]);
|
||||
|
||||
var mac: [Mac.mac_length]u8 = undefined;
|
||||
var offset: usize = 0;
|
||||
var timer = try Timer.start();
|
||||
const start = timer.lap();
|
||||
while (offset < bytes) : (offset += in.len) {
|
||||
Mac.create(key[0..], in[0..], key[0..]);
|
||||
Mac.create(mac[0..], in[0..], key[0..]);
|
||||
blackBox(&mac);
|
||||
}
|
||||
const end = timer.read();
|
||||
|
||||
@@ -106,6 +116,7 @@ pub fn benchmarkKeyExchange(comptime DhKeyExchange: anytype, comptime exchange_c
|
||||
var i: usize = 0;
|
||||
while (i < exchange_count) : (i += 1) {
|
||||
_ = DhKeyExchange.create(out[0..], out[0..], in[0..]);
|
||||
blackBox(&out);
|
||||
}
|
||||
}
|
||||
const end = timer.read();
|
||||
@@ -118,7 +129,7 @@ pub fn benchmarkKeyExchange(comptime DhKeyExchange: anytype, comptime exchange_c
|
||||
|
||||
const signatures = [_]Crypto{Crypto{ .ty = crypto.sign.Ed25519, .name = "ed25519" }};
|
||||
|
||||
pub fn benchmarkSignatures(comptime Signature: anytype, comptime signatures_count: comptime_int) !u64 {
|
||||
pub fn benchmarkSignature(comptime Signature: anytype, comptime signatures_count: comptime_int) !u64 {
|
||||
var seed: [Signature.seed_length]u8 = undefined;
|
||||
prng.random.bytes(seed[0..]);
|
||||
const msg = [_]u8{0} ** 64;
|
||||
@@ -129,7 +140,8 @@ pub fn benchmarkSignatures(comptime Signature: anytype, comptime signatures_coun
|
||||
{
|
||||
var i: usize = 0;
|
||||
while (i < signatures_count) : (i += 1) {
|
||||
_ = try Signature.sign(&msg, key_pair, null);
|
||||
const s = try Signature.sign(&msg, key_pair, null);
|
||||
blackBox(&s);
|
||||
}
|
||||
}
|
||||
const end = timer.read();
|
||||
@@ -140,6 +152,40 @@ pub fn benchmarkSignatures(comptime Signature: anytype, comptime signatures_coun
|
||||
return throughput;
|
||||
}
|
||||
|
||||
const aeads = [_]Crypto{
|
||||
Crypto{ .ty = crypto.aead.ChaCha20Poly1305, .name = "chacha20Poly1305" },
|
||||
Crypto{ .ty = crypto.aead.XChaCha20Poly1305, .name = "xchacha20Poly1305" },
|
||||
Crypto{ .ty = crypto.aead.Gimli, .name = "gimli-aead" },
|
||||
};
|
||||
|
||||
pub fn benchmarkAead(comptime Aead: anytype, comptime bytes: comptime_int) !u64 {
|
||||
var in: [512 * KiB]u8 = undefined;
|
||||
prng.random.bytes(in[0..]);
|
||||
|
||||
var tag: [Aead.tag_length]u8 = undefined;
|
||||
|
||||
var key: [Aead.key_length]u8 = undefined;
|
||||
prng.random.bytes(key[0..]);
|
||||
|
||||
var nonce: [Aead.nonce_length]u8 = undefined;
|
||||
prng.random.bytes(nonce[0..]);
|
||||
|
||||
var offset: usize = 0;
|
||||
var timer = try Timer.start();
|
||||
const start = timer.lap();
|
||||
while (offset < bytes) : (offset += in.len) {
|
||||
Aead.encrypt(in[0..], tag[0..], in[0..], &[_]u8{}, nonce, key);
|
||||
Aead.decrypt(in[0..], in[0..], tag, &[_]u8{}, nonce, key) catch unreachable;
|
||||
}
|
||||
blackBox(&in);
|
||||
const end = timer.read();
|
||||
|
||||
const elapsed_s = @intToFloat(f64, end - start) / time.ns_per_s;
|
||||
const throughput = @floatToInt(u64, 2 * bytes / elapsed_s);
|
||||
|
||||
return throughput;
|
||||
}
|
||||
|
||||
fn usage() void {
|
||||
std.debug.warn(
|
||||
\\throughput_test [options]
|
||||
@@ -198,29 +244,36 @@ pub fn main() !void {
|
||||
|
||||
inline for (hashes) |H| {
|
||||
if (filter == null or std.mem.indexOf(u8, H.name, filter.?) != null) {
|
||||
const throughput = try benchmarkHash(H.ty, mode(32 * MiB));
|
||||
try stdout.print("{:>11}: {:5} MiB/s\n", .{ H.name, throughput / (1 * MiB) });
|
||||
const throughput = try benchmarkHash(H.ty, mode(128 * MiB));
|
||||
try stdout.print("{:>17}: {:7} MiB/s\n", .{ H.name, throughput / (1 * MiB) });
|
||||
}
|
||||
}
|
||||
|
||||
inline for (macs) |M| {
|
||||
if (filter == null or std.mem.indexOf(u8, M.name, filter.?) != null) {
|
||||
const throughput = try benchmarkMac(M.ty, mode(128 * MiB));
|
||||
try stdout.print("{:>11}: {:5} MiB/s\n", .{ M.name, throughput / (1 * MiB) });
|
||||
try stdout.print("{:>17}: {:7} MiB/s\n", .{ M.name, throughput / (1 * MiB) });
|
||||
}
|
||||
}
|
||||
|
||||
inline for (exchanges) |E| {
|
||||
if (filter == null or std.mem.indexOf(u8, E.name, filter.?) != null) {
|
||||
const throughput = try benchmarkKeyExchange(E.ty, mode(1000));
|
||||
try stdout.print("{:>11}: {:5} exchanges/s\n", .{ E.name, throughput });
|
||||
try stdout.print("{:>17}: {:7} exchanges/s\n", .{ E.name, throughput });
|
||||
}
|
||||
}
|
||||
|
||||
inline for (signatures) |E| {
|
||||
if (filter == null or std.mem.indexOf(u8, E.name, filter.?) != null) {
|
||||
const throughput = try benchmarkSignatures(E.ty, mode(1000));
|
||||
try stdout.print("{:>11}: {:5} signatures/s\n", .{ E.name, throughput });
|
||||
const throughput = try benchmarkSignature(E.ty, mode(1000));
|
||||
try stdout.print("{:>17}: {:7} signatures/s\n", .{ E.name, throughput });
|
||||
}
|
||||
}
|
||||
|
||||
inline for (aeads) |E| {
|
||||
if (filter == null or std.mem.indexOf(u8, E.name, filter.?) != null) {
|
||||
const throughput = try benchmarkAead(E.ty, mode(128 * MiB));
|
||||
try stdout.print("{:>17}: {:7} MiB/s\n", .{ E.name, throughput / (1 * MiB) });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ fn initContext(key: [8]u32, d: [4]u32) [16]u32 {
|
||||
}
|
||||
|
||||
// The chacha family of ciphers are based on the salsa family.
|
||||
fn chacha20Core(x: []u32, input: [16]u32) void {
|
||||
inline fn chacha20Core(x: []u32, input: [16]u32) void {
|
||||
for (x) |_, i|
|
||||
x[i] = input[i];
|
||||
|
||||
|
||||
@@ -180,10 +180,14 @@ test "hash" {
|
||||
}
|
||||
|
||||
pub const Aead = struct {
|
||||
pub const tag_length = State.RATE;
|
||||
pub const nonce_length = 16;
|
||||
pub const key_length = 32;
|
||||
|
||||
/// ad: Associated Data
|
||||
/// npub: public nonce
|
||||
/// k: private key
|
||||
fn init(ad: []const u8, npub: [16]u8, k: [32]u8) State {
|
||||
fn init(ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) State {
|
||||
var state = State{
|
||||
.data = undefined,
|
||||
};
|
||||
@@ -229,7 +233,7 @@ pub const Aead = struct {
|
||||
/// ad: Associated Data
|
||||
/// npub: public nonce
|
||||
/// k: private key
|
||||
pub fn encrypt(c: []u8, at: *[State.RATE]u8, m: []const u8, ad: []const u8, npub: [16]u8, k: [32]u8) void {
|
||||
pub fn encrypt(c: []u8, at: *[tag_length]u8, m: []const u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) void {
|
||||
assert(c.len == m.len);
|
||||
|
||||
var state = Aead.init(ad, npub, k);
|
||||
@@ -275,7 +279,7 @@ pub const Aead = struct {
|
||||
/// npub: public nonce
|
||||
/// k: private key
|
||||
/// NOTE: the check of the authentication tag is currently not done in constant time
|
||||
pub fn decrypt(m: []u8, c: []const u8, at: [State.RATE]u8, ad: []const u8, npub: [16]u8, k: [32]u8) !void {
|
||||
pub fn decrypt(m: []u8, c: []const u8, at: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) !void {
|
||||
assert(c.len == m.len);
|
||||
|
||||
var state = Aead.init(ad, npub, k);
|
||||
|
||||
Reference in New Issue
Block a user