use cram_codec::codec::ErrorBound;
use cram_codec::gpuwav3::{self, Gpu3Config, Model3};
fn opt(args: &[String], k: &str, d: &str) -> String {
args.iter()
.position(|a| a == k)
.map(|i| args[i + 1].clone())
.unwrap_or_else(|| d.to_string())
}
const ORIG: [&str; 5] = [
"miranda-density",
"miranda-velocityx",
"hurricane-TC",
"hurricane-U",
"nyx-temperature",
];
fn main() {
let args: Vec<String> = std::env::args().skip(1).collect();
let root = std::path::PathBuf::from(&args[0]);
let threads: usize = opt(&args, "--threads", "16").parse().unwrap();
let lanes: usize = opt(&args, "--l3", "8").parse().unwrap();
let m = opt(&args, "--model3", "static");
let model = if m == "static" {
Model3::Static
} else if let Some(p) = m.strip_prefix("pretrained:") {
let net = std::sync::Arc::new(
cram_codec::learned::Net::from_bytes(&std::fs::read(p).expect("model")).expect("parse"),
);
cram_codec::learned::register(net.clone());
Model3::Pretrained(net)
} else {
panic!("model3 {m}")
};
let mut fields: Vec<(String, std::path::PathBuf, Vec<usize>)> = ORIG
.iter()
.map(|n| {
(
n.to_string(),
root.join(format!("out/{n}-64x128x128.f32")),
vec![64, 128, 128],
)
})
.collect();
let man = std::fs::read_to_string(root.join("data/heldout/manifest.json")).unwrap();
for obj in man.split('{').skip(1) {
let name = obj
.split("\"name\"")
.nth(1)
.unwrap()
.split('"')
.nth(1)
.unwrap()
.to_string();
let sh = obj
.split("\"shape\"")
.nth(1)
.unwrap()
.split('[')
.nth(1)
.unwrap()
.split(']')
.next()
.unwrap();
let shape: Vec<usize> = sh.split(',').map(|v| v.trim().parse().unwrap()).collect();
fields.push((
name.clone(),
root.join(format!("data/heldout/{name}.f32")),
shape,
));
}
let only = opt(&args, "--only", "");
let ser: std::collections::HashMap<(String, String), f64> =
std::fs::read_to_string(root.join("out/gpu_ratio.jsonl"))
.unwrap()
.lines()
.filter(|l| l.contains("\"tc-wavelet\""))
.map(|l| {
let g = |k: &str| {
l.split(&format!("\"{k}\":"))
.nth(1)
.unwrap()
.split([',', '}'])
.next()
.unwrap()
.trim()
.trim_matches('"')
.to_string()
};
(
(g("field").replace("-64x128x128", ""), g("target")),
g("bpv").parse().unwrap(),
)
})
.collect();
let mut acc: std::collections::HashMap<(String, String), Vec<f64>> = Default::default();
for t in opt(&args, "--targets", "p80").split(',') {
for (name, path, dims) in &fields {
if !only.is_empty() && !only.split(',').any(|o| name.contains(o)) {
continue;
}
let b = std::fs::read(path).unwrap();
let x: Vec<f32> = b
.as_chunks::<4>()
.0
.iter()
.map(|&c| f32::from_le_bytes(c))
.collect();
let range = x.iter().fold(f32::NEG_INFINITY, |a, &b| a.max(b)) as f64
- x.iter().fold(f32::INFINITY, |a, &b| a.min(b)) as f64;
let bound = if let Some(p) = t.strip_prefix('p') {
ErrorBound::Psnr(p.parse().unwrap())
} else {
ErrorBound::MaxAbs(t[1..].parse::<f64>().unwrap() * range)
};
let cfg = Gpu3Config {
lanes,
compact: !args.iter().any(|a| a == "--nocompact"),
axis: !args.iter().any(|a| a == "--noaxis"),
model: model.clone(),
..Gpu3Config::new(bound)
};
let (s, _) = gpuwav3::encode3_f32(&x, dims, &cfg, threads);
let bpv = s.len() as f64 * 8.0 / x.len() as f64;
let r = ser.get(&(name.clone(), t.to_string())).map(|sb| bpv / sb);
println!(
"{{\"field\": \"{name}\", \"target\": \"{t}\", \"bpv\": {bpv}, \"vs_serial\": {}}}",
r.map_or("null".into(), |v| format!("{v:.4}"))
);
if let Some(r) = r {
let g = if ORIG.contains(&name.as_str()) {
"orig"
} else {
"held"
};
acc.entry((t.to_string(), "all".into()))
.or_default()
.push(r);
acc.entry((t.to_string(), g.into())).or_default().push(r);
}
}
}
let mut keys: Vec<_> = acc.keys().cloned().collect();
keys.sort();
for k in keys {
let v = &acc[&k];
let gm = (v.iter().map(|x| x.ln()).sum::<f64>() / v.len() as f64).exp();
println!("GEOMEAN {} {} n={} {:.4}", k.0, k.1, v.len(), gm);
}
}