Skip to content

Effects

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

CI instruction counts

The Library comparison tab compares retired instructions per operation across engines for the latest CI measurement; the Trends tab follows each scenario across commits. The tab last chosen opens on every benchmark page, and the runtime and metric carry over between tabs. Choose a runtime, metric or recorded commit, filter scenarios by name, and toggle libraries in the legend.

The trend panels follow each scenario across every recorded commit, one panel per Ranvier variant. By default each line shows its change since its first record; libraries other than Ranvier are pinned versions, so their lines mark the run-to-run noise floor. A change since the first record of 5 % or more is set in bold. The Metric control switches between instructions, cycles, allocated bytes, branch misses and CPU time on x64. The shaded band spans the lowest and highest of the five processes behind each median, and a ▲ under the axis marks a commit at which the Ranvier variant’s library counters changed; the readout lists those counters.

Loading recorded measurements…

Effects are the push side of the graph: nothing reads them, so the scheduler runs them. A write that reaches an effect therefore pays for queueing and flushing on top of notification. See Benchmarks for configuration, environment and caveats.

EffectBenchmarks

One signal observed by Effects effects (1, 8 or 64).

MethodWhat it measures
WriteAndFlushOne write, then the flush that runs every effect the write invalidated.
BatchOfTenWritesTen writes inside one batch, followed by a single flush that runs each effect once.

Recorded 2026-09-28. Source: Ranvier.Benchmarks.Effects.EffectBenchmarks at commit d87920f.

MethodEffectsMeanErrorStdDevOp/sAllocated
WriteAndFlush117.81 ns0.184 ns0.154 ns56,133,012.1-
BatchOfTenWrites136.89 ns0.311 ns0.276 ns27,104,757.4-
WriteAndFlush8114.76 ns1.007 ns0.841 ns8,713,660.1-
BatchOfTenWrites8179.76 ns2.292 ns2.032 ns5,562,912.3-
WriteAndFlush64835.32 ns6.463 ns5.729 ns1,197,153.0-
BatchOfTenWrites641,298.42 ns13.692 ns12.138 ns770,167.6-

BatchOfTenWrites performs ten writes per operation where WriteAndFlush performs one, so the rows are not comparable per operation. The batch row shows the cost of ten writes that share one flush.

FieldWriteBenchmarks

One field of a Fields-field model (8, 64 or 256) changed per write, with one effect per field. The model is an int[], so a copy costs what copying a record of Fields fields costs. See Elmish for the patterns compared.

MethodWhat it measures
FieldSignalWriteBaseline. A write to one field's own signal, waking its one effect. Flat in Fields.
SelectorMemoWriteA copy of a root signal's model with one field changed, re-running all Fields selector memos and waking one effect.
MvuDispatchSelectorMemoWrite through Mvu.Dispatch and Mvu.Select.
ModelCopyOnlyThe model copy alone: the part of SelectorMemoWrite spent outside the graph.

The counter scenario mvu-dispatch compares a signal per field (FieldSignalWrite) with Mvu.Dispatch (MvuDispatch) at 64 fields. Figures are retired instructions per operation at commit e13f159, .NET 10 with tiered compilation and PGO off (see Instruction counts):

Variantinstr/opbytes/opLibrary counters/op
A signal per field6070EffectRuns 1, Flushes 1
Mvu.Dispatch48,133400MemoRecomputes 64, EffectRuns 1, Flushes 1

A dispatch costs about 80 times a field signal write: every observed selector re-runs, 64 recomputes per dispatch. SelectorMemoWrite, ModelCopyOnly and the 8- and 256-field cases are in the suite for local runs (dotnet run --project bench/Ranvier.Benchmarks -c Release -- --filter "*FieldWriteBenchmarks*") and have no published figure yet. The full report is e13f159.md.

EditableBenchmarks

A writable derived value (createEditable) against a plain signal, each read by one effect.

MethodWhat it measures
PlainSignalWriteBaseline. One signal write, waking one effect.
LocalEditA local edit, waking the effect that reads the editable.
UpstreamChangeAn upstream write propagating through the source, the seed, the editable and the effect.

The counter scenarios editable-edit and editable-upstream measure the same two writes over 1000 editables, each read by one effect. One operation writes every 10th editable, 100 writes in all. Figures are retired instructions per operation at commit e13f159, .NET 10 with tiered compilation and PGO off (see Instruction counts):

Scenarioinstr/opbytes/opLibrary counters/op
editable-edit145,0060MemoRecomputes 100, EffectRuns 100, Flushes 100
editable-upstream194,4492,400MemoRecomputes 200, EffectRuns 100, Flushes 100

A local edit costs about 1,450 instructions and allocates nothing; an upstream write costs about 1,940 instructions and 24 bytes, and recomputes two memos per write. PlainSignalWrite has no counter scenario of its own and no published figure yet; the timing cases are in the suite for local runs (dotnet run --project bench/Ranvier.Benchmarks -c Release -- --filter "*EditableBenchmarks*"). The full report is e13f159.md.

BindingBenchmarks

A fixed-dependency binding (bindSignal, bindMemo) against the effect that does the same copy: one consumer of one source, writing that source's value. Nodes is the consumer count (1, 64 or 1024).

