The whole point of incremental view maintenance is that the cost of a write tracks the change, not the table. This page is the measured version of that claim — real numbers, on a real dataset, with the methodology you can rerun.
The headline: on the in-process engine — the same one that runs in the browser via
@rindle/wasmand on the local-first client — an incremental update costs a few hundred nanoseconds to a few microseconds, and stays essentially flat as the dataset grows 30×.
Incremental maintenance is sub-microsecond — and flat
Measured over the Chinook dataset, scaled 1× / 10× / 30× (up to ~124k source rows), applying one row change to a materialized view and reading the result:
| Pattern | Per write (in-process engine) | As data grows 30× |
|---|---|---|
| filtered match | ~430 ns | flat |
ordered top-N (limit) |
~420 ns | flat |
nested child (sub) |
~2.5 µs | flat |
flipped EXISTS |
~880 ns | flat |
The number that matters is the last column. A push_nested_child costs ~2.5 µs at
6.8k rows and ~4 µs at 124k — it does not grow with the table. That is the IVM
contract made physical: you pay for the delta, not the dataset.
Compare that to the alternative everyone reaches for first — re-running the query on every write. A from-scratch hydrate of the same view is in the hundreds of microseconds to low milliseconds range and grows with the data. Incremental maintenance replaces that per-write recompute with a sub-microsecond delta. The gap widens every time the table does.
A small, embeddable engine
The engine is built to go anywhere, so the footprint is part of the contract:
- ~228 kB gzipped — the entire engine, compiled to WebAssembly, running in-process in a browser tab. No server, no worker pool, no round-trip.
std-only core — the Rust engine (rindle) has no required C toolchain and no heavy dependencies; it embeds directly in a binary.- Single-threaded by design — one engine per thread (
!Send), so there are no locks on the hot path. You scale with independent engines and message passing, not a shared mutex.
The data structure underneath: faster than its JavaScript equivalent
The hottest structure in Rindle’s engine — the copy-on-write B-tree that backs
every index and view — was benchmarked 1:1 against an equivalent JavaScript
BTreeSet, same operations, same data shape:
- Rust wins 13 of 18 operations.
- Iteration 1.45–2.13× faster — the per-row hot path for every view. The lending cursor vends a borrowed row with zero allocation per row.
- Index build / sort 1.05–2.32× faster — building an index from rows.
- Copy-on-write fork 2.37× faster — one refcount bump vs. a marked clone.
- Realistic string-key lookups 1.84–1.97× faster.
The five operations where V8 edges ahead are all trivial integer point-lookups, where a small-integer fast path JITs to a register compare — tens of nanoseconds, and noisy run-to-run. On everything that dominates at scale, Rust wins.
Reproduce it
Both benchmarks are in the repository and runnable:
# end-to-end IVM matrix over Chinook (hydration + incremental push, both leaves)
cargo run -p rindle-sqlite --release --features fast-alloc --example bench_chinook_rs
# the COW B-tree, Rust vs the equivalent JavaScript implementation
cargo run --release --features fast-alloc --example bench_rs
See CHINOOK-PERF.md and BENCHMARKS.md for the full matrices, the timing
harness, and the methodology.
Honesty about the edges
The numbers above are the in-process engine — the one the browser and local-first
clients run. Two caveats, both documented in CHINOOK-PERF.md:
- Hydration is not incremental. Materializing a view from scratch is proportional to the result it produces (hundreds of µs to low ms). It’s the steady-state push that’s sub-microsecond. Most apps hydrate once and push forever.
- One pattern needs index stats. A correlated
EXISTSagainst the SQLite-backed replica hydrates quadratically until you runANALYZEon the database — a one-line, data-driven fix that returns it to linear. The in-process engine is unaffected.
Next steps
- How it works — why incremental beats recompute.
- The local-first client — where sub-microsecond local maintenance becomes an instant UI.
- Supported queries — the honest matrix of query shapes.