mirror of
https://github.com/storytold/photocraft.git
synced 2026-10-24 09:57:08 -06:00
Merge PR #8529: perf(bench): run bench_filters2 from cargo xtask perf, four heavy filters now tracked (P64-P67)
docs/scorecard.md: conflict in generated file, kept ours (b7-base); regenerated later. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,13 +1,33 @@
|
||||
//! Timing for the heavier second-batch filters on a ~24 MP (6000×4000) RGBA8 layer.
|
||||
//! `cargo run --release -p photocraft-algo --example bench_filters2 [filter-name-substring]`
|
||||
//!
|
||||
//! `cargo run --release -p photocraft-algo --example bench_filters2 [name-substring] [--reps N] [--quick] [--json <file>]`
|
||||
//!
|
||||
//! `--json` writes the report `cargo xtask perf` reads: `{"rows": [{"name", "samples_ms"}]}`.
|
||||
//! `--quick` runs the list on a 1500×1000 layer instead, for `cargo xtask perf --quick`.
|
||||
use photocraft_algo::*;
|
||||
use photocraft_color::PixelFormat;
|
||||
use photocraft_geom::Rect;
|
||||
use photocraft_raster::Surface;
|
||||
|
||||
fn main() {
|
||||
let only = std::env::args().nth(1);
|
||||
let (w, h) = (6000, 4000);
|
||||
let a: Vec<String> = std::env::args().skip(1).collect();
|
||||
let flag = |k: &str| a.iter().position(|v| v == k);
|
||||
let value = |k: &str| flag(k).and_then(|i| a.get(i + 1)).cloned();
|
||||
let mut only: Option<String> = None;
|
||||
let mut i = 0;
|
||||
while i < a.len() {
|
||||
match a[i].as_str() {
|
||||
"--json" | "--reps" => i += 2,
|
||||
"--quick" => i += 1,
|
||||
_ => {
|
||||
only.get_or_insert_with(|| a[i].clone());
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
let reps: usize = value("--reps").and_then(|v| v.parse().ok()).unwrap_or(1).max(1);
|
||||
let quick = flag("--quick").is_some();
|
||||
let (w, h) = if quick { (1500, 1000) } else { (6000, 4000) };
|
||||
let r = Rect::new(0, 0, w, h);
|
||||
let mut s = Surface::new(PixelFormat::RGBA8);
|
||||
// Busy deterministic content (gradients plus texture) so edge-aware filters do real work.
|
||||
@@ -55,19 +75,49 @@ fn main() {
|
||||
},
|
||||
FilterParams::Relight { angle: 45.0, elevation: 40.0, intensity: 40.0, ambient: 55.0, warmth: 0.0, softness: 25.0 },
|
||||
];
|
||||
let mut rows: Vec<(String, Vec<f64>)> = Vec::new();
|
||||
for p in cases {
|
||||
if only.as_deref().is_some_and(|o| !p.label().to_lowercase().contains(&o.to_lowercase())) {
|
||||
continue;
|
||||
}
|
||||
let area = output_area(&p, s.content_bounds(), r, None).intersect(&r);
|
||||
let t = std::time::Instant::now();
|
||||
let out = apply(&s, &p, area, r, None);
|
||||
// Smart Blur's cost peaks near radius 12, where High quality visits every pixel in the circle.
|
||||
let name = match p {
|
||||
FilterParams::SmartBlur { radius, .. } => format!("{} r{radius}", p.label()),
|
||||
_ => p.label().to_string(),
|
||||
};
|
||||
println!("{name:<18} {w}x{h} RGBA8: {:.2?}", t.elapsed());
|
||||
drop(out);
|
||||
let mut samples = Vec::with_capacity(reps);
|
||||
for _ in 0..reps {
|
||||
let t = std::time::Instant::now();
|
||||
let out = apply(&s, &p, area, r, None);
|
||||
let ms = t.elapsed().as_secs_f64() * 1e3;
|
||||
samples.push(ms);
|
||||
drop(out);
|
||||
}
|
||||
let mut sorted = samples.clone();
|
||||
sorted.sort_by(f64::total_cmp);
|
||||
let median = match sorted.len() % 2 {
|
||||
1 => sorted[sorted.len() / 2],
|
||||
_ => (sorted[sorted.len() / 2 - 1] + sorted[sorted.len() / 2]) / 2.0,
|
||||
};
|
||||
println!("{name:<18} {w}x{h} RGBA8: {median:.1} ms{reps_note}", reps_note = if reps > 1 { format!(" (median of {reps})") } else { String::new() });
|
||||
rows.push((name, samples));
|
||||
}
|
||||
if let Some(path) = value("--json") {
|
||||
// Two fields per row and ASCII filter labels: hand-written rather than a serde dependency
|
||||
// this crate does not otherwise need.
|
||||
let body: Vec<String> = rows
|
||||
.iter()
|
||||
.map(|(name, samples)| {
|
||||
let s: Vec<String> = samples.iter().map(|v| format!("{v:.4}")).collect();
|
||||
format!("{{\"name\":\"{name}\",\"samples_ms\":[{}]}}", s.join(","))
|
||||
})
|
||||
.collect();
|
||||
let text = format!("{{\"rows\":[{}]}}", body.join(","));
|
||||
if let Err(e) = std::fs::write(&path, text) {
|
||||
eprintln!("write {path}: {e}");
|
||||
std::process::exit(1);
|
||||
}
|
||||
println!("report: {path}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -560,3 +560,53 @@ title = "File > New 10 x 5 m at 200 ppi (3.1 GP) + first reduced canvas refresh"
|
||||
issue = 4017
|
||||
bench = "perf_scenarios"
|
||||
row = "new 3 GP document + first reduced canvas refresh"
|
||||
|
||||
# ---- #211: the heavy algo filters nothing measured yet ----------------------------------------
|
||||
#
|
||||
# `bench_filters2` times the second-batch filters on a 24 MP RGBA8 layer. The example existed but was
|
||||
# never wired into this file, so nothing caught a regression in any of them. The target is the one
|
||||
# second a filter applied once to a 24 MP layer should cost; all four rows are over it today
|
||||
# (2026-10-22, 4-core laptop, default rayon pool), which is what #211's slow-algorithm ledger is for.
|
||||
|
||||
[[bench]]
|
||||
name = "bench_filters2"
|
||||
package = "photocraft-algo"
|
||||
args = ["--reps", "3"]
|
||||
quick_args = ["--quick", "--reps", "1"]
|
||||
note = "24 MP RGBA8: Oil Paint, Relight, Reduce Noise, Smart Blur, the blur gallery, Crystallize, Color Halftone"
|
||||
|
||||
[[scenario]]
|
||||
id = "P64"
|
||||
title = "Oil Paint on 24 MP"
|
||||
issue = 211
|
||||
bench = "bench_filters2"
|
||||
row = "Oil Paint"
|
||||
budget_ms = 1000
|
||||
enforce = false # 8.8 s measured 2026-10-22
|
||||
|
||||
[[scenario]]
|
||||
id = "P65"
|
||||
title = "Smart Blur r 12 on 24 MP (High quality)"
|
||||
issue = 211
|
||||
bench = "bench_filters2"
|
||||
row = "Smart Blur r12"
|
||||
budget_ms = 1000
|
||||
enforce = false # 11.6 s measured 2026-10-22: the peak of the radius sweep, where High quality visits every pixel in the circle
|
||||
|
||||
[[scenario]]
|
||||
id = "P66"
|
||||
title = "Relight on 24 MP"
|
||||
issue = 211
|
||||
bench = "bench_filters2"
|
||||
row = "Relight"
|
||||
budget_ms = 1000
|
||||
enforce = false # 3.7 s measured 2026-10-22
|
||||
|
||||
[[scenario]]
|
||||
id = "P67"
|
||||
title = "Reduce Noise on 24 MP"
|
||||
issue = 211
|
||||
bench = "bench_filters2"
|
||||
row = "Reduce Noise"
|
||||
budget_ms = 1000
|
||||
enforce = false # 2.8 s measured 2026-10-22
|
||||
|
||||
Reference in New Issue
Block a user