Configuration & Performance Tuning
Dial each profiler subsystem on or off, and tune the drain pipeline, without touching a build.
Status: โ Implemented ยท Visibility: Public ยท Level: ๐ต Core ยท Category: Profiler
๐ฏ What it solves
Profiling is only "zero when disabled" if disabling it truly costs nothing, and only safe to enable in production if you can turn on just the subsystem you need โ not the whole tracer. Typhon exposes both knobs outside the binary: a config file (or environment variables) toggles each opt-in subsystem independently, and a settings object governs how the drain pipeline buffers events, trading latency against CPU.
โ๏ธ How it works (in brief)
typhon.telemetry.json (or TYPHON__PROFILER__* env vars) is read once, at class load, into
TelemetryConfig โ a set of static readonly bool gates, one per subsystem, each combined with the master
Profiler.Enabled switch (e.g. ProfilerGcTracingActive = ProfilerActive && ProfilerGcTracingEnabled). Because
the fields are static readonly, the JIT proves a false gate can never flip and deletes the guarded code
outright. ProfilerOptions is a separate settings object governing the consumer thread's drain cadence and
buffering; today it ships with engine-tuned defaults (see limits).
๐ป Usage
// typhon.telemetry.json โ dropped next to the executable, or set the TYPHON__PROFILER__* equivalents:
// {
// "Typhon": {
// "Profiler": {
// "Enabled": true,
// "GcTracing": { "Enabled": true },
// "MemoryAllocations": { "Enabled": false },
// "CpuSampling": { "Enabled": false },
// "Gauges": { "Enabled": true }
// }
// }
// }
using Typhon.Engine;
// Force the static constructor to run (and the JIT gate to resolve) before your hot paths compile,
// then log exactly what's active โ useful at startup for a diagnostic banner.
TelemetryConfig.EnsureInitialized();
Console.WriteLine(TelemetryConfig.GetConfigurationSummary());
// Individual gates are public fields โ branch on them for app-side diagnostics if you need to:
if (TelemetryConfig.ProfilerGcTracingActive)
{
Console.WriteLine("GC tracing is capturing to the trace.");
}
# Override a single sub-flag for one run, no file edit:
TYPHON__PROFILER__GAUGES__ENABLED=false dotnet run
| Config key | Default | Enables |
|---|---|---|
Typhon:Profiler:Enabled |
false |
Master gate โ every flag below is forced off when this is false |
Typhon:Profiler:GcTracing:Enabled |
false |
GC ingestion thread; GcStart/GcEnd/GcSuspension records |
Typhon:Profiler:MemoryAllocations:Enabled |
false |
Per-alloc/free MemoryAllocEvent for PinnedMemoryBlock traffic |
Typhon:Profiler:CpuSampling:Enabled |
false |
In-process CPU stack sampling, embedded in the trace file |
Typhon:Profiler:Gauges:Enabled |
false |
Per-tick PerTickSnapshot (memory, page cache, WAL, tx/UoW) |
ProfilerOptions fields (engine-internal defaults today โ see limits):
| Field | Default | Controls |
|---|---|---|
ConsumerCadence |
1 ms | Drain-thread wake interval โ lower = lower live-viewer latency, higher CPU |
PerExporterChannelDepth |
4 | Batches buffered per exporter before drop-newest kicks in |
MergeBufferBytes |
512 KB | Per-pass merge scratch capacity on the drain thread |
SpilloverBufferCount / SpilloverBufferSizeBytes |
8 ร 16 MiB | Overflow rings a producer chains onto when its primary ring fills |
โ ๏ธ Guarantees & limits
- Disabled subsystems cost exactly 0 ns โ Tier 1 JIT dead-code-eliminates the guarded block (ADR-019),
proven by benchmark, not just claimed. Every sub-flag is independent:
GcTracingcosts nothing onMemoryAllocationsorGauges. - Gates resolve once, at class load โ editing the JSON/env after the process starts has no effect until restart.
- Effective precedence, highest to lowest: environment variables โ
typhon.telemetry.jsonโ built-in (all-false) defaults. Keys live under theTyphon:Profiler:*namespace. - Measured overhead with GcTracing + MemoryAllocations + Gauges all on: under 1% scheduler-thread cost on tested workloads.
ProfilerOptionstuning is not yet exposed on the standard zero-code path (AddTyphonProfiler+TyphonRuntime.Create): the internal bootstrap always constructsnew ProfilerOptions(). Setting a custom instance requires callingTyphonProfiler.Startdirectly, which isinternalโ reachable only by hosts with noTyphonRuntimeof their own (today, Typhon's own tooling). No public override hook exists yet.TyphonProfiler.TotalDroppedEvents > 0after a run means the producer outran the consumer โ checkConsumerCadenceand spillover sizing first.
๐งช Tests
- TelemetryConfigGateShapeTests โ enforces every
*Activegate field ispublic static readonly bool, the structural precondition the JIT dead-code-elimination claim depends on - TelemetryConfigCpuSamplingTests โ one gate worked end-to-end: reads from
typhon.telemetry.json, composesActive = ProfilerActive && Enabled, surfaces inGetConfigurationSummary
๐ Related
- Sibling: Profiler Session Lifecycle & Zero-Code Bootstrap โ reads this config to decide which exporters and subsystems to start
- Sibling: Domain-Specific Tracing Instrumentation Expansion โ the Tier-2 per-domain gating scheme layered on top of this mechanism
- Source:
src/Typhon.Engine/Observability/public/TelemetryConfig.cs,src/Typhon.Engine/Profiler/public/ProfilerOptions.cs,src/Typhon.Engine/Profiler/internals/ProfilerBootstrap.cs