Statement

cost(mutation) = O(k), where k is the number of paths that actually depend on what changed — independent of n, the total size of the graph. This is a locality claim, not a speed guarantee: when k itself is large, the cost is genuinely large.

Definitions

n = total graph size
k = |affected dependency frontier| for a given mutation
cost(mutation) = O(k)

Benchmark: Root_Fanout_100k

n = 100,000 dependents, all subscribed to one root.

phasemeasured
wiring 100,000 subscribers137,451.08 ms
one mutation, k = 100,00021,283.534 ms

This is k at its worst: every one of 100,000 nodes genuinely depends on the changed value, so all 100,000 recompute. Real cost, ~21.3s, not hidden by the formula. O(k) does not mean fast — it means proportional to k, and here k is enormous.

Benchmark: Hemisphere_1M

n = 1,000,000 nodes.

phasemeasured
setup, 1,000,000 nodes21,899 ms
one mutation, k = 64.346 ms
heap~533 MB

This is the actual demonstration of O(k), not O(n): a million-node graph, but the specific mutation tested only has 6 real dependents — so it costs 4.346ms, not something scaled to a million. n being huge did not touch this number. k did.

Benchmark: explain() Overhead

3,000 nodes, 300 iterations — whole-loop latency, not per-node.

modep95
baseline0.0154 ms
with explain()0.0198 ms (+28.11%)
An earlier draft of this page cited "~0.06ms per node" for this benchmark — that figure does not appear anywhere in the actual test output and misdescribes what's measured (a full mutation+read loop over 3,000 nodes, not a per-node cost). Dropped; the real numbers above replace it.

Historical: Array → Set

Subscriber tracking and recompute-wave tracking were both refactored from linear-scan array patterns to Set-backed structures — confirmed in the kernel's git history (commit a186449):

// before
if (!list.includes(value)) list.push(value);

// after
set.add(value);

Applied in both cascade.ts's dependency graph and derivation.ts's refSubscribers and recompute-wave tracking. This is what makes membership checks and inserts O(1) instead of O(k) on top of the already-real O(k) traversal — removing a second, avoidable cost layered on the first.