Skip to content

Formula engine performance

Recalculation timings by dependency shape, and how the two engine builds compare.
On this page
Validated evidence. 53 recalculation workloads across dependency shapes; every workload passed the protocol's safety ceilings.
Captured
2026-10-29 04:24 UTC
Commit
ac4ab632d80a clean worktree
Raw artifact
bench/results/formula-results.json
workloadlog scale — every tick is 10× — shorter is fastermedianp95
Countif 100k
100,000 cells0.87 ms0.91 ms
Criteria multi range edit
100,000 cells4.25 ms4.31 ms
Criteria range edit
100,000 cells1.68 ms1.74 ms
Cross sheet range edit
1,000 cells0.25 ms0.26 ms
Cycles
1,000 cells0.50 ms0.51 ms
Diamond edit
32 cells0.04 ms0.06 ms
Distinct range edit
1,000 cells0.01 ms0.02 ms
Error propagation
1,000 cells0.25 ms0.26 ms
Formula constant edit
100,000 cells0.03 ms0.04 ms
Incremental dependency closure edit
100,000 cells0.02 ms0.03 ms
Independent first recompute
1,000 cells0.45 ms0.47 ms
10,000 cells2.71 ms2.82 ms
100,000 cells39.5 ms41.3 ms
Independent parse load
1,000 cells0.99 ms1.32 ms
10,000 cells5.97 ms8.62 ms
100,000 cells53.0 ms53.3 ms
Iterative finance
1,000 cells2.85 ms2.93 ms
Let repeat
100,000 cells1.71 ms1.71 ms
Let reuse edit
100,000 cells0.50 ms0.51 ms
Linear chain
8 cells0.02 ms0.04 ms
16 cells0.02 ms0.02 ms
32 cells0.03 ms0.03 ms
64 cells0.03 ms0.04 ms
Lookup range edit
100,000 cells1.73 ms1.79 ms
Percentile covariance
100,000 cells2.85 ms3.10 ms
Removed sheet ref
1,000 cells0.21 ms0.34 ms
Shared range edit
1,000 cells0.28 ms0.31 ms
Spill filter resize
100,000 cells11.8 ms13.5 ms
Spill sequence admission
100,000 cells4.38 ms5.27 ms
Sum multi range
100,000 cells3.79 ms3.93 ms
Sumifs 3 criteria
100,000 cells5.79 ms5.93 ms
Sumproduct matrix edit
100,000 cells1.54 ms1.59 ms
Sumproduct vector edit
100,000 cells1.57 ms2.10 ms
Text lookup many
1,000 cells2.23 ms2.77 ms
Text lookup many edit
1,000 cells1.62 ms1.68 ms
Text lookup many sorted
1,000 cells3.37 ms3.43 ms
Text lookup many sorted edit
1,000 cells3.16 ms3.41 ms
Topology remove add
10,000 cells5.42 ms5.91 ms
Unicode text date edit
1,000 cells0.78 ms0.87 ms
Vlookup many
1,000 cells0.96 ms1.06 ms
Vlookup many edit
1,000 cells0.86 ms0.87 ms
Vlookup many sorted
1,000 cells1.31 ms1.32 ms
Vlookup many sorted edit
1,000 cells1.23 ms1.27 ms
Wide fan out edit
1,000 cells0.14 ms0.16 ms
100,000 cells22.0 ms22.8 ms
Xlookup many
1,000 cells0.94 ms0.94 ms
Xlookup many edit
1,000 cells0.83 ms0.86 ms
Xlookup many sorted
1,000 cells1.32 ms1.35 ms
Xlookup many sorted edit
1,000 cells1.19 ms1.79 ms
Scalar edit affects
0 affected3 µs3 µs
1 affected3 µs0.01 ms
1,000 affected0.17 ms0.19 ms
100,000 affected21.7 ms22.1 ms
Methodology - what each workload does

