Class RuntimeOptions
Configuration options for the DAG scheduler and runtime tick loop.
public class RuntimeOptions
- Inheritance
-
RuntimeOptions
- Inherited Members
Constructors
RuntimeOptions()
public RuntimeOptions()
Properties
AdaptiveFenceCost
When true, MigrationCost and AabbCost are continuously calibrated from a 64-tick sliding window of per-chunk measurements. Static FenceCostModel values seed the model at startup; subsequent ticks converge toward the measured µs/unit. Disable for repeatable benchmarks or to pin behaviour to the static seed.
public bool AdaptiveFenceCost { get; set; }
Property Value
BaseTickRate
Target tick rate in Hz. Default: 60. The scheduler uses metronome-style tick advancement to prevent drift.
public int BaseTickRate { get; set; }
Property Value
CostBasedChunking
Size parallel QuerySystem chunks by their measured cost rather than their entity count. Default: true.
public bool CostBasedChunking { get; set; }
Property Value
Remarks
From its second dispatch on, a system's chunk count comes from its previous dispatch's worker time per entity. Its chunks spread over the
width, round(WorkerCount × ChunksPerWorker), as long as each carries 25–100 µs of work; below that band it gets fewer chunks of 25 µs, above it
more chunks than the width, of 100 µs, up to twice the width, which the workers claim as they come free, so the pool no longer waits on one long last
chunk. At most one entity per chunk, and beyond the width at most one cluster per chunk.
A system's first dispatch, a checkerboard system and a system with its own MinChunkSize keep the entity
rule: min(round(WorkerCount × ChunksPerWorker), ceil(entities / ParallelQueryMinChunkSize)).
EnableParallelFence
When true (default), WriteTickFence is parallelized across the worker pool via the internal sub-DAG (FenceExec).
When false, the runtime falls back to the legacy single-threaded serial fence — useful for diagnostics and as a safety
fallback if a regression is suspected. Enabling adds FenceExec to the scheduler's full system array, but
SystemCount reports user-registered systems only — see AllSystemCount
for the total including internal systems.
public bool EnableParallelFence { get; set; }
Property Value
EntityCommandsPerTick
Entity commands (ctx.Commands.Spawn / Destroy) this runtime is sized for per tick, across every worker (#1099). Default 4 096.
public int EntityCommandsPerTick { get; init; }
Property Value
Remarks
A hint for sizing and a ceiling for loss — not a limit that fails the tick. The budget is split across worker slots at bind and each segment grows on demand toward the whole figure, so a skewed tick can legitimately exceed the per-slot share. Past the figure itself a command is dropped, OverflowCount counts it exactly, and the tick continues. That is a deliberate choice against failing the tick: this is a number an application has to pick before it has met its own worst tick, and the worst tick is the mass-death event nobody sizes correctly the first time. Losing some corpses loudly beats losing the frame.
It also derives the key-block size, which is why that is capped rather than trusted. Each producer takes a block of
clamp(EntityCommandsPerTick / stride, 16, max(16, 1024 / stride)) keys per generation, and an unissued block tail is key space spent. The
1 024 product cap bounds that at about 0.57 % of an archetype's 2^48 keys per year at a continuous 50 Hz; derived uncapped from this option it
would burn 36.8 % per year at stride 8 and 73.5 % at stride 32. So raising this option costs memory and nothing else.
EntityMapBulkMinEntriesPerBucket
Minimum expected entries per bucket (migrants / bucketCount) before migration stages its EntityMap patches for the bulk phase instead of
applying them inline. Below it, the inline per-entity path runs and the phase emits nothing.
public float EntityMapBulkMinEntriesPerBucket { get; init; }
Property Value
Remarks
The bulk path's entire gain comes from entries sharing a bucket, and a small batch has none to share. Measured A/B on --fence-phase
against a 400 000-entity archetype whose map holds ~57 000 buckets, comparing Migrate CPU + EntityMapUpdate CPU per migrant:
| 100 000 migrants (~1.8 per bucket): bulk 402 / 511 / 558 ns at W = 1 / 4 / 8 against inline 691 / 849 / 758. Bulk wins at every worker count. |
| 10 000 migrants (~0.17 per bucket): bulk 1287 / 1240 / 1350 ns against inline 1571 / 1022 / 1066. Inline wins at W ≥ 2, and the spread within a single arm is wide enough that the two are not separable — the EntityMap term is a small part of a noisy Migrate phase at that batch size. |
The default is set from the mechanism, not from a resolved crossover. Runs shorter than one entry amortise nothing by construction, so
1.0 is where the bulk path's advantage begins to exist at all; the crossover is somewhere below it and the benchmark could not resolve where.
Treat the value as provisional and re-measure when step 10's re-clustering produces a realistic migration rate — that is the workload this should be
tuned against, not the synthetic churn above.
Set to 0 to force the bulk path always, and MaxValue to force the inline path for any archetype whose map has buckets;
both arms of the measurement above used exactly that, via --fence-phase --bulk-min=. The MaxValue sentinel relies on
.NET Core 3.0+ SATURATING float-to-integer conversion — (long)(float.MaxValue * bucketCount) overflows to infinity and saturates to
MaxValue, which no migrant count reaches. Under the older unspecified conversion the same expression could yield
MinValue and force the BULK path instead, silently inverting the sentinel.
FenceChunkOversubscription
Oversubscription factor for fence-chunk dispatch: the chunk-count cap becomes FenceChunkOversubscription × WorkerCount.
Above 1 lets the scheduler smooth out per-worker preemption jitter — a healthy worker can pick up the next queued chunk
while a preempted worker finishes its current one. Must be ≥ 1. Default: 2.
public int FenceChunkOversubscription { get; set; }
Property Value
FenceCostModel
Cost model coefficients for the fence work-planner. Each per-stage cost is the hint count multiplied by the matching coefficient; the planner uses the total cost to pick chunk count and split splittable items. Defaults are 1.0 everywhere — tune from real-world traces.
When AdaptiveFenceCost is true (default), this value is only used as the initial seed; runtime
continuously recalibrates MigrationCost and AabbCost from measured chunk wall-time.
public FenceCostModel FenceCostModel { get; set; }
Property Value
Overload
Overload detection and response configuration. Always active with sensible defaults.
public OverloadOptions Overload { get; set; }
Property Value
ParallelQueryMinChunkSize
Minimum number of entities per chunk for parallel QuerySystem dispatch.
Controls granularity: fewer entities per chunk = more parallelism but more overhead (Transaction creation per chunk).
Entity sets smaller than this value still use the parallel chunk path with totalChunks=1.
Default: 64.
public int ParallelQueryMinChunkSize { get; set; }
Property Value
Subscriptions
Memory and policy rails for engine-owned replication — the pools, the per-session ceilings and the skip thresholds. Always present with defaults; a runtime with no session connected pays nothing for it.
public SubscriptionsOptions Subscriptions { get; set; }
Property Value
Remarks
It lives here rather than on TyphonOptions because replication rides the tick: every budget below is spent by the subscriptions track, which the runtime dispatches, and none of it is database state. What an application declares — projections, profiles, commands — goes to Subscriptions instead; this is the operator's half of the same subsystem.
SystemExceptionPolicy
What an unhandled system exception means for the rest of the tick. Default: Isolate — fault isolation, the behaviour Typhon has always had. Set AbortTickAndStop for hosts that treat one tick as the unit of publication and must not publish a partially-executed tick (issue #567). That policy is terminal: the runtime stops ticking after the first fatal system exception.
public SystemExceptionPolicy SystemExceptionPolicy { get; set; }
Property Value
TelemetryRingCapacity
Capacity of the telemetry ring buffer (number of ticks retained). Must be a power of 2. Default: 1024 (~17 seconds at 60Hz, ~200KB).
public int TelemetryRingCapacity { get; set; }
Property Value
WorkerCount
Number of worker threads for parallel system execution.
Set to -1 (default) for automatic: Math.Max(1, Environment.ProcessorCount - 4).
Set to 1 for single-threaded debug mode (systems execute in topological order on the timer thread).
public int WorkerCount { get; set; }