1use serde::{Deserialize, Serialize};
9
10#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
12pub struct BenchmarkCounters {
13 pub rotation_evaluations: u64,
14 #[serde(default)]
15 pub rotation_ast_lowerings: u64,
16 pub events: u64,
17 pub damage_events: u64,
18 pub aura_events: u64,
19 pub damage_action_setups: u64,
20 pub damage_owner_setups: u64,
21 pub damage_target_setups: u64,
22 pub damage_targets: u64,
23 pub aura_slots_visited: u64,
24 #[serde(default)]
25 pub planned_guardian_slots_visited: u64,
26 pub planned_aura_bindings: u64,
27 pub scanned_aura_definitions: u64,
28 pub planned_aura_spell_bindings: u64,
29 pub scanned_aura_spells: u64,
30 pub planned_aura_projection_bindings: u64,
31 pub scanned_aura_projections: u64,
32 pub planned_rotation_projection_bindings: u64,
33 pub scanned_rotation_projection_spells: u64,
34 pub keyed_lookups: u64,
35 pub game_data_hot_reads: u64,
36 pub damage_plan_keyed_lookups: u64,
37 pub damage_plan_raw_binary_searches: u64,
38 pub target_plan_runtime_lowerings: u64,
39 pub target_plan_runtime_fallbacks: u64,
40 pub resets: u64,
41 pub reset_time_ns: u64,
42}
43
44#[cfg(feature = "bench-metrics")]
45mod enabled {
46 use std::{cell::Cell, time::Instant};
47
48 use super::BenchmarkCounters;
49
50 thread_local! {
51 static COUNTERS: Cell<BenchmarkCounters> = const { Cell::new(BenchmarkCounters {
52 rotation_evaluations: 0,
53 rotation_ast_lowerings: 0,
54 events: 0,
55 damage_events: 0,
56 aura_events: 0,
57 damage_action_setups: 0,
58 damage_owner_setups: 0,
59 damage_target_setups: 0,
60 damage_targets: 0,
61 aura_slots_visited: 0,
62 planned_guardian_slots_visited: 0,
63 planned_aura_bindings: 0,
64 scanned_aura_definitions: 0,
65 planned_aura_spell_bindings: 0,
66 scanned_aura_spells: 0,
67 planned_aura_projection_bindings: 0,
68 scanned_aura_projections: 0,
69 planned_rotation_projection_bindings: 0,
70 scanned_rotation_projection_spells: 0,
71 keyed_lookups: 0,
72 game_data_hot_reads: 0,
73 damage_plan_keyed_lookups: 0,
74 damage_plan_raw_binary_searches: 0,
75 target_plan_runtime_lowerings: 0,
76 target_plan_runtime_fallbacks: 0,
77 resets: 0,
78 reset_time_ns: 0,
79 }) };
80 }
81
82 fn update(apply: impl FnOnce(&mut BenchmarkCounters)) {
83 COUNTERS.with(|cell| {
84 let mut counters = cell.get();
85
86 apply(&mut counters);
87 cell.set(counters);
88 });
89 }
90
91 pub(super) fn reset() {
92 COUNTERS.with(|cell| cell.set(BenchmarkCounters::default()));
93 }
94
95 pub(super) fn snapshot() -> BenchmarkCounters {
96 COUNTERS.with(Cell::get)
97 }
98
99 pub(super) fn rotation_evaluation() {
100 update(|counters| counters.rotation_evaluations += 1);
101 }
102
103 pub(super) fn rotation_ast_lowering() {
104 update(|counters| counters.rotation_ast_lowerings += 1);
105 }
106
107 pub(super) fn event() {
108 update(|counters| counters.events += 1);
109 }
110
111 pub(super) fn damage_event() {
112 update(|counters| counters.damage_events += 1);
113 }
114
115 pub(super) fn aura_event() {
116 update(|counters| counters.aura_events += 1);
117 }
118
119 pub(super) fn damage_action_setup() {
120 update(|counters| counters.damage_action_setups += 1);
121 }
122
123 pub(super) fn damage_owner_setup() {
124 update(|counters| counters.damage_owner_setups += 1);
125 }
126
127 pub(super) fn damage_target_setup() {
128 update(|counters| counters.damage_target_setups += 1);
129 }
130
131 pub(super) fn damage_setup_stages() {
132 update(|counters| {
133 counters.damage_action_setups += 1;
134 counters.damage_owner_setups += 1;
135 counters.damage_target_setups += 1;
136 });
137 }
138
139 pub(super) fn damage_target() {
140 update(|counters| counters.damage_targets += 1);
141 }
142
143 pub(super) fn aura_slot_visit() {
144 update(|counters| counters.aura_slots_visited += 1);
145 }
146
147 pub(super) fn planned_guardian_slot_visit() {
148 update(|counters| counters.planned_guardian_slots_visited += 1);
149 }
150
151 pub(super) fn planned_aura_bindings(amount: u64) {
152 update(|counters| counters.planned_aura_bindings += amount);
153 }
154
155 pub(super) fn scanned_aura_definitions(amount: u64) {
156 update(|counters| counters.scanned_aura_definitions += amount);
157 }
158
159 pub(super) fn planned_aura_spell_bindings(amount: u64) {
160 update(|counters| counters.planned_aura_spell_bindings += amount);
161 }
162
163 pub(super) fn scanned_aura_spells(amount: u64) {
164 update(|counters| counters.scanned_aura_spells += amount);
165 }
166
167 pub(super) fn planned_aura_projection_bindings(amount: u64) {
168 update(|counters| counters.planned_aura_projection_bindings += amount);
169 }
170
171 pub(super) fn scanned_aura_projections(amount: u64) {
172 update(|counters| counters.scanned_aura_projections += amount);
173 }
174
175 pub(super) fn planned_rotation_projection_binding() {
176 update(|counters| counters.planned_rotation_projection_bindings += 1);
177 }
178
179 pub(super) fn scanned_rotation_projection_spell() {
180 update(|counters| counters.scanned_rotation_projection_spells += 1);
181 }
182
183 pub(super) fn keyed_lookup() {
184 update(|counters| counters.keyed_lookups += 1);
185 }
186
187 pub(super) fn game_data_hot_read() {
188 update(|counters| counters.game_data_hot_reads += 1);
189 }
190
191 pub(super) fn damage_plan_keyed_lookup() {
192 update(|counters| counters.damage_plan_keyed_lookups += 1);
193 }
194
195 pub(super) fn damage_plan_raw_binary_search() {
196 update(|counters| counters.damage_plan_raw_binary_searches += 1);
197 }
198
199 pub(super) fn target_plan_runtime_lowering() {
200 update(|counters| counters.target_plan_runtime_lowerings += 1);
201 }
202
203 pub(super) fn target_plan_runtime_fallback() {
204 update(|counters| counters.target_plan_runtime_fallbacks += 1);
205 }
206
207 pub(super) fn measure_reset<R>(reset: impl FnOnce() -> R) -> R {
208 let started_at = Instant::now();
209 let result = reset();
210 let elapsed = u64::try_from(started_at.elapsed().as_nanos()).unwrap_or(u64::MAX);
211
212 update(|counters| {
213 counters.resets += 1;
214 counters.reset_time_ns = counters.reset_time_ns.saturating_add(elapsed);
215 });
216
217 result
218 }
219}
220
221#[inline]
223pub fn reset() {
224 #[cfg(feature = "bench-metrics")]
225 enabled::reset();
226}
227
228#[inline]
230#[must_use]
231pub fn snapshot() -> BenchmarkCounters {
232 #[cfg(feature = "bench-metrics")]
233 {
234 enabled::snapshot()
235 }
236
237 #[cfg(not(feature = "bench-metrics"))]
238 {
239 BenchmarkCounters::default()
240 }
241}
242
243macro_rules! recorder {
244 ($name:ident, $enabled:ident) => {
245 #[inline(always)]
246 pub fn $name() {
247 #[cfg(feature = "bench-metrics")]
248 enabled::$enabled();
249 }
250 };
251}
252
253recorder!(record_rotation_evaluation, rotation_evaluation);
254recorder!(record_rotation_ast_lowering, rotation_ast_lowering);
255recorder!(record_event, event);
256recorder!(record_damage_event, damage_event);
257recorder!(record_aura_event, aura_event);
258recorder!(record_damage_action_setup, damage_action_setup);
259recorder!(record_damage_owner_setup, damage_owner_setup);
260recorder!(record_damage_target_setup, damage_target_setup);
261recorder!(record_damage_setup_stages, damage_setup_stages);
262recorder!(record_damage_target, damage_target);
263recorder!(record_aura_slot_visit, aura_slot_visit);
264recorder!(
265 record_planned_guardian_slot_visit,
266 planned_guardian_slot_visit
267);
268recorder!(record_keyed_lookup, keyed_lookup);
269recorder!(record_game_data_hot_read, game_data_hot_read);
270recorder!(record_damage_plan_keyed_lookup, damage_plan_keyed_lookup);
271recorder!(
272 record_damage_plan_raw_binary_search,
273 damage_plan_raw_binary_search
274);
275recorder!(
276 record_target_plan_runtime_lowering,
277 target_plan_runtime_lowering
278);
279recorder!(
280 record_target_plan_runtime_fallback,
281 target_plan_runtime_fallback
282);
283recorder!(
284 record_planned_rotation_projection_binding,
285 planned_rotation_projection_binding
286);
287recorder!(
288 record_scanned_rotation_projection_spell,
289 scanned_rotation_projection_spell
290);
291
292macro_rules! amount_recorder {
293 ($name:ident, $enabled:ident) => {
294 #[inline(always)]
295 pub fn $name(amount: usize) {
296 #[cfg(feature = "bench-metrics")]
297 enabled::$enabled(u64::try_from(amount).unwrap_or(u64::MAX));
298
299 #[cfg(not(feature = "bench-metrics"))]
300 let _ = amount;
301 }
302 };
303}
304
305amount_recorder!(record_planned_aura_bindings, planned_aura_bindings);
306amount_recorder!(record_scanned_aura_definitions, scanned_aura_definitions);
307amount_recorder!(
308 record_planned_aura_spell_bindings,
309 planned_aura_spell_bindings
310);
311amount_recorder!(record_scanned_aura_spells, scanned_aura_spells);
312amount_recorder!(
313 record_planned_aura_projection_bindings,
314 planned_aura_projection_bindings
315);
316amount_recorder!(record_scanned_aura_projections, scanned_aura_projections);
317
318#[inline]
320pub fn measure_reset<R>(reset: impl FnOnce() -> R) -> R {
321 #[cfg(feature = "bench-metrics")]
322 {
323 enabled::measure_reset(reset)
324 }
325
326 #[cfg(not(feature = "bench-metrics"))]
327 {
328 reset()
329 }
330}
331
332#[cfg(test)]
333#[path = "bench_metrics/tests.rs"]
334mod tests;