#[cfg(feature = "profile-timers")]
use std::sync::atomic::{AtomicU64, Ordering};
#[cfg(feature = "profile-timers")]
pub struct StageTimer {
nanos: AtomicU64,
calls: AtomicU64,
name: &'static str,
}
#[cfg(feature = "profile-timers")]
impl StageTimer {
pub const fn new(name: &'static str) -> Self {
Self {
nanos: AtomicU64::new(0),
calls: AtomicU64::new(0),
name,
}
}
pub fn record(&self, elapsed_nanos: u64) {
self.nanos.fetch_add(elapsed_nanos, Ordering::Relaxed);
self.calls.fetch_add(1, Ordering::Relaxed);
}
pub fn snapshot(&self) -> (u64, u64) {
(
self.nanos.load(Ordering::Relaxed),
self.calls.load(Ordering::Relaxed),
)
}
pub fn reset(&self) {
self.nanos.store(0, Ordering::Relaxed);
self.calls.store(0, Ordering::Relaxed);
}
}
#[cfg(feature = "profile-timers")]
pub static SAILING_BOUNDARY: StageTimer = StageTimer::new("sailing_boundary");
#[cfg(feature = "profile-timers")]
pub static TIME_NESTED_MOVER: StageTimer = StageTimer::new("time_nested_mover");
#[cfg(feature = "profile-timers")]
pub static SEGMENT_CACHE_BUILD: StageTimer = StageTimer::new("segment_cache_build");
#[cfg(feature = "profile-timers")]
pub static OUTER_FIT: StageTimer = StageTimer::new("outer_fit");
#[cfg(feature = "profile-timers")]
pub static INNER_FIT: StageTimer = StageTimer::new("inner_fit");
#[cfg(feature = "profile-timers")]
pub static INNER_MOVER: StageTimer = StageTimer::new("inner_mover");
#[cfg(feature = "profile-timers")]
pub static INNER_BOUNDARY: StageTimer = StageTimer::new("inner_boundary");
#[cfg(feature = "profile-timers")]
pub static INIT_POS_INNER: StageTimer = StageTimer::new("init_pos_inner");
#[cfg(feature = "profile-timers")]
const ALL_TIMERS: &[&StageTimer] = &[
&SAILING_BOUNDARY,
&TIME_NESTED_MOVER,
&SEGMENT_CACHE_BUILD,
&OUTER_FIT,
&INNER_FIT,
&INNER_MOVER,
&INNER_BOUNDARY,
&INIT_POS_INNER,
];
#[cfg(feature = "profile-timers")]
pub(crate) struct TimedMover<M> {
inner: M,
timer: &'static StageTimer,
}
#[cfg(feature = "profile-timers")]
impl<M> TimedMover<M> {
pub fn new(inner: M, timer: &'static StageTimer) -> Self {
Self { inner, timer }
}
}
#[cfg(feature = "profile-timers")]
impl<M> swarmkit::Contextful for TimedMover<M>
where
M: swarmkit::Contextful,
{
type TContext = M::TContext;
fn set_context(&mut self, context: Self::TContext) {
self.inner.set_context(context);
}
fn set_iteration(&mut self, iteration: usize, max_iteration: usize) {
self.inner.set_iteration(iteration, max_iteration);
}
}
#[cfg(feature = "profile-timers")]
impl<M> swarmkit::ParticleMover for TimedMover<M>
where
M: swarmkit::ParticleMover,
{
type TUnit = M::TUnit;
type TCommon = M::TCommon;
const PAR_LEAF_SIZE: usize = M::PAR_LEAF_SIZE;
fn update<R: rand::Rng>(
&self,
common: &Self::TCommon,
rng: &mut R,
idx: usize,
p: &mut swarmkit::ParticleRefMut<Self::TUnit>,
) {
let start = std::time::Instant::now();
self.inner.update(common, rng, idx, p);
self.timer.record(start.elapsed().as_nanos() as u64);
}
}
#[cfg(feature = "profile-timers")]
pub fn reset_all() {
for t in ALL_TIMERS {
t.reset();
}
}
#[cfg(feature = "profile-timers")]
pub fn dump_to_stderr() {
eprintln!("=== Profile timers (sub-stage breakdown) ===");
eprintln!(
" {:<22} {:>12} {:>12} {:>14}",
"stage", "total_ms", "calls", "avg_us_per_call",
);
for t in ALL_TIMERS {
let (ns, calls) = t.snapshot();
let total_ms = (ns as f64) / 1_000_000.0;
let avg_us = if calls > 0 {
(ns as f64 / calls as f64) / 1_000.0
} else {
0.0
};
eprintln!(
" {:<22} {:>12.3} {:>12} {:>14.3}",
t.name, total_ms, calls, avg_us,
);
}
}