.me

Rust vs TypeScript Mirror Benchmarks

Run #002 · Aug 26, 2026 · Suis-MacBook-Air.local

This file compares the Rust .me kernel against the TypeScript .me kernel with a mirror suite: same machine, same operation shapes, same measured percentiles, and JSON output from both implementations.

Run #002 includes the Rust lazy-invalidation optimization: lazy writes now bump source path versions and defer dependency freshness checks to read_fresh() instead of walking every subscriber at mutation time.

Raw results:

Versions:

implementation version
Rust this-me 0.3.1
TypeScript this.me 4.0.1

Commands:

cd Rust
cargo run --release --bin bench-mirror > bench-results/rust-mirror-run-002.json

cd ../Typescript
node tests/Benchmarks/benchmark.mirror.ts > ../Rust/bench-results/typescript-mirror-run-001.json

What The Mirror Suite Measures

This intentionally excludes vector search and IVF sidecars. Those are currently TypeScript-only layers, not Rust kernel parity surfaces.

1. O(k) Isolation

Lower is better. rust/ts below 1.00x means Rust was faster.

nodes Rust p95 ms TypeScript p95 ms rust/ts Rust k TS k
10 0.0458 0.0334 1.37x 1 1
100 0.0079 0.0217 0.36x 1 1
1,000 0.0217 0.0183 1.18x 1 1
5,000 0.0142 0.0201 0.71x 1 1
10,000 0.0095 0.0248 0.39x 1 1

Both kernels preserve the .me shape: irrelevant memory does not inflate the recompute wave. The absolute envelope remains tiny for both implementations.

2. Sustained Mutation

metric Rust TypeScript rust/ts
p95 ms 0.0165 0.0286 0.57x
p95 drift 6.85% -73.51% n/a

Both stay in a very small absolute envelope. Rust had the lower p95. TypeScript had negative drift in this run, meaning its late window was faster than its first window.

3. Push vs Pull

Eager mode pays when the source changes. Lazy mode should make mutation cheap and move recompute toward first read.

Eager Mutation P95

fanout Rust p95 ms TypeScript p95 ms rust/ts Rust k TS k
10 0.0827 0.1187 0.70x 10 10
100 0.8529 1.2004 0.71x 100 100
500 7.7877 8.1948 0.95x 500 500
1,000 10.0756 13.8945 0.73x 1,000 1,000
2,500 28.4035 40.5338 0.70x 2,500 2,500
5,000 55.3975 99.7792 0.56x 5,000 5,000

Rust is ahead when both kernels do real eager fanout work.

Lazy Mutation P95

Run #001 exposed the original Rust gap: lazy mutation still walked the subscriber set and scaled with fanout. Run #002 fixes that by using source path versions and stale-on-read checks.

fanout Rust #001 p95 ms Rust #002 p95 ms TypeScript p95 ms Rust #002 / TS Rust k TS k
10 0.0086 0.0016 0.0043 0.38x 1 1
100 0.0719 0.0017 0.0065 0.26x 1 1
500 0.4526 0.0021 0.0056 0.38x 1 1
1,000 1.0103 0.0021 0.0040 0.52x 1 1
2,500 2.7658 0.0026 0.0045 0.58x 1 1
5,000 5.9301 0.0025 0.0053 0.47x 1 1

This is the important change in Run #002. Rust lazy mutation no longer scales with fanout in this mirror workload. The recompute wave remains k = 1, and the mutation path is now below the TypeScript p95 in every measured fanout.

Lazy First Read P95

fanout Rust p95 ms TypeScript p95 ms rust/ts
10 0.0082 0.0174 0.47x
100 0.0091 0.0238 0.38x
500 0.0128 0.0196 0.65x
1,000 0.0108 0.0177 0.61x
2,500 0.0148 0.0213 0.69x
5,000 0.0169 0.0225 0.75x

The cost moved where lazy semantics say it should move: toward read_fresh(). Even there, Rust stayed below TypeScript p95 in this run.

4. Secret Scope

case scope Rust p95 ms TypeScript p95 ms rust/ts Rust k TS k
write_read public 0.0021 0.0050 0.43x 0 0
write_read secret 0.0157 0.0501 0.31x 0 0
derivation_lazy public 0.0151 0.0250 0.60x 1 1
derivation_lazy secret 0.0286 0.5689 0.05x 1 1

Rust is faster across all measured secret-scope rows in Run #002. Direct secret write/read remains slower than public, as expected, but the Rust overhead stays inside a small absolute envelope.

Interpretation

Rust .me is now more than a faithful port. It preserves the TypeScript kernel contracts while taking advantage of Rust’s tight execution model in the hot paths:

The main architectural lesson from Run #002:

lazy freshness belongs to the relation between a derivation and the versions of the paths it depends on, not to a global subscriber walk during mutation.

That is closer to the .me model: a write states a fact; a fresh read resolves whether a relation needs to be recomputed.

Status

Run #001 found the real Rust gap. Run #002 closes it for this mirror workload. The benchmark suite is now useful both as evidence and as a regression guard: