Keyboard input: performance evidence¶
Planning-grade wall-clock measurements for the backend-independent keyboard reducer (spec design section 8, requirement K12). These are NOT a keyboard hardware-to-photon latency claim and NOT a whole-engine determinism guarantee; they establish that runtime storage and per-step work are bounded and that the hot paths allocate nothing and take no locks.
Environment¶
| Field | Value |
|---|---|
| Compiler | Clang/LLVM 18.1.8 (pinned reference toolchain) |
| Build preset | linux-clang-development (RelWithDebInfo, -O2) |
| CPU | Intel Xeon Platinum 8488C, 8 vCPU (sandbox) |
| OS | Amazon Linux 2023 (kernel 6.1) |
| Harness | modules/input/benchmarks/input_bench.cpp |
| Iterations / warmup | 2000 / 200 per workload; median and p95 reported |
The sandbox is a shared virtualized host, so absolute nanoseconds carry noise; the shape (bounded, no allocations) is the load-bearing result.
Storage footprint¶
| Component | Size |
|---|---|
sizeof(InputSystem) public handle |
16 bytes (a UniquePtr<Impl, DomainDeleter<Impl>>: object and domain) |
Reducer<256,64,256> (the heap-allocated Impl) |
16,136 bytes (15.8 KiB) |
InputDebugTrace (optional) |
16,424 bytes (16.0 KiB) |
| Reducer + enabled trace | 31.8 KiB |
31.8 KiB with the trace enabled is well under the ≤128 KiB per-instance planning
target. The reducer storage is allocated exactly once at construction, through
TryCreateOwned and the system allocation domain, and freed through that same
domain; the trace is a separate caller-owned object.
Allocations¶
ludus_input_bench counts global operator new calls:
- Construction: 1 allocation (the reducer
Impl). - Hot loop (5000 iterations of ingest/consume/query): 0 allocations.
The dedicated ludus_input_alloc_tests gate asserts the same (one at
construction, zero across 2000 hot iterations) and is part of the ctest suite.
No lock is taken on any path (single-thread, instance-owned; no atomics/mutex in
the reducer).
Workload timings (median / p95, nanoseconds)¶
Representative run at d8a2bbf:
| Workload | Median | p95 |
|---|---|---|
| Idle step (no events) | 66 | 66 |
| Typing/movement batch (6 transitions, button+2 axes) | 151 | 172 |
| Repeated/unknown batch (64 ignored/rejected records) | 214 | 313 |
| 256 transitions × 256 bindings (saturated) | 29,685 | 32,062 |
| Reset storm (16 resets + taps per step) | 380 | 412 |
| Trace off vs on (typing batch) | 85 | 118 (on) |
Notes:
- The saturated 256×256 case is the design's acknowledged worst case: step work
is bounded by
events × bindingswith the simple flat binding scan. ~30 µs for a fully saturated step (far beyond any realistic keyboard batch) is acceptable for 64 actions; a per-key adjacency table is a documented later optimization only if profiling of real workloads shows it matters. - Trace-on adds a bounded, constant per-record cost (no formatting, no allocation); trace-off is a predictable null-pointer branch.
Reproduction¶
./scripts/build linux-clang-development
./out/build/linux-clang-development/modules/input/ludus_input_bench
The ludus_input_alloc_tests zero-allocation gate runs under:
./scripts/test linux-clang-debug -L alloc