Benchmarks.
Two causl engines, measured against Jotai, Redux Toolkit and MobX by
causl-bench:
causl-ts is @causlts/core in TypeScript, and
causl-wasm is the @causl/causl-wasm-ts client on the Rust
causl-core-rs engine. Each cell is one (library × scenario × scale),
timed on a world built fresh for the step, in its own process. The charts show median
and p95 run by run; the section below reports the
latest sweep in full, cell by cell.
Every causl cell carries a runtime attestation of the engine that
executed the graph that was timed, so the engine label is an observation rather than
a configuration flag. That distinction is not academic. This page once served three
wasm-labelled series, two of which were the same TypeScript engine under a wasm
label, published for months before anyone noticed. Those two series are now
deleted from the feed rather than left on the charts under a
correction, and the one attested series carries the name: there is exactly one
causl-wasm line here, and every point on it is Rust.
Per-cell median + p95 + verdict.
The page first tries ./history.json; if the
nightly publish hasn't landed yet it falls back to
./history.sample.json, a checked-in multi-run
stub so the dashboard is never empty. The “source” cell in the
meta strip below tells you which one is showing.
Each cell carries a profile link that points at
./profiles/latest/<library-scenario-scale>/.
Those directories are populated by the nightly
perf-snapshot workflow, which is currently parked
in .github/workflows-disabled/perf-snapshot.yml
until GitHub Actions is re-enabled.
The links remain useful as discoverable placeholders so the
dashboard surface doesn't change shape when CI is restored.
The boundaryCrossings field, and why it says nothing
A handful of the 2026-05 entries in
history.json carry a
boundaryCrossings integer meant to count JS↔WASM bridge crossings
inside the timed region. After the withdrawn wasm series were deleted, the only rows
left carrying it are causl-ts rows, where it is 0 because
there is no bridge. So the field survives as a residue of a retired harness and
measures nothing on the series anyone would want it for.
It is also not the interesting quantity. A raw boundary crossing costs about
1.6 ns on this host, so the ~2,000 crossings a
linear-chain@1000 iteration makes come to roughly
0.13% of that cell. The current sweeps do not publish the field;
the cells built to probe it are inapplicable on every runner.
Charting library: vanilla SVG (decision log)
D3 (~70 KB minified) and Plotly (~3.5 MB minified) were both
evaluated as upgrades over the inline-SVG MVP. We stayed on
vanilla SVG and brought the visible upgrades directly into
dashboard.js: cubic-Hermite (Catmull-Rom α=0.5)
line smoothing, D3-style "nice" axis ticks at
1/2/5 × 10k, and per-point hover tooltips covering
every run.
The trade we declined: a lazy-loaded D3 bundle would have
added ~70 KB of parse/compile cost to a page whose only
interactive surface is a small per-cell sparkline grid.
Lighthouse Performance budgets penalise any third-party JS
that touches the main thread post-paint, and Plotly is
two orders of magnitude over budget. The visible upgrades
the issue wanted were ~80 lines of inline math each, not a
framework dependency. The dashboard ships with zero external
runtime dependencies; the <noscript> block
below points no-JS readers at the raw history JSON.
Two withdrawn series were removed from this feed, not corrected in place.
Until now this page served three wasm-labelled series. Two of them,
causl-wasm and causl-wasm-all, were produced by the
TypeScript engine running under a wasm label: their engine tag was
asserted by configuration, never observed. Their generator was deleted upstream in
causl-bench#110, which
stopped new numbers without retracting the ones this feed was still serving.
So I deleted their 219 samples and 85 skip rows from
history.json outright. I did not relabel
them: relabelling would attach a Rust-engine claim to a TypeScript-engine
measurement, which is the original defect wearing a new name. Losing those
historical points is the intended cost. What remains is one causl-wasm
series, every cell of it attested at runtime to the Rust engine that ran it:
the latest sweep, in full →
Every cell, and which engine actually ran it.
One archived causl-bench sweep, reported in full: 395 measured cells and
55 that produced no number, across 5 runners. Each causl cell carries a
runtime attestation of the engine that executed the graph that was timed, so the engine label is an
observation rather than a configuration flag. Asserting that label instead of observing it is exactly
what put two withdrawn series on this page, and the reason they were deleted from the feed rather than
annotated on it.
Provenance.
- run
20260821T095952Z-09524a1-47177732, captured2026-08-21T10:21:49.972Z - host
Apple M5·darwin/arm64· nodev26.5.1 causl-ts→@causlts/core@0.3.3· enginejs-ssotattestedcausl-wasm→@causl/causl-wasm-ts@0.7.0· enginerust-ssotattestedjotai→jotai@2.20.2mobx→mobx@6.16.1redux-rtk→@reduxjs/toolkit@2.9.0
The full aggregate, its four integrity digests and the per-runner inputs are archived in
causl-bench under runners/archive/20260821T095952Z-09524a1-47177732/. The raw rows behind every
table below are in history.json.
Cross-library comparison: 5 runners, 21 ranked groups
Median wall-time in milliseconds for one step on a world built fresh for that step. Fastest in each row is marked; rows whose winner falls inside the tie band read tied, and rows the aggregator refused to crown read withheld. Rank comes from the aggregate, not from sorting these medians. It applies the tie band and the work-equivalence exclusions, and a table that sorted for itself would quietly disagree with both.
| Cell | causl-ts | causl-wasm | jotai | mobx | redux-rtk | fastest |
|---|---|---|---|---|---|---|
avoidablePropagation @ 10000 | 0.6739 high | 0.2916 high | 0.0755 high | 0.2639 high | mobx | |
dynamic-dep-flip @ 100 | 0.0072 high | 0.0551 high | 0.0111 high | 0.0087 high | causl-ts | |
dynamic-dep-flip @ 10000 | 0.0716 high | 0.0082 high | mobx | |||
equality-cutoff-noop @ 10000 | 0.0024 high | 0.0249 high | 3.464 high | 0.1315 high | 2.425 high | causl-ts |
linear-chain @ 100 | 0.3299 high | 0.0339 high | 0.0293 high | redux-rtk | ||
linear-chain @ 1000 | 2.390 high | 0.8975 high | 0.1649 high | 0.2645 high | mobx | |
linear-chain @ 10000 | 4.149 high | 26.96 high | inapplicable | inapplicable | inapplicable | causl-ts |
mux @ 100 | 3.484 high | 24.77 high | 10.43 high | 2.188 high | 4.154 high | mobx |
mux @ 1000 | 24.67 high | 10.30 high | 2.217 high | 4.171 high | mobx | |
mux @ 10000 | 3.561 high | 24.75 high | 10.42 high | 2.187 high | 4.148 high | mobx |
mux-ballast @ 100 | 3.543 high | 24.67 high | 10.41 high | 2.249 high | 4.139 high | mobx |
mux-ballast @ 1000 | 3.507 high | 25.68 high | 10.42 high | 2.259 high | 4.169 high | mobx |
mux-ballast @ 10000 | 3.687 high | 27.33 high | 10.50 high | 2.502 high | 4.295 high | mobx |
op-commit-noderived-1k @ 1000 | 4.476 high | 0.0326 high | mobx | |||
op-input-create-1k @ 1000 | 0.1531 high | 0.0464 high | 85.33 high | jotai | ||
scrolling-viewport @ 100 | 0.0036 high | 0.0271 high | 0.0480 high | 0.0080 high | 0.4586 high | causl-ts |
subscriber-churn-1k @ 100 | 0.0229 high | 6.671 high | 0.1876 high | 0.8736 high | causl-ts | |
subscriber-churn-1k @ 1000 | 0.1771 high | 7.163 high | 6.448 high | 3.779 high | causl-ts | |
subscriber-churn-1k @ 10000 | 0.1719 high | 6.898 high | 6.339 high | 14.60 high | 61.04 high | causl-ts |
unstable @ 100 | 0.0092 high | 0.0153 high | 0.0081 high | 0.0075 high | redux-rtk | |
unstable @ 10000 | 0.2758 high | 0.4080 high | 0.0678 high | redux-rtk |
Full results: every cell, every runner
One block per runner, 90 cells each. p95 is the 95th percentile of the
repetitions the runner kept, not a model fitted to the median. 143 of 395 measured cells
declare themselves low-confidence; the chip on each row says which, and a low-confidence median is a number
whose own driver says not to quote it.
causl-ts: 84 measured, 6 not measured · @causlts/core@0.3.3
| Cell | median (ms) | p95 (ms) | 95% interval | CoV | confidence | recomputes |
|---|---|---|---|---|---|---|
adversarial-fanin-100 @ 100 | 0.0230 | 0.0283 | 0.0219–0.0250 | 29.5% | low | 1 |
adversarial-fanin-100 @ 1000 | 0.0226 | 0.0559 | 0.0210–0.0241 | 46.9% | low | 1 |
adversarial-fanin-100 @ 10000 | 0.0206 | 0.0252 | 0.0197–0.0218 | 9.3% | high | 1 |
async-race @ 100 | inapplicable: the engine commits synchronously; an async race has no analogue without inventing a scheduler that is not part of the library | |||||
async-race @ 1000 | inapplicable: the engine commits synchronously; an async race has no analogue without inventing a scheduler that is not part of the library | |||||
async-race @ 10000 | inapplicable: the engine commits synchronously; an async race has no analogue without inventing a scheduler that is not part of the library | |||||
avoidablePropagation @ 100 | 0.0098 | 0.0129 | 0.0077–0.0105 | 23.4% | low | 2 |
avoidablePropagation @ 1000 | 0.0244 | 0.0291 | 0.0231–0.0255 | 8.8% | high | 2 |
avoidablePropagation @ 10000 | 0.1720 | 0.2271 | 0.1588–0.1923 | 15.1% | low | 2 |
batch-commit @ 100 | 0.0455 | 0.0742 | 0.0439–0.0488 | 19.1% | low | 1 |
batch-commit @ 1000 | 1.258 | 1.306 | 1.250–1.297 | 2.3% | high | 1 |
batch-commit @ 10000 | 97.02 | 113.01 | 96.50–110.22 | 7.6% | low | 1 |
commit-firehose-1000-subs @ 100 | 0.0216 | 0.0245 | 0.0199–0.0228 | 10.0% | high | 1 |
commit-firehose-1000-subs @ 1000 | 0.0208 | 0.0244 | 0.0201–0.0218 | 9.7% | high | 1 |
commit-firehose-1000-subs @ 10000 | 0.0202 | 0.0226 | 0.0200–0.0209 | 6.1% | high | 1 |
commit-firehose-subs @ 100 | 0.0071 | 0.0075 | 0.0070–0.0072 | 9.6% | high | 1 |
commit-firehose-subs @ 1000 | 0.0203 | 0.0237 | 0.0195–0.0220 | 8.1% | high | 1 |
commit-firehose-subs @ 10000 | 0.1047 | 0.1123 | 0.1031–0.1059 | 9.8% | high | 1 |
commit-idle-subs @ 100 | 0.0033 | 0.0037 | 0.0032–0.0034 | 6.4% | high | 0 |
commit-idle-subs @ 1000 | 0.0037 | 0.0045 | 0.0034–0.0039 | 12.0% | high | 0 |
commit-idle-subs @ 10000 | 0.0040 | 0.0047 | 0.0036–0.0043 | 13.5% | high | 0 |
diamond @ 100 | 0.0082 | 0.0096 | 0.0077–0.0085 | 10.0% | high | 3 |
diamond @ 1000 | 0.0081 | 0.0118 | 0.0075–0.0093 | 23.7% | low | 3 |
diamond @ 10000 | 0.0078 | 0.0101 | 0.0070–0.0086 | 25.9% | low | 3 |
dynamic-dep-flip @ 100 | 0.0072 | 0.0082 | 0.0068–0.0074 | 8.9% | high | 1 |
dynamic-dep-flip @ 1000 | 0.0073 | 0.0085 | 0.0069–0.0076 | 11.0% | high | 1 |
dynamic-dep-flip @ 10000 | 0.0087 | 0.0156 | 0.0082–0.0092 | 74.1% | low | 1 |
equality-cutoff @ 100 | 0.0135 | 0.0171 | 0.0131–0.0144 | 11.7% | high | 10 |
equality-cutoff @ 1000 | 0.0464 | 0.0526 | 0.0443–0.0483 | 19.1% | low | 100 |
equality-cutoff @ 10000 | 0.6279 | 0.6786 | 0.6219–0.6368 | 18.1% | low | 1000 |
equality-cutoff-fanout-10k @ 100 | 0.0208 | 0.0217 | 0.0204–0.0210 | 2.8% | high | 1 |
equality-cutoff-fanout-10k @ 1000 | 0.7288 | 0.7417 | 0.7255–0.7314 | 1.5% | high | 1 |
equality-cutoff-fanout-10k @ 10000 | 91.90 | 98.48 | 91.62–92.94 | 3.0% | high | 1 |
equality-cutoff-noop @ 100 | 0.0022 | 0.0028 | 0.0019–0.0025 | 15.7% | low | 0 |
equality-cutoff-noop @ 1000 | 0.0021 | 0.0025 | 0.0020–0.0023 | 9.4% | high | 0 |
equality-cutoff-noop @ 10000 | 0.0024 | 0.0028 | 0.0023–0.0025 | 9.1% | high | 0 |
linear-chain @ 100 | 0.0409 | 0.0491 | 0.0384–0.0430 | 9.9% | low | 100 |
linear-chain @ 1000 | 0.6082 | 0.6591 | 0.4197–0.6256 | 20.0% | low | 1000 |
linear-chain @ 10000 | 4.149 | 4.273 | 4.038–4.218 | 4.3% | high | 10000 |
mixed-editor-60s-seed42 @ 100 | 0.0047 | 0.0053 | 0.0046–0.0051 | 7.0% | high | 1 |
mixed-editor-60s-seed42 @ 1000 | 0.0062 | 0.0077 | 0.0059–0.0066 | 14.2% | high | 1 |
mixed-editor-60s-seed42 @ 10000 | 0.0138 | 0.0160 | 0.0131–0.0149 | 11.2% | high | 1 |
mixed-editor-60s-seed42-50k @ 100 | 0.0258 | 0.0292 | 0.0246–0.0270 | 7.9% | high | 1 |
mixed-editor-60s-seed42-50k @ 1000 | 0.0248 | 0.0294 | 0.0237–0.0258 | 9.5% | high | 1 |
mixed-editor-60s-seed42-50k @ 10000 | 0.0260 | 0.0340 | 0.0251–0.0284 | 15.0% | low | 1 |
mux @ 100 | 3.484 | 3.692 | 3.467–3.541 | 3.1% | high | 10200 |
mux @ 1000 | 3.475 | 3.694 | 3.435–3.513 | 3.6% | low | 10200 |
mux @ 10000 | 3.561 | 3.804 | 3.537–3.586 | 4.3% | high | 10200 |
mux-ballast @ 100 | 3.543 | 3.716 | 3.521–3.633 | 3.8% | high | 10200 |
mux-ballast @ 1000 | 3.507 | 3.572 | 3.494–3.538 | 1.0% | high | 10200 |
mux-ballast @ 10000 | 3.687 | 3.757 | 3.679–3.708 | 1.5% | high | 10200 |
op-commit-noderived-1k @ 1000 | 0.2951 | 0.3274 | 0.2892–0.3188 | 5.9% | low | 0 |
op-commit-rollback-1k @ 1000 | 8.510 | 8.585 | 8.468–8.526 | 0.7% | high | 499 |
op-derived-create-1k-fresh @ 1000 | 0.5292 | 0.5657 | 0.5171–0.5424 | 6.2% | high | 1000 |
op-derived-recompute-1k @ 1000 | 0.6189 | 0.6781 | 0.6148–0.6290 | 17.8% | low | 1000 |
op-derived-rollback-1k @ 1000 | 0.7363 | 0.7643 | 0.7189–0.7519 | 6.1% | low | 1000 |
op-input-create-1k @ 1000 | 0.1531 | 0.1574 | 0.1498–0.1545 | 7.9% | high | 0 |
op-no-subscriber-firehose-1k @ 1000 | 0.3378 | 0.3825 | 0.3350–0.3649 | 10.1% | low | 0 |
op-phase-d-bfs-1k @ 1000 | 1.342 | 1.365 | 1.341–1.356 | 1.9% | high | 1001 |
op-read-cold-1k @ 1000 | 0.0184 | 0.0191 | 0.0166–0.0188 | 13.6% | low | 0 |
op-rust-bridge-floor-1k @ 1000 | inapplicable: no Rust bridge exists on the TypeScript engine | |||||
op-subscribe-dispose-1k-pairs @ 1000 | 0.2006 | 0.2206 | 0.1680–0.2106 | 12.3% | low | 0 |
op-tx-set-equal-1k @ 1000 | 0.1656 | 0.1738 | 0.1632–0.1670 | 6.2% | high | 0 |
op-tx-set-isolated-1k @ 1000 | 0.3135 | 0.3598 | 0.3113–0.3467 | 6.3% | low | 0 |
op-tx-shadow-read-1k @ 1000 | inapplicable: the read-shadow path is not reachable from the public API: the transaction object handed to `commit` exposes only `set` (probed at runtime on 0.3.x, with no `get`). The original cell reached an internal `tx.get`, so measuring it would require an engine change, not a runner change | |||||
op-wasm-boundary-1k @ 1000 | inapplicable: this runner is the TypeScript engine, so there is no JS/WASM boundary to cross. The original cell measured `wasmCommitStub`, a Phase-0 stub whose package is gitignored and absent, so it never measured a real bridge either | |||||
readFraction @ 100 | 0.0662 | 0.0712 | 0.0631–0.0674 | 5.8% | low | 200 |
readFraction @ 1000 | 0.5144 | 0.5536 | 0.4890–0.5256 | 5.6% | high | 2000 |
readFraction @ 10000 | 5.850 | 6.015 | 5.802–5.898 | 1.7% | high | 20000 |
repeatedObservers @ 100 | 0.0065 | 0.0072 | 0.0061–0.0067 | 7.1% | low | 1 |
repeatedObservers @ 1000 | 0.0163 | 0.0208 | 0.0150–0.0174 | 14.4% | high | 1 |
repeatedObservers @ 10000 | 0.0814 | 0.0839 | 0.0812–0.0816 | 1.2% | high | 1 |
scrolling-viewport @ 100 | 0.0036 | 0.0043 | 0.0035–0.0037 | 10.3% | high | 0 |
scrolling-viewport @ 1000 | 0.0033 | 0.0047 | 0.0031–0.0036 | 19.1% | low | 0 |
scrolling-viewport @ 10000 | 0.0042 | 0.0057 | 0.0039–0.0044 | 19.3% | low | 0 |
scrolling-viewport-derived @ 100 | 0.0055 | 0.0097 | 0.0051–0.0066 | 25.2% | low | 1 |
scrolling-viewport-derived @ 1000 | 0.0061 | 0.0076 | 0.0057–0.0068 | 16.0% | low | 1 |
scrolling-viewport-derived @ 10000 | 0.0060 | 0.0075 | 0.0059–0.0064 | 14.1% | high | 1 |
spreadsheet-100x100 @ 100 | 0.0239 | 0.0265 | 0.0231–0.0246 | 6.5% | high | 1 |
spreadsheet-100x100 @ 1000 | 0.0230 | 0.0253 | 0.0222–0.0233 | 6.5% | high | 1 |
spreadsheet-100x100 @ 10000 | 0.0237 | 0.0248 | 0.0233–0.0243 | 5.0% | high | 1 |
subscriber-churn-1k @ 100 | 0.0229 | 0.0246 | 0.0224–0.0235 | 3.9% | high | 0 |
subscriber-churn-1k @ 1000 | 0.1771 | 0.1797 | 0.1757–0.1777 | 2.2% | high | 0 |
subscriber-churn-1k @ 10000 | 0.1719 | 0.1760 | 0.1689–0.1731 | 3.6% | high | 0 |
subscriber-churn-derived @ 100 | 0.0268 | 0.0283 | 0.0262–0.0272 | 12.3% | high | 0 |
subscriber-churn-derived @ 1000 | 0.2007 | 0.2104 | 0.1989–0.2071 | 3.2% | low | 0 |
subscriber-churn-derived @ 10000 | 0.2022 | 0.2105 | 0.1995–0.2034 | 2.6% | high | 0 |
unstable @ 100 | 0.0092 | 0.0114 | 0.0090–0.0100 | 9.7% | high | 3 |
unstable @ 1000 | 0.0239 | 0.0381 | 0.0225–0.0273 | 23.5% | low | 21 |
unstable @ 10000 | 0.2758 | 0.2888 | 0.2722–0.2811 | 4.4% | high | 201 |
causl-wasm: 84 measured, 6 not measured · @causl/causl-wasm-ts@0.7.0
| Cell | median (ms) | p95 (ms) | 95% interval | CoV | confidence | recomputes |
|---|---|---|---|---|---|---|
adversarial-fanin-100 @ 100 | 0.0746 | 0.0820 | 0.0705–0.0790 | 8.4% | low | 1 |
adversarial-fanin-100 @ 1000 | 0.0719 | 0.0934 | 0.0677–0.0790 | 14.0% | high | 1 |
adversarial-fanin-100 @ 10000 | 0.0723 | 0.0823 | 0.0700–0.0748 | 7.6% | low | 1 |
async-race @ 100 | inapplicable: the engine commits synchronously; an async race has no analogue without inventing a scheduler that is not part of the library | |||||
async-race @ 1000 | inapplicable: the engine commits synchronously; an async race has no analogue without inventing a scheduler that is not part of the library | |||||
async-race @ 10000 | inapplicable: the engine commits synchronously; an async race has no analogue without inventing a scheduler that is not part of the library | |||||
avoidablePropagation @ 100 | 0.0508 | 0.0656 | 0.0492–0.0582 | 46.2% | low | 2 |
avoidablePropagation @ 1000 | 0.1213 | 0.1327 | 0.1193–0.1243 | 6.5% | high | 2 |
avoidablePropagation @ 10000 | 0.6739 | 0.7022 | 0.6676–0.6813 | 2.3% | high | 2 |
batch-commit @ 100 | 0.2060 | 0.2289 | 0.1986–0.2191 | 6.9% | high | 1 |
batch-commit @ 1000 | 1.410 | 1.507 | 1.397–1.457 | 3.7% | high | 1 |
batch-commit @ 10000 | 12.45 | 12.92 | 12.40–12.53 | 1.6% | high | 1 |
commit-firehose-1000-subs @ 100 | 0.3584 | 0.3935 | 0.3513–0.3702 | 4.8% | high | 1 |
commit-firehose-1000-subs @ 1000 | 0.3523 | 0.3866 | 0.3445–0.3689 | 6.0% | high | 1 |
commit-firehose-1000-subs @ 10000 | 0.3567 | 0.4072 | 0.3515–0.3660 | 6.5% | high | 1 |
commit-firehose-subs @ 100 | 0.0879 | 0.0997 | 0.0843–0.0904 | 8.7% | high | 1 |
commit-firehose-subs @ 1000 | 0.3530 | 0.3899 | 0.3421–0.3646 | 5.4% | high | 1 |
commit-firehose-subs @ 10000 | 1.751 | 1.822 | 1.731–1.778 | 2.2% | high | 1 |
commit-idle-subs @ 100 | 0.0267 | 0.0321 | 0.0261–0.0283 | 12.8% | high | 0 |
commit-idle-subs @ 1000 | 0.0262 | 0.0279 | 0.0246–0.0267 | 6.2% | high | 0 |
commit-idle-subs @ 10000 | 0.0548 | 0.0637 | 0.0528–0.0589 | 10.1% | high | 0 |
diamond @ 100 | 0.0570 | 0.0803 | 0.0556–0.0653 | 15.7% | low | 3 |
diamond @ 1000 | 0.0613 | 0.0704 | 0.0583–0.0635 | 12.0% | high | 3 |
diamond @ 10000 | 0.0584 | 0.0750 | 0.0554–0.0660 | 12.6% | high | 3 |
dynamic-dep-flip @ 100 | 0.0551 | 0.0690 | 0.0510–0.0577 | 12.3% | high | 2 |
dynamic-dep-flip @ 1000 | 0.0515 | 0.0701 | 0.0504–0.0554 | 15.7% | low | 2 |
dynamic-dep-flip @ 10000 | 0.0716 | 0.0805 | 0.0681–0.0744 | 7.6% | high | 2 |
equality-cutoff @ 100 | 0.0932 | 0.1059 | 0.0871–0.0988 | 9.9% | high | 10 |
equality-cutoff @ 1000 | 0.3297 | 0.3630 | 0.3225–0.3349 | 5.1% | high | 100 |
equality-cutoff @ 10000 | 2.431 | 2.477 | 2.411–2.442 | 1.9% | high | 1000 |
equality-cutoff-fanout-10k @ 100 | 0.2720 | 0.3045 | 0.2616–0.2767 | 6.0% | high | 1 |
equality-cutoff-fanout-10k @ 1000 | 1.631 | 2.141 | 1.595–1.658 | 13.7% | low | 1 |
equality-cutoff-fanout-10k @ 10000 | 1.682 | 2.153 | 1.650–2.112 | 13.1% | high | 1 |
equality-cutoff-noop @ 100 | 0.0237 | 0.0260 | 0.0223–0.0244 | 7.7% | high | 0 |
equality-cutoff-noop @ 1000 | 0.0248 | 0.0328 | 0.0240–0.0279 | 15.0% | low | 0 |
equality-cutoff-noop @ 10000 | 0.0249 | 0.0270 | 0.0245–0.0261 | 6.7% | high | 0 |
linear-chain @ 100 | 0.3299 | 0.3468 | 0.3276–0.3379 | 4.8% | high | 100 |
linear-chain @ 1000 | 2.390 | 2.517 | 2.358–2.425 | 2.6% | high | 1000 |
linear-chain @ 10000 | 26.96 | 27.47 | 26.80–27.14 | 1.5% | high | 10000 |
mixed-editor-60s-seed42 @ 100 | 0.0440 | 0.0508 | 0.0423–0.0467 | 8.9% | high | 1 |
mixed-editor-60s-seed42 @ 1000 | 0.0462 | 0.0536 | 0.0451–0.0492 | 8.3% | low | 1 |
mixed-editor-60s-seed42 @ 10000 | 0.0977 | 0.1193 | 0.0944–0.1034 | 19.8% | low | 1 |
mixed-editor-60s-seed42-50k @ 100 | 0.1475 | 0.1779 | 0.1452–0.1533 | 8.6% | high | 1 |
mixed-editor-60s-seed42-50k @ 1000 | 0.1451 | 0.1611 | 0.1406–0.1492 | 6.3% | high | 1 |
mixed-editor-60s-seed42-50k @ 10000 | 0.1478 | 0.1635 | 0.1461–0.1541 | 6.1% | high | 1 |
mux @ 100 | 24.77 | 25.00 | 24.33–24.92 | 1.9% | high | 10200 |
mux @ 1000 | 24.67 | 24.86 | 24.20–24.75 | 2.1% | high | 10200 |
mux @ 10000 | 24.75 | 25.05 | 24.29–24.80 | 2.2% | high | 10200 |
mux-ballast @ 100 | 24.67 | 25.13 | 24.47–24.85 | 1.9% | high | 10200 |
mux-ballast @ 1000 | 25.68 | 25.93 | 25.25–25.81 | 1.8% | high | 10200 |
mux-ballast @ 10000 | 27.33 | 27.72 | 26.88–27.48 | 2.0% | high | 10200 |
op-commit-noderived-1k @ 1000 | 4.476 | 4.785 | 4.430–4.505 | 4.4% | high | 0 |
op-commit-rollback-1k @ 1000 | 6.604 | 7.024 | 6.392–6.794 | 4.4% | low | 499 |
op-derived-create-1k-fresh @ 1000 | 1.983 | 2.150 | 1.949–2.020 | 5.0% | low | 1000 |
op-derived-recompute-1k @ 1000 | 2.401 | 2.485 | 2.377–2.432 | 2.1% | high | 1000 |
op-derived-rollback-1k @ 1000 | 28.91 | 29.68 | 28.76–29.05 | 3.4% | high | 1000 |
op-input-create-1k @ 1000 | 1.191 | 1.309 | 1.175–1.216 | 4.8% | low | 0 |
op-no-subscriber-firehose-1k @ 1000 | 4.732 | 5.102 | 4.580–4.820 | 4.2% | low | 0 |
op-phase-d-bfs-1k @ 1000 | 2.554 | 2.649 | 2.523–2.572 | 2.2% | high | 1001 |
op-read-cold-1k @ 1000 | 0.0672 | 0.1051 | 0.0564–0.0950 | 27.9% | low | 0 |
op-rust-bridge-floor-1k @ 1000 | inapplicable: same Phase-0 stub seam as op-wasm-boundary-1k. The bridge floor is a TIME and the attestation reports a COUNT, so the attestation cannot cover it: this cell is an outstanding gap, not a satisfied one. Measuring it needs a real round-trip against the shipped bridge | |||||
op-subscribe-dispose-1k-pairs @ 1000 | 1.938 | 2.115 | 1.927–2.048 | 3.8% | low | 0 |
op-tx-set-equal-1k @ 1000 | 3.277 | 3.682 | 3.263–3.328 | 4.8% | low | 0 |
op-tx-set-isolated-1k @ 1000 | 4.659 | 5.180 | 4.602–4.931 | 6.0% | low | 0 |
op-tx-shadow-read-1k @ 1000 | inapplicable: the read-shadow path is not reachable from the public API: the transaction object handed to `commit` exposes only `set` (probed at runtime on 0.3.x, with no `get`). The original cell reached an internal `tx.get`, so measuring it would require an engine change, not a runner change | |||||
op-wasm-boundary-1k @ 1000 | inapplicable: this measured `wasmCommitStub`, a Phase-0 stub seam whose package (wasm-stub-pkg/) is gitignored and absent. It never measured the real bridge, and resurrecting it would measure a stub rather than the engine. NOTE: the attestation does NOT substitute for this: it counts crossings and never times them, so the boundary COST is currently unmeasured, not covered | |||||
readFraction @ 100 | 0.5514 | 0.5953 | 0.5394–0.5601 | 4.3% | low | 200 |
readFraction @ 1000 | 4.729 | 4.844 | 4.700–4.769 | 1.4% | high | 2000 |
readFraction @ 10000 | 55.51 | 57.57 | 55.17–55.97 | 2.0% | high | 20000 |
repeatedObservers @ 100 | 0.0435 | 0.0536 | 0.0426–0.0452 | 11.5% | high | 1 |
repeatedObservers @ 1000 | 0.0590 | 0.0703 | 0.0562–0.0628 | 12.6% | high | 1 |
repeatedObservers @ 10000 | 0.1686 | 0.1747 | 0.1661–0.1712 | 2.4% | high | 1 |
scrolling-viewport @ 100 | 0.0271 | 0.0310 | 0.0264–0.0286 | 8.6% | high | 0 |
scrolling-viewport @ 1000 | 0.0280 | 0.0340 | 0.0271–0.0288 | 9.7% | high | 0 |
scrolling-viewport @ 10000 | 0.0578 | 0.0933 | 0.0550–0.0665 | 24.6% | low | 0 |
scrolling-viewport-derived @ 100 | 0.0447 | 0.0532 | 0.0422–0.0477 | 10.0% | high | 1 |
scrolling-viewport-derived @ 1000 | 0.0471 | 0.0626 | 0.0430–0.0508 | 15.2% | low | 1 |
scrolling-viewport-derived @ 10000 | 0.0667 | 0.0839 | 0.0637–0.0726 | 12.7% | low | 1 |
spreadsheet-100x100 @ 100 | 0.1060 | 0.1391 | 0.1019–0.1101 | 15.5% | low | 1 |
spreadsheet-100x100 @ 1000 | 0.1023 | 0.1073 | 0.0997–0.1038 | 5.6% | high | 1 |
spreadsheet-100x100 @ 10000 | 0.1037 | 0.1131 | 0.0975–0.1071 | 7.6% | high | 1 |
subscriber-churn-1k @ 100 | 0.3899 | 0.4375 | 0.3811–0.4080 | 5.8% | low | 0 |
subscriber-churn-1k @ 1000 | 7.163 | 7.298 | 7.044–7.205 | 1.6% | high | 0 |
subscriber-churn-1k @ 10000 | 6.898 | 7.022 | 6.869–6.942 | 1.3% | high | 0 |
subscriber-churn-derived @ 100 | 0.4830 | 0.5110 | 0.4742–0.4908 | 3.4% | high | 0 |
subscriber-churn-derived @ 1000 | 7.319 | 7.523 | 7.278–7.414 | 1.7% | high | 0 |
subscriber-churn-derived @ 10000 | 7.325 | 7.552 | 7.258–7.400 | 2.2% | high | 0 |
unstable @ 100 | 0.0666 | 0.0729 | 0.0649–0.0682 | 13.1% | low | 4 |
unstable @ 1000 | 0.1532 | 0.1787 | 0.1503–0.1668 | 9.4% | high | 22 |
unstable @ 10000 | 0.7856 | 0.8207 | 0.7751–0.7962 | 3.1% | low | 202 |
jotai: 73 measured, 17 not measured · jotai@2.20.2
| Cell | median (ms) | p95 (ms) | 95% interval | CoV | confidence | recomputes |
|---|---|---|---|---|---|---|
adversarial-fanin-100 @ 100 | skipped: measuring this cell to the declared protocol projects to 570s, above the 240s cell budget: one fresh-world cycle costs 18994.4 ms and the protocol needs at least 30 of them. This scenario's setup dominates its measured step, so the honest options are to raise --cell-budget-ms or to split setup from step in the scenario, not to measure fewer than 20 repetitions. | |||||
adversarial-fanin-100 @ 1000 | skipped: measuring this cell to the declared protocol projects to 591s, above the 240s cell budget: one fresh-world cycle costs 19699.7 ms and the protocol needs at least 30 of them. This scenario's setup dominates its measured step, so the honest options are to raise --cell-budget-ms or to split setup from step in the scenario, not to measure fewer than 20 repetitions. | |||||
adversarial-fanin-100 @ 10000 | skipped: measuring this cell to the declared protocol projects to 571s, above the 240s cell budget: one fresh-world cycle costs 19028.9 ms and the protocol needs at least 30 of them. This scenario's setup dominates its measured step, so the honest options are to raise --cell-budget-ms or to split setup from step in the scenario, not to measure fewer than 20 repetitions. | |||||
async-race @ 100 | 0.0053 | 0.0073 | 0.0049–0.0056 | 17.6% | low | 0 |
async-race @ 1000 | 0.0051 | 0.0082 | 0.0049–0.0055 | 21.5% | low | 0 |
async-race @ 10000 | 0.0053 | 0.0067 | 0.0051–0.0056 | 13.6% | high | 0 |
avoidablePropagation @ 100 | 0.0145 | 0.0236 | 0.0131–0.0158 | 45.7% | low | 2 |
avoidablePropagation @ 1000 | 0.0392 | 0.0418 | 0.0378–0.0403 | 5.7% | high | 2 |
avoidablePropagation @ 10000 | 0.2916 | 0.3024 | 0.2838–0.2971 | 3.8% | high | 2 |
batch-commit @ 100 | 0.0445 | 0.0499 | 0.0440–0.0457 | 5.8% | high | 0 |
batch-commit @ 1000 | 0.3664 | 0.4499 | 0.3479–0.4078 | 12.0% | low | 0 |
batch-commit @ 10000 | 3.831 | 3.992 | 3.766–3.884 | 2.5% | high | 0 |
commit-firehose-1000-subs @ 100 | inapplicable: jotai has exactly one subscription primitive, `store.sub(atom, cb)`, which fires when that atom's value changes. The scenario needs two kinds the library does not have: a read-projection registrant over a cold node that must never fire, and a commit-level registrant that fires on every commit regardless of which node changed. jotai has no commit boundary to subscribe to, so the property being measured, per-projection dispatch being O(1) rather than O(registrations), has no expressible analogue. Simulating it with a hand-rolled "commit counter" atom would measure the simulation, not jotai. | |||||
commit-firehose-1000-subs @ 1000 | inapplicable: jotai has exactly one subscription primitive, `store.sub(atom, cb)`, which fires when that atom's value changes. The scenario needs two kinds the library does not have: a read-projection registrant over a cold node that must never fire, and a commit-level registrant that fires on every commit regardless of which node changed. jotai has no commit boundary to subscribe to, so the property being measured, per-projection dispatch being O(1) rather than O(registrations), has no expressible analogue. Simulating it with a hand-rolled "commit counter" atom would measure the simulation, not jotai. | |||||
commit-firehose-1000-subs @ 10000 | inapplicable: jotai has exactly one subscription primitive, `store.sub(atom, cb)`, which fires when that atom's value changes. The scenario needs two kinds the library does not have: a read-projection registrant over a cold node that must never fire, and a commit-level registrant that fires on every commit regardless of which node changed. jotai has no commit boundary to subscribe to, so the property being measured, per-projection dispatch being O(1) rather than O(registrations), has no expressible analogue. Simulating it with a hand-rolled "commit counter" atom would measure the simulation, not jotai. | |||||
commit-firehose-subs @ 100 | inapplicable: This cell sweeps the number of causl subscribers attached to one derived and commits once, which is a causl-only question: it exists to price causl's own notification path against its own history, not to rank libraries. jotai has no commit boundary and no derived-with-observers shape to sweep, so measuring anything here would be measuring a different thing under the same name. | |||||
commit-firehose-subs @ 1000 | inapplicable: This cell sweeps the number of causl subscribers attached to one derived and commits once, which is a causl-only question: it exists to price causl's own notification path against its own history, not to rank libraries. jotai has no commit boundary and no derived-with-observers shape to sweep, so measuring anything here would be measuring a different thing under the same name. | |||||
commit-firehose-subs @ 10000 | inapplicable: This cell sweeps the number of causl subscribers attached to one derived and commits once, which is a causl-only question: it exists to price causl's own notification path against its own history, not to rank libraries. jotai has no commit boundary and no derived-with-observers shape to sweep, so measuring anything here would be measuring a different thing under the same name. | |||||
commit-idle-subs @ 100 | inapplicable: This cell sweeps the number of causl subscribers attached to one derived and commits once, which is a causl-only question: it exists to price causl's own notification path against its own history, not to rank libraries. jotai has no commit boundary and no derived-with-observers shape to sweep, so measuring anything here would be measuring a different thing under the same name. | |||||
commit-idle-subs @ 1000 | inapplicable: This cell sweeps the number of causl subscribers attached to one derived and commits once, which is a causl-only question: it exists to price causl's own notification path against its own history, not to rank libraries. jotai has no commit boundary and no derived-with-observers shape to sweep, so measuring anything here would be measuring a different thing under the same name. | |||||
commit-idle-subs @ 10000 | inapplicable: This cell sweeps the number of causl subscribers attached to one derived and commits once, which is a causl-only question: it exists to price causl's own notification path against its own history, not to rank libraries. jotai has no commit boundary and no derived-with-observers shape to sweep, so measuring anything here would be measuring a different thing under the same name. | |||||
diamond @ 100 | 0.0101 | 0.0119 | 0.0100–0.0106 | 8.3% | high | 3 |
diamond @ 1000 | 0.0114 | 0.0125 | 0.0111–0.0120 | 6.2% | high | 3 |
diamond @ 10000 | 0.0113 | 0.0130 | 0.0111–0.0116 | 6.0% | high | 3 |
dynamic-dep-flip @ 100 | 0.0111 | 0.0136 | 0.0106–0.0116 | 13.4% | high | 1 |
dynamic-dep-flip @ 1000 | 0.0103 | 0.0140 | 0.0100–0.0108 | 16.1% | low | 1 |
dynamic-dep-flip @ 10000 | 0.0117 | 0.0157 | 0.0114–0.0125 | 23.6% | low | 1 |
equality-cutoff @ 100 | 0.0550 | 0.0577 | 0.0544–0.0556 | 3.8% | high | 0 |
equality-cutoff @ 1000 | 0.4095 | 0.4788 | 0.3734–0.4549 | 11.3% | low | 0 |
equality-cutoff @ 10000 | 3.567 | 3.690 | 3.518–3.601 | 2.7% | high | 0 |
equality-cutoff-fanout-10k @ 100 | 0.0852 | 0.0924 | 0.0833–0.0854 | 6.5% | high | 10 |
equality-cutoff-fanout-10k @ 1000 | 0.6508 | 0.7694 | 0.6189–0.7384 | 10.0% | low | 100 |
equality-cutoff-fanout-10k @ 10000 | 7.704 | 7.778 | 7.551–7.747 | 1.7% | high | 1000 |
equality-cutoff-noop @ 100 | 0.0536 | 0.0591 | 0.0526–0.0543 | 5.0% | high | 0 |
equality-cutoff-noop @ 1000 | 0.4199 | 0.4902 | 0.3845–0.4611 | 11.6% | low | 0 |
equality-cutoff-noop @ 10000 | 3.464 | 3.538 | 3.450–3.500 | 1.2% | high | 0 |
linear-chain @ 100 | 0.1090 | 0.1207 | 0.1079–0.1148 | 5.0% | low | 100 |
linear-chain @ 1000 | 0.8975 | 0.9355 | 0.8866–0.9043 | 2.5% | high | 1000 |
linear-chain @ 10000 | inapplicable: jotai evaluates a derived chain by mutual recursion through readAtomState, with no iterative fallback. At a chain depth of 10000 the first mounted read exhausts V8's call stack (RangeError: Maximum call stack size exceeded) on the default 984 KiB main stack, so the graph cannot be constructed at all. There is nothing to measure, at any number of repetitions. Shallower scales of the same scenario are measured normally. | |||||
mixed-editor-60s-seed42 @ 100 | 4240 | 4263 | 4233–4246 | 0.3% | low | 1924529 |
mixed-editor-60s-seed42 @ 1000 | 3839 | 3879 | 3828–3844 | 0.5% | low | 1924529 |
mixed-editor-60s-seed42 @ 10000 | 4227 | 4255 | 4221–4239 | 0.4% | low | 1924529 |
mixed-editor-60s-seed42-50k @ 100 | 937.22 | 945.35 | 935.68–938.58 | 0.5% | high | 237404 |
mixed-editor-60s-seed42-50k @ 1000 | 938.30 | 942.97 | 934.75–940.04 | 0.4% | high | 237404 |
mixed-editor-60s-seed42-50k @ 10000 | 993.80 | 998.81 | 993.08–994.52 | 0.4% | low | 237404 |
mux @ 100 | 10.43 | 10.63 | 10.37–10.48 | 1.0% | high | 10200 |
mux @ 1000 | 10.30 | 10.46 | 10.28–10.35 | 0.9% | high | 10200 |
mux @ 10000 | 10.42 | 10.76 | 10.37–10.54 | 1.6% | high | 10200 |
mux-ballast @ 100 | 10.41 | 10.56 | 10.32–10.44 | 1.4% | high | 10200 |
mux-ballast @ 1000 | 10.42 | 10.65 | 10.36–10.53 | 1.4% | high | 10200 |
mux-ballast @ 10000 | 10.50 | 10.69 | 10.48–10.61 | 1.0% | high | 10200 |
op-commit-noderived-1k @ 1000 | 0.3634 | 0.4920 | 0.3222–0.4269 | 18.2% | low | 0 |
op-commit-rollback-1k @ 1000 | inapplicable: jotai has no transaction and no atomic rollback. `store.set` commits per call, and a throw out of a write function or a derived getter leaves the already-written atoms written. The cell can only be imitated by catching the throw and re-issuing the prior value by hand, which measures a user-space workaround rather than an engine primitive, so it is reported as absent rather than as a comparable number. | |||||
op-derived-create-1k-fresh @ 1000 | 0.0485 | 0.0512 | 0.0408–0.0495 | 26.4% | low | 0 |
op-derived-recompute-1k @ 1000 | 867.00 | 882.11 | 863.82–872.11 | 0.9% | high | 1000000 |
op-derived-rollback-1k @ 1000 | inapplicable: jotai has no transaction and no atomic rollback. `store.set` commits per call, and a throw out of a write function or a derived getter leaves the already-written atoms written. The cell can only be imitated by catching the throw and re-issuing the prior value by hand, which measures a user-space workaround rather than an engine primitive, so it is reported as absent rather than as a comparable number. | |||||
op-input-create-1k @ 1000 | 0.0464 | 0.0485 | 0.0456–0.0472 | 13.2% | high | 0 |
op-no-subscriber-firehose-1k @ 1000 | 0.6608 | 0.7343 | 0.5207–0.6934 | 18.1% | low | 0 |
op-phase-d-bfs-1k @ 1000 | 874.61 | 892.91 | 871.71–881.76 | 1.2% | high | 1000000 |
op-read-cold-1k @ 1000 | 0.1033 | 0.1195 | 0.0982–0.1069 | 9.3% | high | 0 |
op-rust-bridge-floor-1k @ 1000 | inapplicable: measures a causl backend seam (the JS<->Rust/WASM call boundary). jotai has no WASM backend and no operation to compare against; any number published here would be a different measurement wearing the same column header. | |||||
op-subscribe-dispose-1k-pairs @ 1000 | 0.4536 | 0.4750 | 0.4488–0.4634 | 8.6% | high | 0 |
op-tx-set-equal-1k @ 1000 | 0.1317 | 0.1752 | 0.1308–0.1329 | 11.3% | high | 0 |
op-tx-set-isolated-1k @ 1000 | 0.5700 | 0.6133 | 0.5183–0.5885 | 12.1% | high | 0 |
op-tx-shadow-read-1k @ 1000 | 0.8121 | 0.9026 | 0.7715–0.8533 | 10.8% | low | 0 |
op-wasm-boundary-1k @ 1000 | inapplicable: measures a causl backend seam (the JS<->Rust/WASM call boundary). jotai has no WASM backend and no operation to compare against; any number published here would be a different measurement wearing the same column header. | |||||
readFraction @ 100 | 0.0256 | 0.0270 | 0.0251–0.0262 | 3.1% | low | 40 |
readFraction @ 1000 | 0.2092 | 0.2205 | 0.2059–0.2111 | 2.8% | low | 400 |
readFraction @ 10000 | 1.583 | 1.607 | 1.575–1.594 | 1.2% | high | 4000 |
repeatedObservers @ 100 | 0.0184 | 0.0208 | 0.0180–0.0197 | 6.3% | high | 1 |
repeatedObservers @ 1000 | 0.0767 | 0.1120 | 0.0761–0.0916 | 18.2% | low | 1 |
repeatedObservers @ 10000 | 0.6761 | 0.6805 | 0.6730–0.6771 | 0.5% | high | 1 |
scrolling-viewport @ 100 | 0.0480 | 0.0497 | 0.0475–0.0485 | 3.6% | high | 0 |
scrolling-viewport @ 1000 | 0.0563 | 0.0726 | 0.0554–0.0575 | 21.3% | low | 0 |
scrolling-viewport @ 10000 | 0.1056 | 0.1130 | 0.1030–0.1101 | 5.2% | high | 0 |
scrolling-viewport-derived @ 100 | 0.0889 | 0.1018 | 0.0868–0.0916 | 6.4% | high | 10 |
scrolling-viewport-derived @ 1000 | 0.3269 | 0.3906 | 0.3223–0.3511 | 8.6% | low | 99 |
scrolling-viewport-derived @ 10000 | 0.4590 | 0.4764 | 0.4508–0.4634 | 3.1% | high | 99 |
spreadsheet-100x100 @ 100 | 717.12 | 721.63 | 715.68–718.12 | 0.4% | high | 110481 |
spreadsheet-100x100 @ 1000 | 713.15 | 718.59 | 711.11–714.33 | 0.5% | high | 110481 |
spreadsheet-100x100 @ 10000 | 717.81 | 724.33 | 716.51–721.05 | 0.6% | high | 110481 |
subscriber-churn-1k @ 100 | 6.671 | 7.612 | 6.460–6.945 | 6.9% | high | 0 |
subscriber-churn-1k @ 1000 | 6.448 | 7.415 | 6.008–6.811 | 9.8% | high | 0 |
subscriber-churn-1k @ 10000 | 6.339 | 7.543 | 6.079–6.525 | 8.6% | high | 0 |
subscriber-churn-derived @ 100 | 6.663 | 6.814 | 6.633–6.707 | 1.3% | high | 220 |
subscriber-churn-derived @ 1000 | 6.730 | 6.837 | 6.655–6.783 | 1.5% | high | 220 |
subscriber-churn-derived @ 10000 | 6.704 | 6.838 | 6.623–6.741 | 1.5% | high | 220 |
unstable @ 100 | 0.0153 | 0.0187 | 0.0141–0.0164 | 10.7% | high | 3 |
unstable @ 1000 | 0.0612 | 0.0683 | 0.0588–0.0622 | 7.9% | high | 21 |
unstable @ 10000 | 0.4080 | 0.4176 | 0.4012–0.4113 | 2.8% | high | 201 |
mobx: 78 measured, 12 not measured · mobx@6.16.1
| Cell | median (ms) | p95 (ms) | 95% interval | CoV | confidence | recomputes |
|---|---|---|---|---|---|---|
adversarial-fanin-100 @ 100 | 11.94 | 12.06 | 11.90–11.98 | 0.7% | high | 10000 |
adversarial-fanin-100 @ 1000 | 121.05 | 123.57 | 120.82–121.58 | 0.8% | high | 100000 |
adversarial-fanin-100 @ 10000 | 1224 | 1231 | 1223–1226 | 0.6% | low | 1000000 |
async-race @ 100 | 0.0029 | 0.0036 | 0.0028–0.0034 | 27.7% | low | 0 |
async-race @ 1000 | 0.0027 | 0.0037 | 0.0025–0.0030 | 35.7% | low | 0 |
async-race @ 10000 | 0.0028 | 0.0039 | 0.0025–0.0031 | 30.9% | low | 0 |
avoidablePropagation @ 100 | 0.0060 | 0.0100 | 0.0056–0.0063 | 24.3% | low | 2 |
avoidablePropagation @ 1000 | 0.0144 | 0.0179 | 0.0134–0.0156 | 13.4% | low | 2 |
avoidablePropagation @ 10000 | 0.0755 | 0.0800 | 0.0750–0.0764 | 2.6% | high | 2 |
batch-commit @ 100 | 0.0061 | 0.0062 | 0.0060–0.0062 | 3.7% | high | 0 |
batch-commit @ 1000 | 0.0322 | 0.0362 | 0.0320–0.0344 | 6.1% | high | 0 |
batch-commit @ 10000 | 0.2722 | 0.2800 | 0.2709–0.2744 | 1.5% | high | 0 |
commit-firehose-1000-subs @ 100 | 0.0024 | 0.0027 | 0.0023–0.0025 | 7.3% | high | 0 |
commit-firehose-1000-subs @ 1000 | 0.0023 | 0.0029 | 0.0021–0.0026 | 16.0% | low | 0 |
commit-firehose-1000-subs @ 10000 | 0.0050 | 0.0053 | 0.0048–0.0051 | 6.8% | low | 0 |
commit-firehose-subs @ 100 | inapplicable: this sweeps the number of causl subscribers on one derived and commits once, to price causl's notification path against its own history. mobx has no commit boundary, so a mobx reading here would be a different measurement wearing the same id | |||||
commit-firehose-subs @ 1000 | inapplicable: this sweeps the number of causl subscribers on one derived and commits once, to price causl's notification path against its own history. mobx has no commit boundary, so a mobx reading here would be a different measurement wearing the same id | |||||
commit-firehose-subs @ 10000 | inapplicable: this sweeps the number of causl subscribers on one derived and commits once, to price causl's notification path against its own history. mobx has no commit boundary, so a mobx reading here would be a different measurement wearing the same id | |||||
commit-idle-subs @ 100 | inapplicable: the twin of commit-firehose-subs, holding a population of observers the commit cannot reach. It exists to separate the cost of notifying an observer from the cost of having one inside causl, and mobx has no commit boundary to hang it on | |||||
commit-idle-subs @ 1000 | inapplicable: the twin of commit-firehose-subs, holding a population of observers the commit cannot reach. It exists to separate the cost of notifying an observer from the cost of having one inside causl, and mobx has no commit boundary to hang it on | |||||
commit-idle-subs @ 10000 | inapplicable: the twin of commit-firehose-subs, holding a population of observers the commit cannot reach. It exists to separate the cost of notifying an observer from the cost of having one inside causl, and mobx has no commit boundary to hang it on | |||||
diamond @ 100 | 0.0089 | 0.0118 | 0.0084–0.0093 | 16.9% | low | 3 |
diamond @ 1000 | 0.0083 | 0.0112 | 0.0081–0.0094 | 15.0% | high | 3 |
diamond @ 10000 | 0.0092 | 0.0108 | 0.0088–0.0097 | 12.3% | high | 3 |
dynamic-dep-flip @ 100 | 0.0087 | 0.0104 | 0.0081–0.0093 | 13.1% | high | 1 |
dynamic-dep-flip @ 1000 | 0.0085 | 0.0118 | 0.0080–0.0093 | 17.3% | low | 1 |
dynamic-dep-flip @ 10000 | 0.0082 | 0.0100 | 0.0079–0.0084 | 11.5% | high | 1 |
equality-cutoff @ 100 | 0.0042 | 0.0043 | 0.0041–0.0043 | 4.9% | high | 0 |
equality-cutoff @ 1000 | 0.0220 | 0.0348 | 0.0202–0.0268 | 24.6% | low | 0 |
equality-cutoff @ 10000 | 0.1885 | 0.1919 | 0.1879–0.1896 | 1.2% | high | 0 |
equality-cutoff-fanout-10k @ 100 | 0.0180 | 0.0245 | 0.0164–0.0193 | 18.8% | low | 10 |
equality-cutoff-fanout-10k @ 1000 | 0.1093 | 0.1255 | 0.1060–0.1155 | 9.5% | low | 100 |
equality-cutoff-fanout-10k @ 10000 | 0.5046 | 0.5244 | 0.4996–0.5152 | 2.5% | high | 1000 |
equality-cutoff-noop @ 100 | 0.0031 | 0.0032 | 0.0030–0.0031 | 7.6% | high | 0 |
equality-cutoff-noop @ 1000 | 0.0144 | 0.0178 | 0.0140–0.0161 | 18.9% | low | 0 |
equality-cutoff-noop @ 10000 | 0.1315 | 0.1374 | 0.1313–0.1328 | 3.5% | high | 0 |
linear-chain @ 100 | 0.0339 | 0.0393 | 0.0335–0.0365 | 7.4% | high | 100 |
linear-chain @ 1000 | 0.1649 | 0.1737 | 0.1643–0.1676 | 2.5% | high | 1000 |
linear-chain @ 10000 | inapplicable: mobx evaluates a computed chain by mutual recursion through its derivation graph, with no iterative fallback. At a chain depth of 10000 reading the tail exceeds the V8 call stack (RangeError: Maximum call stack size exceeded) on the default 984 KiB main stack, so the graph cannot be read at all. There is nothing to measure, at any number of repetitions. Shallower scales of the same scenario are measured normally. | |||||
mixed-editor-60s-seed42 @ 100 | 1.294 | 1.653 | 0.9615–1.425 | 22.6% | low | 639 |
mixed-editor-60s-seed42 @ 1000 | 22.29 | 23.72 | 15.56–22.99 | 18.0% | low | 23724 |
mixed-editor-60s-seed42 @ 10000 | 1530 | 1558 | 1519–1545 | 1.4% | low | 1934077 |
mixed-editor-60s-seed42-50k @ 100 | 1.288 | 1.822 | 1.138–1.677 | 22.8% | low | 484 |
mixed-editor-60s-seed42-50k @ 1000 | 13.61 | 14.75 | 13.07–13.85 | 8.0% | high | 6861 |
mixed-editor-60s-seed42-50k @ 10000 | 326.05 | 339.32 | 320.30–335.42 | 2.8% | high | 239050 |
mux @ 100 | 2.188 | 2.220 | 2.164–2.200 | 1.5% | high | 10200 |
mux @ 1000 | 2.217 | 2.244 | 2.199–2.238 | 1.2% | high | 10200 |
mux @ 10000 | 2.187 | 2.243 | 2.167–2.210 | 1.5% | high | 10200 |
mux-ballast @ 100 | 2.249 | 2.275 | 2.231–2.257 | 1.3% | high | 10200 |
mux-ballast @ 1000 | 2.259 | 2.289 | 2.231–2.275 | 1.3% | high | 10200 |
mux-ballast @ 10000 | 2.502 | 2.615 | 2.441–2.539 | 2.9% | high | 10200 |
op-commit-noderived-1k @ 1000 | 0.0326 | 0.0373 | 0.0324–0.0330 | 6.0% | high | 0 |
op-commit-rollback-1k @ 1000 | 0.9835 | 1.009 | 0.9784–0.9867 | 21.5% | low | 999 |
op-derived-create-1k-fresh @ 1000 | 0.3272 | 0.3572 | 0.3038–0.3413 | 11.7% | low | 1000 |
op-derived-recompute-1k @ 1000 | 0.1676 | 0.1729 | 0.1666–0.1692 | 1.6% | high | 1000 |
op-derived-rollback-1k @ 1000 | inapplicable: mobx has no transactional rollback. A throw from inside a computed propagates to the reader and is cached, but no prior writes are unwound, so there is no rollback envelope to measure | |||||
op-input-create-1k @ 1000 | 0.0975 | 0.1154 | 0.0325–0.1101 | 39.9% | low | 0 |
op-no-subscriber-firehose-1k @ 1000 | 0.0324 | 0.0352 | 0.0318–0.0335 | 5.8% | low | 0 |
op-phase-d-bfs-1k @ 1000 | inapplicable: this isolates the BFS/Kahn propagation walk in the causl engine. mobx propagates by invalidating dependents through reaction links, with no separable topological pass to time | |||||
op-read-cold-1k @ 1000 | 0.0357 | 0.0466 | 0.0355–0.0417 | 22.4% | low | 1000 |
op-rust-bridge-floor-1k @ 1000 | inapplicable: mobx has no Rust bridge | |||||
op-subscribe-dispose-1k-pairs @ 1000 | 0.0399 | 0.0662 | 0.0395–0.0400 | 25.6% | low | 0 |
op-tx-set-equal-1k @ 1000 | 0.0301 | 0.0560 | 0.0289–0.0473 | 40.2% | low | 0 |
op-tx-set-isolated-1k @ 1000 | 0.0370 | 0.0414 | 0.0359–0.0389 | 6.6% | low | 0 |
op-tx-shadow-read-1k @ 1000 | inapplicable: there is no transaction object in mobx, so there is no staged read-shadow to measure. Reads inside an action see values already applied | |||||
op-wasm-boundary-1k @ 1000 | inapplicable: mobx is pure JavaScript with no WebAssembly boundary to cross | |||||
readFraction @ 100 | 0.0107 | 0.0157 | 0.0103–0.0117 | 55.9% | low | 40 |
readFraction @ 1000 | 0.0649 | 0.0767 | 0.0564–0.0659 | 13.0% | low | 400 |
readFraction @ 10000 | 0.4062 | 0.4130 | 0.4030–0.4079 | 1.5% | high | 4000 |
repeatedObservers @ 100 | 0.0061 | 0.0070 | 0.0058–0.0064 | 8.8% | low | 1 |
repeatedObservers @ 1000 | 0.0080 | 0.0104 | 0.0077–0.0090 | 16.3% | low | 1 |
repeatedObservers @ 10000 | 0.0239 | 0.0458 | 0.0238–0.0252 | 31.7% | low | 1 |
scrolling-viewport @ 100 | 0.0080 | 0.0087 | 0.0079–0.0081 | 5.7% | high | 0 |
scrolling-viewport @ 1000 | 0.0070 | 0.0090 | 0.0069–0.0080 | 11.9% | high | 0 |
scrolling-viewport @ 10000 | 0.0115 | 0.0136 | 0.0114–0.0122 | 8.8% | high | 0 |
scrolling-viewport-derived @ 100 | 0.1124 | 0.1210 | 0.1100–0.1162 | 4.7% | low | 100 |
scrolling-viewport-derived @ 1000 | 0.1137 | 0.1210 | 0.1114–0.1146 | 4.2% | high | 100 |
scrolling-viewport-derived @ 10000 | 0.0569 | 0.0625 | 0.0561–0.0577 | 44.4% | low | 100 |
spreadsheet-100x100 @ 100 | 0.7252 | 0.7547 | 0.7220–0.7331 | 2.1% | high | 1119 |
spreadsheet-100x100 @ 1000 | 7.166 | 7.329 | 7.117–7.199 | 1.2% | high | 11125 |
spreadsheet-100x100 @ 10000 | 67.01 | 69.11 | 66.27–67.45 | 1.9% | high | 110551 |
subscriber-churn-1k @ 100 | 0.1876 | 0.1938 | 0.1861–0.1891 | 2.8% | high | 0 |
subscriber-churn-1k @ 1000 | 1.233 | 1.411 | 1.228–1.294 | 7.6% | low | 0 |
subscriber-churn-1k @ 10000 | 14.60 | 15.21 | 14.58–14.79 | 1.8% | high | 0 |
subscriber-churn-derived @ 100 | 3.332 | 3.546 | 2.561–3.418 | 16.3% | low | 1303 |
subscriber-churn-derived @ 1000 | 16.83 | 21.72 | 14.39–19.71 | 18.9% | low | 1500 |
subscriber-churn-derived @ 10000 | 152.57 | 158.83 | 151.81–154.53 | 1.9% | high | 1500 |
unstable @ 100 | 0.0081 | 0.0098 | 0.0077–0.0084 | 10.1% | high | 3 |
unstable @ 1000 | 0.0192 | 0.0240 | 0.0189–0.0197 | 13.4% | high | 21 |
unstable @ 10000 | 0.1020 | 0.1396 | 0.0955–0.1246 | 19.0% | low | 201 |
redux-rtk: 76 measured, 14 not measured · @reduxjs/toolkit@2.9.0
| Cell | median (ms) | p95 (ms) | 95% interval | CoV | confidence | recomputes |
|---|---|---|---|---|---|---|
adversarial-fanin-100 @ 100 | 16.76 | 16.86 | 16.74–16.80 | 0.4% | high | 10000 |
adversarial-fanin-100 @ 1000 | 1566 | 1580 | 1564–1570 | 0.5% | low | 100000 |
adversarial-fanin-100 @ 10000 | skipped: projected ~1.0e+10 input-selector evaluations per repetition (10000 commits × 10000 derivations × fan-in 100). Redux has no per-node invalidation: every dispatch re-runs every mounted selector's input functions, so this is quadratic in scale by construction. Exceeds this runner's 5.0e+8-operation per-repetition budget. | |||||
async-race @ 100 | 0.0086 | 0.0138 | 0.0080–0.0106 | 25.7% | low | 0 |
async-race @ 1000 | 0.0169 | 0.0200 | 0.0115–0.0188 | 27.4% | low | 0 |
async-race @ 10000 | 0.0087 | 0.0113 | 0.0083–0.0095 | 32.4% | low | 0 |
avoidablePropagation @ 100 | 0.0108 | 0.0153 | 0.0105–0.0128 | 21.4% | low | 2 |
avoidablePropagation @ 1000 | 0.0288 | 0.0326 | 0.0283–0.0297 | 5.6% | high | 2 |
avoidablePropagation @ 10000 | 0.2639 | 0.2828 | 0.2600–0.2732 | 3.6% | high | 2 |
batch-commit @ 100 | 0.0379 | 0.0414 | 0.0372–0.0387 | 4.8% | high | 0 |
batch-commit @ 1000 | 0.3219 | 0.3717 | 0.3123–0.3486 | 8.0% | low | 0 |
batch-commit @ 10000 | 2.985 | 3.074 | 2.954–3.025 | 1.8% | high | 0 |
commit-firehose-1000-subs @ 100 | 0.0187 | 0.0230 | 0.0179–0.0209 | 11.0% | high | 0 |
commit-firehose-1000-subs @ 1000 | 0.1611 | 0.1787 | 0.1552–0.1677 | 7.1% | high | 0 |
commit-firehose-1000-subs @ 10000 | 1.257 | 1.331 | 1.239–1.270 | 3.9% | high | 0 |
commit-firehose-subs @ 100 | inapplicable: this sweeps the observer population on one causl derived and commits once, to price causl against its own history. redux-rtk subscribes to the store, not to a derived node, so it has no population to sweep and a reading here would answer a different question under the same name. | |||||
commit-firehose-subs @ 1000 | inapplicable: this sweeps the observer population on one causl derived and commits once, to price causl against its own history. redux-rtk subscribes to the store, not to a derived node, so it has no population to sweep and a reading here would answer a different question under the same name. | |||||
commit-firehose-subs @ 10000 | inapplicable: this sweeps the observer population on one causl derived and commits once, to price causl against its own history. redux-rtk subscribes to the store, not to a derived node, so it has no population to sweep and a reading here would answer a different question under the same name. | |||||
commit-idle-subs @ 100 | inapplicable: this sweeps the observer population on one causl derived and commits once, to price causl against its own history. redux-rtk subscribes to the store, not to a derived node, so it has no population to sweep and a reading here would answer a different question under the same name. | |||||
commit-idle-subs @ 1000 | inapplicable: this sweeps the observer population on one causl derived and commits once, to price causl against its own history. redux-rtk subscribes to the store, not to a derived node, so it has no population to sweep and a reading here would answer a different question under the same name. | |||||
commit-idle-subs @ 10000 | inapplicable: this sweeps the observer population on one causl derived and commits once, to price causl against its own history. redux-rtk subscribes to the store, not to a derived node, so it has no population to sweep and a reading here would answer a different question under the same name. | |||||
diamond @ 100 | 0.0080 | 0.0115 | 0.0073–0.0090 | 29.7% | low | 3 |
diamond @ 1000 | 0.0086 | 0.0115 | 0.0082–0.0093 | 13.8% | high | 3 |
diamond @ 10000 | 0.0091 | 0.0127 | 0.0087–0.0098 | 18.3% | low | 3 |
dynamic-dep-flip @ 100 | 0.0102 | 0.0113 | 0.0099–0.0108 | 18.1% | low | 2 |
dynamic-dep-flip @ 1000 | 0.0103 | 0.0127 | 0.0100–0.0107 | 15.6% | low | 2 |
dynamic-dep-flip @ 10000 | 0.0097 | 0.0145 | 0.0092–0.0101 | 20.0% | low | 2 |
equality-cutoff @ 100 | 0.0368 | 0.0429 | 0.0363–0.0373 | 6.7% | high | 0 |
equality-cutoff @ 1000 | 0.3077 | 0.3515 | 0.2944–0.3353 | 8.3% | low | 0 |
equality-cutoff @ 10000 | 2.719 | 2.762 | 2.706–2.740 | 2.1% | high | 0 |
equality-cutoff-fanout-10k @ 100 | 0.0413 | 0.0478 | 0.0409–0.0439 | 7.3% | high | 10 |
equality-cutoff-fanout-10k @ 1000 | 0.3291 | 0.3503 | 0.3193–0.3380 | 4.5% | low | 100 |
equality-cutoff-fanout-10k @ 10000 | 3.102 | 3.130 | 3.084–3.116 | 0.8% | high | 1000 |
equality-cutoff-noop @ 100 | 0.0296 | 0.0365 | 0.0289–0.0321 | 9.6% | high | 0 |
equality-cutoff-noop @ 1000 | 0.2855 | 0.2985 | 0.2791–0.2901 | 4.5% | low | 0 |
equality-cutoff-noop @ 10000 | 2.425 | 2.476 | 2.413–2.446 | 1.4% | high | 0 |
linear-chain @ 100 | 0.0293 | 0.0319 | 0.0286–0.0304 | 7.5% | high | 100 |
linear-chain @ 1000 | 0.2645 | 0.2898 | 0.2592–0.2740 | 4.6% | high | 1000 |
linear-chain @ 10000 | inapplicable: reselect evaluates a chained selector by recursive invocation, so reading the tail of a 10000-link chain recurses 10000 deep and exhausts the V8 call stack (RangeError: Maximum call stack size exceeded) on the default 984 KiB main stack. That happens during construction, before any timed region, so there is nothing to measure at any number of repetitions. Expressing this depth in redux would mean replacing reselect with a hand-rolled iterative evaluator, which would no longer be a reselect measurement. Shallower scales of the same scenario are measured normally. | |||||
mixed-editor-60s-seed42 @ 100 | 2.127 | 2.449 | 2.060–2.269 | 8.2% | high | 112 |
mixed-editor-60s-seed42 @ 1000 | 173.99 | 175.31 | 173.07–174.60 | 0.7% | high | 1035 |
mixed-editor-60s-seed42 @ 10000 | skipped: projected ~6.0e+8 operations per repetition (30000 write dispatches × 10000 record keys copied by Immer, plus 30000 × ~10000 graph-global store listeners). Redux has neither a partial-state write nor a per-key subscription, so both terms are architectural; the cell exceeds this runner's 5.0e+8-operation per-repetition budget. | |||||
mixed-editor-60s-seed42-50k @ 100 | 12.54 | 12.81 | 12.44–12.70 | 1.7% | high | 112 |
mixed-editor-60s-seed42-50k @ 1000 | 5054 | 5079 | 5051–5060 | 0.3% | low | 1035 |
mixed-editor-60s-seed42-50k @ 10000 | skipped: projected ~3.3e+9 operations per repetition (30000 write dispatches × 100000 record keys copied by Immer, plus 30000 × ~10000 graph-global store listeners). Redux has neither a partial-state write nor a per-key subscription, so both terms are architectural; the cell exceeds this runner's 5.0e+8-operation per-repetition budget. | |||||
mux @ 100 | 4.154 | 4.269 | 4.143–4.177 | 2.0% | high | 10200 |
mux @ 1000 | 4.171 | 4.346 | 4.139–4.229 | 4.1% | high | 10200 |
mux @ 10000 | 4.148 | 4.451 | 4.092–4.230 | 3.3% | high | 10200 |
mux-ballast @ 100 | 4.139 | 4.220 | 4.122–4.167 | 1.7% | high | 10200 |
mux-ballast @ 1000 | 4.169 | 4.357 | 4.148–4.199 | 1.8% | high | 10200 |
mux-ballast @ 10000 | 4.295 | 4.365 | 4.285–4.317 | 0.9% | high | 10200 |
op-commit-noderived-1k @ 1000 | 0.5301 | 0.5662 | 0.4662–0.5423 | 13.2% | low | 0 |
op-commit-rollback-1k @ 1000 | 0.7870 | 0.8619 | 0.7600–0.8240 | 4.9% | high | 0 |
op-derived-create-1k-fresh @ 1000 | 0.3431 | 0.4163 | 0.3388–0.3672 | 11.4% | low | 0 |
op-derived-recompute-1k @ 1000 | 209.74 | 211.93 | 208.88–210.37 | 0.8% | high | 1000000 |
op-derived-rollback-1k @ 1000 | inapplicable: redux derivations are pull-based and evaluate after `dispatch` has already published the new state, so a throwing selector has no in-flight commit to unwind. The only redux-side number available would be a user-space compensating re-dispatch, which measures rollback *simulation* rather than the primitive this cell names. (The sibling cell `op-commit-rollback-1k` IS measured: a throw inside the reducer body aborts the Immer draft and leaves the store state untouched.) | |||||
op-input-create-1k @ 1000 | 85.33 | 86.39 | 85.18–85.45 | 0.6% | high | 0 |
op-no-subscriber-firehose-1k @ 1000 | 38.36 | 39.00 | 38.24–38.72 | 1.1% | high | 0 |
op-phase-d-bfs-1k @ 1000 | inapplicable: isolates the BFS + Kahn topological walk inside causl's `recomputeAffected`. Redux has no propagation walk to isolate: dispatch runs one reducer and notifies every listener, and derivation order is whatever order readers happen to pull in. There is no corresponding data structure to probe. | |||||
op-read-cold-1k @ 1000 | 0.0024 | 0.0032 | 0.0023–0.0028 | 61.5% | low | 0 |
op-rust-bridge-floor-1k @ 1000 | inapplicable: measures the bare wasm-bindgen scalar-call floor (`read_input(slot, gen) -> u64`) of the causl Rust bridge. Redux Toolkit is pure JavaScript; there is no bridge and no floor to measure. | |||||
op-subscribe-dispose-1k-pairs @ 1000 | 0.0906 | 0.1000 | 0.0874–0.0924 | 8.6% | low | 0 |
op-tx-set-equal-1k @ 1000 | 0.2543 | 0.2618 | 0.2510–0.2571 | 2.8% | high | 0 |
op-tx-set-isolated-1k @ 1000 | 0.5436 | 0.5705 | 0.5382–0.5568 | 2.9% | low | 0 |
op-tx-shadow-read-1k @ 1000 | 0.5472 | 0.5994 | 0.4215–0.5574 | 15.0% | low | 0 |
op-wasm-boundary-1k @ 1000 | inapplicable: measures the JS↔WASM `commit(state, action)` boundary of the causl backend. Redux Toolkit has no WebAssembly backend and no boundary to cross, so there is no operation to compare against. | |||||
readFraction @ 100 | 0.0166 | 0.0192 | 0.0155–0.0173 | 9.0% | high | 40 |
readFraction @ 1000 | 0.1132 | 0.1153 | 0.1119–0.1140 | 2.6% | high | 400 |
readFraction @ 10000 | 0.8668 | 1.011 | 0.8520–0.9068 | 6.1% | low | 4000 |
repeatedObservers @ 100 | 0.0082 | 0.0103 | 0.0080–0.0086 | 19.1% | low | 1 |
repeatedObservers @ 1000 | 0.0174 | 0.0212 | 0.0167–0.0184 | 9.2% | high | 1 |
repeatedObservers @ 10000 | 0.1105 | 0.1246 | 0.1098–0.1160 | 6.0% | high | 1 |
scrolling-viewport @ 100 | 0.4586 | 0.4694 | 0.4547–0.4631 | 1.9% | high | 0 |
scrolling-viewport @ 1000 | 3.411 | 3.592 | 3.370–3.489 | 2.7% | low | 0 |
scrolling-viewport @ 10000 | 30.81 | 31.64 | 30.73–30.92 | 1.0% | high | 0 |
scrolling-viewport-derived @ 100 | 0.6000 | 0.6434 | 0.5952–0.6144 | 3.4% | low | 100 |
scrolling-viewport-derived @ 1000 | 5.005 | 5.179 | 4.981–5.028 | 2.1% | high | 100 |
scrolling-viewport-derived @ 10000 | 50.70 | 51.37 | 50.57–50.74 | 1.0% | high | 100 |
spreadsheet-100x100 @ 100 | 63.58 | 64.11 | 63.34–63.92 | 0.9% | high | 11085 |
spreadsheet-100x100 @ 1000 | 557.91 | 600.49 | 556.72–564.08 | 3.2% | high | 110481 |
spreadsheet-100x100 @ 10000 | 5445 | 5459 | 5431–5455 | 0.3% | low | 1107212 |
subscriber-churn-1k @ 100 | 0.8736 | 1.072 | 0.8682–0.8819 | 7.7% | high | 0 |
subscriber-churn-1k @ 1000 | 3.779 | 3.836 | 3.762–3.797 | 0.9% | high | 0 |
subscriber-churn-1k @ 10000 | 61.04 | 61.57 | 60.82–61.16 | 0.7% | high | 0 |
subscriber-churn-derived @ 100 | 1.181 | 1.325 | 1.172–1.201 | 4.2% | high | 204 |
subscriber-churn-derived @ 1000 | 4.337 | 4.376 | 4.324–4.356 | 0.6% | high | 220 |
subscriber-churn-derived @ 10000 | 58.42 | 59.24 | 58.10–58.71 | 1.0% | high | 211 |
unstable @ 100 | 0.0075 | 0.0085 | 0.0071–0.0078 | 14.7% | high | 3 |
unstable @ 1000 | 0.0134 | 0.0181 | 0.0126–0.0151 | 15.4% | low | 21 |
unstable @ 10000 | 0.0678 | 0.0729 | 0.0667–0.0686 | 4.2% | high | 201 |
Why some (scenario × library) cells are absent.
Some cells in the comparison table are absent because of
structural limits in the comparator library, not because
the scenario was selected to disadvantage it. Where a
comparator can run a cell, its result is published as-is;
where it cannot, the structural reason is stated and the
cell renders as (skipped: …) with a
reason string after #1293.
The fairness-audit discipline
(docs/bench/2026-05-12-fairness-audit.md)
classifies every skip into one of the three buckets below.
Recursive read-path overflows V8 at linear-chain × 10000.
Affected cells: linear-chain × 10000 for
jotai, mobx, redux-toolkit.
Causl reports a real median here post-#956.
Each comparator's read path evaluates the chain via mutual
recursion through its track/get entry point, so the V8 call
stack overflows at depth 10 000. The harnesses throw
a typed error up-front, so the doomed setup never runs and the
runner records the cell inapplicable, a fact about
what the library can express, distinct from skipped (the
clock ran out) and failed (it tried and broke). Each cell
carries its own reason string, and every one of them is on this page.
Causl's Phase-D driver is iterative end-to-end after
#956:
the registration walk and the commit-time fix-point both run
through an explicit StackFrame[] rather than
recursive calls. SPEC §5.1 Amendment 3 pins this as an
implementation-detail trail with no contract change:
every §3 atomicity and §5.1 phase invariant is
preserved byte-identically; only the depth ceiling moves.
The asymmetry is structural to the JS engine shape, not the
workload, and tracked under
#721 part 3
(mobx investigation) /
#798 /
#922 /
#926.
Scenario's acceptance gate cannot be expressed in the comparator's public surface.
Affected cells:
commit-firehose-1000-subs,
multi-fetch-race-N10,
and op-wasm-boundary-1k, each skipped for
jotai, mobx, redux-toolkit.
These scenarios require commit-metadata semantics
(subscribeReads vs subscribeCommits
split, SPEC §5.1 Phase F),
staleness-aware async (a commit-seq surface so a late fetch
result can detect that a newer write has landed; see
SPEC.async.md),
or the WASM-FFI boundary contract (SPEC §17.6) Enterprise.
None of the three comparator libraries have an equivalent
public-API surface, so the cell's acceptance gate is not
architecturally expressible.
The harness throws a typed
ExpansionScenarioNotImplementedError with a
reason string identifying the public-API gap, deferred
explicitly under #843
rather than implementing a cell that runs but cannot answer
the question the scenario was constructed to ask
(the “loud silence” failure mode).
Catch-all for typed skips that fall outside Classes 1 and 2.
Any cell where the harness throws
ExpansionScenarioNotImplementedError for a
per-cell reason that does not reduce to a structural stack
overflow or to a public-API gap. None are currently active
in the published comparison table (every entry today
falls into Class 1 or Class 2), but the bucket exists
so future expansion scenarios with library-specific gaps
have a documented home rather than appearing as a silent
drop. The per-cell reason string in
comparison_table.md always names the gap; this
class is the residual category.
Three axes, picked because they fail in the field.
Microbenchmarks lie about real workloads, so each axis is anchored to a scenario that already exists as a runnable demo in the repository. No synthetic loops without a corresponding adopter shape.
Time-to-coherent-state.
Spreadsheet-diamond and linear-chain scenarios from
causl-bench's scenario contract commit a single
root edit and measure wall-clock time until every
dependent has settled to its glitch-free value.
Reported as median + p95 across runs, library by library.
Full-import @causl/core.
Measured against the SPEC §14.2 budget (the 15 KB to
30 KB band with an 18 KB working target) using the same
size-limit configuration that already gates
every PR. Surfaced via bundle-table.ts and
will land on this page in a follow-up.
Bounded enumerator under load.
The BFS frontier / traces / races caps are swept across
cap settings, plotting
states_visited versus wall-clock so adopters
can pick a cap without guessing.
For the full methodology behind these numbers (the SPEC §17.5 capability-cost residual band and how to run the bench harness against your own workload), read the Performance usage guide. Opting into the WebAssembly backend Enterprise and its benchmark story are covered in the Enterprise wasm-performance guide.