use std::sync::atomic::{AtomicU64, Ordering};
#[derive(Debug, Default)]
pub(crate) struct ContractExecMetrics {
summarize_fast_hits: AtomicU64,
summarize_reload_hits: AtomicU64,
summarize_wasm_calls: AtomicU64,
summarize_wasm_uncached: AtomicU64,
delta_fast_hits: AtomicU64,
delta_reload_hits: AtomicU64,
delta_wasm_calls: AtomicU64,
delta_wasm_uncached: AtomicU64,
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub(crate) struct ContractExecSnapshot {
pub summarize_fast_hits: u64,
pub summarize_reload_hits: u64,
pub summarize_wasm_calls: u64,
pub summarize_wasm_uncached: u64,
pub delta_fast_hits: u64,
pub delta_reload_hits: u64,
pub delta_wasm_calls: u64,
pub delta_wasm_uncached: u64,
}
impl ContractExecSnapshot {
pub(crate) fn window_deltas(&self, prev: &mut ContractExecSnapshot) -> ContractExecSnapshot {
let deltas = ContractExecSnapshot {
summarize_fast_hits: self
.summarize_fast_hits
.saturating_sub(prev.summarize_fast_hits),
summarize_reload_hits: self
.summarize_reload_hits
.saturating_sub(prev.summarize_reload_hits),
summarize_wasm_calls: self
.summarize_wasm_calls
.saturating_sub(prev.summarize_wasm_calls),
summarize_wasm_uncached: self
.summarize_wasm_uncached
.saturating_sub(prev.summarize_wasm_uncached),
delta_fast_hits: self.delta_fast_hits.saturating_sub(prev.delta_fast_hits),
delta_reload_hits: self
.delta_reload_hits
.saturating_sub(prev.delta_reload_hits),
delta_wasm_calls: self.delta_wasm_calls.saturating_sub(prev.delta_wasm_calls),
delta_wasm_uncached: self
.delta_wasm_uncached
.saturating_sub(prev.delta_wasm_uncached),
};
*prev = *self;
deltas
}
}
impl ContractExecMetrics {
#[inline]
pub(crate) fn record_summarize_fast_hit(&self) {
self.summarize_fast_hits.fetch_add(1, Ordering::Relaxed);
}
#[inline]
pub(crate) fn record_summarize_reload_hit(&self) {
self.summarize_reload_hits.fetch_add(1, Ordering::Relaxed);
}
#[inline]
pub(crate) fn record_summarize_wasm_call(&self) {
self.summarize_wasm_calls.fetch_add(1, Ordering::Relaxed);
}
#[inline]
pub(crate) fn record_summarize_wasm_uncached(&self) {
self.summarize_wasm_uncached.fetch_add(1, Ordering::Relaxed);
}
#[inline]
pub(crate) fn record_delta_fast_hit(&self) {
self.delta_fast_hits.fetch_add(1, Ordering::Relaxed);
}
#[inline]
pub(crate) fn record_delta_reload_hit(&self) {
self.delta_reload_hits.fetch_add(1, Ordering::Relaxed);
}
#[inline]
pub(crate) fn record_delta_wasm_call(&self) {
self.delta_wasm_calls.fetch_add(1, Ordering::Relaxed);
}
#[inline]
pub(crate) fn record_delta_wasm_uncached(&self) {
self.delta_wasm_uncached.fetch_add(1, Ordering::Relaxed);
}
pub(crate) fn snapshot(&self) -> ContractExecSnapshot {
ContractExecSnapshot {
summarize_fast_hits: self.summarize_fast_hits.load(Ordering::Relaxed),
summarize_reload_hits: self.summarize_reload_hits.load(Ordering::Relaxed),
summarize_wasm_calls: self.summarize_wasm_calls.load(Ordering::Relaxed),
summarize_wasm_uncached: self.summarize_wasm_uncached.load(Ordering::Relaxed),
delta_fast_hits: self.delta_fast_hits.load(Ordering::Relaxed),
delta_reload_hits: self.delta_reload_hits.load(Ordering::Relaxed),
delta_wasm_calls: self.delta_wasm_calls.load(Ordering::Relaxed),
delta_wasm_uncached: self.delta_wasm_uncached.load(Ordering::Relaxed),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn each_recorder_lands_on_its_own_counter() {
let m = ContractExecMetrics::default();
assert_eq!(m.snapshot(), ContractExecSnapshot::default());
m.record_summarize_fast_hit();
for _ in 0..2 {
m.record_summarize_reload_hit();
}
for _ in 0..3 {
m.record_summarize_wasm_call();
}
for _ in 0..4 {
m.record_summarize_wasm_uncached();
}
for _ in 0..5 {
m.record_delta_fast_hit();
}
for _ in 0..6 {
m.record_delta_reload_hit();
}
for _ in 0..7 {
m.record_delta_wasm_call();
}
for _ in 0..8 {
m.record_delta_wasm_uncached();
}
let s = m.snapshot();
assert_eq!(s.summarize_fast_hits, 1);
assert_eq!(s.summarize_reload_hits, 2);
assert_eq!(s.summarize_wasm_calls, 3);
assert_eq!(s.summarize_wasm_uncached, 4);
assert_eq!(s.delta_fast_hits, 5);
assert_eq!(s.delta_reload_hits, 6);
assert_eq!(s.delta_wasm_calls, 7);
assert_eq!(s.delta_wasm_uncached, 8);
}
#[test]
fn each_field_differences_its_own_twin() {
let mut prev = ContractExecSnapshot {
summarize_fast_hits: 10,
summarize_reload_hits: 20,
summarize_wasm_calls: 30,
summarize_wasm_uncached: 40,
delta_fast_hits: 50,
delta_reload_hits: 60,
delta_wasm_calls: 70,
delta_wasm_uncached: 80,
};
let now = ContractExecSnapshot {
summarize_fast_hits: 11,
summarize_reload_hits: 22,
summarize_wasm_calls: 33,
summarize_wasm_uncached: 44,
delta_fast_hits: 55,
delta_reload_hits: 66,
delta_wasm_calls: 77,
delta_wasm_uncached: 88,
};
let d = now.window_deltas(&mut prev);
assert_eq!(d.summarize_fast_hits, 1);
assert_eq!(d.summarize_reload_hits, 2);
assert_eq!(d.summarize_wasm_calls, 3);
assert_eq!(d.summarize_wasm_uncached, 4);
assert_eq!(d.delta_fast_hits, 5);
assert_eq!(d.delta_reload_hits, 6);
assert_eq!(d.delta_wasm_calls, 7);
assert_eq!(d.delta_wasm_uncached, 8);
assert_eq!(prev, now);
assert_eq!(
now.window_deltas(&mut prev),
ContractExecSnapshot::default()
);
}
#[test]
fn snapshot_is_non_destructive() {
let m = ContractExecMetrics::default();
m.record_summarize_wasm_call();
assert_eq!(m.snapshot().summarize_wasm_calls, 1);
assert_eq!(m.snapshot().summarize_wasm_calls, 1);
m.record_summarize_wasm_call();
assert_eq!(m.snapshot().summarize_wasm_calls, 2);
}
}