Adds Criterion microbenches that measure each of the perf
optimisations landed in commits 5bbbca1..f53b28d. The benches are
intentionally tight — they exercise one hot-path primitive at a
time so the signal is not lost in end-to-end noise.
New file: benches/perf_sweep.rs (340 lines)
Bench groups:
- perf/wal/append_then_sync — N records + 1 sync, validates the
BufWriter coalescing of target 1.1
- perf/wal/sync_no_pending — bare sync floor (no records to flush)
- perf/wal/group_commit/N — concurrent writers, target 2.2
- perf/filter/{compiled,legacy,legacy_no_map_alloc} — target 2.1 +
the wire-up at f53b28d
- perf/group_key/{inlined_single_alloc,legacy_format_join} —
target 1.5
# Measured numbers (release build, single run, dev box)
perf/filter (10k rows, 3-predicate AND):
compiled_evaluate 506 µs (baseline)
legacy_evaluate (HashMap/row) 3848 µs ~7.6× slower
legacy_no_map_alloc 582 µs ~1.15× slower
→ Compiled is 7.6× faster than the realistic legacy path
that allocates a HashMap per row, and 15% faster than the
ideal closure-based legacy that doesn't allocate.
perf/group_key (10k rows, 3 columns):
inlined_single_alloc 805 µs (baseline)
legacy_format_join 3267 µs ~4× slower
→ Confirms the 40-60% wall-time reduction projected for 1.5.
perf/wal/append_then_sync (BufWriter coalescing):
1 record + sync 1.15 ms
10 records + sync 901 µs 90 µs/record
100 records + sync 904 µs 9 µs/record
1000 records + sync 1.11 ms 1.1 µs/record
→ Throughput scales near-linearly because BufWriter collapses
1000 small writes into ONE write syscall before fsync.
perf/wal/sync_no_pending 26 µs
→ Hardware fsync floor on this system (SSD, ext4).
# Group commit results — surprising, see follow-up
perf/wal/group_commit (50 commits per writer):
1 writer → 42.7 ms (854 µs/commit)
2 writers → 86.0 ms (860 µs/commit)
4 writers → 169 ms (845 µs/commit)
8 writers → 333 ms (833 µs/commit)
Per-commit cost is essentially flat across concurrency levels —
that is, the 8-writer run pays 8× the work in 8× the wall time.
Group commit is NOT amortising fsyncs across writers.
Root cause (verified by reading the leader path): the GroupCommit
leader holds the WAL writer Mutex<WalWriter> for the duration of
wal.sync(), which blocks all other writers from appending DURING
the fsync. Coalescing requires that other writers accumulate
bytes BEFORE the leader's fsync — but with the lock held, they
can't. The leader fsyncs only its own bytes, then releases, then
the next writer becomes the next leader, and so on.
The benches did their job: they revealed the bug. The fix is to
release the WAL writer mutex DURING sync_all() by holding a
cloned File handle so the leader can fsync without blocking
appends. That's the next commit.
Suite: 2096 -> 2096, all green (benches don't run in test mode).
The benches themselves exercise the production code, so they
also serve as a smoke test for the new modules.