canic_core/
perf.rs

1use crate::cdk::api::performance_counter;
2use canic_cdk::candid::CandidType;
3use serde::{Deserialize, Serialize};
4use std::{cell::RefCell, collections::HashMap};
5
6thread_local! {
7    pub static PERF_LAST: RefCell<u64> = RefCell::new(performance_counter(1));
8    static PERF_TABLE: RefCell<HashMap<String, PerfSlot>> = RefCell::new(HashMap::new());
9}
10
11///
12/// PerfSlot
13///
14
15#[derive(Default)]
16struct PerfSlot {
17    count: u64,
18    total_instructions: u64,
19}
20
21impl PerfSlot {
22    const fn increment(&mut self, delta: u64) {
23        self.count = self.count.saturating_add(1);
24        self.total_instructions = self.total_instructions.saturating_add(delta);
25    }
26}
27
28///
29/// PerfEntry
30/// Aggregated perf counters keyed by label.
31///
32
33#[derive(CandidType, Clone, Debug, Deserialize, Serialize)]
34pub struct PerfEntry {
35    pub label: String,
36    pub count: u64,
37    pub total_instructions: u64,
38}
39
40// wrapper around performance_counter just in case
41#[must_use]
42#[allow(clippy::missing_const_for_fn)]
43pub fn perf_counter() -> u64 {
44    #[cfg(target_arch = "wasm32")]
45    {
46        performance_counter(1)
47    }
48    #[cfg(not(target_arch = "wasm32"))]
49    {
50        0
51    }
52}
53
54/// Record an instruction delta under the provided label.
55pub fn record(label: &str, delta: u64) {
56    PERF_TABLE.with_borrow_mut(|table| {
57        table.entry(label.to_string()).or_default().increment(delta);
58    });
59}
60
61/// Snapshot all recorded perf counters, sorted by label.
62#[must_use]
63pub fn entries() -> Vec<PerfEntry> {
64    PERF_TABLE.with_borrow(|table| {
65        let mut entries: Vec<PerfEntry> = table
66            .iter()
67            .map(|(label, slot)| PerfEntry {
68                label: label.clone(),
69                count: slot.count,
70                total_instructions: slot.total_instructions,
71            })
72            .collect();
73
74        entries.sort_by(|a, b| a.label.cmp(&b.label));
75        entries
76    })
77}
78
79#[cfg(test)]
80pub fn reset() {
81    PERF_TABLE.with_borrow_mut(HashMap::clear);
82    PERF_LAST.with_borrow_mut(|last| *last = 0);
83}