Stage 4a: a tool that times the rules of paths and the key of words

tool/pathrule_bench.dart times, on the VM or compiled to JavaScript,
checkPath of a path of ASCII and of one that R6c projects with its 15
tables, checkComment of a comment of 16 351 bytes, checkTree of 1000
paths, the largest head (65 535 paths checked and their tree),
normalizeWords with checkWords, and wordKey with its 600 000 iterations
of PBKDF2. Medians of three runs on the VM: 9.5 and 28 microseconds for
the paths, 0.68 ms for the comment, 2.0 s for the largest head and
1.14 s for the key; on Node.js 11.8 and 38 microseconds, 0.63 ms, 2.3 s
and 0.73 s.

The header of tool/pathrule_go_vectors.go says now that its named cases
hold both sides of each limit of R2, R3 and R6b.

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

@ -0,0 +1,103 @@
// Timings of the rules of paths and texts and of the key of words (stage
// 4a), on the VM or compiled to JavaScript:
//
// dart run tool/pathrule_bench.dart
// dart compile js -O2 -o /tmp/pathrule_bench.js tool/pathrule_bench.dart && node /tmp/pathrule_bench.js
//
// ignore_for_file: avoid_print
library;
import 'package:datekeys/src/bytes.dart';
import 'package:datekeys/src/pathrule.dart';
import 'package:datekeys/src/wordkey.dart';
/// The mean time of [f] in milliseconds, over at least [minRuns] runs and
/// about [budgetMs] milliseconds, after one run to warm up.
double time(void Function() f, {int minRuns = 3, int budgetMs = 2000}) {
f();
final sw = Stopwatch()..start();
var runs = 0;
while (runs < minRuns || sw.elapsedMilliseconds < budgetMs) {
f();
runs++;
}
return sw.elapsedMicroseconds / 1000 / runs;
}
String ms(double v) => v < 0.1
? '${(v * 1000).toStringAsFixed(1)} µs'
: '${v.toStringAsFixed(v < 10 ? 2 : 0)} ms';
String cp(List<int> runes) => String.fromCharCodes(runes);
void main() {
// A path of ASCII, and one with letters and a symbol that the best-fit
// tables project, so that R6c projects it with each of its 15 tables.
const ascii = 'fotos/2025/verano/playa con amigos.jpg';
final accented =
'm${cp([0xfa])}sica/canci${cp([0xf3])}n de cuna ${cp([0x2665])}.mp3';
checkPath(ascii);
checkPath(accented);
print(
'checkPath, ${ascii.length} ASCII characters: '
'${ms(time(() => checkPath(ascii)))}',
);
print(
'checkPath, ${accented.length} characters with accents and a symbol: '
'${ms(time(() => checkPath(accented)))}',
);
// A comment of 16 384 bytes, the longest: lines of Spanish with accents,
// an emoji with its selector and a tab.
final line = StringBuffer()
..write('Querida Ana: te escribo desde la playa, donde el a')
..write(cp([0xf1]))
..write('o pasado fuimos juntos ')
..write(cp([0x2764, 0xfe0f]))
..write('\t');
var comment = '';
while (utf8Bytes('$comment$line\n').length <= maxCommentLen) {
comment = '$comment$line\n';
}
checkComment(comment);
print(
'checkComment, ${utf8Bytes(comment).length} bytes: '
'${ms(time(() => checkComment(comment)))}',
);
// The tree of 1000 paths in 10 folders.
final paths = [
for (var i = 0; i < 1000; i++) 'carpeta ${i % 10}/foto $i.jpg',
];
checkTree(paths);
print('checkTree, 1000 paths: ${ms(time(() => checkTree(paths)))}');
// The largest head: 65 535 paths like the second one, in 100 folders,
// each checked and then the tree, as the reader of a head does.
final head = [
for (var i = 0; i < maxImplicitDirs; i++)
'carpeta ${i % 100}/canci${cp([0xf3])}n $i ${cp([0x2665])}.mp3',
];
void checkHead() {
head.forEach(checkPath);
checkTree(head);
}
print(
'checkPath of each and checkTree, 65 535 paths: '
'${ms(time(checkHead, minRuns: 1, budgetMs: 0))}',
);
final words = normalizeWords('Perro LUNA casa verde tr${cp([0xe9])}n mar');
checkWords(words);
print(
'normalizeWords and checkWords: '
'${ms(time(() => checkWords(normalizeWords('Perro LUNA casa verde tren mar'))))}',
);
final chain = List<int>.filled(32, 0x52);
final id = List<int>.generate(16, (i) => i);
print(
'wordKey, 600 000 iterations of PBKDF2: '
'${ms(time(() => wordKey(words, chain, 1000, id), minRuns: 3, budgetMs: 0))}',
);
}

@ -7,11 +7,12 @@
// hand.
//
// - strings: the cases of the tests of internal/pathrule and of
// pathrule.test.ts of datekeys-ts, then strings drawn from a fixed seed,
// pathrule.test.ts of datekeys-ts, edges of this port and both sides of
// each limit of R2, R3 and R6b; then strings drawn from a fixed seed,
// random and adversarial: combining marks in and out of canonical order,
// Hangul, the ignorables and the whitelist of R4 in every place, emoji
// with and without their selectors, the best-fit look-alikes of ASCII,
// device names and 8.3 aliases, segments at the limits of R2 and R3,
// device names and 8.3 aliases, segments near the limits of R2 and R3,
// texts of several lines, and bytes that are not valid UTF-8. Each with
// the result of CheckPath, CheckComment and CheckAuthor, and its NFD and
// Key. Fold maps each code point alone: the planes check it.

Loading…
Cancel
Save

Powered by TurnKey Linux.