Each workload builds a fresh WASM cell-store of the named dependency shape and times the recalculation from one action - usually a single edit. Sizes are formula-cell counts; bright = median, faded = p95.

linear-chain
A chain A1 -> A2 -> ... -> AN; editing the head recomputes the full depth.
wide-fan-out-edit
One scalar feeds N dependent formulas; edit the scalar.
diamond-edit
Fan-out that reconverges (diamond graph); edit the apex.
shared-range-edit
N formulas aggregate one shared range; edit one cell inside it.
distinct-range-edit
Each formula owns its own range; one edit recomputes only its owner.
cross-sheet-range-edit
Summary-sheet formulas range over another sheet; edit the source.
scalar-edit-affects-N
One scalar edit invalidating exactly N dependents; N=0 touches an unrelated cell, so it prices pure dependency lookup.
topology-remove-add
Remove and re-add rows so the dependency graph itself changes shape.
cycles
Introduce a reference cycle; detection and cycle-error propagation.
removed-sheet-ref
Formulas referencing a deleted sheet must all degrade to reference errors.
error-propagation
An error value (=1/0) flows through every dependent.
criteria-range-edit
Criteria-style aggregation (SUMIF shape) over 100k cells; edit inside the criteria range.
lookup-range-edit
Lookup-shape formulas over 100k cells; edit inside the looked-up range.

Reproduce with:

Formula engine evidence
bun run --filter @sheetwrite/bench bench:formula

Choose a formula engine

Shared functions use the same implementation in both builds. Choose by function coverage and download size, not small timing differences.

Default engine

@sheetwrite/wasm

Core spreadsheet functions, with the smaller download.

290.7 KiBBrotli WASM · 991.6 KiB uncompressed
Full engine

@sheetwrite/formulas

Default functions, plus statistics, finance, LAMBDA, grouping, and other analysis functions.

409.8 KiBBrotli WASM · 1.3 MiB uncompressed

Browse functions by engine · Use the full engine

All measurements

Analysis workloads

Each workload has 1,000 formulas. It has one untimed warmup and five measured samples. Each sample uses a fresh store. The timer measures first recompute. Setup and output reads are outside the timer. Every result is read and summed as a checked checksum. The default engine does not run these analysis functions.

LINEST, SORTBY, and MAP/REDUCE use 100 input rows. DSUM uses 100 database rows, a header, and the criterion key > 0. XIRR uses two cash flows one year apart. The text workload uses three fields. MODE.MULT uses five scalar inputs. MMULT uses two 2-by-2 matrices. Array outputs are reduced to scalar results.

analysis workloadlog scale — every tick is 10× — shorter is fastermedianp95
Linest
1,000 formulas3.80 ms6.00 ms
Xirr
1,000 formulas0.92 ms1.33 ms
Dsum
1,000 formulas2.96 ms3.18 ms
Textsplit regexreplace
1,000 formulas2.77 ms2.84 ms
Sortby
1,000 formulas3.46 ms4.40 ms
Mode mult
1,000 formulas0.81 ms0.84 ms
Map reduce
1,000 formulas22.6 ms23.1 ms
Mmult
1,000 formulas0.82 ms0.92 ms

Brotli size uses quality 11 on the WASM file only. It is not the full package transfer size. The raw artifacts retain cold-initialization samples; those samples do not establish that one engine initializes faster.

Raw evidence: bench/results/full-engine-matched-results.json and bench/results/full-engine-results.json. They retain time samples, output checks, allocation samples, source hashes, and WASM hashes. The full workload captures are bench/results/formula-default-results.json and bench/results/formula-full-results.json. Their baseline gates stay blocked in output mode. A valid capture is not a passed timing regression gate.

Analysis capture
2026-10-29 04:24 UTC
Analysis commit
ac4ab632d80a clean worktree

Shared-workload captures are validation data, not evidence that one package is faster.

Reproduce after bun run build:packages:

Default and full engine evidence
cd bench
bun run bench:formula:engines
bun run bench:formula:matched
Documentation