use rlx_runtime::Device;
use std::time::Instant;
pub struct Lcg(pub u64);
impl Lcg {
pub fn new(seed: u64) -> Self {
Lcg(seed ^ 0x9E37_79B9_7F4A_7C15)
}
#[inline]
pub fn next_u64(&mut self) -> u64 {
self.0 = self.0.wrapping_add(0x9E37_79B9_7F4A_7C15);
let mut z = self.0;
z = (z ^ (z >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
z = (z ^ (z >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
z ^ (z >> 31)
}
#[inline]
pub fn idx(&mut self, n: usize) -> u32 {
(self.next_u64() % n.max(1) as u64) as u32
}
#[inline]
pub fn f(&mut self) -> f32 {
((self.next_u64() >> 40) as f32 / (1u64 << 24) as f32) * 2.0 - 1.0
}
}
pub fn synth_codes_qmajor(n_q: usize, t: usize, codebook_size: usize, seed: u64) -> Vec<Vec<u32>> {
let mut r = Lcg::new(seed);
(0..n_q)
.map(|_| (0..t).map(|_| r.idx(codebook_size)).collect())
.collect()
}
pub fn synth_codes_i64(
n_groups: usize,
t: usize,
codebook_size: usize,
seed: u64,
) -> Vec<Vec<i64>> {
let mut r = Lcg::new(seed);
(0..n_groups)
.map(|_| (0..t).map(|_| r.idx(codebook_size) as i64).collect())
.collect()
}
pub fn synth_pcm(n: usize, amp: f32, seed: u64) -> Vec<f32> {
let mut r = Lcg::new(seed);
(0..n).map(|_| r.f() * amp).collect()
}
pub fn synth_f32(n: usize, seed: u64) -> Vec<f32> {
let mut r = Lcg::new(seed);
(0..n).map(|_| r.f()).collect()
}
pub fn time_median_ms<T>(
warmup: usize,
iters: usize,
mut f: impl FnMut() -> anyhow::Result<T>,
) -> anyhow::Result<f64> {
for _ in 0..warmup {
f()?;
}
let mut samples = Vec::with_capacity(iters.max(1));
for _ in 0..iters.max(1) {
let t0 = Instant::now();
f()?;
samples.push(t0.elapsed().as_secs_f64() * 1000.0);
}
samples.sort_by(|a, b| a.partial_cmp(b).unwrap());
Ok(samples[samples.len() / 2])
}
pub fn time_once_ms<T>(mut f: impl FnMut() -> anyhow::Result<T>) -> anyhow::Result<(T, f64)> {
let t0 = Instant::now();
let out = f()?;
Ok((out, t0.elapsed().as_secs_f64() * 1000.0))
}
pub fn report(
krate: &str,
backend: Device,
op: &str,
dur_s: f64,
t_frames: usize,
compile_ms: f64,
run_ms: f64,
) {
let rtf = if run_ms > 0.0 {
dur_s / (run_ms / 1000.0)
} else {
0.0
};
println!(
"BENCH crate={krate} backend={backend:?} op={op} dur_s={dur_s:.3} t={t_frames} \
compile_ms={compile_ms:.2} run_ms={run_ms:.2} rtf={rtf:.1} ok=true"
);
}
pub fn report_fail(krate: &str, backend: Device, op: &str, reason: &str) {
let reason = reason.replace('\n', " ");
println!("BENCH crate={krate} backend={backend:?} op={op} ok=false reason=\"{reason}\"");
}
pub fn parse_dur_iters() -> (f64, usize) {
let args: Vec<String> = std::env::args().skip(1).collect();
let mut dur = 4.0f64;
let mut iters = 5usize;
let mut i = 0;
while i < args.len() {
match args[i].as_str() {
"--dur" | "--seconds" => {
i += 1;
if let Some(v) = args.get(i).and_then(|s| s.parse().ok()) {
dur = v;
}
}
"--iters" | "-n" => {
i += 1;
if let Some(v) = args.get(i).and_then(|s| s.parse().ok()) {
iters = v;
}
}
_ => {}
}
i += 1;
}
(dur, iters)
}
pub fn available(candidates: &[Device]) -> Vec<Device> {
let mut v = vec![Device::Cpu];
for &d in candidates {
if d != Device::Cpu && rlx_runtime::is_available(d) {
v.push(d);
}
}
v
}