class_name SimStats extends RefCounted
## SIMULATION COST counters (hyper-games #114 §7: the bot / creature benchmark) — STATIC,
## not an autoload, because creatures and bot brains are compiled by `--script` gates
## BEFORE autoloads exist, where a bare `PerfMetrics` identifier is a compile error
## (gate_creature_target / gate_bot_object broke on exactly that; #113 §6 is the same
## trap with Keymap). PerfMetrics exposes these over /global/perf.
##
## Per KEY ("creature.crawler", "bot.nav", ...): the ticks, the CPU microseconds inside
## them and the ground/nav QUERIES they made. Accumulated, never sampled; two
## get_ticks_usec per tick is the whole overhead.
static var _sim: Dictionary = {} # key -> PackedInt64Array [calls, usec, queries]
static var _t0_ms: int = 0
static func add(key: String, usec: int, queries: int = 0) -> void:
var a: PackedInt64Array = _sim.get(key, PackedInt64Array([0, 0, 0]))
a[0] += 1
a[1] += usec
a[2] += queries
_sim[key] = a
static func reset() -> void:
_sim.clear()
_t0_ms = Time.get_ticks_msec()
## Per key: totals since reset(), and the rates a benchmark compares — us_per_call,
## ms_per_s (CPU per wall second; 100 ms/s is 10 % of a core), queries_per_s.
static func snapshot() -> Dictionary:
var window_s := maxf(float(Time.get_ticks_msec() - _t0_ms) / 1000.0, 0.001)
var out: Dictionary = {"window_s": window_s}
for k: String in _sim:
var a: PackedInt64Array = _sim[k]
out[k] = {"calls": a[0], "usec": a[1], "queries": a[2],
"us_per_call": float(a[1]) / maxf(float(a[0]), 1.0),
"ms_per_s": float(a[1]) / 1000.0 / window_s,
"queries_per_s": float(a[2]) / window_s}
return out