Files
settled-reach/server/examples/bench_layer1.rs
T
jpmschweitzerandClaude Opus 4.7 f3e017a12f style(simulation): rustfmt + clippy fixes for Layer-1/heightmap code (#953 #963)
Pre-push surfaced fmt + clippy (-D warnings) failures in the new code:
- rustfmt the 6 new/changed atlas files + the bench example.
- features.rs: HashMap → BTreeMap (project bans HashMap for determinism via
  clippy disallowed_types; the bucket map is lookup-only either way).
- attractor_matching.rs: drop now-redundant .clone() on AttractorType (it
  became Copy in #953) — clippy clone_on_copy.
- drainage.rs tests: manual range → (1..=12).contains(&n).
- features.rs test: drop .clone() on Copy AttractorType.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 10:11:43 +02:00

105 lines
3.9 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! 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");
}