Stage 6a: a tool that times the age writer

tool/age_writer_bench.dart times the header of each age file of a
capsule, the file of an author key and a large PAYLOAD_AGE in streaming,
with the CSPRNG of the platform. On the VM, 64 MiB take 1.38 s, 46
MiB/s; compiled to JavaScript, 16 MiB take 0.33 s, 49 MiB/s. The header
of PAYLOAD_AGE costs 4 ms, INNER_ACCESS_AGE with 16 stanzas about 45 ms
on the VM and 35 ms in Node.js, OUTER_TIME_AGE 40 ms and 0.55 s, and an
author key file with scrypt of logN 16, 0.6 s and 0.85 s.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
v0.11
dev 2 days ago
parent d2b4c88f8d
commit fbd0dd7b0a

@ -0,0 +1,89 @@
// Times the age writer of datekeys-dart, stage 6a, on the VM and compiled
// to JavaScript: the throughput of a large PAYLOAD_AGE encrypted in
// streaming, in pieces of 1 MiB, 64 MiB on the VM and 16 MiB in Node.js; and
// the header of each file of a capsule: PAYLOAD_AGE for one X25519
// recipient, INNER_ACCESS_AGE for 16 and a control of 103 bytes,
// OUTER_TIME_AGE for the tlock recipient, and the file of an author key, a
// scrypt recipient with logN 16. Each is the median of a few runs. The
// random values come from the CSPRNG of the platform, as in production.
//
// dart run tool/age_writer_bench.dart
//
// dart compile js -O2 -o age_writer_bench.js tool/age_writer_bench.dart
// node -e "globalThis.self = globalThis; require('./age_writer_bench.js')"
//
// ignore_for_file: avoid_print
import 'dart:typed_data';
import 'package:datekeys/datekeys.dart' show quicknet;
import 'package:datekeys/src/age_writer.dart';
import 'package:datekeys/src/random.dart';
import 'package:datekeys/src/recipient.dart';
const isWeb = identical(0, 0.0);
double median(List<double> xs) => (xs..sort())[xs.length ~/ 2];
/// The median time of [f], in milliseconds, over [runs] runs.
double time(int runs, void Function() f) {
final times = <double>[];
for (var i = 0; i < runs; i++) {
final sw = Stopwatch()..start();
f();
times.add(sw.elapsedMicroseconds / 1000);
}
return median(times);
}
void main() {
final id = generateX25519Identity();
final payload = X25519Recipient.of(id);
final slots = [
for (var i = 0; i < 16; i++) X25519Recipient.of(generateX25519Identity()),
];
final tlock = TimeRecipient(quicknet(), 1000);
final scrypt = ScryptRecipient('a passphrase', workFactor: 16);
final control = Uint8List(103);
print('age writer, ${isWeb ? 'Node.js' : 'VM'}');
print(
'PAYLOAD_AGE header, one X25519 stanza: '
'${time(15, () => AgeEncryptor([payload]).close()).toStringAsFixed(2)} ms',
);
print(
'INNER_ACCESS_AGE, 16 X25519 stanzas, 103 bytes: '
'${time(7, () => ageEncrypt(control, slots)).toStringAsFixed(1)} ms',
);
print(
'OUTER_TIME_AGE, the tlock stanza of round 1000, 2128 bytes: '
'${time(5, () => ageEncrypt(Uint8List(1773), [tlock])).toStringAsFixed(0)} ms',
);
print(
'an author key file, scrypt with logN 16: '
'${time(3, () => ageEncrypt(Uint8List(120), [scrypt])).toStringAsFixed(0)} ms',
);
// The throughput in streaming, in pieces of 1 MiB.
final mib = isWeb ? 16 : 64;
final piece = Uint8List(1 << 20);
for (var i = 0; i < piece.length; i++) {
piece[i] = (31 * i + 7) & 0xff;
}
final ms = time(3, () {
final e = AgeEncryptor([payload], random: secureRandom);
var n = e.header.length + e.nonce.length;
for (var i = 0; i < mib; i++) {
for (final c in e.add(piece)) {
n += c.length;
}
}
n += e.close().length;
if (n != ageFileLength([x25519StanzaLength], mib << 20)) {
throw StateError('$n bytes');
}
});
print(
'$mib MiB in streaming: ${(ms / 1000).toStringAsFixed(2)} s, '
'${(mib / ms * 1000).toStringAsFixed(1)} MiB/s',
);
}
Loading…
Cancel
Save

Powered by TurnKey Linux.