Files
settled-reach/server/tests/bmv_gridunit_bench.rs
T

1251 lines
54 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.
//! Body Map Viewer workshop-gate benches (T-1178 / T-1154, brief appendix
//! ②/③; tyre-implications.md §3.2). Two anti-extrapolation measurements the
//! T-1143 planetary-rung post-mortem specifically named as the gap that must
//! close before round 1 of the body-map-viewer workshop can start:
//!
//! - **T-1178 (②):** does the row-chunked `par_iter` throughput measured at
//! 4,096 cells (`zoom_ladder_bench.rs`, 1.785 µs/cell District cutoff)
//! HOLD at real step-canvas sizes (330K, 2.07M, 8.3M cells)? MEASURE, never
//! extrapolate.
//! - **T-1154 (③):** per-cell derive cost at block (128 m) and tile-adjacent
//! (14 m) spacing — the ladder's un-costed bottom rungs
//! (`atlas-zoom-ladder-t1143.md` §2: "not estimated").
//!
//! ## Why this is a NEW file, not an extension of `zoom_ladder_bench.rs`
//!
//! `zoom_ladder_bench.rs` measures per-cell rate at the FIXED 4,096-cell
//! shape (`grid_side = 64`, matching the D-226 window wire cap) — that shape
//! is deliberate there (it's the served-window ceiling). This file measures
//! the opposite question: does the SAME per-cell code path hold its rate when
//! swept out to real canvas dimensions the served-window ceiling forbids.
//! Different shape, different question, kept in its own file per the batch
//! instruction (do not touch `server/src/atlas/mod.rs`; extend the bench or
//! add a new file — this adds a new file to avoid entangling two
//! differently-shaped measurement passes in one).
//!
//! ## The `n`-cap problem (why this cannot go through `AtlasLayerRequest`)
//!
//! `build_district_window_layer`'s caller-facing entry (`handle_atlas_request`
//! / `clamp_window_n_v2`) hard-clamps `n` so `side² ≤ WIRE_CAP_CELLS = 4,096`
//! — a REAL client can never request a 330K-cell window over the wire, by
//! design (D-226 T-1124 §2). So "run it through the production path at
//! 330K/8.3M cells" cannot mean "send an `AtlasLayerRequest` for that size" —
//! no such request is legal. It means: call the actual
//! `build_district_window_layer` function — same signature, same row-chunked
//! `into_par_iter()` loop, same `derive_window_cell`/`scatter_row` internals,
//! same `derive_at_metres` calls — with an `n` no wire request could carry,
//! because `build_district_window_layer` itself has NO internal clamp (the
//! clamp lives one layer up, in the request handler). Confirmed by direct
//! read of `layer_proxy.rs:1515-1628` this session. This is exactly what the
//! ticket anticipates: "If the production path caps window n such that you
//! cannot request 330K cells through it directly, bench the underlying
//! chunked loop at those counts and say exactly what you ran."
//!
//! ## Square vs. rectangular canvases
//!
//! `build_district_window_layer` only derives SQUARE `side×side` grids ('n'
//! is a single extent). The real step-canvas shapes the ticket names are
//! 16:9 rectangles (768×432, 1920×1080, 3840×2160) — not square. Two
//! measurements are taken for each cell-count target:
//!
//! 1. **Square, through `build_district_window_layer` itself** (District
//! granularity, `n = side`, real function call, unmodified, REAL
//! `RiverNetwork` passed in) — the closest possible approach to "the
//! actual production entry point," at the nearest square cell count to
//! the target (e.g. side=576 → 331,776 cells, matching 768×432's 331,776
//! exactly). This path exercises the ACTUAL course-invention +
//! riparian-cull machinery `build_district_window_layer` runs in
//! production (`invent_courses_near_window`/`crop_courses_for_wire`) —
//! confirmed non-empty at every measured shape (`layer.courses.len()` is
//! printed and was 3/6/10 at 330K/2.07M/8.3M respectively on the
//! synthetic gradient body centred at the world origin; see the results
//! doc's H3 correction for exactly what this does and does not validate).
//!
//! 2. **Real 16:9 rectangle, via a row-chunked loop that mirrors
//! `build_district_window_layer`'s internals cell-for-cell** (same
//! `into_par_iter()` row chunking, same `derive_at_metres` call, same
//! per-cell output quantization copied from `derive_window_cell`) but
//! sized to the actual non-square canvas. This is necessarily a
//! hand-written replica for the rectangular case (documented inline,
//! diffed explicitly against the real function in the module doc above),
//! since no production entry point derives a rectangle. Labelled
//! "MEASURED (replica loop)" in the results table to distinguish it from
//! "MEASURED (production fn)".
//!
//! **DISCLOSED GAP (PR #198 review, Hoshe H2):** [`rect_window_replica`]
//! always calls `derive_at_metres` with an EMPTY `&[]` course slice — it
//! has no `RiverNetwork`/`invent_courses_near_window` wiring at all, by
//! construction (courses are invented ONCE per window, ahead of the
//! per-cell loop, inside `build_district_window_layer` itself —
//! replicating that machinery was out of scope for a bench loop whose job
//! is the per-cell derive rate, not course invention). This means EVERY
//! rectangular-canvas number in this file (the three named 16:9 shapes
//! AND the 83K deep-step bench) **excludes the per-cell
//! `river_course::near_perennial_water` riparian-test cost** production
//! pays on every cell of a window with real nearby course geometry.
//! `zoom_ladder_bench.rs`'s own `bench_course_cost_on_vs_off` measured
//! that cost at District cap (n=64, real GJ1c geometry): **+0.090.21 ms
//! against a ~5 ms baseline, under 5%** — small, but real, and this file's
//! rectangular numbers do not include it. Full disclosure and the
//! corrected "what converges with what" statement is in the results doc's
//! H2/H3 section — read that section before citing any rectangular-canvas
//! number here as courses-inclusive. It is not.
//!
//! **Convergence claim, corrected (H3):** the square production-fn path is
//! courses-INCLUSIVE (light density: 3/6/10 courses at 330K/2.07M/8.3M) and
//! the rectangular replica-loop path is courses-EMPTY (always `&[]`) — so
//! their agreement at 331,776 ≈ 576² (~191 ns/cell either way) validates the
//! ROW-CHUNKED LOOP MECHANICS (chunking granularity, dispatch overhead,
//! per-cell derive cost) converging across two independently-written call
//! sites, NOT a courses-empty-vs-courses-inclusive equivalence claim — the
//! two paths differ in exactly one respect (courses present vs absent) and
//! happen to land within noise of each other at this course DENSITY (3 out
//! of 331,776 cells is far too sparse to move the aggregate ns/cell figure
//! outside the run-to-run noise band, consistent with the <5% per-cell
//! course-cost delta `zoom_ladder_bench.rs` measured directly). The
//! courses-inclusive rate at REAL production course density (not this
//! sparse an origin-window) is covered only by the separate GJ1c real-body
//! cross-check bench below (18 courses in a 331,776-cell window, deliberately
//! centred on real river geometry) — see that bench's own doc and the
//! results doc for the exact scope of what each number does and does not
//! include.
//!
//! Run: `cargo test --release --test bmv_gridunit_bench -- --ignored --nocapture`
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::{load_heightmap_png, BodyHeightmap};
use settled_reach_server::atlas::layer_proxy::{build_district_window_layer, WindowGranularity};
use settled_reach_server::atlas::river_course::{self, InventedCourse};
use settled_reach_server::atlas::scale;
use settled_reach_server::seed::{SeedChain, SeedDomain};
// ---------------------------------------------------------------------------
// Shared fixtures — same shape as zoom_ladder_bench.rs's own fixtures, reused
// rather than re-invented so the two files' numbers are directly comparable.
// ---------------------------------------------------------------------------
fn bench_hm() -> BodyHeightmap {
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_river_network(
hm: &BodyHeightmap,
) -> settled_reach_server::atlas::body_world_state::RiverNetwork {
drainage::analyze(&hm.data, hm.width, hm.height, hm.sea_level).river_network
}
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()
}
}
/// The real committed GJ1c heightmap, downsampled to the production working
/// grid (512×256) — used for the "real body" cross-check bench so the
/// headline numbers are not solely a synthetic-gradient artifact.
fn gj1c_fixture() -> (BodyHeightmap, TerrainAnalysis) {
let src = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("../wiki/star-systems/GJ-1/bodies/GJ1c/heightmap.png");
let heightmap = load_heightmap_png(&src, "GJ1c", 0.3).expect("decode committed GJ1c heightmap");
let small = heightmap.downsample(512, 256);
let dr = drainage::analyze(&small.data, small.width, small.height, small.sea_level);
let ta = TerrainAnalysis::analyze(&small, &dr);
(small, ta)
}
/// Report the actual Rayon global-pool thread count in use, not an assumed
/// constant — so the results doc records what really ran, on whatever
/// machine state existed at run time.
fn rayon_threads_report() -> String {
format!(
"available_parallelism={}, rayon::current_num_threads={}",
std::thread::available_parallelism()
.map(|n| n.get())
.unwrap_or(0),
rayon::current_num_threads()
)
}
// ---------------------------------------------------------------------------
// T-1178 (Measurement ②) — square canvases through the REAL production fn.
// ---------------------------------------------------------------------------
/// Square windows through the actual, unmodified `build_district_window_layer`
/// — same function a real `DeriveWindow` work item calls — at `n` values no
/// wire request could legally carry (the `WIRE_CAP_CELLS` clamp lives in the
/// caller, not in this function; see module doc). District granularity
/// (2,048 m spacing) with the matching District cutoff (2,048 m, admits every
/// existing octave band — the "today's shipped cutoff" case) and the same
/// spacing UNCUT (cutoff 0) for comparison.
#[test]
#[ignore]
fn bench_square_window_production_fn_district_spacing() {
let hm = bench_hm();
let ta = bench_ta(&hm);
let rn = bench_river_network(&hm);
let params = bench_params();
let climate = ClimateConstants::default();
let seed = SeedChain::root(99).derive(SeedDomain::Body, 1);
println!("\n=== T-1178 square-window production-fn bench (District spacing) ===");
println!(" {}\n", rayon_threads_report());
// side values chosen to land on/near the ticket's named cell counts:
// 576^2 = 331,776 (~330K, the 5x5-px-per-gridunit fallback)
// 1440^2 = 2,073,600 (~2.07M, the 1920x1080 midpoint)
// 2880^2 = 8,294,400 (~8.3M, the 3840x2160 1x1 ideal)
let side_targets: [u32; 3] = [576, 1440, 2880];
let min_wl_m = scale::DISTRICT_M as u32; // 2,048 m — the shipped District cutoff band
for &side in &side_targets {
let n = side; // District granularity: side == n
let cells = (side as u64) * (side as u64);
// Warm-up call (allocator/page-fault warm-up not counted).
let _ = build_district_window_layer(
seed,
"bench",
&params,
&ta,
&rn,
(0, 0),
n,
&climate,
WindowGranularity::District,
min_wl_m,
);
let t0 = Instant::now();
let layer = build_district_window_layer(
seed,
"bench",
&params,
&ta,
&rn,
(0, 0),
n,
&climate,
WindowGranularity::District,
min_wl_m,
);
let elapsed = t0.elapsed();
std::hint::black_box(layer.elev_q.len());
let ms = elapsed.as_secs_f64() * 1000.0;
let ns_per_cell = elapsed.as_secs_f64() * 1e9 / cells as f64;
println!(
" side={side:>5} n={n:>5} cells={cells:>10} (target ~{}): \
{ms:>9.2} ms warm, {ns_per_cell:>7.1} ns/cell ({:.3} us/cell), \
courses_in_window={}",
match cells {
c if c < 500_000 => "330K",
c if c < 4_000_000 => "2.07M",
_ => "8.3M",
},
ns_per_cell / 1000.0,
layer.courses.len()
);
}
println!();
}
/// Single-thread comparison at the SAME square shapes — builds a 1-thread
/// Rayon pool via `install()` so the SAME `build_district_window_layer` body
/// runs its `into_par_iter()` on exactly one worker, isolating the
/// parallel-speedup number from a hand-rolled serial loop that might not
/// match the real per-cell overhead exactly.
#[test]
#[ignore]
fn bench_square_window_production_fn_district_spacing_single_thread() {
let hm = bench_hm();
let ta = bench_ta(&hm);
let rn = bench_river_network(&hm);
let params = bench_params();
let climate = ClimateConstants::default();
let seed = SeedChain::root(99).derive(SeedDomain::Body, 1);
println!(
"\n=== T-1178 square-window production-fn bench, SINGLE THREAD (District spacing) ===\n"
);
// 8.3M cells single-thread is the ~12s-class case the design doc
// extrapolated (§7); 330K/2.07M included for the full comparison table.
// Skipped: none — this is the case that must NOT be assumed cheap.
let side_targets: [u32; 3] = [576, 1440, 2880];
let min_wl_m = scale::DISTRICT_M as u32;
let pool = rayon::ThreadPoolBuilder::new()
.num_threads(1)
.build()
.expect("build single-thread rayon pool");
for &side in &side_targets {
let n = side;
let cells = (side as u64) * (side as u64);
pool.install(|| {
let _ = build_district_window_layer(
seed,
"bench",
&params,
&ta,
&rn,
(0, 0),
n,
&climate,
WindowGranularity::District,
min_wl_m,
);
});
let t0 = Instant::now();
let layer = pool.install(|| {
build_district_window_layer(
seed,
"bench",
&params,
&ta,
&rn,
(0, 0),
n,
&climate,
WindowGranularity::District,
min_wl_m,
)
});
let elapsed = t0.elapsed();
std::hint::black_box(layer.elev_q.len());
let ms = elapsed.as_secs_f64() * 1000.0;
let ns_per_cell = elapsed.as_secs_f64() * 1e9 / cells as f64;
println!(
" side={side:>5} n={n:>5} cells={cells:>10} (1 thread): \
{ms:>9.2} ms, {ns_per_cell:>7.1} ns/cell ({:.3} us/cell)",
ns_per_cell / 1000.0
);
}
println!();
}
/// Real-body cross-check: the same square sweep, but on the committed GJ1c
/// heightmap/TerrainAnalysis (not the synthetic gradient) and with the real
/// river network live (courses ON, window centred on real river geometry —
/// not an empty network, and not a window that happens to cull every course
/// out) — confirms the synthetic-fixture numbers above are not an artifact of
/// a trivial gradient body or an empty river network's near-zero
/// course-culling cost. Window centring follows `bench_course_cost_on_vs_off`
/// (`zoom_ladder_bench.rs`)'s exact technique: convert a real river cell to
/// world metres via the same `pixel_to_world_m`-equivalent formula, then to
/// the covering `DistrictPos`, so the window is genuinely near GJ1c's rivers
/// rather than at the arbitrary world origin (which measured courses_in_window=0
/// on a first attempt — corrected here). Run at the 330K shape only
/// (real-body I/O + full sweep would duplicate the synthetic-fixture table
/// for no new signal at the larger sizes — the per-cell RATE is what's being
/// cross-checked, not re-measuring 8.3M twice).
#[test]
#[ignore]
fn bench_square_window_production_fn_gj1c_real_body_crosscheck() {
let (hm, ta) = gj1c_fixture();
let dr = drainage::analyze(&hm.data, hm.width, hm.height, hm.sea_level);
let rn = dr.river_network.clone();
let params = BodyParams {
hydrosphere: Some("ocean".into()),
atmosphere: Some("breathable".into()),
planet_class: Some("temperate".into()),
body_radius_km: Some(6371.0),
..Default::default()
};
let climate = ClimateConstants::default();
let seed = SeedChain::root(0xC0FFEE_u64).derive(SeedDomain::Body, 7);
assert!(
!rn.river_cells.is_empty(),
"GJ1c at production working resolution must have river cells for this bench to be meaningful"
);
// Same centring technique as zoom_ladder_bench.rs's bench_course_cost_on_vs_off.
let river_cell = rn.river_cells[rn.river_cells.len() / 2];
let r_km = params.body_radius_km.unwrap();
let world_pos = (
river_cell.1 as f64 / ta.w as f64 * (std::f64::consts::TAU * r_km * 1000.0),
(river_cell.0 as f64 / (ta.h - 1) as f64 - 0.5) * (std::f64::consts::PI * r_km * 1000.0),
);
let center: (i32, i32) = (
(world_pos.0 / scale::DISTRICT_M as f64).floor() as i32,
(world_pos.1 / scale::DISTRICT_M as f64).floor() as i32,
);
println!("\n=== T-1178 GJ1c real-body cross-check (District spacing, courses ON) ===");
println!(" window centred at district {center:?} (river cell {river_cell:?})");
println!(" {}\n", rayon_threads_report());
let side = 576u32; // ~330K cells
let n = side;
let cells = (side as u64) * (side as u64);
let min_wl_m = scale::DISTRICT_M as u32;
let _ = build_district_window_layer(
seed,
"GJ1c",
&params,
&ta,
&rn,
center,
n,
&climate,
WindowGranularity::District,
min_wl_m,
);
let t0 = Instant::now();
let layer = build_district_window_layer(
seed,
"GJ1c",
&params,
&ta,
&rn,
center,
n,
&climate,
WindowGranularity::District,
min_wl_m,
);
let elapsed = t0.elapsed();
let ms = elapsed.as_secs_f64() * 1000.0;
let ns_per_cell = elapsed.as_secs_f64() * 1e9 / cells as f64;
println!(
" side={side} n={n} cells={cells} courses_in_window={}: \
{ms:.2} ms, {ns_per_cell:.1} ns/cell ({:.3} us/cell)",
layer.courses.len(),
ns_per_cell / 1000.0
);
println!();
}
// ---------------------------------------------------------------------------
// T-1178 (Measurement ②) — real 16:9 rectangular canvases, replica loop.
// ---------------------------------------------------------------------------
/// Row-chunked derive over a REAL (non-square) canvas rectangle, mirroring
/// `build_district_window_layer`'s internal loop shape cell-for-cell (see
/// module doc for the explicit diff against the real function). Returns
/// (elapsed, per_cell_ns).
///
/// **Courses are FORCE-EMPTY here, disclosed (PR #198 review, Hoshe H2):**
/// every call site below passes `&[]` for `nearby_courses` — there is no
/// `RiverNetwork`, no `invent_courses_near_window` call, and no
/// `river_course::near_perennial_water` riparian test running per cell. This
/// is a real, measured gap versus production, not a rounding footnote:
/// `zoom_ladder_bench.rs`'s `bench_course_cost_on_vs_off` measured the
/// courses-on-vs-off delta directly at District cap (n=64, real GJ1c
/// geometry) as **+0.090.21 ms against a ~5 ms baseline (under 5%)**. Every
/// number produced by this function — the three 16:9 canvas benches AND the
/// 83K deep-step bench — excludes that cost. It is EXCLUDED, not zero in
/// production; readers citing a rectangular-canvas number from this file as
/// "the real per-cell cost including courses" are citing it wrong. The
/// courses-inclusive numbers live only in the square
/// `build_district_window_layer`-backed benches above (which pass a real
/// `RiverNetwork` and print `courses_in_window`).
#[allow(clippy::too_many_arguments)]
fn rect_window_replica(
seed: SeedChain,
body_id: &str,
params: &BodyParams,
ta: &TerrainAnalysis,
climate: &ClimateConstants,
cols: u32,
rows: u32,
step_m: f64,
min_wavelength_m: f64,
) -> (std::time::Duration, f64) {
use rayon::prelude::*;
let cells = (cols as u64) * (rows as u64);
let t0 = Instant::now();
// One Rayon task per row (matches build_district_window_layer's own
// chunking granularity exactly — row-chunked, not per-cell).
let row_results: Vec<u64> = (0..rows)
.into_par_iter()
.map(|row| {
let mut row_acc: u64 = 0;
for col in 0..cols {
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,
&[],
);
// Mirror derive_window_cell's per-cell quantization cost
// (six field casts/clamps) rather than reading one field —
// this is the SAME output shape the real function produces,
// just accumulated into a checksum instead of six Vec<u8>
// scatters (allocation-identical scatter cost is a one-time
// Vec::with_capacity, not a per-cell cost worth replicating
// here; the per-cell COMPUTE is what's being measured).
let morphology = prof.morphology_zone as u8;
let elev_q = prof.elev_q.clamp(0, 100) as u8;
let moisture_q = prof.moisture_q.clamp(0, 100) as u8;
let vegetation = prof.vegetation_class as u8;
let glaciation = prof.glaciation_grade as u8;
row_acc ^= morphology as u64
^ elev_q as u64
^ moisture_q as u64
^ vegetation as u64
^ glaciation as u64;
}
std::hint::black_box(row_acc)
})
.collect();
let checksum: u64 = row_results.into_iter().fold(0, |a, b| a ^ b);
std::hint::black_box(checksum);
let elapsed = t0.elapsed();
let ns_per_cell = elapsed.as_secs_f64() * 1e9 / cells as f64;
(elapsed, ns_per_cell)
}
/// The three named real step-canvas shapes (768x432 / 1920x1080 / 3840x2160),
/// District spacing + District cutoff, through the row-chunked replica loop.
/// This is the DIRECT answer to "does 4,096-cell par_iter throughput hold at
/// real canvas sizes" for the actual non-square shapes the workshop brief
/// names, cross-checked against the square production-fn numbers above.
#[test]
#[ignore]
fn bench_rect_canvas_district_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 min_wl_m = scale::DISTRICT_M as f64;
println!(
"\n=== T-1178 real 16:9 canvas bench, replica row-chunked loop (District spacing) ==="
);
println!(" {}\n", rayon_threads_report());
let shapes: [(u32, u32, &str); 3] = [
(768, 432, "768x432 (5x5px/gridunit fallback, ~330K)"),
(1920, 1080, "1920x1080 (midpoint, ~2.07M)"),
(3840, 2160, "3840x2160 (1x1 ideal, ~8.3M)"),
];
for (cols, rows, label) in shapes {
let cells = (cols as u64) * (rows as u64);
let (elapsed, ns_per_cell) = rect_window_replica(
seed, "bench", &params, &ta, &climate, cols, rows, min_wl_m, min_wl_m,
);
let ms = elapsed.as_secs_f64() * 1000.0;
println!(
" {label}: cells={cells:>10} {ms:>9.2} ms warm, {ns_per_cell:>7.1} ns/cell \
({:.3} us/cell)",
ns_per_cell / 1000.0
);
}
println!();
}
/// Single-thread version of the same three rectangles (cutoff-matched) —
/// completes the parallel-speedup comparison for the real canvas shapes.
#[test]
#[ignore]
fn bench_rect_canvas_district_spacing_single_thread() {
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 min_wl_m = scale::DISTRICT_M as f64;
println!("\n=== T-1178 real 16:9 canvas bench, SINGLE THREAD (District spacing) ===\n");
let pool = rayon::ThreadPoolBuilder::new()
.num_threads(1)
.build()
.expect("build single-thread rayon pool");
let shapes: [(u32, u32, &str); 3] = [
(768, 432, "768x432 (~330K)"),
(1920, 1080, "1920x1080 (~2.07M)"),
(3840, 2160, "3840x2160 (~8.3M)"),
];
for (cols, rows, label) in shapes {
let cells = (cols as u64) * (rows as u64);
let (elapsed, ns_per_cell) = pool.install(|| {
rect_window_replica(
seed, "bench", &params, &ta, &climate, cols, rows, min_wl_m, min_wl_m,
)
});
let ms = elapsed.as_secs_f64() * 1000.0;
println!(
" {label}: cells={cells:>10} {ms:>9.2} ms, {ns_per_cell:>7.1} ns/cell \
({:.3} us/cell)",
ns_per_cell / 1000.0
);
}
println!();
}
// ---------------------------------------------------------------------------
// T-1154 (Measurement ③) — block (128 m) and tile-adjacent spacing.
// ---------------------------------------------------------------------------
/// Per-cell derive cost at Block (128 m) and Tile-adjacent (1 m, 4 m)
/// spacing, with the matching octave cutoff active — the ladder rungs
/// `atlas-zoom-ladder-t1143.md` §2 marks "not estimated". Uses the SAME
/// 64x64 = 4,096-cell sweep shape as `zoom_ladder_bench.rs`'s District/
/// Quarter sweeps so the per-cell rate is directly comparable across every
/// rung on one table.
///
/// `MIN_WL_BANDS_M` (the request-facing quantized cutoff set,
/// `layer_proxy.rs:451`) bottoms out at 1,024 m (Quarter's own band) — there
/// is NO existing wire-facing band for Block or Tile. This bench calls
/// `derive_at_metres` directly with a cutoff value no real window request can
/// carry today (same "call the underlying function, not the wire path"
/// discipline as the T-1178 benches above), matching each spacing to its OWN
/// Nyquist floor (cutoff = spacing) the same way the existing District/
/// Quarter sweeps do.
#[test]
#[ignore]
fn bench_block_and_tile_spacing_4096_cells() {
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 — matches zoom_ladder_bench.rs's shape
println!("\n=== T-1154 block/tile-spacing derive_at_metres benchmark (4,096-cell sweep) ===");
println!(
"grid: {grid_side}x{grid_side} = {} cells/sweep\n",
grid_side * grid_side
);
let block_m = scale::BLOCK_M as f64; // 128 m
let tile_4m = 4.0_f64; // coarsest "tile-adjacent" spacing named in the ticket
let tile_1m = 1.0_f64; // the literal voxel/tile spacing (D-243)
for (label, step_m, cutoff_m) in [
("block (128m), cutoff=128m", block_m, block_m),
("tile-adjacent (4m), cutoff=4m", tile_4m, tile_4m),
("tile (1m), cutoff=1m", tile_1m, tile_1m),
] {
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, "bench", &params, &ta, wx, wy, &climate, cutoff_m, &[]);
std::hint::black_box(prof.elev_q);
}
}
let elapsed = t0.elapsed();
let per_cell_ns = elapsed.as_secs_f64() * 1e9 / n_cells as f64;
println!(
" {label:<32}: {:>8.2} ms total, {:>7.1} ns/cell ({:.3} us/cell)",
elapsed.as_secs_f64() * 1000.0,
per_cell_ns,
per_cell_ns / 1000.0
);
}
println!();
}
/// The realistic deep-step canvas: at the ~10 px-per-1m-tile bottom-out on a
/// 2,160-px SMALLER axis (a 1920x1080 viewport, portrait-safe on the smaller
/// dimension per the brief's "viewport's smaller axis" convention), the
/// world extent covered is `1080 px / 10 px-per-tile = 108 tiles = 108 m` on
/// the smaller axis, `1920 / 10 = 192 m` on the larger — a 192m x 108m
/// window at 1 m spacing. Re-stated against the ticket's own worked example
/// (2160 px smaller axis, ~216m x ~384m) for the 3840x2160 canvas instead:
/// `2160/10 = 216 m` (smaller axis), `3840/10 = 384 m` (larger axis) — EXACT
/// geometry used below, matching the ticket's stated numbers precisely.
///
/// At 1 m spacing that is a 216x384 CELL grid (1 world-metre per gridunit,
/// 10 screen px per gridunit) = 82,944 cells — the ticket's "~83K cells"
/// figure, confirmed exactly (216 * 384 = 82,944).
#[test]
#[ignore]
fn bench_deep_step_realistic_canvas_83k_cells() {
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);
// Geometry: 3840x2160 canvas, 10 px per 1m tile, smaller axis 2160.
// world extent: 216 m (smaller/rows) x 384 m (larger/cols), 1 m spacing.
let rows = 216u32; // world metres on the smaller (2160px/10) axis
let cols = 384u32; // world metres on the larger (3840px/10) axis
let cells = (rows as u64) * (cols as u64);
assert_eq!(
cells, 82_944,
"geometry must match the ticket's stated ~83K cells exactly"
);
let step_m = 1.0_f64;
let cutoff_m = 1.0_f64; // Nyquist-matched to 1 m spacing
println!("\n=== T-1154 deep-step realistic-canvas bench (216m x 384m @ 1m spacing) ===");
println!(
" geometry: 3840x2160 canvas @ 10px/tile, smaller axis 2160 -> 216m, larger axis 3840 -> 384m"
);
println!(" cells = 216 * 384 = {cells}");
println!(" {}\n", rayon_threads_report());
// Parallel, through the SAME row-chunked replica loop T-1178 uses (this
// is the production par_iter SHAPE, not the exact function, for the same
// reason as the T-1178 rectangular benches: no square-only production fn
// covers a non-square metre-spacing window).
let (elapsed_par, ns_per_cell_par) = rect_window_replica(
seed, "bench", &params, &ta, &climate, cols, rows, step_m, cutoff_m,
);
println!(
" PARALLEL (row-chunked): {:.2} ms, {:.1} ns/cell ({:.3} us/cell)",
elapsed_par.as_secs_f64() * 1000.0,
ns_per_cell_par,
ns_per_cell_par / 1000.0
);
// Single-thread comparison.
let pool = rayon::ThreadPoolBuilder::new()
.num_threads(1)
.build()
.expect("build single-thread rayon pool");
let (elapsed_seq, ns_per_cell_seq) = pool.install(|| {
rect_window_replica(
seed, "bench", &params, &ta, &climate, cols, rows, step_m, cutoff_m,
)
});
println!(
" SINGLE-THREAD: {:.2} ms, {:.1} ns/cell ({:.3} us/cell)",
elapsed_seq.as_secs_f64() * 1000.0,
ns_per_cell_seq,
ns_per_cell_seq / 1000.0
);
println!(
" speedup: {:.2}x\n",
elapsed_seq.as_secs_f64() / elapsed_par.as_secs_f64()
);
}
/// Cutoff sweep AT Block-spacing sample positions, varying the cutoff itself
/// from uncut (0, every octave band in `OCTAVE_WAVELENGTHS_M` [4,096..32,768]
/// AND `VOXEL_OCTAVE_WAVELENGTHS_M` [128..1,024] survives) up through
/// District-coarse (2,048m, truncates nothing extra vs. uncut — every
/// `OCTAVE_WAVELENGTHS_M` entry is still ≥2,048) to Region-coarse (204,800m,
/// truncates EVERY octave in both bands, `enveloped_fbm`'s "every octave cut"
/// empty-sum guard fires).
///
/// **Why "cutoff=128m (Block's own Nyquist floor)" shows ZERO delta vs.
/// uncut** (confirmed by direct read of `enveloped_fbm`,
/// `detail_scatter.rs:198-251`: `if wl < min_wavelength_m { skip }` — a
/// cutoff only skips octaves STRICTLY FINER than itself. At Block's own
/// floor (128m), every entry in BOTH octave arrays is `>= 128m`
/// (`VOXEL_OCTAVE_WAVELENGTHS_M`'s finest is exactly 128m, `>=` not `<`), so
/// nothing is skipped — Block sits at the bottom of the invented-detail
/// octave stack, with nothing finer left to truncate. This is a genuine,
/// verified finding (not a bench bug): **the cutoff mechanism has no
/// truncation work left to do at Block spacing or finer** — every rung from
/// Block down to Tile pays the SAME full per-cell octave-sum cost, because
/// the const octave arrays bottom out at 128m and neither
/// `MOSAIC_OCTAVE_WAVELENGTHS_M` (64/32/16/8m) nor any Tile-specific band is
/// wired into `derive_at_metres`'s call graph (see the module doc's
/// voxel_mosaic finding). The cutoff only pays off at COARSER rungs
/// (District, Quarter, Region) where it truncates the fine end of the octave
/// stack that those rungs' sample density can't resolve anyway.
#[test]
#[ignore]
fn bench_block_cutoff_confirms_savings() {
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;
let block_m = scale::BLOCK_M as f64;
let district_m = scale::DISTRICT_M as f64;
let region_m = scale::REGION_M as f64;
println!("\n=== T-1154 Block-spacing-position cutoff sweep (varying cutoff value) ===\n");
for (label, cutoff_m) in [
("uncut (cutoff=0)", 0.0),
(
"cutoff=128m (Block's own floor, expect NO delta vs uncut)",
block_m,
),
(
"cutoff=2048m (District-coarse, truncates the VOXEL band, real savings)",
district_m,
),
(
"cutoff=204800m (Region-coarse, expect EVERY octave truncated)",
region_m,
),
] {
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 {
// Sample POSITIONS stay at Block spacing throughout — only the
// cutoff VALUE varies — so this isolates the cutoff's cost
// effect from a spacing change.
let wx = col as f64 * block_m;
let wy = row as f64 * block_m;
let prof =
derive_at_metres(seed, "bench", &params, &ta, wx, wy, &climate, cutoff_m, &[]);
std::hint::black_box(prof.elev_q);
}
}
let elapsed = t0.elapsed();
let per_cell_ns = elapsed.as_secs_f64() * 1e9 / n_cells as f64;
println!(
" {label:<58}: {:>8.2} ms total, {:>7.1} ns/cell ({:.3} us/cell)",
elapsed.as_secs_f64() * 1000.0,
per_cell_ns,
per_cell_ns / 1000.0
);
}
println!();
}
// ---------------------------------------------------------------------------
// Interview-2 redirect: chunk (64 m) — the never-benched rung, now the
// proposed deepest Atlas ladder rung (Jeroen, interview 2: "the actual tile
// level rung seems unusable. maybe replace with 64?" — tile/voxel dropped
// from the Atlas ladder; chunk becomes the bottom). Same discipline as the
// original T-1154 benches above: call `derive_at_metres` directly (no
// wire-facing cutoff band exists below Quarter's 1,024 m `MIN_WL_BANDS_M`
// floor either), matched cutoff = spacing (Nyquist), 4,096-cell sweep for
// direct comparability with the existing Block/Tile row above, plus a
// realistic deep-step VIEWPORT canvas at chunk spacing (replacing the old
// 216x384m/1m-spacing deep-step bench, which measured the now-dropped tile
// rung).
// ---------------------------------------------------------------------------
/// Chunk (64 m) per-cell derive cost, matched to the existing Block/Tile
/// 4,096-cell sweep shape (`bench_block_and_tile_spacing_4096_cells` above)
/// so this row slots directly into the same comparison table. This is the
/// ONE rung on the D-243 ladder nobody had measured before interview 2 (T-1154
/// tested Block 128m and Tile-adjacent 1m/4m; chunk's 64m spacing sits
/// between them and was never run). `VOXEL_OCTAVE_WAVELENGTHS_M`'s finest
/// entry is 128m (`detail_scatter.rs:46`), so per the SAME cutoff-mechanism
/// finding `bench_block_cutoff_confirms_savings` already established for
/// Block, a cutoff at 64m (finer than every entry in that array) should
/// truncate nothing either — this bench CONFIRMS that expectation for chunk
/// specifically rather than assuming it transfers from Block's own result.
#[test]
#[ignore]
fn bench_chunk_spacing_4096_cells() {
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 — matches the Block/Tile sweep above
println!(
"\n=== Interview-2: chunk (64m) spacing derive_at_metres benchmark (4,096-cell sweep) ==="
);
println!(
"grid: {grid_side}x{grid_side} = {} cells/sweep\n",
grid_side * grid_side
);
let chunk_m = scale::CHUNK_M as f64; // 64 m
let block_m = scale::BLOCK_M as f64; // 128 m, for direct side-by-side
for (label, step_m, cutoff_m) in [
("chunk (64m), cutoff=64m", chunk_m, chunk_m),
("chunk (64m), UNCUT (cutoff=0)", chunk_m, 0.0),
(
"block (128m), cutoff=128m [reference row]",
block_m,
block_m,
),
] {
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, "bench", &params, &ta, wx, wy, &climate, cutoff_m, &[]);
std::hint::black_box(prof.elev_q);
}
}
let elapsed = t0.elapsed();
let per_cell_ns = elapsed.as_secs_f64() * 1e9 / n_cells as f64;
println!(
" {label:<44}: {:>8.2} ms total, {:>7.1} ns/cell ({:.3} us/cell)",
elapsed.as_secs_f64() * 1000.0,
per_cell_ns,
per_cell_ns / 1000.0
);
}
println!();
}
/// The NEW realistic deepest-step canvas — chunk (64 m) spacing replacing
/// the dropped tile (1 m) rung. Jeroen's interview-2 ruling: the old
/// 10px/tile bottom-out ("full 1920 screen at 10px/tile shows ~192x108m") is
/// in-world viewport territory (Phase 5), not Atlas map content — chunk (64
/// m, D-243's "stream/derive unit") is the new floor.
///
/// Display-band derivation for the new bottom-out, stated precisely (this is
/// the number Tyre's amendment text needs): at 1x1 px-per-gridunit (the
/// workshop's own ideal ratio, premise 5), a 3840x2160 canvas at 64 m
/// spacing covers `2160 * 64 = 138,240 m` (~138.2 km) on the smaller axis and
/// `3840 * 64 = 245,760 m` (~245.8 km) on the larger axis — i.e. the
/// bottom-out is "1 screen px per 64m gridunit", NOT "10 px per chunk" (10
/// px/gridunit would need a canvas 10x larger in EXTENT for the same pixel
/// budget, which no longer makes sense once chunk stands in for what tile's
/// 10x margin was there to buy: legibility of a 1m ground feature at low
/// pixel density; chunk itself IS the smallest legible Atlas content unit
/// now, so it wants 1x1, not a magnification margin on top of 1x1). Full
/// working: `canvas_px / gridunit_spacing_m = world_extent_m` per axis (the
/// same arithmetic the old tile bottom-out used, just at 64m instead of 1m
/// and without the 10x margin factor tile's own screen-legibility problem
/// needed). This bench uses the FULL 3840x2160 canvas at 1x1 (matching every
/// other rung's fixed-px-budget convention, per round 2 §(c) — chunk is the
/// first deepest rung that does NOT need the display-ratio-sized-canvas
/// exception the old tile rung required, precisely because it's not
/// undersized relative to a legibility margin the way 1m/10px was).
#[test]
#[ignore]
fn bench_chunk_deep_step_realistic_canvas() {
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);
// Full 3840x2160 canvas, 1 gridunit per screen px, 64 m spacing.
let cols = 3840u32;
let rows = 2160u32;
let cells = (rows as u64) * (cols as u64);
let step_m = scale::CHUNK_M as f64; // 64 m
let cutoff_m = step_m; // Nyquist-matched
let world_w_km = cols as f64 * step_m / 1000.0;
let world_h_km = rows as f64 * step_m / 1000.0;
println!("\n=== Interview-2: chunk (64m) deep-step realistic-canvas bench (3840x2160 @ 1x1 px/gridunit) ===");
println!(
" geometry: 3840x2160 canvas @ 64m/gridunit, 1x1 px-per-gridunit -> {world_w_km:.1} km x {world_h_km:.1} km world extent"
);
println!(" cells = 3840 * 2160 = {cells}");
println!(" {}\n", rayon_threads_report());
let (elapsed_par, ns_per_cell_par) = rect_window_replica(
seed, "bench", &params, &ta, &climate, cols, rows, step_m, cutoff_m,
);
println!(
" PARALLEL (row-chunked): {:.2} ms, {:.1} ns/cell ({:.3} us/cell)",
elapsed_par.as_secs_f64() * 1000.0,
ns_per_cell_par,
ns_per_cell_par / 1000.0
);
let pool = rayon::ThreadPoolBuilder::new()
.num_threads(1)
.build()
.expect("build single-thread rayon pool");
let (elapsed_seq, ns_per_cell_seq) = pool.install(|| {
rect_window_replica(
seed, "bench", &params, &ta, &climate, cols, rows, step_m, cutoff_m,
)
});
println!(
" SINGLE-THREAD: {:.2} ms, {:.1} ns/cell ({:.3} us/cell)",
elapsed_seq.as_secs_f64() * 1000.0,
ns_per_cell_seq,
ns_per_cell_seq / 1000.0
);
println!(
" speedup: {:.2}x\n",
elapsed_seq.as_secs_f64() / elapsed_par.as_secs_f64()
);
}
// ---------------------------------------------------------------------------
// S2 (ruled: before filing) — deep-step x high-river-density COURSES-INCLUSIVE
// cost. The last zero-data-point cell: every courses-inclusive number
// measured so far (Cross-check 1 in the results doc, 18 courses/331,776
// cells) is at District spacing. Nothing has measured courses-on cost at the
// NEW deepest rung (chunk, 64m, per interview 2) or at Block (128m) — both
// below District, where a real river window would have MORE edges in view
// per unit area (finer spacing = smaller world extent per canvas, but a real
// river network's edge density near a river is roughly constant per unit
// ground area, so a narrower window can still contain a densely-braided
// stretch). Builds real InventedCourse fixtures via the actual PUBLIC
// invention pipeline (`river_course::build_edges` + `river_course::invent_course`
// — both `pub`, unlike `layer_proxy::invent_courses_near_window` itself,
// which is private to that module; this bench replicates its per-edge
// invention loop using the same public primitives, same discipline as
// `rect_window_replica` already replicates `build_district_window_layer`'s
// internals elsewhere in this file).
// ---------------------------------------------------------------------------
/// Build a high-density `InventedCourse` set from the REAL GJ1c river
/// network — every edge whose invented course falls within
/// `inflate_m` of the given world-metre window, at the given station
/// spacing/cutoff. This is the densest REAL course set available in this
/// repo (GJ1c is the only body with a river network already wired into a
/// bench fixture) rather than a synthetic worst case — real geometry is
/// preferred per this workshop's own measurement discipline (T-1178's cross-
/// check pattern: synthetic first, then confirm on real geometry). Returns
/// the course list plus the count found, so callers can report density
/// alongside cost.
fn build_gj1c_courses_near_window(
seed: SeedChain,
ta: &TerrainAnalysis,
params: &BodyParams,
river_network: &settled_reach_server::atlas::body_world_state::RiverNetwork,
win_x0: f64,
win_y0: f64,
win_x1: f64,
win_y1: f64,
station_spacing_m: f64,
min_wavelength_m: f64,
) -> Vec<InventedCourse> {
let edges = river_course::build_edges(river_network);
let r_km = params.body_radius_km.expect("body_radius_km required");
// Inline the same pixel->world-metres formula the existing GJ1c
// cross-check bench above already uses (district_profile::pixel_to_world_m
// is `pub(crate)`, not reachable from an integration test — this is the
// SAME formula, inlined, not a different one; consistent with how
// `bench_square_window_production_fn_gj1c_real_body_crosscheck` and
// zoom_ladder_bench.rs's `bench_course_cost_on_vs_off` already do this).
let to_world = |row: u16, col: u16| -> (f64, f64) {
(
col as f64 / ta.w as f64 * (std::f64::consts::TAU * r_km * 1000.0),
(row as f64 / (ta.h - 1) as f64 - 0.5) * (std::f64::consts::PI * r_km * 1000.0),
)
};
let mut courses = Vec::new();
for edge in &edges {
let anchor_a = to_world(edge.upstream.0, edge.upstream.1);
let anchor_b = to_world(edge.downstream.0, edge.downstream.1);
let chord_m =
((anchor_a.0 - anchor_b.0).powi(2) + (anchor_a.1 - anchor_b.1).powi(2)).sqrt();
// Same 0.08 inflation fraction layer_proxy.rs's COURSE_BBOX_INFLATION_FRACTION
// uses (that constant itself is private; the value is stated in its
// own doc and reproduced here for the same bbox-cull purpose — a
// bench-local approximation of the real cull, not a claim of exact
// production parity for the cull step itself, which doesn't affect
// measured PER-CELL cost once a course is in the list).
let inflate_m = chord_m * 0.08;
let (bx0, bx1) = (
anchor_a.0.min(anchor_b.0) - inflate_m,
anchor_a.0.max(anchor_b.0) + inflate_m,
);
let (by0, by1) = (
anchor_a.1.min(anchor_b.1) - inflate_m,
anchor_a.1.max(anchor_b.1) + inflate_m,
);
if bx1 < win_x0 || bx0 > win_x1 || by1 < win_y0 || by0 > win_y1 {
continue;
}
courses.push(river_course::invent_course(
seed,
edge,
ta,
params,
station_spacing_m,
min_wavelength_m,
));
}
courses
}
/// S2: courses-on vs courses-off, at BOTH the new deepest rung (chunk, 64m)
/// and Block (128m), over a window picked to maximize real river-edge
/// density (the densest real region in the only river-network fixture this
/// repo's benches have — GJ1c). Reports the delta as both absolute ms and
/// percentage, matching `bench_course_cost_on_vs_off`'s own reporting shape
/// (District's own courses-on-vs-off number: +0.09-0.21ms against a ~5ms
/// baseline, under 5%) so this fills in the two remaining zero-data-point
/// cells on the same comparison axis.
#[test]
#[ignore]
fn bench_s2_courses_density_at_chunk_and_block() {
let (hm, ta) = gj1c_fixture();
let dr = drainage::analyze(&hm.data, hm.width, hm.height, hm.sea_level);
let rn = dr.river_network.clone();
let params = bench_params();
let climate = ClimateConstants::default();
let seed = SeedChain::root(0xC0FFEE_u64).derive(SeedDomain::Body, 7);
assert!(
!rn.river_cells.is_empty(),
"GJ1c at production working resolution must have river cells for this bench to be meaningful"
);
// Find the highest-density river region: scan river cells and pick the
// one with the most OTHER river cells within a fixed pixel radius — a
// proxy for confluence/braided density, maximizing edges-per-window
// rather than picking an arbitrary river cell as the existing single
// cross-check bench does.
let radius_px = 8i64; // small radius = local confluence density, not just "near any river"
let mut best_cell = rn.river_cells[0];
let mut best_count = -1i64;
for &cell in &rn.river_cells {
let mut count = 0i64;
for &other in &rn.river_cells {
let dr_ = cell.0 as i64 - other.0 as i64;
let dc_ = cell.1 as i64 - other.1 as i64;
if dr_ * dr_ + dc_ * dc_ <= radius_px * radius_px {
count += 1;
}
}
if count > best_count {
best_count = count;
best_cell = cell;
}
}
let r_km = params.body_radius_km.expect("body_radius_km required");
let to_world = |row: u16, col: u16| -> (f64, f64) {
(
col as f64 / ta.w as f64 * (std::f64::consts::TAU * r_km * 1000.0),
(row as f64 / (ta.h - 1) as f64 - 0.5) * (std::f64::consts::PI * r_km * 1000.0),
)
};
let center_world = to_world(best_cell.0, best_cell.1);
println!("\n=== S2: courses-on vs courses-off density bench (chunk 64m + block 128m, densest GJ1c river region) ===");
println!(
" densest river cell: {best_cell:?} ({best_count} river cells within {radius_px}px radius), world center {center_world:?}"
);
for (label, step_m) in [
("chunk (64m)", scale::CHUNK_M as f64),
("block (128m)", scale::BLOCK_M as f64),
] {
// Realistic-shape window at this spacing: 64x64 cells (4,096, matching
// this file's other 4,096-cell sweeps for direct comparability).
let grid_side = 64u32;
let half_extent_m = (grid_side as f64 / 2.0) * step_m;
let win_x0 = center_world.0 - half_extent_m;
let win_x1 = center_world.0 + half_extent_m;
let win_y0 = center_world.1 - half_extent_m;
let win_y1 = center_world.1 + half_extent_m;
let courses = build_gj1c_courses_near_window(
seed, &ta, &params, &rn, win_x0, win_y0, win_x1, win_y1, step_m, step_m,
);
let total_points: usize = courses.iter().map(|c| c.points.len()).sum();
let avg_points = if courses.is_empty() {
0.0
} else {
total_points as f64 / courses.len() as f64
};
println!(
"\n --- {label}: {grid_side}x{grid_side} window ({:.0}m x {:.0}m), courses_in_window={}, avg_points_per_course={:.1} (station_spacing_m={step_m}) ---",
half_extent_m * 2.0,
half_extent_m * 2.0,
courses.len(),
avg_points
);
let n_cells = (grid_side * grid_side) as u64;
let iterations = 200; // higher rep count — a single 4,096-cell sweep is sub-10ms, noisy at n=1
// Courses OFF.
let t_off = Instant::now();
for _ in 0..iterations {
for row in 0..grid_side {
for col in 0..grid_side {
let wx = win_x0 + col as f64 * step_m;
let wy = win_y0 + row as f64 * step_m;
let prof =
derive_at_metres(seed, "GJ1c", &params, &ta, wx, wy, &climate, step_m, &[]);
std::hint::black_box(prof.elev_q);
}
}
}
let elapsed_off = t_off.elapsed();
// Courses ON.
let t_on = Instant::now();
for _ in 0..iterations {
for row in 0..grid_side {
for col in 0..grid_side {
let wx = win_x0 + col as f64 * step_m;
let wy = win_y0 + row as f64 * step_m;
let prof = derive_at_metres(
seed, "GJ1c", &params, &ta, wx, wy, &climate, step_m, &courses,
);
std::hint::black_box(prof.elev_q);
}
}
}
let elapsed_on = t_on.elapsed();
let ms_off_per_sweep = elapsed_off.as_secs_f64() * 1000.0 / iterations as f64;
let ms_on_per_sweep = elapsed_on.as_secs_f64() * 1000.0 / iterations as f64;
let delta_ms = ms_on_per_sweep - ms_off_per_sweep;
let delta_pct = 100.0 * delta_ms / ms_off_per_sweep;
let ns_per_cell_off =
elapsed_off.as_secs_f64() * 1e9 / (n_cells * iterations as u64) as f64;
let ns_per_cell_on = elapsed_on.as_secs_f64() * 1e9 / (n_cells * iterations as u64) as f64;
println!(" courses OFF: {ms_off_per_sweep:.4} ms/sweep ({ns_per_cell_off:.1} ns/cell)");
println!(" courses ON: {ms_on_per_sweep:.4} ms/sweep ({ns_per_cell_on:.1} ns/cell)");
println!(
" delta: {delta_ms:+.4} ms/sweep ({delta_pct:+.2}%), {} courses in window",
courses.len()
);
}
println!();
}