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:
bun run --filter @sheetwrite/bench bench:formulaChoose a formula engine
Shared functions use the same implementation in both builds. Choose by function coverage and download size, not small timing differences.
@sheetwrite/wasm
Core spreadsheet functions, with the smaller download.
290.7 KiBBrotli WASM · 991.6 KiB uncompressed@sheetwrite/formulas
Default functions, plus statistics, finance, LAMBDA, grouping, and other analysis functions.
409.8 KiBBrotli WASM · 1.3 MiB uncompressedBrowse 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.
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.
Shared-workload captures are validation data, not evidence that one package is faster.
Reproduce after bun run build:packages:
cd benchbun run bench:formula:enginesbun run bench:formula:matched