Files
gamedev-the-steward/docs/benchmarks/SIMULATION_SCALING_BASELINE_02.md
T
2026-07-16 23:51:08 +02:00

71 lines
3.3 KiB
Markdown

# Simulation scaling baseline 02
This is the reviewed comparison after introducing one reusable per-tick
`SimulationPopulationView`. The view indexes all, living, and starving NPCs by
stable ID, is refreshed as each NPC advances, and is shared by relationship and
action-selection queries. It is transient derived state and is not serialized.
The machine-readable samples are in
[`simulation_scaling_baseline_02.json`](simulation_scaling_baseline_02.json).
The workload, seed, host, exclusions, warmup, and three-sample median contract
match [baseline 01](SIMULATION_SCALING_BASELINE_01.md).
## Results
| Case | NPCs | Seeded history | Median us/tick | Ticks/s | Simulated realtime | Arrival share | End JSON |
| --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: |
| Population 6 | 6 | 0 | 63.85 | 15,662.93 | 18,795.5x | 4.5% | 0.06 MiB |
| Population 60 | 60 | 0 | 662.62 | 1,509.17 | 1,811.0x | 5.7% | 0.56 MiB |
| Population 600 | 600 | 0 | 11,654.10 | 85.81 | 103.0x | 18.0% | 5.67 MiB |
| History 600 | 60 | 600 | 722.60 | 1,383.90 | 1,660.7x | 5.2% | 0.71 MiB |
| History 6,000 | 60 | 6,000 | 1,430.65 | 698.98 | 838.8x | 2.6% | 2.04 MiB |
All samples within each case were deterministic. More importantly, every final
checksum exactly matches the corresponding baseline-01 checksum, so the
optimization preserves selection, interleaved NPC updates, event growth, and
serialized continuation state.
## Comparison
| Case | Baseline 01 us/tick | Baseline 02 us/tick | Change |
| --- | ---: | ---: | ---: |
| Population 6 | 55.74 | 63.85 | 14.6% slower |
| Population 60 | 621.87 | 662.62 | 6.6% slower |
| Population 600 | 15,187.74 | 11,654.10 | **23.3% faster** |
| History 600 | 677.12 | 722.60 | 6.7% slower |
| History 6,000 | 1,421.19 | 1,430.65 | 0.7% slower |
The shared dictionaries add a small fixed per-tick cost, visible in tiny
fixtures and normal local timing noise. At the measured population knee they
remove repeated all-NPC lookup construction and improve the 600-NPC case from
65.84 to 85.81 ticks per second. The local 50-ticks-per-second reference target
remains comfortably met without introducing simulation LOD.
## Decision and next measurement
Keep the population view. It has a real relationship/action consumer, improves
the measured pressure point, and preserves exact state. Do not add batching or
active/abstract NPC modes merely to improve this data-only number.
The next bounded scale slice should support the world vision directly: a
loaded-resource spatial query owned by `ActiveWorldAdapter`. It should let
villagers discover finite, stable-ID `ResourceNode` anchors placed much farther
apart across Terrain3D while preserving the current score, reachability,
reservation, and save/rebind contracts. A benchmark should compare 18, 180,
and 1,800 loaded candidates before choosing a grid, tree, or other index.
Terrain3D-instanced visual trees and grass are not automatically authoritative
resources. Intentional harvest anchors must continue to bind simulation-owned
`ResourceStateRecord` data by stable ID; large decorative populations can stay
presentation-only until a gameplay rule needs them.
## Capture command
```bash
/Applications/Godot.app/Contents/MacOS/Godot \
--headless --path "$PWD" \
--script res://tools/benchmark_simulation_scaling.gd -- \
--host-label=Apple_M1_Max_64_GB \
--output=res://docs/benchmarks/simulation_scaling_baseline_02.json
```