Skip to content

Suspension

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

The pending channel is what lets a computation read an async source that has no value yet. On .NET, a transparent read of a Pending source aborts the reading body by throwing, and an exception is the most expensive single operation in the design. These benchmarks measure what that costs and how a boundary limits it. See Benchmarks for configuration, environment and caveats.

SuspensionBenchmarks

A chain of Depth frames (1, 4 or 16) between the suspending read and the reader. Each iteration writes a trigger that the chain's first memo reads, so every read re-runs the chain.

MethodWhat it measures
RecomputeSettledChainBaseline. Re-running the chain to completion over a settled source.
ThrowThroughChainRe-running the chain and aborting it on a source that never settles. The difference from the baseline is the price of the throw.

Recorded 2026-09-28. Source: Ranvier.Benchmarks.Suspension.SuspensionBenchmarks at commit d87920f.

MethodDepthMeanErrorStdDevOp/sRatioRatioSDGen0AllocatedAlloc Ratio
RecomputeSettledChain118.30 ns0.232 ns0.217 ns54,656,258.3baseline****--NA
ThrowThroughChain11,460.60 ns11.381 ns10.089 ns684,650.179.84x slower1.06x0.0286496 BNA
RecomputeSettledChain464.20 ns0.850 ns0.795 ns15,577,452.1baseline****--NA
ThrowThroughChain41,517.24 ns14.803 ns13.847 ns659,091.423.64x slower0.35x0.0286496 BNA
RecomputeSettledChain16235.95 ns2.367 ns1.977 ns4,238,127.7baseline****--NA
ThrowThroughChain161,637.23 ns17.495 ns16.365 ns610,786.56.94x slower0.09x0.0286496 BNA

BoundaryBenchmarks

A boundary catches the pending channel instead of letting it propagate, so the cost of a suspended subtree is bounded by where the boundary sits rather than by the depth of the whole graph. Each iteration writes a trigger both bodies read, so every read re-runs the body.

MethodWhat it measures
CatchingPendingRe-running a body that is still waiting, with the boundary catching its throw.
CleanBoundaryBaseline. Re-running the body over settled sources: the price of a boundary when nothing is in flight.

Recorded 2026-09-28. Source: Ranvier.Benchmarks.Suspension.BoundaryBenchmarks at commit d87920f.

MethodMeanErrorStdDevOp/sRatioRatioSDGen0AllocatedAlloc Ratio
CatchingPending979.54 ns15.940 ns14.910 ns1,020,886.040.76x slower0.95x0.0153256 B8.00x more
CleanBoundary24.04 ns0.450 ns0.442 ns41,600,534.4baseline0.001932 B

SettleBenchmarks

Settling a source is where a pending subtree becomes live, and it is the only path in the library that can arrive from another thread.

MethodWhat it measures
CreateAndReadBaseline. Constructing an AsyncSource and reading it: the floor for SettleInline.
SettleInlineConstructing a source and settling it on the graph's own thread, which takes the inline path and never touches the dispatcher.

Recorded 2026-09-28. Source: Ranvier.Benchmarks.Suspension.SettleBenchmarks at commit d87920f.

MethodMeanErrorStdDevOp/sRatioRatioSDGen0AllocatedAlloc Ratio
SettleInline10.240 ns0.2031 ns0.1696 ns97,652,054.41.90x slower0.04x0.0076128 B1.33x more
CreateAndRead5.402 ns0.0920 ns0.0816 ns185,130,337.5baseline0.005796 B

FlightPolicyBenchmarks

The price of cancellation per async-memo flight. Each iteration writes a trigger the body reads, then reads the memo, which launches one flight. The body ignores its token, so the cancel runs no callbacks. Flight is Completed when the body returns a completed task, and Superseded when it returns a task that never completes, so every launch supersedes a flight in progress.

MethodWhat it measures
KeepLatestBaseline. A launch under FlightPolicy.KeepLatest, where every flight shares the token source allocated at the first flight.
CancelPreviousA launch under FlightPolicy.CancelPrevious, which cancels and disposes the previous flight's token source and allocates a new one.

Not yet recorded.

FlightBenchmarks

Repeated flight launches under each FlightPolicy, set by Policy. Writes applies to WritesDuringFlight only.

MethodWhat it measures
RelaunchSettledBaseline. Writes a trigger the body reads and reads the memo. The body returns a completed task, so every flight settles on launch.
WritesDuringFlightStarts a flight whose task stays open, writes and reads Writes more times, then completes every open task, reading after each round. Under FinishCurrent the body runs twice whatever Writes is; under the other policies it runs Writes + 1 times.

The counter scenario flight runs the same shape at one write during a flight, once per policy: two writes and reads, then a settle of every flight the writes started. Figures are retired instructions per operation at commit e13f159, .NET 10 with tiered compilation and PGO off (see Instruction counts):

Policyinstr/opbytes/opLibrary counters/op
CancelPrevious7,5201,7200
KeepLatest7,3481,6720
Queue15,0653,1840
FinishCurrent7,6551,7440

CancelPrevious, KeepLatest and FinishCurrent cost the same within about 4 %. Queue costs about twice as much in instructions and bytes. The timing cases are in the suite for local runs (dotnet run --project bench/Ranvier.Benchmarks -c Release -- --filter "*FlightBenchmarks*") and have no published figure yet. The full report is e13f159.md.

FailureBenchmarks

The error channel's cost on a recomputation. Each iteration writes a trigger the memo reads, so every read re-runs the body.

MethodWhat it measures
SucceedingRecomputeBaseline. Re-running a memo whose body succeeds.
FailingRecomputeRe-running a memo whose body throws a fresh exception. The failure records the memo as its origin.

The counter scenario fail-recompute measures the same recomputation with one reader, then reads the reader's ErrorOrigin. 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
Succeeding1,1310MemoRecomputes 2
Failing62,6861,576MemoRecomputes 2

A failing recompute costs about 55 times a succeeding one, and the figure includes throwing a fresh .NET exception. The timing cases are in the suite for local runs (dotnet run --project bench/Ranvier.Benchmarks -c Release -- --filter "*FailureBenchmarks*") and have no published figure yet. The full report is e13f159.md.

FailureChainBenchmarks

A failure's cost per reader: a memo whose body throws a fresh exception, read through Depth memos (1, 4 or 16) that each read the one before with Value. Each reader adopts the upstream failure and its origin.

MethodWhat it measures
FailingRecomputeOneHopRe-running the failing memo and every reader on the path, then reading the last reader's failure.

The failing row of fail-recompute above measures Depth 1. Depth 4 and 16 are in the suite for local runs (dotnet run --project bench/Ranvier.Benchmarks -c Release -- --filter "*FailureChainBenchmarks*") and have no published figure yet.

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