Files
settled-reach/server/tests/zoom_ladder_bench.rs
T
jpmschweitzer afff859a2c feat(simulation): T-1149 derive_at_metres extraction + min_wavelength octave cutoff
Extract derive_district's fractional-metres interior into the metres-
addressable derive_at_metres(seed, body, params, ta, wx, wy, climate,
min_wavelength_m) — the zoom ladder's keystone (design doc §8 step 1).
derive_district is now a thin DistrictPos-quantizing wrapper calling it
with cutoff 0.0; all four golden/believability/derivation harnesses pass
byte-identical.

enveloped_fbm/terrain_detail/voxel_relief gain min_wavelength_m: octaves
below the cutoff are hard-truncated (amp still advances so surviving
octaves keep relative weight; norm==0 guarded). Amplitude-fade-near-
cutoff is documented as a seam, not built — the pop-risk A/B needs the
client ladder (T-1153). Cutoff 0.0 is bit-identical to pre-change
output, asserted by test.

Measured (release, 4096-cell sweeps, zoom_ladder_bench.rs): district
spacing 1.200µs/cell (cutoff 0) / 1.201µs (cutoff 2048m — all octaves
survive, plumbing check); quarter spacing 1.165µs/cell (cutoff 512m).
Matches the design doc's ~1.2-1.4µs/cell release estimate.
2026-07-22 00:27:21 +02:00

176 lines
5.8 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.
//! Zoom-ladder derivation benchmarks (T-1149, design doc §2/§7/§8 step 1).
//!
//! Measures `derive_at_metres` per-cell cost at district spacing (2,048 m) and
//! quarter spacing (512 m), with and without a `min_wavelength_m` octave
//! cutoff — the exact numbers the design doc flags as UNBUILT/UNMEASURED
//! (§7: "Octave-cutoff derive (min_wavelength_m-bearing) ... not measured").
//!
//! Manual `Instant`-based timing, matching every other bench in this repo
//! (`shadowcast_bench.rs`, `perf_bench.rs`) and the same technique
//! `aliveness_probe --render` used to produce the ~1.2–1.4 µs/district
//! release figure the design doc cites — no criterion dependency exists here.
//!
//! Run: `cargo test --release --test zoom_ladder_bench -- --ignored --nocapture`
//! (debug numbers are ~5x slower and not representative of the design doc's
//! release-build figures; run `--release` for numbers worth recording).
use std::time::Instant;
use settled_reach_server::atlas::district_profile::{
derive_at_metres, BodyParams, ClimateConstants,
};
use settled_reach_server::atlas::drainage;
use settled_reach_server::atlas::features::TerrainAnalysis;
use settled_reach_server::atlas::heightmap::BodyHeightmap;
use settled_reach_server::atlas::scale;
use settled_reach_server::seed::{SeedChain, SeedDomain};
fn bench_hm() -> BodyHeightmap {
// Same shape as district_profile.rs's own test_hm/window_test_hm fixtures
// — a smooth gradient, deterministic, no PNG I/O.
let (w, h) = (128u32, 64u32);
let n = (w * h) as usize;
let data = (0..n)
.map(|i| {
let r = (i / w as usize) as f32 / h as f32;
let c = (i % w as usize) as f32 / w as f32;
(r * 0.6 + c * 0.4).min(1.0)
})
.collect();
BodyHeightmap {
body_id: "bench".into(),
width: w,
height: h,
data,
sea_level: 0.3,
}
}
fn bench_ta(hm: &BodyHeightmap) -> TerrainAnalysis {
let dr = drainage::analyze(&hm.data, hm.width, hm.height, hm.sea_level);
TerrainAnalysis::analyze(hm, &dr)
}
fn bench_params() -> BodyParams {
BodyParams {
hydrosphere: Some("ocean".into()),
atmosphere: Some("breathable".into()),
planet_class: Some("temperate".into()),
body_radius_km: Some(6371.0),
..Default::default()
}
}
/// Time `n_cells` sequential `derive_at_metres` calls on a spacing-`step_m`
/// grid starting at world origin, with the given octave cutoff. Returns
/// (total_elapsed, per_cell_ns).
fn time_derive_sweep(
seed: SeedChain,
body_id: &str,
params: &BodyParams,
ta: &TerrainAnalysis,
climate: &ClimateConstants,
grid_side: u32,
step_m: f64,
min_wavelength_m: f64,
) -> (std::time::Duration, f64) {
let n_cells = (grid_side * grid_side) as u64;
let t0 = Instant::now();
for row in 0..grid_side {
for col in 0..grid_side {
let wx = col as f64 * step_m;
let wy = row as f64 * step_m;
let prof = derive_at_metres(
seed,
body_id,
params,
ta,
wx,
wy,
climate,
min_wavelength_m,
);
// Prevent the optimizer from hoisting the call out of the loop.
std::hint::black_box(prof.elev_q);
}
}
let elapsed = t0.elapsed();
let per_cell_ns = elapsed.as_secs_f64() * 1e9 / n_cells as f64;
(elapsed, per_cell_ns)
}
#[test]
#[ignore]
fn bench_derive_at_metres_district_and_quarter_spacing() {
let hm = bench_hm();
let ta = bench_ta(&hm);
let params = bench_params();
let climate = ClimateConstants::default();
let seed = SeedChain::root(99).derive(SeedDomain::Body, 1);
let grid_side = 64u32; // 4,096 cells per sweep — matches the D-226 window cap
println!("\n=== T-1149 zoom-ladder derive_at_metres benchmark ===");
println!(
"grid: {grid_side}x{grid_side} = {} cells/sweep\n",
grid_side * grid_side
);
let district_m = scale::DISTRICT_M as f64;
let quarter_m = scale::QUARTER_M as f64;
// District spacing (2,048 m), cutoff 0 — today's uncut behavior.
let (elapsed, per_cell_ns) = time_derive_sweep(
seed, "bench", &params, &ta, &climate, grid_side, district_m, 0.0,
);
println!(
"district spacing, cutoff=0: {:>8.2} ms total, {:>7.1} ns/cell ({:.3} µs/cell)",
elapsed.as_secs_f64() * 1000.0,
per_cell_ns,
per_cell_ns / 1000.0
);
// District spacing, cutoff 2,048 m — truncates every OCTAVE_WAVELENGTHS_M
// entry below the district's own spacing (finest is 4,096 m, so this
// cutoff is BELOW that — confirms the cutoff plumbing at district scale
// without changing which octaves survive, since 2,048 < 4,096 admits all
// of them; recorded for the design doc's requested (district, cutoff
// 2048) combination regardless).
let (elapsed, per_cell_ns) = time_derive_sweep(
seed,
"bench",
&params,
&ta,
&climate,
grid_side,
district_m,
2_048.0,
);
println!(
"district spacing, cutoff=2048m: {:>8.2} ms total, {:>7.1} ns/cell ({:.3} µs/cell)",
elapsed.as_secs_f64() * 1000.0,
per_cell_ns,
per_cell_ns / 1000.0
);
// Quarter spacing (512 m), cutoff 512 m — the T-1150 Option B rung: full
// reclassification at quarter spacing with the matching octave cutoff.
let (elapsed, per_cell_ns) = time_derive_sweep(
seed,
"bench",
&params,
&ta,
&climate,
grid_side,
quarter_m,
512.0,
);
println!(
"quarter spacing, cutoff=512m: {:>8.2} ms total, {:>7.1} ns/cell ({:.3} µs/cell)",
elapsed.as_secs_f64() * 1000.0,
per_cell_ns,
per_cell_ns / 1000.0
);
println!();
}