Skip to content

Lifetimes

Preview — Ranvier is pre-release; its APIs may change.

Construction and teardown are not the hottest path, but they are the path a UI takes on every mount and unmount, and the one where an owner that only grows would show up as a leak rather than as a slowdown. See Benchmarks for configuration, environment and caveats.

ConstructionBenchmarks

MethodWhat it measures
CreateSignalBaseline. Constructing a signal.
CreateAndDisposeMemoConstructing and disposing a memo that reads nothing.
CreateAndDisposeEffectConstructing and disposing an effect that reads nothing.

CreateAndDisposeSeededMemo constructs and disposes a memo through the seeded constructor, Memo (graph, seed, compute), which adds one adapter closure over the seed and the compute. It postdates the table below and has no published figure yet; it is in the suite for local runs (dotnet run --project bench/Ranvier.Benchmarks -c Release -- --filter "*ConstructionBenchmarks*").

There is deliberately no create-without-dispose case for memos. A memo attaches itself to its enclosing owner, so one that is never disposed is retained for the life of the graph by design; measuring that would measure garbage collection over a growing child list rather than construction.

Recorded 2026-09-28. Source: Ranvier.Benchmarks.Lifetimes.ConstructionBenchmarks at commit d87920f.

MethodMeanErrorStdDevOp/sRatioRatioSDGen0AllocatedAlloc Ratio
CreateSignal8.552 ns0.1997 ns0.2219 ns116,926,213.1baseline0.005388 B
CreateAndDisposeMemo25.348 ns0.4896 ns0.4580 ns39,451,210.82.97x slower0.09x0.0148248 B2.82x more
CreateAndDisposeEffect35.357 ns0.7312 ns0.8980 ns28,282,776.94.14x slower0.15x0.0114192 B2.18x more

ReadingNodeBenchmarks

The same lifecycle for nodes that read a source, so the dependency edge is part of the cost: linked on the first run, unlinked on disposal.

MethodWhat it measures
CreateAndDisposeReadingEffectAn effect that reads one signal, created and disposed.
CreateAndDisposeReadingMemoA memo that reads one signal, created and disposed.

Recorded 2026-09-28. Source: Ranvier.Benchmarks.Lifetimes.ReadingNodeBenchmarks at commit d87920f.

MethodMeanErrorStdDevOp/sGen0Allocated
CreateAndDisposeReadingEffect39.67 ns0.811 ns0.719 ns25,208,600.50.0129216 B
CreateAndDisposeReadingMemo40.46 ns0.800 ns0.821 ns24,716,778.60.0162272 B

FanOutLifecycleBenchmarks

Nodes computations (1, 64 or 1024) on one source, created in a scope and torn down with it. The larger cases cross the observer list's indexing threshold, so they include building and draining that index. Divide by Nodes for the cost per node.

MethodWhat it measures
EffectsOnOneSourceNodes effects reading one signal, created and disposed as a unit.
MemosOnOneSourceNodes memos reading one signal, created and disposed as a unit.

Recorded 2026-09-28. Source: Ranvier.Benchmarks.Lifetimes.FanOutLifecycleBenchmarks at commit d87920f.

MethodNodesMeanErrorStdDevOp/sGen0Gen1Allocated
EffectsOnOneSource168.62 ns1.392 ns1.547 ns14,573,344.50.0257-432 B
MemosOnOneSource167.41 ns0.959 ns0.850 ns14,833,915.80.0291-488 B
EffectsOnOneSource644,008.59 ns53.988 ns47.859 ns249,464.30.92320.045815552 B
MemosOnOneSource643,992.81 ns77.870 ns72.840 ns250,450.31.14060.068719136 B
EffectsOnOneSource102469,184.00 ns1,100.919 ns1,029.800 ns14,454.214.64846.7139245952 B
MemosOnOneSource102468,837.85 ns1,084.798 ns961.645 ns14,526.918.06649.2773303296 B

ScopeBenchmarks

A scope with Children children (1, 8 or 64): the shape a component mount takes.

MethodWhat it measures
CreateAndDisposeScopeThe scope and its children, created and torn down as a unit.
DisposeChildrenIndividuallyThe same children, each disposed on its own rather than through the scope. This checks that an individually disposed child leaves its owner's child list.

Recorded 2026-09-28. Source: Ranvier.Benchmarks.Lifetimes.ScopeBenchmarks at commit d87920f.

MethodChildrenMeanErrorStdDevOp/sGen0Gen1Allocated
CreateAndDisposeScope167.45 ns0.841 ns0.703 ns14,826,300.90.0291-488 B
DisposeChildrenIndividually142.37 ns0.829 ns0.987 ns23,602,124.60.0196-328 B
CreateAndDisposeScope8380.10 ns6.883 ns6.438 ns2,630,910.60.15260.00102560 B
DisposeChildrenIndividually8391.09 ns4.925 ns4.607 ns2,556,979.70.14640.00102456 B
CreateAndDisposeScope643,961.65 ns38.597 ns34.215 ns252,420.31.14060.068719136 B
DisposeChildrenIndividually644,581.74 ns40.383 ns35.799 ns218,257.81.15970.061019480 B

SizeOfProbe

The shallow size of each node type: the Allocated column of an uninitialised instance, which counts the object's own fields and none of the objects they reference. It tracks how a change to a node's fields moves its footprint.

MethodType
MemoIntMemo<int>
AsyncMemoIntAsyncMemo<int>
BoundaryIntBoundary<int>
EffectEffect
AsyncSourceIntAsyncSource<int>
FailureFailure, the record a failed node holds.

SizeOfProbe has no counter scenario and no published figure yet. It is in the suite for local runs: dotnet run --project bench/Ranvier.Benchmarks -c Release -- --filter "*SizeOfProbe*".

The Ratio and Alloc Ratio columns, where present, compare each method with the baseline method of the same class on the same run. They describe the relative cost of two operations inside Ranvier, not a comparison with any other library.

Edit this page