1use std::cell::{Cell, RefCell};
9use std::sync::atomic::{AtomicU64, Ordering};
10use std::sync::OnceLock;
11use std::time::{Duration, Instant};
12
13#[derive(Debug, Clone, Copy)]
14pub enum Metric {
15 ContinuousEffectsCalls,
16 ContinuousEffectsNs,
17 ContinuousStaticSourceClones,
18 ContinuousTargetVecs,
19 ParamsParses,
20 ParamsLookups,
21 ParamsUnscopedLookups,
22 ContinuousParamsLookups,
23 ActionSpaceParamsLookups,
24 ActionSpacePlayableParamsLookups,
25 ActionSpaceActivatableParamsLookups,
26 ActionSpaceManaSourceParamsLookups,
27 ActionSpaceUntapParamsLookups,
28 TriggerParamsLookups,
29 ReplacementParamsLookups,
30 CostParamsLookups,
31 TargetParamsLookups,
32 AbilityBuildParamsLookups,
33 StaticAbilityParamsLookups,
34 ValidFilterParamsLookups,
35 SelectorParses,
36 SelectorMatches,
37 SelectorRawPredicates,
38 GameLoopParamsLookups,
39 PhaseParamsLookups,
40 PriorityParamsLookups,
41 PrioritySbaParamsLookups,
42 PriorityTriggerParamsLookups,
43 PrioritySnapshotsTaken,
44 PrioritySnapshotParamsLookups,
45 PrioritySnapshotSyncParamsLookups,
46 PrioritySnapshotMetadataParamsLookups,
47 PrioritySnapshotAbilityParamsLookups,
48 PrioritySnapshotCardCloneParamsLookups,
49 PriorityChoiceParamsLookups,
50 PriorityExecutionParamsLookups,
51 StackResolutionParamsLookups,
52 CombatParamsLookups,
53 SpellAbilityClones,
54 StackEntryClones,
55 GameStateTargetingClones,
56 SnapshotClones,
57 CardStateCollectionClones,
58}
59
60static CONTINUOUS_EFFECTS_CALLS: AtomicU64 = AtomicU64::new(0);
61static CONTINUOUS_EFFECTS_NS: AtomicU64 = AtomicU64::new(0);
62static CONTINUOUS_STATIC_SOURCE_CLONES: AtomicU64 = AtomicU64::new(0);
63static CONTINUOUS_TARGET_VECS: AtomicU64 = AtomicU64::new(0);
64static PARAMS_PARSES: AtomicU64 = AtomicU64::new(0);
65static PARAMS_LOOKUPS: AtomicU64 = AtomicU64::new(0);
66static PARAMS_UNSCOPED_LOOKUPS: AtomicU64 = AtomicU64::new(0);
67static CONTINUOUS_PARAMS_LOOKUPS: AtomicU64 = AtomicU64::new(0);
68static ACTION_SPACE_PARAMS_LOOKUPS: AtomicU64 = AtomicU64::new(0);
69static ACTION_SPACE_PLAYABLE_PARAMS_LOOKUPS: AtomicU64 = AtomicU64::new(0);
70static ACTION_SPACE_ACTIVATABLE_PARAMS_LOOKUPS: AtomicU64 = AtomicU64::new(0);
71static ACTION_SPACE_MANA_SOURCE_PARAMS_LOOKUPS: AtomicU64 = AtomicU64::new(0);
72static ACTION_SPACE_UNTAP_PARAMS_LOOKUPS: AtomicU64 = AtomicU64::new(0);
73static TRIGGER_PARAMS_LOOKUPS: AtomicU64 = AtomicU64::new(0);
74static REPLACEMENT_PARAMS_LOOKUPS: AtomicU64 = AtomicU64::new(0);
75static COST_PARAMS_LOOKUPS: AtomicU64 = AtomicU64::new(0);
76static TARGET_PARAMS_LOOKUPS: AtomicU64 = AtomicU64::new(0);
77static ABILITY_BUILD_PARAMS_LOOKUPS: AtomicU64 = AtomicU64::new(0);
78static STATIC_ABILITY_PARAMS_LOOKUPS: AtomicU64 = AtomicU64::new(0);
79static VALID_FILTER_PARAMS_LOOKUPS: AtomicU64 = AtomicU64::new(0);
80static SELECTOR_PARSES: AtomicU64 = AtomicU64::new(0);
81static SELECTOR_MATCHES: AtomicU64 = AtomicU64::new(0);
82static SELECTOR_RAW_PREDICATES: AtomicU64 = AtomicU64::new(0);
83static GAME_LOOP_PARAMS_LOOKUPS: AtomicU64 = AtomicU64::new(0);
84static PHASE_PARAMS_LOOKUPS: AtomicU64 = AtomicU64::new(0);
85static PRIORITY_PARAMS_LOOKUPS: AtomicU64 = AtomicU64::new(0);
86static PRIORITY_SBA_PARAMS_LOOKUPS: AtomicU64 = AtomicU64::new(0);
87static PRIORITY_TRIGGER_PARAMS_LOOKUPS: AtomicU64 = AtomicU64::new(0);
88static PRIORITY_SNAPSHOTS_TAKEN: AtomicU64 = AtomicU64::new(0);
89static PRIORITY_SNAPSHOT_PARAMS_LOOKUPS: AtomicU64 = AtomicU64::new(0);
90static PRIORITY_SNAPSHOT_SYNC_PARAMS_LOOKUPS: AtomicU64 = AtomicU64::new(0);
91static PRIORITY_SNAPSHOT_METADATA_PARAMS_LOOKUPS: AtomicU64 = AtomicU64::new(0);
92static PRIORITY_SNAPSHOT_ABILITY_PARAMS_LOOKUPS: AtomicU64 = AtomicU64::new(0);
93static PRIORITY_SNAPSHOT_CARD_CLONE_PARAMS_LOOKUPS: AtomicU64 = AtomicU64::new(0);
94static PRIORITY_CHOICE_PARAMS_LOOKUPS: AtomicU64 = AtomicU64::new(0);
95static PRIORITY_EXECUTION_PARAMS_LOOKUPS: AtomicU64 = AtomicU64::new(0);
96static STACK_RESOLUTION_PARAMS_LOOKUPS: AtomicU64 = AtomicU64::new(0);
97static COMBAT_PARAMS_LOOKUPS: AtomicU64 = AtomicU64::new(0);
98static SPELL_ABILITY_CLONES: AtomicU64 = AtomicU64::new(0);
99static STACK_ENTRY_CLONES: AtomicU64 = AtomicU64::new(0);
100static GAME_STATE_TARGETING_CLONES: AtomicU64 = AtomicU64::new(0);
101static SNAPSHOT_CLONES: AtomicU64 = AtomicU64::new(0);
102static CARD_STATE_COLLECTION_CLONES: AtomicU64 = AtomicU64::new(0);
103
104#[derive(Debug, Clone, Copy, PartialEq, Eq)]
105pub enum ParamsLookupScope {
106 Continuous,
107 ActionSpace,
108 ActionSpacePlayable,
109 ActionSpaceActivatable,
110 ActionSpaceManaSource,
111 ActionSpaceUntap,
112 Trigger,
113 Replacement,
114 Cost,
115 Target,
116 AbilityBuild,
117 StaticAbility,
118 ValidFilter,
119 GameLoop,
120 Phase,
121 Priority,
122 PrioritySba,
123 PriorityTrigger,
124 PrioritySnapshot,
125 PrioritySnapshotSync,
126 PrioritySnapshotMetadata,
127 PrioritySnapshotAbility,
128 PrioritySnapshotCardClone,
129 PriorityChoice,
130 PriorityExecution,
131 StackResolution,
132 Combat,
133}
134
135thread_local! {
136 static PARAMS_LOOKUP_SCOPE: Cell<Option<ParamsLookupScope>> = const { Cell::new(None) };
137 static WALL_START: RefCell<Option<Instant>> = const { RefCell::new(None) };
138 static TURN_WALL_MS: RefCell<Vec<u64>> = const { RefCell::new(Vec::new()) };
139}
140
141fn enabled_cell() -> bool {
142 std::env::var_os("manabrew_engine_PERF").is_some()
143}
144
145pub fn enabled() -> bool {
146 static ENABLED: OnceLock<bool> = OnceLock::new();
147 *ENABLED.get_or_init(enabled_cell)
148}
149
150fn trace_enabled() -> bool {
151 static TRACE_ENABLED: OnceLock<bool> = OnceLock::new();
152 *TRACE_ENABLED.get_or_init(|| std::env::var_os("manabrew_engine_PERF_TRACE").is_some())
153}
154
155fn report_every() -> Option<u64> {
156 static REPORT_EVERY: OnceLock<Option<u64>> = OnceLock::new();
157 *REPORT_EVERY.get_or_init(|| {
158 std::env::var("manabrew_engine_PERF_REPORT_EVERY")
159 .ok()
160 .and_then(|v| v.parse::<u64>().ok())
161 .filter(|&n| n > 0)
162 })
163}
164
165fn counter(metric: Metric) -> &'static AtomicU64 {
166 match metric {
167 Metric::ContinuousEffectsCalls => &CONTINUOUS_EFFECTS_CALLS,
168 Metric::ContinuousEffectsNs => &CONTINUOUS_EFFECTS_NS,
169 Metric::ContinuousStaticSourceClones => &CONTINUOUS_STATIC_SOURCE_CLONES,
170 Metric::ContinuousTargetVecs => &CONTINUOUS_TARGET_VECS,
171 Metric::ParamsParses => &PARAMS_PARSES,
172 Metric::ParamsLookups => &PARAMS_LOOKUPS,
173 Metric::ParamsUnscopedLookups => &PARAMS_UNSCOPED_LOOKUPS,
174 Metric::ContinuousParamsLookups => &CONTINUOUS_PARAMS_LOOKUPS,
175 Metric::ActionSpaceParamsLookups => &ACTION_SPACE_PARAMS_LOOKUPS,
176 Metric::ActionSpacePlayableParamsLookups => &ACTION_SPACE_PLAYABLE_PARAMS_LOOKUPS,
177 Metric::ActionSpaceActivatableParamsLookups => &ACTION_SPACE_ACTIVATABLE_PARAMS_LOOKUPS,
178 Metric::ActionSpaceManaSourceParamsLookups => &ACTION_SPACE_MANA_SOURCE_PARAMS_LOOKUPS,
179 Metric::ActionSpaceUntapParamsLookups => &ACTION_SPACE_UNTAP_PARAMS_LOOKUPS,
180 Metric::TriggerParamsLookups => &TRIGGER_PARAMS_LOOKUPS,
181 Metric::ReplacementParamsLookups => &REPLACEMENT_PARAMS_LOOKUPS,
182 Metric::CostParamsLookups => &COST_PARAMS_LOOKUPS,
183 Metric::TargetParamsLookups => &TARGET_PARAMS_LOOKUPS,
184 Metric::AbilityBuildParamsLookups => &ABILITY_BUILD_PARAMS_LOOKUPS,
185 Metric::StaticAbilityParamsLookups => &STATIC_ABILITY_PARAMS_LOOKUPS,
186 Metric::ValidFilterParamsLookups => &VALID_FILTER_PARAMS_LOOKUPS,
187 Metric::SelectorParses => &SELECTOR_PARSES,
188 Metric::SelectorMatches => &SELECTOR_MATCHES,
189 Metric::SelectorRawPredicates => &SELECTOR_RAW_PREDICATES,
190 Metric::GameLoopParamsLookups => &GAME_LOOP_PARAMS_LOOKUPS,
191 Metric::PhaseParamsLookups => &PHASE_PARAMS_LOOKUPS,
192 Metric::PriorityParamsLookups => &PRIORITY_PARAMS_LOOKUPS,
193 Metric::PrioritySbaParamsLookups => &PRIORITY_SBA_PARAMS_LOOKUPS,
194 Metric::PriorityTriggerParamsLookups => &PRIORITY_TRIGGER_PARAMS_LOOKUPS,
195 Metric::PrioritySnapshotsTaken => &PRIORITY_SNAPSHOTS_TAKEN,
196 Metric::PrioritySnapshotParamsLookups => &PRIORITY_SNAPSHOT_PARAMS_LOOKUPS,
197 Metric::PrioritySnapshotSyncParamsLookups => &PRIORITY_SNAPSHOT_SYNC_PARAMS_LOOKUPS,
198 Metric::PrioritySnapshotMetadataParamsLookups => &PRIORITY_SNAPSHOT_METADATA_PARAMS_LOOKUPS,
199 Metric::PrioritySnapshotAbilityParamsLookups => &PRIORITY_SNAPSHOT_ABILITY_PARAMS_LOOKUPS,
200 Metric::PrioritySnapshotCardCloneParamsLookups => {
201 &PRIORITY_SNAPSHOT_CARD_CLONE_PARAMS_LOOKUPS
202 }
203 Metric::PriorityChoiceParamsLookups => &PRIORITY_CHOICE_PARAMS_LOOKUPS,
204 Metric::PriorityExecutionParamsLookups => &PRIORITY_EXECUTION_PARAMS_LOOKUPS,
205 Metric::StackResolutionParamsLookups => &STACK_RESOLUTION_PARAMS_LOOKUPS,
206 Metric::CombatParamsLookups => &COMBAT_PARAMS_LOOKUPS,
207 Metric::SpellAbilityClones => &SPELL_ABILITY_CLONES,
208 Metric::StackEntryClones => &STACK_ENTRY_CLONES,
209 Metric::GameStateTargetingClones => &GAME_STATE_TARGETING_CLONES,
210 Metric::SnapshotClones => &SNAPSHOT_CLONES,
211 Metric::CardStateCollectionClones => &CARD_STATE_COLLECTION_CLONES,
212 }
213}
214
215fn name(metric: Metric) -> &'static str {
216 match metric {
217 Metric::ContinuousEffectsCalls => "continuous_effects.calls",
218 Metric::ContinuousEffectsNs => "continuous_effects.ns",
219 Metric::ContinuousStaticSourceClones => "continuous_effects.static_source_clones",
220 Metric::ContinuousTargetVecs => "continuous_effects.target_vecs",
221 Metric::ParamsParses => "params.parses",
222 Metric::ParamsLookups => "params.lookups",
223 Metric::ParamsUnscopedLookups => "params.lookups.unscoped",
224 Metric::ContinuousParamsLookups => "params.lookups.continuous",
225 Metric::ActionSpaceParamsLookups => "params.lookups.action_space",
226 Metric::ActionSpacePlayableParamsLookups => "params.lookups.action_space_playable",
227 Metric::ActionSpaceActivatableParamsLookups => "params.lookups.action_space_activatable",
228 Metric::ActionSpaceManaSourceParamsLookups => "params.lookups.action_space_mana_source",
229 Metric::ActionSpaceUntapParamsLookups => "params.lookups.action_space_untap",
230 Metric::TriggerParamsLookups => "params.lookups.trigger",
231 Metric::ReplacementParamsLookups => "params.lookups.replacement",
232 Metric::CostParamsLookups => "params.lookups.cost",
233 Metric::TargetParamsLookups => "params.lookups.target",
234 Metric::AbilityBuildParamsLookups => "params.lookups.ability_build",
235 Metric::StaticAbilityParamsLookups => "params.lookups.static_ability",
236 Metric::ValidFilterParamsLookups => "params.lookups.valid_filter",
237 Metric::SelectorParses => "selector.parses",
238 Metric::SelectorMatches => "selector.matches",
239 Metric::SelectorRawPredicates => "selector.raw_predicates",
240 Metric::GameLoopParamsLookups => "params.lookups.game_loop",
241 Metric::PhaseParamsLookups => "params.lookups.phase",
242 Metric::PriorityParamsLookups => "params.lookups.priority",
243 Metric::PrioritySbaParamsLookups => "params.lookups.priority_sba",
244 Metric::PriorityTriggerParamsLookups => "params.lookups.priority_trigger",
245 Metric::PrioritySnapshotsTaken => "priority.snapshots_taken",
246 Metric::PrioritySnapshotParamsLookups => "params.lookups.priority_snapshot",
247 Metric::PrioritySnapshotSyncParamsLookups => "params.lookups.priority_snapshot_sync",
248 Metric::PrioritySnapshotMetadataParamsLookups => {
249 "params.lookups.priority_snapshot_metadata"
250 }
251 Metric::PrioritySnapshotAbilityParamsLookups => "params.lookups.priority_snapshot_ability",
252 Metric::PrioritySnapshotCardCloneParamsLookups => {
253 "params.lookups.priority_snapshot_card_clone"
254 }
255 Metric::PriorityChoiceParamsLookups => "params.lookups.priority_choice",
256 Metric::PriorityExecutionParamsLookups => "params.lookups.priority_execution",
257 Metric::StackResolutionParamsLookups => "params.lookups.stack_resolution",
258 Metric::CombatParamsLookups => "params.lookups.combat",
259 Metric::SpellAbilityClones => "spell_ability.clones",
260 Metric::StackEntryClones => "stack_entry.clones",
261 Metric::GameStateTargetingClones => "game_state.targeting_clones",
262 Metric::SnapshotClones => "snapshot.clones",
263 Metric::CardStateCollectionClones => "card_state.collection_clones",
264 }
265}
266
267pub fn increment(metric: Metric, amount: u64) {
268 if !enabled() || amount == 0 {
269 return;
270 }
271 let total = counter(metric).fetch_add(amount, Ordering::Relaxed) + amount;
272 if trace_enabled() {
273 eprintln!("[forge-perf] {} +{} total={}", name(metric), amount, total);
274 }
275 if matches!(metric, Metric::ContinuousEffectsCalls) {
276 if let Some(every) = report_every() {
277 if total.is_multiple_of(every) {
278 print_summary();
279 }
280 }
281 }
282}
283
284pub fn increment_params_lookup() {
285 increment(Metric::ParamsLookups, 1);
286 if !enabled() {
287 return;
288 }
289 PARAMS_LOOKUP_SCOPE.with(|scope| match scope.get() {
290 Some(ParamsLookupScope::Continuous) => increment(Metric::ContinuousParamsLookups, 1),
291 Some(ParamsLookupScope::ActionSpace) => increment(Metric::ActionSpaceParamsLookups, 1),
292 Some(ParamsLookupScope::ActionSpacePlayable) => {
293 increment(Metric::ActionSpacePlayableParamsLookups, 1)
294 }
295 Some(ParamsLookupScope::ActionSpaceActivatable) => {
296 increment(Metric::ActionSpaceActivatableParamsLookups, 1)
297 }
298 Some(ParamsLookupScope::ActionSpaceManaSource) => {
299 increment(Metric::ActionSpaceManaSourceParamsLookups, 1)
300 }
301 Some(ParamsLookupScope::ActionSpaceUntap) => {
302 increment(Metric::ActionSpaceUntapParamsLookups, 1)
303 }
304 Some(ParamsLookupScope::Trigger) => increment(Metric::TriggerParamsLookups, 1),
305 Some(ParamsLookupScope::Replacement) => increment(Metric::ReplacementParamsLookups, 1),
306 Some(ParamsLookupScope::Cost) => increment(Metric::CostParamsLookups, 1),
307 Some(ParamsLookupScope::Target) => increment(Metric::TargetParamsLookups, 1),
308 Some(ParamsLookupScope::AbilityBuild) => increment(Metric::AbilityBuildParamsLookups, 1),
309 Some(ParamsLookupScope::StaticAbility) => increment(Metric::StaticAbilityParamsLookups, 1),
310 Some(ParamsLookupScope::ValidFilter) => increment(Metric::ValidFilterParamsLookups, 1),
311 Some(ParamsLookupScope::GameLoop) => increment(Metric::GameLoopParamsLookups, 1),
312 Some(ParamsLookupScope::Phase) => increment(Metric::PhaseParamsLookups, 1),
313 Some(ParamsLookupScope::Priority) => increment(Metric::PriorityParamsLookups, 1),
314 Some(ParamsLookupScope::PrioritySba) => increment(Metric::PrioritySbaParamsLookups, 1),
315 Some(ParamsLookupScope::PriorityTrigger) => {
316 increment(Metric::PriorityTriggerParamsLookups, 1)
317 }
318 Some(ParamsLookupScope::PrioritySnapshot) => {
319 increment(Metric::PrioritySnapshotParamsLookups, 1)
320 }
321 Some(ParamsLookupScope::PrioritySnapshotSync) => {
322 increment(Metric::PrioritySnapshotSyncParamsLookups, 1)
323 }
324 Some(ParamsLookupScope::PrioritySnapshotMetadata) => {
325 increment(Metric::PrioritySnapshotMetadataParamsLookups, 1)
326 }
327 Some(ParamsLookupScope::PrioritySnapshotAbility) => {
328 increment(Metric::PrioritySnapshotAbilityParamsLookups, 1)
329 }
330 Some(ParamsLookupScope::PrioritySnapshotCardClone) => {
331 increment(Metric::PrioritySnapshotCardCloneParamsLookups, 1)
332 }
333 Some(ParamsLookupScope::PriorityChoice) => {
334 increment(Metric::PriorityChoiceParamsLookups, 1)
335 }
336 Some(ParamsLookupScope::PriorityExecution) => {
337 increment(Metric::PriorityExecutionParamsLookups, 1)
338 }
339 Some(ParamsLookupScope::StackResolution) => {
340 increment(Metric::StackResolutionParamsLookups, 1)
341 }
342 Some(ParamsLookupScope::Combat) => increment(Metric::CombatParamsLookups, 1),
343 None => increment(Metric::ParamsUnscopedLookups, 1),
344 });
345}
346
347pub fn increment_params_parse() {
348 increment(Metric::ParamsParses, 1);
349}
350
351pub fn increment_priority_snapshot() {
352 increment(Metric::PrioritySnapshotsTaken, 1);
353}
354
355pub struct ParamsLookupScopeGuard {
356 previous: Option<ParamsLookupScope>,
357}
358
359impl ParamsLookupScopeGuard {
360 pub fn enter(scope: ParamsLookupScope) -> Option<Self> {
361 if !enabled() {
362 return None;
363 }
364 Some(PARAMS_LOOKUP_SCOPE.with(|current| {
365 let previous = current.replace(Some(scope));
366 Self { previous }
367 }))
368 }
369}
370
371pub fn current_params_lookup_scope() -> Option<ParamsLookupScope> {
372 if !enabled() {
373 return None;
374 }
375 PARAMS_LOOKUP_SCOPE.with(|current| current.get())
376}
377
378impl Drop for ParamsLookupScopeGuard {
379 fn drop(&mut self) {
380 PARAMS_LOOKUP_SCOPE.with(|current| current.set(self.previous));
381 }
382}
383
384pub fn add_duration(metric: Metric, duration: Duration) {
385 let nanos = duration.as_nanos().min(u128::from(u64::MAX)) as u64;
386 increment(metric, nanos);
387}
388
389fn reset_wall_start() {
390 if !enabled() {
391 return;
392 }
393 WALL_START.with(|start| *start.borrow_mut() = Some(Instant::now()));
394}
395
396fn wall_total_ms() -> u64 {
397 WALL_START.with(|start| {
398 start
399 .borrow()
400 .map(|instant| instant.elapsed().as_millis() as u64)
401 .unwrap_or(0)
402 })
403}
404
405pub fn record_turn_wall(duration: Duration) {
406 if !enabled() {
407 return;
408 }
409 TURN_WALL_MS.with(|turn_wall_ms| turn_wall_ms.borrow_mut().push(duration.as_millis() as u64));
410}
411
412fn turn_wall_ms_summary() -> String {
413 TURN_WALL_MS.with(|turn_wall_ms| {
414 let turn_wall_ms = turn_wall_ms.borrow();
415 let mut out = String::from("[");
416 for (idx, ms) in turn_wall_ms.iter().enumerate() {
417 if idx > 0 {
418 out.push(',');
419 }
420 out.push_str(&ms.to_string());
421 }
422 out.push(']');
423 out
424 })
425}
426
427pub struct ScopeTimer {
428 metric: Metric,
429 start: Instant,
430}
431
432impl ScopeTimer {
433 pub fn start(call_metric: Metric, duration_metric: Metric) -> Option<Self> {
434 if !enabled() {
435 return None;
436 }
437 increment(call_metric, 1);
438 Some(Self {
439 metric: duration_metric,
440 start: Instant::now(),
441 })
442 }
443}
444
445impl Drop for ScopeTimer {
446 fn drop(&mut self) {
447 add_duration(self.metric, self.start.elapsed());
448 }
449}
450
451pub struct SummaryGuard;
452
453impl SummaryGuard {
454 pub fn new() -> Option<Self> {
455 if !enabled() {
456 return None;
457 }
458 reset_wall_start();
459 Some(Self)
460 }
461}
462
463impl Drop for SummaryGuard {
464 fn drop(&mut self) {
465 print_summary();
466 }
467}
468
469pub fn print_summary() {
470 if !enabled() {
471 return;
472 }
473 let calls = CONTINUOUS_EFFECTS_CALLS.load(Ordering::Relaxed);
474 let continuous_ns = CONTINUOUS_EFFECTS_NS.load(Ordering::Relaxed);
475 let continuous_avg_us = if calls == 0 {
476 0
477 } else {
478 continuous_ns / calls / 1_000
479 };
480 let turn_wall_ms = turn_wall_ms_summary();
481 eprintln!(
482 concat!(
483 "[forge-perf] summary ",
484 "wall_total_ms={} turn_wall_ms={} ",
485 "continuous_calls={} continuous_total_us={} continuous_avg_us={} ",
486 "static_source_clones={} target_vecs={} params_parses={} params_lookups={} ",
487 "params_unscoped_lookups={} ",
488 "continuous_params_lookups={} action_space_params_lookups={} ",
489 "action_space_playable_params_lookups={} ",
490 "action_space_activatable_params_lookups={} ",
491 "action_space_mana_source_params_lookups={} ",
492 "action_space_untap_params_lookups={} ",
493 "trigger_params_lookups={} replacement_params_lookups={} ",
494 "cost_params_lookups={} target_params_lookups={} ",
495 "ability_build_params_lookups={} static_ability_params_lookups={} ",
496 "valid_filter_params_lookups={} selector_parses={} selector_matches={} ",
497 "selector_raw_predicates={} game_loop_params_lookups={} ",
498 "phase_params_lookups={} priority_params_lookups={} ",
499 "priority_sba_params_lookups={} priority_trigger_params_lookups={} ",
500 "priority_snapshots_taken={} ",
501 "priority_snapshot_params_lookups={} ",
502 "priority_snapshot_sync_params_lookups={} ",
503 "priority_snapshot_metadata_params_lookups={} ",
504 "priority_snapshot_ability_params_lookups={} ",
505 "priority_snapshot_card_clone_params_lookups={} ",
506 "priority_choice_params_lookups={} ",
507 "priority_execution_params_lookups={} ",
508 "stack_resolution_params_lookups={} combat_params_lookups={} ",
509 "spell_ability_clones={} stack_entry_clones={} ",
510 "game_state_targeting_clones={} snapshot_clones={} ",
511 "card_state_collection_clones={}"
512 ),
513 wall_total_ms(),
514 turn_wall_ms,
515 calls,
516 continuous_ns / 1_000,
517 continuous_avg_us,
518 CONTINUOUS_STATIC_SOURCE_CLONES.load(Ordering::Relaxed),
519 CONTINUOUS_TARGET_VECS.load(Ordering::Relaxed),
520 PARAMS_PARSES.load(Ordering::Relaxed),
521 PARAMS_LOOKUPS.load(Ordering::Relaxed),
522 PARAMS_UNSCOPED_LOOKUPS.load(Ordering::Relaxed),
523 CONTINUOUS_PARAMS_LOOKUPS.load(Ordering::Relaxed),
524 ACTION_SPACE_PARAMS_LOOKUPS.load(Ordering::Relaxed),
525 ACTION_SPACE_PLAYABLE_PARAMS_LOOKUPS.load(Ordering::Relaxed),
526 ACTION_SPACE_ACTIVATABLE_PARAMS_LOOKUPS.load(Ordering::Relaxed),
527 ACTION_SPACE_MANA_SOURCE_PARAMS_LOOKUPS.load(Ordering::Relaxed),
528 ACTION_SPACE_UNTAP_PARAMS_LOOKUPS.load(Ordering::Relaxed),
529 TRIGGER_PARAMS_LOOKUPS.load(Ordering::Relaxed),
530 REPLACEMENT_PARAMS_LOOKUPS.load(Ordering::Relaxed),
531 COST_PARAMS_LOOKUPS.load(Ordering::Relaxed),
532 TARGET_PARAMS_LOOKUPS.load(Ordering::Relaxed),
533 ABILITY_BUILD_PARAMS_LOOKUPS.load(Ordering::Relaxed),
534 STATIC_ABILITY_PARAMS_LOOKUPS.load(Ordering::Relaxed),
535 VALID_FILTER_PARAMS_LOOKUPS.load(Ordering::Relaxed),
536 SELECTOR_PARSES.load(Ordering::Relaxed),
537 SELECTOR_MATCHES.load(Ordering::Relaxed),
538 SELECTOR_RAW_PREDICATES.load(Ordering::Relaxed),
539 GAME_LOOP_PARAMS_LOOKUPS.load(Ordering::Relaxed),
540 PHASE_PARAMS_LOOKUPS.load(Ordering::Relaxed),
541 PRIORITY_PARAMS_LOOKUPS.load(Ordering::Relaxed),
542 PRIORITY_SBA_PARAMS_LOOKUPS.load(Ordering::Relaxed),
543 PRIORITY_TRIGGER_PARAMS_LOOKUPS.load(Ordering::Relaxed),
544 PRIORITY_SNAPSHOTS_TAKEN.load(Ordering::Relaxed),
545 PRIORITY_SNAPSHOT_PARAMS_LOOKUPS.load(Ordering::Relaxed),
546 PRIORITY_SNAPSHOT_SYNC_PARAMS_LOOKUPS.load(Ordering::Relaxed),
547 PRIORITY_SNAPSHOT_METADATA_PARAMS_LOOKUPS.load(Ordering::Relaxed),
548 PRIORITY_SNAPSHOT_ABILITY_PARAMS_LOOKUPS.load(Ordering::Relaxed),
549 PRIORITY_SNAPSHOT_CARD_CLONE_PARAMS_LOOKUPS.load(Ordering::Relaxed),
550 PRIORITY_CHOICE_PARAMS_LOOKUPS.load(Ordering::Relaxed),
551 PRIORITY_EXECUTION_PARAMS_LOOKUPS.load(Ordering::Relaxed),
552 STACK_RESOLUTION_PARAMS_LOOKUPS.load(Ordering::Relaxed),
553 COMBAT_PARAMS_LOOKUPS.load(Ordering::Relaxed),
554 SPELL_ABILITY_CLONES.load(Ordering::Relaxed),
555 STACK_ENTRY_CLONES.load(Ordering::Relaxed),
556 GAME_STATE_TARGETING_CLONES.load(Ordering::Relaxed),
557 SNAPSHOT_CLONES.load(Ordering::Relaxed),
558 CARD_STATE_COLLECTION_CLONES.load(Ordering::Relaxed),
559 );
560}
561
562pub fn reset_counters() {
563 if !enabled() {
564 return;
565 }
566 reset_wall_start();
567 TURN_WALL_MS.with(|turn_wall_ms| turn_wall_ms.borrow_mut().clear());
568 for metric in [
569 Metric::ContinuousEffectsCalls,
570 Metric::ContinuousEffectsNs,
571 Metric::ContinuousStaticSourceClones,
572 Metric::ContinuousTargetVecs,
573 Metric::ParamsParses,
574 Metric::ParamsLookups,
575 Metric::ParamsUnscopedLookups,
576 Metric::ContinuousParamsLookups,
577 Metric::ActionSpaceParamsLookups,
578 Metric::ActionSpacePlayableParamsLookups,
579 Metric::ActionSpaceActivatableParamsLookups,
580 Metric::ActionSpaceManaSourceParamsLookups,
581 Metric::ActionSpaceUntapParamsLookups,
582 Metric::TriggerParamsLookups,
583 Metric::ReplacementParamsLookups,
584 Metric::CostParamsLookups,
585 Metric::TargetParamsLookups,
586 Metric::AbilityBuildParamsLookups,
587 Metric::StaticAbilityParamsLookups,
588 Metric::ValidFilterParamsLookups,
589 Metric::SelectorParses,
590 Metric::SelectorMatches,
591 Metric::SelectorRawPredicates,
592 Metric::GameLoopParamsLookups,
593 Metric::PhaseParamsLookups,
594 Metric::PriorityParamsLookups,
595 Metric::PrioritySbaParamsLookups,
596 Metric::PriorityTriggerParamsLookups,
597 Metric::PrioritySnapshotsTaken,
598 Metric::PrioritySnapshotParamsLookups,
599 Metric::PrioritySnapshotSyncParamsLookups,
600 Metric::PrioritySnapshotMetadataParamsLookups,
601 Metric::PrioritySnapshotAbilityParamsLookups,
602 Metric::PrioritySnapshotCardCloneParamsLookups,
603 Metric::PriorityChoiceParamsLookups,
604 Metric::PriorityExecutionParamsLookups,
605 Metric::StackResolutionParamsLookups,
606 Metric::CombatParamsLookups,
607 Metric::SpellAbilityClones,
608 Metric::StackEntryClones,
609 Metric::GameStateTargetingClones,
610 Metric::SnapshotClones,
611 Metric::CardStateCollectionClones,
612 ] {
613 counter(metric).store(0, Ordering::Relaxed);
614 }
615}