84 lines
5.1 KiB
Markdown
84 lines
5.1 KiB
Markdown
# Debounce queue experiment
|
||
|
||
The local profile showed substantial allocation on the distant-terrain chunk
|
||
update queue thread. Direct per-thread allocation counters confirmed activity
|
||
there; sampled stacks repeatedly entered player-position reads and queue
|
||
reinsertion. Inspection of the pinned DH 3.3.4 bytecode explains a mechanism:
|
||
an update younger than its existing 250 ms debounce deadline is removed and
|
||
reinserted, while the outer queue loop sleeps only when both queues are empty.
|
||
A nonempty pending queue can therefore retry continuously before it can do work.
|
||
|
||
The optional `queue-wait` profile keeps balanced graphics, distances, simulation
|
||
settings, mods and worker budgets. Its original hook performs the existing
|
||
debounce reinsertion, then requests a wait of up to 1 ms, bounded by the remaining
|
||
deadline, if no preprocessing work is pending. It only waits on the queue worker,
|
||
never a caller such as the render thread. Incoming queued work unparks a debounce
|
||
wait; the original empty-queue sleep remains unchanged. OS scheduling determines
|
||
when the worker resumes.
|
||
The same update object, priority queue, debounce timestamp, overload policy,
|
||
executor dispatch and normal simulation remain in use. Executor-unavailable and
|
||
rejected-dispatch retries are outside this hook.
|
||
|
||
`adaptive-queue` combines this hook with the existing adaptive terrain budget
|
||
and precise worker pacing. Use `queue-wait` versus `m2-balanced` to isolate the
|
||
queue change, and `adaptive-queue` versus `adaptive-pacing` to evaluate the
|
||
combination. Each profile explicitly enables only its requested experiments.
|
||
Set `enabled=false` in `config/horizon-queue-wait.properties` and restart to
|
||
disable the queue hook. It is disabled by default and requires exact DH 3.3.4.
|
||
|
||
Requested waits are not hard latency bounds: operating-system timers can
|
||
overshoot, and arrival/worker scheduling affect responsiveness. Parking leaves
|
||
the interruption flag intact, so the existing shutdown loop still handles it.
|
||
New arrival wakeups reduce the chance of delaying useful preprocessing, but
|
||
real terrain completion, block-change visibility and platform behavior need
|
||
evaluation alongside CPU, allocations and frame pacing.
|
||
|
||
## Isolated measurement
|
||
|
||
Run `python -c "from tools.build_mod import benchmark_debounce; benchmark_debounce()"`
|
||
after building the mod. It uses the actual publisher's `ChunkPosQueue`, with
|
||
synthetic entries and a 250 ms pending-debounce interval. The upstream mode
|
||
repeatedly pops and reinserts the nearest update; the waiting mode inserts the
|
||
same bounded-wait policy between retries. Warm-ups precede three alternating
|
||
trials at one and 64 queued entries. Each trial checks update identity, nearest
|
||
priority and queue count, then reports wall time, worker CPU and worker allocation.
|
||
|
||
This benchmark isolates redundant polling; it does not simulate game chunk
|
||
lighting, rendering, network traffic or terrain completion. Thread CPU counters
|
||
can be coarse, particularly on Windows, so a zero reading is below measurement
|
||
resolution, not proof of zero CPU cost. Allocation counts are cumulative bytes
|
||
allocated during the interval, not retained heap or process memory. Hardware,
|
||
timer behavior and workload can change results. The CI publishes raw output
|
||
for Linux, Windows and Apple Silicon macOS. No game-FPS claim follows from this
|
||
microbenchmark alone.
|
||
|
||
## Hosted results, 3 October 2026
|
||
|
||
Raw output is checked in under
|
||
[`benchmarks/debounce-queue-2026-10-03`](../benchmarks/debounce-queue-2026-10-03/).
|
||
The tables show medians of three trials. Allocations and CPU refer only to this
|
||
synthetic queue polling interval, not the entire game. The macOS runner is hosted
|
||
Apple Silicon with three logical CPUs; it is not the user's M2 Pro.
|
||
|
||
| Platform / queued entries | Upstream allocation | Waiting allocation | Upstream worker CPU | Waiting worker CPU |
|
||
| --- | ---: | ---: | ---: | ---: |
|
||
| macOS ARM64 / 1 | 80,066,144 B | 768 B | 247.157 ms | 0.873 ms |
|
||
| macOS ARM64 / 64 | 34,344,320 B | 2,112 B | 248.550 ms | 1.154 ms |
|
||
| Linux x64 / 1 | 99,456,064 B | 7,520 B | 248.446 ms | 2.960 ms |
|
||
| Linux x64 / 64 | 31,971,072 B | 7,488 B | 249.987 ms | 2.890 ms |
|
||
| Windows x64 / 1 | 95,728,256 B | 5,536 B | 250.000 ms | Below counter resolution |
|
||
| Windows x64 / 64 | 32,051,200 B | 5,376 B | 250.000 ms | Below counter resolution |
|
||
|
||
Each upstream interval lasted about 250 ms. Waiting medians were 250.068 ms on
|
||
Linux, 250.593–250.856 ms on Windows, and 251.431–252.426 ms on macOS. One macOS
|
||
trial lasted 258.012 ms, demonstrating timer overshoot. These are interval-ending
|
||
times; they are not measurements of a real block change reaching the screen.
|
||
|
||
The real 0.7.0 adaptive-queue demo world renders and records with the hook enabled.
|
||
Thread inspection reached the injected debounce parking path. A 120-second run
|
||
wrote 643 frame intervals and 2,408 server ticks with matching CSV counts/means,
|
||
and 119–120 RSS samples. Distant-terrain data continued updating; game worlds and
|
||
DH databases closed cleanly. A separate 60-second profile confirmed allocation
|
||
counter activity on the queue thread. Its workload/cache state differs from the
|
||
earlier profile, so those profiles are not a numerical before/after comparison.
|