examples/bench_layer1 times run_layer1 on a synthetic 512x256 heightmap with a phase breakdown (drainage / terrain analysis / feature extraction). Post-optimization: ~106ms/body total (drainage ~45ms). Documents the synthetic-terrain caveat (real heightmaps via #963). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
103 lines
3.9 KiB
Rust
103 lines
3.9 KiB
Rust
//! Benchmark: per-body Layer-1 topography generation (#953).
|
||
//!
|
||
//! Times `run_layer1` (D8 drainage → terrain analysis → 7 feature tags →
|
||
//! sub-biome classification) on a synthetic canonical 512×256 heightmap — the
|
||
//! cost of "populating the top-level map for a body." Run with:
|
||
//!
|
||
//! cargo run --release --example bench_layer1
|
||
//!
|
||
//! The committed systems.db carries no heightmaps yet (the importer isn't in
|
||
//! regen-db), so this uses a multi-octave synthetic heightmap representative of
|
||
//! a continental body. Heightmap *content* affects timing (river/attractor
|
||
//! counts), so treat this as an order-of-magnitude figure against the D-208
|
||
//! ~50ms target, not a per-body guarantee.
|
||
|
||
use std::time::Instant;
|
||
|
||
use settled_reach_server::atlas::heightmap::{BodyHeightmap, GRID_H, GRID_W};
|
||
use settled_reach_server::atlas::layer1::run_layer1;
|
||
|
||
/// Deterministic multi-octave sine "noise" → continents, coasts, drainage.
|
||
fn synth_heightmap(w: u32, h: u32) -> Vec<f32> {
|
||
use std::f32::consts::{PI, TAU};
|
||
let mut data = vec![0f32; (w * h) as usize];
|
||
for r in 0..h {
|
||
for c in 0..w {
|
||
let x = c as f32 / w as f32 * TAU;
|
||
let y = r as f32 / h as f32 * PI;
|
||
let e = 0.5
|
||
+ 0.25 * (x * 3.0).sin() * (y * 2.0).sin()
|
||
+ 0.15 * (x * 7.0).cos() * (y * 5.0).sin()
|
||
+ 0.08 * (x * 13.0).sin() * (y * 11.0).cos()
|
||
+ 0.05 * (x * 23.0).cos() * (y * 19.0).sin();
|
||
data[(r * w + c) as usize] = e.clamp(0.0, 1.0);
|
||
}
|
||
}
|
||
data
|
||
}
|
||
|
||
fn main() {
|
||
let (w, h) = (GRID_W, GRID_H);
|
||
let hm = BodyHeightmap {
|
||
body_id: "Bench".into(),
|
||
width: w,
|
||
height: h,
|
||
data: synth_heightmap(w, h),
|
||
sea_level: 0.40,
|
||
};
|
||
|
||
// Warm-up + output characterization.
|
||
let o = run_layer1(&hm);
|
||
let mut by_type: std::collections::BTreeMap<String, usize> = std::collections::BTreeMap::new();
|
||
for a in &o.attractors {
|
||
*by_type.entry(format!("{:?}", a.attractor_type)).or_default() += 1;
|
||
}
|
||
println!("=== Layer-1 output for one 512×256 body ===");
|
||
println!(
|
||
" river cells: {} mouths: {} confluences: {} basins: {}",
|
||
o.river_network.river_cells.len(),
|
||
o.river_network.mouths.len(),
|
||
o.river_network.confluences.len(),
|
||
o.drainage_basins.len(),
|
||
);
|
||
println!(" attractors: {} {:?}", o.attractors.len(), by_type);
|
||
|
||
// Phase breakdown (median of N) to locate the cost.
|
||
use settled_reach_server::atlas::drainage;
|
||
use settled_reach_server::atlas::features::{extract_attractors, TerrainAnalysis};
|
||
let n = 30;
|
||
let med = |mut v: Vec<f64>| {
|
||
v.sort_by(|a, b| a.partial_cmp(b).unwrap());
|
||
v[v.len() / 2]
|
||
};
|
||
let mut t_drain = vec![];
|
||
let mut t_terr = vec![];
|
||
let mut t_feat = vec![];
|
||
let mut t_total = vec![];
|
||
for _ in 0..n {
|
||
let t = Instant::now();
|
||
let dr = drainage::analyze(&hm.data, hm.width, hm.height, hm.sea_level);
|
||
t_drain.push(t.elapsed().as_secs_f64() * 1000.0);
|
||
|
||
let t = Instant::now();
|
||
let ta = TerrainAnalysis::analyze(&hm, &dr);
|
||
t_terr.push(t.elapsed().as_secs_f64() * 1000.0);
|
||
|
||
let t = Instant::now();
|
||
let raw = extract_attractors(&hm, &dr, &ta);
|
||
t_feat.push(t.elapsed().as_secs_f64() * 1000.0);
|
||
std::hint::black_box(&raw);
|
||
|
||
let t = Instant::now();
|
||
let out = run_layer1(&hm);
|
||
std::hint::black_box(&out);
|
||
t_total.push(t.elapsed().as_secs_f64() * 1000.0);
|
||
}
|
||
println!("\n=== phase breakdown (median of {n}, 512×256, release) ===");
|
||
println!(" D8 drainage (D-208): {:.2}ms", med(t_drain));
|
||
println!(" terrain analysis: {:.2}ms", med(t_terr));
|
||
println!(" feature extraction: {:.2}ms", med(t_feat));
|
||
println!(" run_layer1 (total): {:.2}ms", med(t_total));
|
||
println!(" D-208 target: ~50ms/body");
|
||
}
|