Measured 2026-09-07 on Apple M5 Max, macOS 26.6.2, Zig 0.16.0, ReleaseFast.
Reproduce
From src/hikari:
zig build ai-perception-bench -Dgame-src=../../../hikari-games/example/src -Doptimize=fast --summary allThe table reports the median of three consecutive runs for mean and p95 CPU time. Each run uses 600 frames of simulated 60 Hz time per population/budget pair. There is no real-time pacing or discarded warm-up interval. Compilation is outside the timed region. The executable emits one JSON line per case. Run-to-run variation is expected; these measurements are not a performance gate.
Service CPU cost
| Observers | Budget (steps/rays) | Mean µs | p95 µs |
|---|---|---|---|
| 10 | 128/32 | 2.00 | 2.08 |
| 10 | 1024/1024 | 2.00 | 2.13 |
| 100 | 128/32 | 13.19 | 13.83 |
| 100 | 1024/1024 | 20.08 | 23.33 |
| 1,000 | 128/32 | 102.31 | 120.00 |
| 1,000 | 1024/1024 | 209.63 | 225.96 |
Simulated sensing latency and coverage
These values were identical across the three runs. First detection is the latest first positive observation among all observers. Service gap is the largest interval between consecutive observer publications, measured by the callback. It is distinct from the time needed to revisit every target.
| Observers | Budget | Worst first detection, ms | Max service gap, ms | Oldest cached check, ms | Min publications/observer | Total queries |
|---|---|---|---|---|---|---|
| 10 | Normal | 16.67 | 16.67 | 116.67 | 600 | 6,000 |
| 10 | High | 16.67 | 16.67 | 116.67 | 600 | 6,000 |
| 100 | Normal | 66.67 | 66.67 | 400.00 | 192 | 19,200 |
| 100 | High | 16.67 | 16.67 | 116.67 | 600 | 60,000 |
| 1,000 | Normal | 533.33 | 533.33 | 4,150.00 | 19 | 19,200 |
| 1,000 | High | 16.67 | 16.67 | 116.67 | 600 | 600,000 |
At 1,000 observers, Normal spends about 0.10 ms per service tick here, but first detection can take 0.53 seconds and individual cached checks reach 4.15 seconds old. High spends about 0.21 ms and services each observer every simulated frame. A 10 Hz nominal cycle is therefore a requested cadence, not a guarantee under a smaller budget. Guards stop treating unchecked observations as visible after 0.25 seconds; remembered positions expire separately.
Scope and limits
- Eight targets move on deterministic sine paths. Observer positions and orientations are fixed, with 40 m range and an 80° half-angle. These settings deliberately differ from the interactive scene to keep the benchmark cohort in sensing range.
- The timed call includes copied snapshot synchronization, sorting, spatial indexing, memory maintenance, candidate selection, analytic occlusion, and publication into benchmark counters. Target motion preparation and timing aggregation are outside the interval.
- Occlusion is a single analytic XZ box. It does not exercise Tenkai queries, actor-field gathering, World event dispatch, navigation, character motors, physics simulation, or rendering. These numbers do not establish a full 1,000-actor game frame budget.
Service(1024, 64, 8)occupies 933,480 bytes, including both observer buffers and fixed scratch storage. It allocates once before the benchmark; service ticks do not allocate.- Every frame checks both work caps. Every observer must receive a publication and a positive detection before the run ends. Separate unit tests verify candidate continuation, removal/generation/epoch handling, pause, capacity validation, and bounded memory.
- Use the in-game AI Perception panel for real query counts, deferred work, and adapter CPU time. Added load observers are stationary sensors; the two authored guards retain movement.
See the game AI guide for controls and lifecycle semantics.