use std::sync::atomic::{AtomicU64, Ordering};
#[derive(Debug, Clone, Copy)]
pub enum BailReason {
LineCountChange,
NoDamage,
KindGuard,
LazyDepth,
BlankTransition,
CapTrip,
VerifyFailed,
}
#[derive(Debug, Clone, Copy)]
pub enum SuccessPath {
ResetBoundary,
WidenToSafe,
}
pub struct WidenerMetrics {
pub incremental_reset: AtomicU64,
pub incremental_fallback: AtomicU64,
pub full_line_count_change: AtomicU64,
pub full_no_damage: AtomicU64,
pub full_kind_guard: AtomicU64,
pub full_lazy_depth: AtomicU64,
pub full_blank_transition: AtomicU64,
pub full_cap_trip: AtomicU64,
pub full_verify_failed: AtomicU64,
}
impl WidenerMetrics {
const fn new() -> Self {
Self {
incremental_reset: AtomicU64::new(0),
incremental_fallback: AtomicU64::new(0),
full_line_count_change: AtomicU64::new(0),
full_no_damage: AtomicU64::new(0),
full_kind_guard: AtomicU64::new(0),
full_lazy_depth: AtomicU64::new(0),
full_blank_transition: AtomicU64::new(0),
full_cap_trip: AtomicU64::new(0),
full_verify_failed: AtomicU64::new(0),
}
}
pub fn bail<T>(&self, reason: BailReason) -> Option<T> {
let counter = match reason {
BailReason::LineCountChange => &self.full_line_count_change,
BailReason::NoDamage => &self.full_no_damage,
BailReason::KindGuard => &self.full_kind_guard,
BailReason::LazyDepth => &self.full_lazy_depth,
BailReason::BlankTransition => &self.full_blank_transition,
BailReason::CapTrip => &self.full_cap_trip,
BailReason::VerifyFailed => &self.full_verify_failed,
};
counter.fetch_add(1, Ordering::Relaxed);
None
}
pub fn ok(&self, path: SuccessPath) {
let counter = match path {
SuccessPath::ResetBoundary => &self.incremental_reset,
SuccessPath::WidenToSafe => &self.incremental_fallback,
};
counter.fetch_add(1, Ordering::Relaxed);
}
pub fn snapshot(&self) -> Snapshot {
Snapshot {
incremental_reset: self.incremental_reset.load(Ordering::Relaxed),
incremental_fallback: self.incremental_fallback.load(Ordering::Relaxed),
full_line_count_change: self.full_line_count_change.load(Ordering::Relaxed),
full_no_damage: self.full_no_damage.load(Ordering::Relaxed),
full_kind_guard: self.full_kind_guard.load(Ordering::Relaxed),
full_lazy_depth: self.full_lazy_depth.load(Ordering::Relaxed),
full_blank_transition: self.full_blank_transition.load(Ordering::Relaxed),
full_cap_trip: self.full_cap_trip.load(Ordering::Relaxed),
full_verify_failed: self.full_verify_failed.load(Ordering::Relaxed),
}
}
}
pub static METRICS: WidenerMetrics = WidenerMetrics::new();
#[derive(Debug, Clone, Copy)]
pub struct Snapshot {
pub incremental_reset: u64,
pub incremental_fallback: u64,
pub full_line_count_change: u64,
pub full_no_damage: u64,
pub full_kind_guard: u64,
pub full_lazy_depth: u64,
pub full_blank_transition: u64,
pub full_cap_trip: u64,
pub full_verify_failed: u64,
}
impl Snapshot {
pub fn attempted(&self) -> u64 {
self.incremental_reset
+ self.incremental_fallback
+ self.full_line_count_change
+ self.full_no_damage
+ self.full_kind_guard
+ self.full_lazy_depth
+ self.full_blank_transition
+ self.full_cap_trip
+ self.full_verify_failed
}
pub fn successful_incremental(&self) -> u64 {
self.incremental_reset + self.incremental_fallback
}
pub fn successful_incremental_rate(&self) -> f64 {
let denom = self.attempted();
if denom == 0 {
0.0
} else {
self.successful_incremental() as f64 / denom as f64
}
}
pub fn fast_path_share(&self) -> f64 {
let denom = self.successful_incremental();
if denom == 0 {
0.0
} else {
self.incremental_reset as f64 / denom as f64
}
}
pub fn heuristic_path_share(&self) -> f64 {
let denom = self.successful_incremental();
if denom == 0 {
0.0
} else {
self.incremental_fallback as f64 / denom as f64
}
}
pub fn guard_sprawl_rate(&self) -> f64 {
let denom = self.attempted();
if denom == 0 {
0.0
} else {
(self.full_kind_guard + self.full_lazy_depth + self.full_blank_transition) as f64
/ denom as f64
}
}
pub fn verify_hit_rate(&self) -> f64 {
let denom = self.full_verify_failed + self.incremental_fallback;
if denom == 0 {
0.0
} else {
self.full_verify_failed as f64 / denom as f64
}
}
}
pub fn dump_if_enabled() {
if std::env::var("KIMUN_DUMP_WIDENER_METRICS").as_deref() != Ok("1") {
return;
}
let s = METRICS.snapshot();
eprintln!(
"[widener-metrics] session totals\n \
incremental_reset = {:>10} ({:5.1}%)\n \
incremental_fallback = {:>10} ({:5.1}%)\n \
full_line_count_change = {:>10} ({:5.1}%)\n \
full_no_damage = {:>10} ({:5.1}%)\n \
full_kind_guard = {:>10} ({:5.1}%)\n \
full_lazy_depth = {:>10} ({:5.1}%)\n \
full_blank_transition = {:>10} ({:5.1}%)\n \
full_cap_trip = {:>10} ({:5.1}%)\n \
full_verify_failed = {:>10} ({:5.1}%)\n \
attempted (categorised) = {:>10}\n \
---\n \
successful_incremental_rate = {:5.1}%\n \
fast_path_share = {:5.1}%\n \
heuristic_path_share = {:5.1}%\n \
guard_sprawl_rate = {:5.1}%\n \
verify_hit_rate = {:5.1}%",
s.incremental_reset,
pct(s.incremental_reset, s.attempted()),
s.incremental_fallback,
pct(s.incremental_fallback, s.attempted()),
s.full_line_count_change,
pct(s.full_line_count_change, s.attempted()),
s.full_no_damage,
pct(s.full_no_damage, s.attempted()),
s.full_kind_guard,
pct(s.full_kind_guard, s.attempted()),
s.full_lazy_depth,
pct(s.full_lazy_depth, s.attempted()),
s.full_blank_transition,
pct(s.full_blank_transition, s.attempted()),
s.full_cap_trip,
pct(s.full_cap_trip, s.attempted()),
s.full_verify_failed,
pct(s.full_verify_failed, s.attempted()),
s.attempted(),
s.successful_incremental_rate() * 100.0,
s.fast_path_share() * 100.0,
s.heuristic_path_share() * 100.0,
s.guard_sprawl_rate() * 100.0,
s.verify_hit_rate() * 100.0,
);
}
fn pct(numer: u64, denom: u64) -> f64 {
if denom == 0 {
0.0
} else {
(numer as f64 / denom as f64) * 100.0
}
}