BindingLifecycleBenchmarks

MethodWhat it measures
SignalEffectsCreating and disposing Nodes effects under a root, each copying a signal's value. Baseline.
SignalBindingsThe same with bindSignal.
MemoEffectsCreating and disposing Nodes effects under a root, each copying a memo's value.
MemoBindingsThe same with bindMemo.

Recorded 2026-10-13. Source: Ranvier.Benchmarks.Bindings.BindingLifecycleBenchmarks at commit 70291a5.

MethodNodesMeanErrorStdDevOp/sRatioRatioSDGen0Gen1AllocatedAlloc Ratio
SignalEffects1146.8 ns3.22 ns9.43 ns6,810,474.5baseline****0.0372-624 B****
SignalBindings1124.7 ns2.45 ns2.52 ns8,019,152.21.18x faster0.08x0.0281-472 B1.32x less
MemoEffects1155.1 ns3.39 ns10.00 ns6,445,906.91.06x slower0.10x0.0372-624 B1.00x more
MemoBindings1123.8 ns2.51 ns5.02 ns8,075,577.51.19x faster0.09x0.0281-472 B1.32x less
SignalEffects647,736.2 ns152.72 ns344.71 ns129,262.9baseline****1.26650.068721288 B****
SignalBindings645,588.6 ns102.30 ns208.97 ns178,934.81.39x faster0.08x0.68660.022911560 B1.84x less
MemoEffects647,386.9 ns145.74 ns280.79 ns135,374.31.05x faster0.06x1.26650.068721288 B1.00x more
MemoBindings645,506.9 ns110.00 ns227.16 ns181,590.21.41x faster0.08x0.68660.022911560 B1.84x less
SignalEffects1024120,896.0 ns2,408.23 ns4,089.36 ns8,271.6baseline****20.019510.3760336168 B****
SignalBindings102483,838.6 ns1,662.03 ns2,822.26 ns11,927.71.44x faster0.07x10.74223.5400180520 B1.86x less
MemoEffects1024120,891.7 ns2,415.94 ns5,200.56 ns8,271.91.00x slower0.06x20.019510.3760336168 B1.00x more
MemoBindings102493,561.4 ns2,235.33 ns6,590.92 ns10,688.21.30x faster0.10x10.74223.5400180520 B1.86x less

Binding / effect, each binding against the effect of its own source kind:

NodesKindMean ratioAlloc ratio
1signal0.8490.756
1memo0.7980.756
64signal0.7220.543
64memo0.7450.543
1024signal0.6930.537
1024memo0.7740.537

BindingUpdateBenchmarks

MethodWhat it measures
WriteAndFlushOne write to the source, then the flush that runs Nodes consumers. Kind selects signal-effect, signal-binding, memo-effect or memo-binding.

Recorded 2026-10-13. Source: Ranvier.Benchmarks.Bindings.BindingUpdateBenchmarks at commit 70291a5.

MethodNodesKindMeanErrorStdDevMedianOp/sAllocated
WriteAndFlush1memo-binding48.05 ns0.762 ns0.713 ns47.91 ns20,812,758.1-
WriteAndFlush1memo-effect56.29 ns1.603 ns4.701 ns57.57 ns17,766,508.5-
WriteAndFlush1signal-binding21.73 ns0.383 ns0.339 ns21.79 ns46,021,463.3-
WriteAndFlush1signal-effect27.45 ns0.464 ns0.411 ns27.54 ns36,427,456.7-
WriteAndFlush64memo-binding1,123.43 ns21.877 ns29.206 ns1,126.71 ns890,133.8-
WriteAndFlush64memo-effect1,488.38 ns39.909 ns117.674 ns1,485.11 ns671,871.1-
WriteAndFlush64signal-binding913.76 ns18.175 ns38.338 ns913.92 ns1,094,385.0-
WriteAndFlush64signal-effect1,331.63 ns26.623 ns49.347 ns1,331.78 ns750,961.9-
WriteAndFlush1024memo-binding17,798.70 ns355.752 ns668.188 ns17,782.70 ns56,183.9-
WriteAndFlush1024memo-effect23,302.72 ns463.603 ns1,352.351 ns23,355.53 ns42,913.4-
WriteAndFlush1024signal-binding14,720.96 ns286.281 ns401.326 ns14,717.00 ns67,930.3-
WriteAndFlush1024signal-effect21,198.52 ns418.919 ns652.206 ns21,073.43 ns47,173.1-

Binding / effect, each binding against the effect of its own source kind:

NodesKindMean ratio
1signal0.792
1memo0.854
64signal0.686
64memo0.755
1024signal0.694
1024memo0.764

No kind allocates per write. Both bindings are at most 0.854x their effect at every Nodes.

Both reports are a single run on the recording machine: AMD Ryzen 9 9900X, Windows 11, .NET 10.0.12, BenchmarkDotNet v0.15.8. Commit 16d2e63 later reordered owner attachment in Start and added trace edges that compile out of Release builds. The figures were not re-recorded after it. Reproduce with dotnet run --project bench/Ranvier.Benchmarks -c Release -- --anyCategories Binding.

Edit this page