#[derive(Clone, Copy)]
pub enum Stage {
Entropy = 0,
IntraPred = 1,
InterMc = 2,
Reconstruct = 3,
Deblock = 4,
Dequant = 5,
Scatter = 6,
PredBuf = 7,
MvGrid = 8,
Neighbors = 9,
SkipRecon = 10,
Finalize = 11,
Syntax = 12,
DpbClone = 13,
EncSource = 14,
EncSkip = 15,
EncMe = 16,
EncIntraCost = 17,
EncInterCode = 18,
EncIntraCode = 19,
EncFinal = 20,
EncTq = 21,
EncWrite = 22,
EncFree = 23,
EncPrep = 24,
EncNal = 25,
EncMbLoop = 26,
EncMvPred = 27,
EncEmit = 28,
EncBs = 29,
EncAq = 30,
MeHpel = 31,
MeCost = 32,
MeHpelBuild = 33,
MeDiamond = 34,
MeSubpel = 35,
MeRescue = 36,
Total = 37,
DecMbP = 38,
DecMbB = 39,
DecMbI = 40,
DecBDirect = 41,
DecBMc = 42,
DecSetup = 43,
DecBDeriv = 44,
DecBSet = 45,
DecBWeights = 46,
DecBLuma = 47,
DecBChroma = 48,
DecBBlend = 49,
}
pub const N: usize = 50;
#[cfg(feature = "profile")]
mod imp {
use super::{Stage, N};
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Mutex;
use std::time::Instant;
const SUB: usize = Stage::Total as usize;
#[cfg(target_arch = "x86_64")]
#[inline(always)]
fn ticks() -> u64 {
unsafe { core::arch::x86_64::_rdtsc() }
}
#[cfg(not(target_arch = "x86_64"))]
#[inline(always)]
fn ticks() -> u64 {
use std::sync::OnceLock;
static EPOCH: OnceLock<Instant> = OnceLock::new();
EPOCH.get_or_init(Instant::now).elapsed().as_nanos() as u64
}
static ANCHOR: Mutex<Option<(Instant, u64)>> = Mutex::new(None);
const NAMES: [&str; N] = [
"entropy/cavlc",
"intra-pred",
"inter-mc",
"reconstruct",
"deblock",
"dequant",
"scatter(store)",
"pred-buf copy",
"mv+grid",
"neighbors",
"skip-recon",
"finalize",
"syntax-parse",
"dpb-clone",
"enc-source-copy",
"enc-skip-check",
"enc-me(best_part)",
"enc-intra-cost",
"enc-inter-code",
"enc-intra-code",
"enc-finalize",
"enc-T/Q(nested)",
"enc-cavlc-write(nested)",
"enc-skip-freecheck(nested)",
"enc-prep(nested)",
"enc-nal-assembly",
"enc-mb-loop(nested)",
"enc-mvpred(nested)",
"enc-cavlc-emit",
"enc-bs-derive(nested)",
"enc-aq-map(nested)",
"me-hpel-read(nested)",
"me-cost/satd(nested)",
"me-hpel-BUILD(nested)",
"me-diamond(nested)",
"me-subpel(nested)",
"me-rescue(nested)",
"TOTAL",
"dec-mb-P(nested)",
"dec-mb-B(nested)",
"dec-mb-I(nested)",
"b-direct(nested)",
"b-mc(nested)",
"dec-setup",
"b-deriv(nested)",
"b-setmotion(nested)",
"b:weights(nested)",
"b:luma-mc(nested)",
"b:chroma-mc(nested)",
"b:blend(nested)",
];
static NS: [AtomicU64; N] = [const { AtomicU64::new(0) }; N];
static CALLS: [AtomicU64; N] = [const { AtomicU64::new(0) }; N];
pub struct Guard {
stage: usize,
start: u64,
}
impl Drop for Guard {
#[inline]
fn drop(&mut self) {
let d = ticks().wrapping_sub(self.start);
NS[self.stage].fetch_add(d, Ordering::Relaxed);
CALLS[self.stage].fetch_add(1, Ordering::Relaxed);
}
}
#[inline]
#[inline(always)]
pub fn tick() -> u64 {
ticks()
}
pub fn scope(s: Stage) -> Guard {
Guard {
stage: s as usize,
start: ticks(),
}
}
pub fn reset() {
for a in NS.iter().chain(CALLS.iter()) {
a.store(0, Ordering::Relaxed);
}
*ANCHOR.lock().unwrap() = Some((Instant::now(), ticks()));
}
pub fn name(i: usize) -> &'static str {
NAMES.get(i).copied().unwrap_or("?")
}
pub fn snapshot() -> [(f64, u64); N] {
let load = |i: usize| NS[i].load(Ordering::Relaxed);
let ns_per_tick = ANCHOR
.lock()
.unwrap()
.map(|(t0, c0)| {
let wall = t0.elapsed().as_nanos() as f64;
let cyc = ticks().wrapping_sub(c0) as f64;
if cyc > 0.0 {
wall / cyc
} else {
1.0
}
})
.unwrap_or(1.0);
let mut out = [(0.0f64, 0u64); N];
for (i, o) in out.iter_mut().enumerate() {
*o = (load(i) as f64 * ns_per_tick / 1e6, CALLS[i].load(Ordering::Relaxed));
}
out
}
pub fn dump() {
let s = snapshot();
let total = s[SUB].0.max(1e-9);
let sub_sum: f64 = (0..SUB).map(|i| s[i].0).sum();
let mgmt = (total - sub_sum).max(0.0);
let pct = |ms: f64| 100.0 * ms / total;
eprintln!("\n--- decode stage profile (decode() wall = {total:.1} ms) ---");
for i in 0..SUB {
eprintln!(
" {:<15} {:>8.1} ms {:>5.1}% ({} calls)",
NAMES[i], s[i].0, pct(s[i].0), s[i].1,
);
}
eprintln!(
" {:<15} {:>8.1} ms {:>5.1}% <- the OTHER bucket: mb mgmt / mv-pred / nnz / grid / dequant",
"mgmt/other", mgmt, pct(mgmt),
);
eprintln!(" {:<15} {:>8.1} ms 100.0%", NAMES[SUB], total);
}
}
#[cfg(not(feature = "profile"))]
mod imp {
use super::{Stage, N};
pub struct Guard;
#[inline(always)]
pub fn scope(_s: Stage) -> Guard {
Guard
}
#[inline(always)]
pub fn reset() {}
#[inline(always)]
pub fn dump() {}
#[inline(always)]
pub fn snapshot() -> [(f64, u64); N] {
[(0.0, 0); N]
}
#[inline(always)]
pub fn name(_i: usize) -> &'static str {
""
}
}
pub use imp::{dump, name, reset, scope, snapshot, Guard};
#[cfg(feature = "profile")]
pub use imp::tick;