canic_core/macros/perf.rs
1/// Log elapsed instruction counts since the last `perf!` invocation in this thread.
2///
3/// - Uses a thread-local `PERF_LAST` snapshot.
4/// - Computes `delta = now - last`.
5/// - Prints a human-readable line for debugging.
6///
7/// Intended usage:
8/// - Long-running maintenance tasks where you want *checkpoints* in a single call.
9/// - Pair with `perf_scope!` to also capture the *full call total* at scope exit.
10///
11/// Notes:
12/// - On non-wasm targets, `perf_counter()` returns 0, so this becomes a no-op-ish
13/// counter (still records 0 deltas); this keeps unit tests compiling cleanly.
14#[macro_export]
15macro_rules! perf {
16 ($($label:tt)*) => {{
17 $crate::perf::PERF_LAST.with(|last| {
18 // Use the wrapper so non-wasm builds compile.
19 let now = $crate::perf::perf_counter();
20 let then = *last.borrow();
21 let delta = now.saturating_sub(then);
22
23 // Update last checkpoint.
24 *last.borrow_mut() = now;
25
26 // Format label + pretty-print counters.
27 let label = format!($($label)*);
28 let delta_fmt = $crate::utils::instructions::format_instructions(delta);
29 let now_fmt = $crate::utils::instructions::format_instructions(now);
30
31 // ❌ NO structured recording here
32 // ✔️ Debug log only
33 $crate::log!(
34 Info,
35 Topic::Perf,
36 "{}: '{}' used {}i since last (total: {}i)",
37 module_path!(),
38 label,
39 delta_fmt,
40 now_fmt
41 );
42 });
43 }};
44}
45
46#[macro_export]
47macro_rules! perf_scope {
48 ($($label:tt)*) => {
49 // Format the label eagerly at scope entry.
50 let __perf_label = format!($($label)*);
51
52 // Create an RAII guard whose Drop implementation records the
53 // performance measurement when the surrounding scope exits.
54 //
55 // We call the `defer` *function* directly (not the `defer!` macro)
56 // to avoid fixed-name shadowing issues that would cause early drops
57 // if multiple defers were introduced in the same scope.
58 //
59 // The guard is bound to a local variable so it remains alive until
60 // the end of the enclosing scope (including across `.await` points
61 // in async functions).
62 let _perf_scope_guard = $crate::__reexports::defer::defer(move || {
63 let __perf_end = $crate::perf::perf_counter();
64
65 // $crate::log!(Info, "perf: [0] {}, [1] {}", canic_cdk::api::performance_counter(0), canic_cdk::api::performance_counter(1));
66
67 $crate::perf::record_endpoint(&__perf_label, __perf_end);
68 });
69 };
70}