use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Mutex;
use std::sync::OnceLock;
use std::time::Duration;
static ENABLED: AtomicBool = AtomicBool::new(false);
struct Entry {
module: String,
parse: Duration,
build: Duration,
cached: bool,
}
fn collector() -> &'static Mutex<Vec<Entry>> {
static COLLECTOR: OnceLock<Mutex<Vec<Entry>>> = OnceLock::new();
COLLECTOR.get_or_init(|| Mutex::new(Vec::new()))
}
pub fn enable() {
ENABLED.store(true, Ordering::Relaxed);
}
pub fn enable_from_env() {
if std::env::var_os("PERL_LSP_TIMINGS").is_some() {
enable();
}
}
#[inline]
pub fn is_enabled() -> bool {
ENABLED.load(Ordering::Relaxed)
}
pub fn record_built(module: impl Into<String>, parse: Duration, build: Duration) {
if !is_enabled() {
return;
}
record(Entry {
module: module.into(),
parse,
build,
cached: false,
});
}
pub fn record_cached(module: impl Into<String>) {
if !is_enabled() {
return;
}
record(Entry {
module: module.into(),
parse: Duration::ZERO,
build: Duration::ZERO,
cached: true,
});
}
fn record(e: Entry) {
if let Ok(mut v) = collector().lock() {
v.push(e);
}
}
const TOP_N: usize = 50;
pub fn report() {
if !is_enabled() {
return;
}
let mut entries = match collector().lock() {
Ok(mut v) => std::mem::take(&mut *v),
Err(_) => return,
};
if entries.is_empty() {
return;
}
entries.sort_by(|a, b| {
let ta = a.parse + a.build;
let tb = b.parse + b.build;
tb.cmp(&ta)
});
let total: Duration = entries.iter().map(|e| e.parse + e.build).sum();
let built = entries.iter().filter(|e| !e.cached).count();
let cached = entries.len() - built;
eprintln!();
eprintln!(
"=== per-module build timings ({} modules: {} built, {} cache-hit) ===",
entries.len(),
built,
cached
);
eprintln!(
"{:>10} {:>10} {:>10} {:>6} {}",
"total_ms", "parse_ms", "build_ms", "source", "module"
);
for e in entries.iter().take(TOP_N) {
let src = if e.cached { "cache" } else { "built" };
eprintln!(
"{:>10.3} {:>10.3} {:>10.3} {:>6} {}",
(e.parse + e.build).as_secs_f64() * 1000.0,
e.parse.as_secs_f64() * 1000.0,
e.build.as_secs_f64() * 1000.0,
src,
e.module
);
}
if entries.len() > TOP_N {
eprintln!(
"... {} more modules omitted (showing slowest {})",
entries.len() - TOP_N,
TOP_N
);
}
eprintln!(
"=== total build time across {} freshly-built modules: {:.3} ms ===",
built,
total.as_secs_f64() * 1000.0
);
}