1
0
Fork 0
forked from hanez/xtc
xtc/docs/PERFORMANCE.md

5.5 KiB
Raw Blame History

Performance and realtime qualification

make test-performance runs a hardware-independent 48 kHz, 128-frame stereo workload: 64 graph nodes, 4096 MIDI notes (32 simultaneous), 16 tracks and gain automation. Preparation and block timing use CLOCK_MONOTONIC outside the engine; callback code itself does not clock or log. The test checks finite output and reports mean, p99, maximum and CPU-time/audio-duration ratio. A run is a workload measurement, not a universal realtime threshold. Run more blocks with:

./build/test_performance 10000

Initial Phase 9 measurement on an AMD Ryzen Threadripper 1920X, GCC 16.2.1, -O2, 2000 blocks: preparation 3.045 ms, mean 308.301 us, p99 422.153 us, maximum 665.096 us, versus a 2666.667 us block deadline; CPU/audio ratio 0.1156. These are one host/run's observed values and depend on scheduling/load/compiler. The test excludes WAV disk preparation, external/native plugin complexity, GUI redraw, server scheduling and actual hardware latency. Native callbacks must provide their own bounded realtime implementation.

Existing software JACK loopback, stereo MIDI/save/reopen, record/reopen, restart/close and concurrent graph handoff tests remain available. Runtime fault injection covers loss/rate/xrun/null-buffer and lifecycle failures without altering the shared server. ASan/UBSan validate memory/error paths; TSan checks recorder/handoff races, not timing. Sanitized runs are not latency qualification.

Current worst-case storage includes up to 256 MiB preloaded mono WAV samples, paired graph buffers (128 MiB at 256 nodes/65536 frames; GUI limit 32 MiB), bounded MIDI/mix metadata and up to 33 metadata/graph history snapshots. Pending/retired plans can retain additional graph memory. Native preparation can allocate more; the API cannot enforce an extension's memory budget. There is no CPU overload scheduler, disk streaming, latency compensation or callback allocator audit of arbitrary third-party code. High-density interactive projects, physical devices, long-duration stereo/recording and live server termination remain unqualified.

Phase 19 matrix, capacity and contention checks

make benchmark records 24 warmed graph cases: 2/16/64/256 nodes, mono/stereo, 44.1k/31, 48k/128 and 96k/1024. Each reports control-thread compile time separately from 300 process calls, mean/p99/max, block deadline, mean load, and paired graph buffer bytes. Timing, sorting, allocation and logging happen outside processing. Metered gain chains test graph scaling; they do not represent expensive plugins. The checked-in performance-baseline.json records CPU/compiler/flags/kernel and all measurements. Copy a new run only after reviewing changes and conditions.

make benchmark test-performance test-scale test-stress
make benchmark-compare BENCHMARK_MAX_REGRESSION=1.5
# Select a baseline from this same host/compiler/flags:
make benchmark-compare BENCHMARK_BASELINE=/path/to/performance.json

Comparison rejects mismatched CPU/architecture/compiler/flags or missing cases. It fails if mean time exceeds both the ratio budget and a 1 microsecond absolute noise allowance. p99/max are reported for investigation, not universally gated. Repeat surprising results under comparable load; do not silently replace the baseline to make a regression disappear. Scheduling, clocks, thermal state and shared-machine contention still affect results. No real-time scheduling/pinning or external profiler is required. CPU-pressure runs are correctness checks, not comparable timing baselines. Tests/benchmark outputs live below BUILD_DIR.

Initial matrix (Threadripper 1920X, GCC 16.2.1, -O2, 300 blocks): 256-node stereo 48k/128 mean 353.214 us, p99 396.495 us, load .132; 96k/1024 mean 2478.520 us, p99 3092.434 us, load .232. The full JSON is the evidence. These met the local investigation target of mean load <.5 for ordinary graph chains; this is a host-specific development target, not a supported maximum CPU or latency promise.

The mixed 64-node/4096-note workload measured 340–357 us before removing unused live-controller processing and 313 us after (mean over 2000 blocks); p99 varied with system load. The measured hot-path change skips live voices/controller ramps when neither is active and avoids finished-ramp calls. Exact CC/odd-block tests continue to pass. No other graph optimization was justified by this measurement.

test-scale constructs all 64 tracks, 1024 clips, 1024 automation points and 4096 notes, compiles a standalone 256-node metered graph, renders stereo odd-sized blocks and rejects capacity excesses. Project/GUI graphs remain capped at 64 nodes. test-stress runs publication, allocation/retirement, capacity and optional GUI/timer/redraw tests while two ordinary CPU workers compete; subprocesses have timeouts and workers are always terminated. Existing callback guards verify no host allocation/free/loading/unloading. This does not qualify arbitrary plugins.

Callback work is bounded by documented node/frame/clip/note/event/point capacities. Exceeding model/plan/mailbox capacities returns CAPACITY transactionally; invalid or overlarge processing buffers are silenced and reported, JACK xruns are counted. There is no CPU scheduler or preemption inside a DSP callback: an expensive trusted plugin can miss a deadline or hang. Automated LADSPA lanes currently use run(1) and bounded per-sample lane searches and may be costly; they are excluded from the gain-chain baseline. Large WAV memory limits remain those in AUDIO_FILES.md. Physical-device endurance, CPU-overload sound quality and a full GUI latency benchmark remain unqualified.