From b6d1d41c8bd02a998dfc43f4d107d4c559aff944 Mon Sep 17 00:00:00 2001 From: Michael Schimmel Date: Wed, 18 Feb 2026 16:00:21 +0100 Subject: [PATCH] Replace lazy_static with OnceLock This commit replaces the `lazy_static` crate with `std::sync::OnceLock`. This removes an external dependency and utilizes a standard library feature for lazy initialization. Additionally, a benchmark regression test has been added to `src/ast/tester.rs`. --- Cargo.lock | 7 ------- Cargo.toml | 1 - src/ast/tester.rs | 47 ++++++++++++++++++++++++++++++++++++++++++----- src/ast/types.rs | 14 ++++++-------- 4 files changed, 48 insertions(+), 21 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 607f987..74bf3c0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1731,12 +1731,6 @@ version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" -[[package]] -name = "lazy_static" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" - [[package]] name = "leb128fmt" version = "0.1.0" @@ -1890,7 +1884,6 @@ dependencies = [ "chrono", "clap", "eframe", - "lazy_static", "regex", ] diff --git a/Cargo.toml b/Cargo.toml index 7699384..7ef18b7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,6 @@ default-run = "myc" [dependencies] eframe = "0.33.3" -lazy_static = "1.4.0" clap = { version = "4.5", features = ["derive"] } chrono = "0.4" regex = "1.10" diff --git a/src/ast/tester.rs b/src/ast/tester.rs index 292cf0f..7982f3b 100644 --- a/src/ast/tester.rs +++ b/src/ast/tester.rs @@ -116,11 +116,11 @@ fn format_duration(d: Duration) -> String { } fn parse_duration(s: &str) -> Option { - use lazy_static::lazy_static; - lazy_static! { - static ref RE: Regex = Regex::new(r"([\d\.]+)(\w+)").unwrap(); - } - let caps = RE.captures(s)?; + use std::sync::OnceLock; + static RE: OnceLock = OnceLock::new(); + let re = RE.get_or_init(|| Regex::new(r"([\d\.]+)(\w+)").unwrap()); + + let caps = re.captures(s)?; let val: f64 = caps[1].parse().ok()?; let unit = &caps[2]; match unit { @@ -131,3 +131,40 @@ fn parse_duration(s: &str) -> Option { _ => None, } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + #[cfg(not(debug_assertions))] + fn benchmark_regression_test() { + let mut success = false; + let mut last_failures = String::new(); + + for _ in 0..5 { + let results = run_benchmarks(false); + let failures: Vec<_> = results.iter() + .filter(|r| r.status == "FAILED") + .collect(); + + if failures.is_empty() { + success = true; + break; + } + + last_failures = failures.iter() + .map(|r| format!("{}: Median {:?}, Baseline {:?}, Diff {:.2}%", + r.name, r.median, r.baseline.unwrap_or_default(), r.diff_pct.unwrap_or(0.0))) + .collect::>() + .join("\n"); + + // Give the system a tiny bit of breath between retries + std::thread::sleep(std::time::Duration::from_millis(50)); + } + + if !success { + panic!("Performance regression detected after 5 attempts:\n{}", last_failures); + } + } +} diff --git a/src/ast/types.rs b/src/ast/types.rs index 60aeed6..ae74f07 100644 --- a/src/ast/types.rs +++ b/src/ast/types.rs @@ -2,7 +2,7 @@ use std::collections::{HashMap, BTreeMap}; use std::rc::Rc; use std::cell::RefCell; use std::fmt; -use lazy_static::lazy_static; +use std::sync::OnceLock; use std::sync::Mutex; // Still needed for global keyword registry use std::any::Any; @@ -25,18 +25,16 @@ pub type Identity = Rc; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct Keyword(pub u32); -lazy_static! { - static ref KEYWORD_REGISTRY: Mutex> = Mutex::new(HashMap::new()); - static ref KEYWORD_REVERSE: Mutex> = Mutex::new(Vec::new()); -} +static KEYWORD_REGISTRY: OnceLock>> = OnceLock::new(); +static KEYWORD_REVERSE: OnceLock>> = OnceLock::new(); impl Keyword { pub fn intern(name: &str) -> Self { - let mut reg = KEYWORD_REGISTRY.lock().unwrap(); + let mut reg = KEYWORD_REGISTRY.get_or_init(|| Mutex::new(HashMap::new())).lock().unwrap(); if let Some(&id) = reg.get(name) { Keyword(id) } else { - let mut rev = KEYWORD_REVERSE.lock().unwrap(); + let mut rev = KEYWORD_REVERSE.get_or_init(|| Mutex::new(Vec::new())).lock().unwrap(); let id = rev.len() as u32; reg.insert(name.to_string(), id); rev.push(name.to_string()); @@ -45,7 +43,7 @@ impl Keyword { } pub fn name(&self) -> String { - let rev = KEYWORD_REVERSE.lock().unwrap(); + let rev = KEYWORD_REVERSE.get_or_init(|| Mutex::new(Vec::new())).lock().unwrap(); rev[self.0 as usize].clone() } }