test: Benchmarks for lock free apply_block#2370
Conversation
|
Oh that's interesting. Good to see our primary hypothesis being validated. I didn't foresee the concurrency effects of the blocked readers 😁 Effectively at high traffic we should expect to only have a single core working on apply block? And perhaps not even that because we are using async so it will be multiplexed with the reader work.. We can probably improve this, with some other tools, e.g. a dedicated thread pool for writes (not good for RPC traffic though), or blocking new readers while we are writing, or some sort of task priority. Regardless though, this looks like a massive win. |
|
Actually I wonder if we can't use a rayon thread pool with a high priority, which is only used for apply block.. That should just work iiuc. And all our actual apply block work is The SMT backend uses |
Summary
These are benchmarks which will be used to compare results from #2345 work.
Preliminary results:
Mixed read/write benchmark: lock-free store vs
nextWorkload:
benchmark-mixed -n 50000 -p 50 --storage-map-entries 64 --account-update-blocks 20 -r 16(238 blocks applied while 16 concurrent readers loop
get_block_headerwith MMR proof; release build, identical parameters on both branches.)next(locks)-r 0below)Read side: confirms the thesis
P50–P99 are identical — uncontended reads cost the same on both branches. The entire
difference is in the extreme tail, exactly where lock waits live. On
next, a readarriving during a commit window waits for the writer's lock; with ~16 readers × 238
blocks those blocked samples are only ~0.02% of 22M reads, so they surface at P99.9
(9.8ms vs 4.1ms) and the true worst case is hidden — blocked reads on
nextcan waitup to a full insertion (~350ms), which the summary doesn't print (no max/P99.99).
Aggregate read throughput is also +13% despite the lock-free readers doing 50% more
total work.
Write side: confirmed as a benchmark artifact
On
next, the lock throttles the readers: during every commit, all 16 readers parkon the RwLock and donate their cores to the writer and the rayon block-building
pipeline. On the lock-free branch, wait-free readers never park — they run straight
through every commit, competing with the writer for CPU.
nextis not writing fasterbecause locks are cheaper; it is writing faster because its readers are forcibly
idled. A closed-loop, unthrottled reader workload maximizes this effect; production
readers are request-driven and would not saturate cores this way.
This was verified by re-running the identical workload with zero readers
(
seed-store, same parameters), isolating the pure write path:Pure write path benchmark (
-r 0): write perf is at paritynext(locks)With no readers, the lock-free branch inserts blocks within 4% of
next, and theper-block distributions overlap almost entirely — so ≤4% (likely ~0) is the true cost
of the new commit path (per-block RocksDB snapshot creation and
ArcSwapsnapshotpublication). Putting all four data points together:
next: 252ms → 355ms under 16 readers (+41% from reader CPU contention)Both writers slow down heavily when unthrottled readers saturate the machine; the
difference is that
nextpartially shields its writer by stalling every readerduring every commit — which is precisely the read-tail cost the lock-free refactor
removes. The only measurable real regression is
get-batch-inputs(+250µs per call,once per block), which now reads through RocksDB snapshot views instead of the live
trees — a footnote, not a concern.
Bottom line: reads no longer stall behind block commits — the blocking tail is
gone (2.4× better P99.9, more at the unreported max) and read throughput is higher —
while the pure write path is unchanged (≤4%, within noise). The apparent write
slowdown in the mixed run is the writer no longer being subsidized by stalled
readers.
Changelog