Issue #93: with one player in Alizar (world 51, 24,873 creature spawns) the zone
tick's p95 was several hundred milliseconds on the live server, because every
creature of the continent ran its aggro scan and idle AI (wandering, patrols) on
each idle tick, wherever it was. Creatures now sleep unless a player is within
200 units (the players' view range); see Zone.CreatureActivity.
Method
scripts/bench_zone_tick.exs starts a real zone instance with all of the world's
spawns, suspends its own ticking, and calls its tick handler directly, in order,
sleeping 250 ms between calls as the zone does (wander and patrol timers are clock
based). It times the whole tick handler (creature AI, pets, scripts, auras, regeneration).
The "all awake" and "range 200" cases run in the same process, alternating order
(all awake, 200, 200, all awake), so machine load hits both alike.
cd apps/bezgelor_world
MIX_ENV=test BENCH_JSON=out.json ../../scripts/limited.sh mix run ../../scripts/bench_zone_tick.exs
240 ticks (one minute) per case. Idle ticks are every fourth tick (the 1 s idle step).
Machine: shared 20-core x86_64 host, 4 Erlang schedulers. Times in milliseconds.
The raw numbers are in zone-tick-alizar-2026-10-11.json.
Results
| Case | all ticks p50 | all ticks p95 | idle ticks p50 | idle ticks p95 | max |
|---|---|---|---|---|---|
| no player | 4.7 | 9.2 | 4.6 | 10.7 | 93.1 |
| one player, all awake (before) | 6.1 / 5.7 | 49.1 / 42.3 | 43.6 / 38.3 | 78.7 / 80.4 | 453.3 / 195.8 |
| one player, range 200 (after) | 4.9 / 4.3 | 10.7 / 9.0 | 8.8 / 8.0 | 13.3 / 10.3 | 40.5 / 22.0 |
(Two runs per case, separated by "/".)
The tick's p95 with a player went from about 42-49 ms to about 9-11 ms, the idle tick from about 40 ms to about 8 ms, and the worst tick from 196-453 ms to 22-41 ms. What remains is walking the creature table once per tick (the no-player cost). The live server's tick also carries packet and broadcast work that the benchmark does not, which is why the issue saw about 400 ms there.
Profile before
eprof over 8 idle ticks with one player: the time was spent computing each wandering
creature's current position (Movement.path_length, Vector3.distance) and its aggro
scan, for about 25,000 creatures per idle tick.