ic_testkit/benchmark/
marker.rs1pub const DEFAULT_PREFIX: &str = "ICTK";
5
6#[derive(Clone, Copy, Debug, Eq, PartialEq)]
8pub enum BenchmarkEventKind {
9 Start,
11 End,
13}
14
15#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
17pub struct BenchmarkCounters {
18 pub instructions: u128,
20 pub heap_bytes: u128,
22 pub memory_bytes: u128,
24}
25
26#[cfg(not(target_arch = "wasm32"))]
27impl BenchmarkCounters {
28 pub(super) fn checked_delta(self, start: Self) -> Option<Self> {
29 Some(Self {
30 instructions: self.instructions.checked_sub(start.instructions)?,
31 heap_bytes: self.heap_bytes.checked_sub(start.heap_bytes)?,
32 memory_bytes: self.memory_bytes.checked_sub(start.memory_bytes)?,
33 })
34 }
35
36 pub(super) fn checked_add(
37 self,
38 other: Self,
39 ) -> Result<Self, super::analysis::BenchmarkCounter> {
40 Ok(Self {
41 instructions: self
42 .instructions
43 .checked_add(other.instructions)
44 .ok_or(super::analysis::BenchmarkCounter::Instructions)?,
45 heap_bytes: self
46 .heap_bytes
47 .checked_add(other.heap_bytes)
48 .ok_or(super::analysis::BenchmarkCounter::HeapBytes)?,
49 memory_bytes: self
50 .memory_bytes
51 .checked_add(other.memory_bytes)
52 .ok_or(super::analysis::BenchmarkCounter::MemoryBytes)?,
53 })
54 }
55
56 pub(super) fn min_assign(&mut self, other: Self) {
57 self.instructions = self.instructions.min(other.instructions);
58 self.heap_bytes = self.heap_bytes.min(other.heap_bytes);
59 self.memory_bytes = self.memory_bytes.min(other.memory_bytes);
60 }
61
62 pub(super) fn max_assign(&mut self, other: Self) {
63 self.instructions = self.instructions.max(other.instructions);
64 self.heap_bytes = self.heap_bytes.max(other.heap_bytes);
65 self.memory_bytes = self.memory_bytes.max(other.memory_bytes);
66 }
67}
68
69#[derive(Clone, Copy, Debug, Eq, PartialEq, thiserror::Error)]
71pub enum BenchmarkMarkerError {
72 #[error("invalid benchmark marker prefix")]
74 InvalidPrefix,
75 #[error("invalid benchmark marker label")]
77 InvalidLabel,
78}
79
80pub fn format_marker(
89 prefix: &str,
90 label: &str,
91 counters: BenchmarkCounters,
92) -> Result<String, BenchmarkMarkerError> {
93 if prefix.is_empty() || prefix.contains(['|', '\r', '\n']) {
94 return Err(BenchmarkMarkerError::InvalidPrefix);
95 }
96 if label.contains(['|', '\r', '\n']) || split_label_kind(label).is_none() {
97 return Err(BenchmarkMarkerError::InvalidLabel);
98 }
99 Ok(format!(
100 "{}|{}|{}|{}|{}",
101 prefix, label, counters.instructions, counters.heap_bytes, counters.memory_bytes
102 ))
103}
104
105pub(super) fn split_label_kind(label: &str) -> Option<(&str, BenchmarkEventKind)> {
106 label
107 .strip_suffix(":start")
108 .map(|span| (span, BenchmarkEventKind::Start))
109 .or_else(|| {
110 label
111 .strip_suffix(":end")
112 .map(|span| (span, BenchmarkEventKind::End))
113 })
114 .filter(|(span, _)| !span.is_empty())
115}