pub const DEFAULT_PREFIX: &str = "ICTK";
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum BenchmarkEventKind {
Start,
End,
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct BenchmarkCounters {
pub instructions: u128,
pub heap_bytes: u128,
pub memory_bytes: u128,
}
#[cfg(not(target_arch = "wasm32"))]
impl BenchmarkCounters {
pub(super) fn checked_delta(self, start: Self) -> Option<Self> {
Some(Self {
instructions: self.instructions.checked_sub(start.instructions)?,
heap_bytes: self.heap_bytes.checked_sub(start.heap_bytes)?,
memory_bytes: self.memory_bytes.checked_sub(start.memory_bytes)?,
})
}
pub(super) fn checked_add(
self,
other: Self,
) -> Result<Self, super::analysis::BenchmarkCounter> {
Ok(Self {
instructions: self
.instructions
.checked_add(other.instructions)
.ok_or(super::analysis::BenchmarkCounter::Instructions)?,
heap_bytes: self
.heap_bytes
.checked_add(other.heap_bytes)
.ok_or(super::analysis::BenchmarkCounter::HeapBytes)?,
memory_bytes: self
.memory_bytes
.checked_add(other.memory_bytes)
.ok_or(super::analysis::BenchmarkCounter::MemoryBytes)?,
})
}
pub(super) fn min_assign(&mut self, other: Self) {
self.instructions = self.instructions.min(other.instructions);
self.heap_bytes = self.heap_bytes.min(other.heap_bytes);
self.memory_bytes = self.memory_bytes.min(other.memory_bytes);
}
pub(super) fn max_assign(&mut self, other: Self) {
self.instructions = self.instructions.max(other.instructions);
self.heap_bytes = self.heap_bytes.max(other.heap_bytes);
self.memory_bytes = self.memory_bytes.max(other.memory_bytes);
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, thiserror::Error)]
pub enum BenchmarkMarkerError {
#[error("invalid benchmark marker prefix")]
InvalidPrefix,
#[error("invalid benchmark marker label")]
InvalidLabel,
}
pub fn format_marker(
prefix: &str,
label: &str,
counters: BenchmarkCounters,
) -> Result<String, BenchmarkMarkerError> {
if prefix.is_empty() || prefix.contains(['|', '\r', '\n']) {
return Err(BenchmarkMarkerError::InvalidPrefix);
}
if label.contains(['|', '\r', '\n']) || split_label_kind(label).is_none() {
return Err(BenchmarkMarkerError::InvalidLabel);
}
Ok(format!(
"{}|{}|{}|{}|{}",
prefix, label, counters.instructions, counters.heap_bytes, counters.memory_bytes
))
}
pub(super) fn split_label_kind(label: &str) -> Option<(&str, BenchmarkEventKind)> {
label
.strip_suffix(":start")
.map(|span| (span, BenchmarkEventKind::Start))
.or_else(|| {
label
.strip_suffix(":end")
.map(|span| (span, BenchmarkEventKind::End))
})
.filter(|(span, _)| !span.is_empty())
}