use std::sync::atomic::{AtomicU64, AtomicU8, Ordering::Relaxed};
use std::sync::OnceLock;
use std::time::Instant;
#[derive(Clone, Copy)]
pub enum S {
Total = 0, MotionSearch, IntSearch, SubpelSearch, Interp, Mc, FwdTx, Quantize, CoefCost, Trellis, InvTxRecon, IntraPred, Sub8x8Search, SnapRestore, CtxUpdate, MvRefs, PredSse, Sub8x8, RdCost, DecideLeaf, ModeMap, SnapDrop, PartCtx, VarTree, StoreMi, MiCost, Count,
}
pub const N: usize = S::Count as usize;
const NAMES: [&str; N] = [
"TOTAL(decision)",
"motion_search",
" int_search",
" subpel_search",
" interp_8tap",
"mc_subpel",
"fwd_tx",
"quantize",
"coef_cost",
"trellis",
"invtx_recon",
"intra_pred",
" sub8x8_search",
"snap_restore",
"ctx_update",
"mv_refs",
"pred_sse",
"[i]sub8x8",
"[i]rd_cost",
"[i]decide_leaf",
"[i]mode_map",
"[i]snap_drop",
"[i]part_ctx",
"[i]var_tree",
"[i]store_mi",
"[i]mi_cost",
];
const TOPLEVEL: [S; 13] = [
S::MotionSearch, S::Mc, S::FwdTx, S::Quantize, S::CoefCost, S::Trellis,
S::InvTxRecon, S::IntraPred, S::Sub8x8Search, S::SnapRestore, S::CtxUpdate, S::MvRefs,
S::PredSse,
];
static CYC: [AtomicU64; N] = [const { AtomicU64::new(0) }; N];
static COUNT: [AtomicU64; N] = [const { AtomicU64::new(0) }; N];
static ENABLED: AtomicU8 = AtomicU8::new(ENABLED_UNSET);
const ENABLED_UNSET: u8 = 2;
#[inline(always)]
fn enabled() -> bool {
match ENABLED.load(Relaxed) {
0 => false,
1 => true,
_ => {
let on = std::env::var("VP9_PROF2").is_ok();
ENABLED.store(on as u8, Relaxed);
on
}
}
}
pub fn set_enabled(on: bool) {
ENABLED.store(on as u8, Relaxed);
}
pub fn reset() {
for i in 0..N {
CYC[i].store(0, Relaxed);
COUNT[i].store(0, Relaxed);
}
}
pub fn snapshot() -> [(f64, u64); N] {
let (t0, c0) = *CAL.get_or_init(|| (Instant::now(), rdtsc()));
let secs = t0.elapsed().as_secs_f64().max(1e-9);
let hz = (rdtsc().wrapping_sub(c0)) as f64 / secs;
let ovh_self = measure_scope_overhead().0;
let mut out = [(0.0f64, 0u64); N];
for (i, o) in out.iter_mut().enumerate() {
let calls = COUNT[i].load(Relaxed);
let cyc = CYC[i].load(Relaxed).saturating_sub(calls.saturating_mul(ovh_self));
*o = (cyc as f64 / hz * 1e3, calls);
}
out
}
pub fn overhead_ns() -> (f64, f64) {
let (t0, c0) = *CAL.get_or_init(|| (Instant::now(), rdtsc()));
let secs = t0.elapsed().as_secs_f64().max(1e-9);
let hz = ((rdtsc().wrapping_sub(c0)) as f64 / secs).max(1.0);
let (s, f) = measure_scope_overhead();
(s as f64 / hz * 1e9, f as f64 / hz * 1e9)
}
pub fn name(i: usize) -> &'static str {
NAMES.get(i).copied().unwrap_or("?")
}
pub fn parent(i: usize) -> Option<usize> {
let s = |x: S| x as usize;
match i {
x if x == s(S::IntSearch) || x == s(S::SubpelSearch) => Some(s(S::MotionSearch)),
x if x == s(S::Interp) => Some(s(S::SubpelSearch)),
_ => None,
}
}
pub fn is_toplevel(i: usize) -> bool {
TOPLEVEL.iter().any(|&s| s as usize == i)
}
pub fn is_info(i: usize) -> bool {
matches!(
i,
x if x == S::RdCost as usize
|| x == S::Sub8x8 as usize
|| x == S::DecideLeaf as usize
|| x == S::ModeMap as usize
|| x == S::SnapDrop as usize
|| x == S::PartCtx as usize
|| x == S::VarTree as usize
|| x == S::StoreMi as usize
|| x == S::MiCost as usize
)
}
fn measure_scope_overhead() -> (u64, u64) {
let dummy = AtomicU64::new(0);
let (mut ovh_self, mut ovh_full) = (u64::MAX, u64::MAX);
for _ in 0..4000 {
let a = rdtsc();
let b = rdtsc();
ovh_self = ovh_self.min(b.wrapping_sub(a));
let c = rdtsc();
let s = rdtsc().max(1);
dummy.fetch_add(rdtsc().wrapping_sub(s), Relaxed);
dummy.fetch_add(1, Relaxed);
let d = rdtsc();
ovh_full = ovh_full.min(d.wrapping_sub(c));
}
(ovh_self, ovh_full)
}
static CAL: OnceLock<(Instant, u64)> = OnceLock::new();
#[cfg(target_arch = "x86_64")]
#[inline(always)]
fn rdtsc() -> u64 {
unsafe { std::arch::x86_64::_rdtsc() }
}
#[cfg(not(target_arch = "x86_64"))]
#[inline(always)]
fn rdtsc() -> u64 {
std::time::Instant::now().elapsed().as_nanos() as u64
}
pub struct Scope {
stage: usize,
start: u64, }
impl Scope {
#[inline]
pub fn new(stage: S) -> Scope {
let on = enabled();
if on {
let _ = CAL.get_or_init(|| (Instant::now(), rdtsc()));
Scope { stage: stage as usize, start: rdtsc().max(1) }
} else {
Scope { stage: stage as usize, start: 0 }
}
}
}
impl Drop for Scope {
#[inline]
fn drop(&mut self) {
if self.start != 0 {
let d = rdtsc().wrapping_sub(self.start);
CYC[self.stage].fetch_add(d, Relaxed);
COUNT[self.stage].fetch_add(1, Relaxed);
}
}
}
#[macro_export]
macro_rules! prof2 {
($stage:expr, $body:expr) => {{
let _s = $crate::encode::prof::Scope::new($stage);
$body
}};
}
pub fn dump() {
if !enabled() {
return;
}
{
use std::sync::atomic::Ordering::Relaxed;
let m = &crate::encode::frameenc::DEDUP;
let (l, h, mt) = (m[0].load(Relaxed), m[1].load(Relaxed), m[2].load(Relaxed));
if l > 0 {
eprintln!(" dedup: emit_lookups={} key_hits={:.1}% value_matches={:.1}%",
l, 100.0*h as f64/l as f64, 100.0*mt as f64/l as f64);
}
}
let (t0, c0) = *CAL.get_or_init(|| (Instant::now(), rdtsc()));
let secs = t0.elapsed().as_secs_f64().max(1e-9);
let hz = (rdtsc().wrapping_sub(c0)) as f64 / secs;
let (ovh_self, ovh_full) = measure_scope_overhead();
let _ = ovh_full; let raw = |i: usize| CYC[i].load(Relaxed);
let cnt = |i: usize| COUNT[i].load(Relaxed);
let cor = |i: usize| raw(i).saturating_sub(cnt(i).saturating_mul(ovh_self));
let total = TOPLEVEL.iter().map(|&s| cor(s as usize)).sum::<u64>().max(1);
let ms = |c: u64| c as f64 / hz * 1e3;
let us_call = |c: u64, n: u64| if n > 0 { c as f64 / hz * 1e6 / n as f64 } else { 0.0 };
eprintln!(
"VP9_PROF2 (rdtsc @ {:.2} GHz; self-overhead {} cyc/scope removed; % = share of Σ measured primitives):",
hz / 1e9,
ovh_self,
);
for i in 1..N {
let c = cor(i);
eprintln!(
" {:16} {:8.1} ms {:5.1}% calls={:>11} {:.4} us/call",
NAMES[i],
ms(c),
c as f64 * 100.0 / total as f64,
cnt(i),
us_call(c, cnt(i)),
);
}
let wall = cor(S::Total as usize);
let inner: u64 = (0..N).filter(|&i| i != S::Total as usize).map(cnt).sum();
let instr = inner.saturating_mul(ovh_full);
let orch = wall.saturating_sub(total).saturating_sub(instr);
eprintln!(
" [context] Σ measured primitives = {:.0} ms (denominator)",
ms(total)
);
eprintln!(
" [context] raw decision wall = {:.0} ms | profiler instrument tax ≈ {:.0} ms | unscoped orchestration ≈ {:.0} ms",
ms(wall),
ms(instr),
ms(orch),
);
}