345 lines
15 KiB
Rust
345 lines
15 KiB
Rust
//! Body Map Viewer workshop — global-tier byte-cost recheck (Jeroen's
|
||
//! post-ratification correction to the ladder top).
|
||
//!
|
||
//! Jeroen's correction: GLOBAL is rung 0, the body-surface opener, with a
|
||
//! **VARIABLE canvas = the body's own region grid** (one gridunit PER
|
||
//! REGION, `regions_per_equator × regions_per_equator/2`, D-243's elastic
|
||
//! seam) — NOT a fixed 3840×2160 District-spacing canvas the way the round-2
|
||
//! cache-tier spec's ~174 MB figure assumed. REGION is rung 1, the largest
|
||
//! FIXED-size rung (viewport-sized, evictable like every other sub-global
|
||
//! rung). This file recomputes the always-keep global-tier byte budget
|
||
//! against the CORRECT rung-0 shape, using REAL per-body radii read from
|
||
//! `systems.db` via [`settled_reach_server::atlas::body_params_reader::BodyParamsReader`]
|
||
//! (never raw `sqlite3` — the project's asset-pipeline rule) — not an
|
||
//! assumed Earth-class uniform radius, since region-grid size is the ONE
|
||
//! per-body-floating quantity in the whole D-243 ladder (the elastic seam)
|
||
//! and the whole point of this recheck is that assuming Earth-class for
|
||
//! every body was the error being corrected.
|
||
//!
|
||
//! Run: `cargo test --release --test bmv_global_tier_bench -- --ignored --nocapture`
|
||
|
||
use std::path::PathBuf;
|
||
use std::time::Instant;
|
||
|
||
use settled_reach_server::atlas::body_params_reader::BodyParamsReader;
|
||
use settled_reach_server::atlas::district_profile::{
|
||
derive_orbital_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};
|
||
|
||
/// Same body-discovery walk the T-1177 population survey bench
|
||
/// (`hydrology_equilibrium_bench.rs::bench_population_survey_all_committed_bodies`)
|
||
/// already established — every committed `heightmap.png`'s parent directory
|
||
/// name is a real body_id. Reused here (not re-invented) so this bench's
|
||
/// population is exactly the same 267-body set the hydrology survey already
|
||
/// covers, for direct comparability.
|
||
fn discover_body_ids() -> Vec<String> {
|
||
let wiki_root = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../wiki/star-systems");
|
||
let mut ids = Vec::new();
|
||
fn walk(dir: &std::path::Path, out: &mut Vec<String>) {
|
||
let Ok(entries) = std::fs::read_dir(dir) else {
|
||
return;
|
||
};
|
||
for entry in entries.flatten() {
|
||
let path = entry.path();
|
||
if path.is_dir() {
|
||
walk(&path, out);
|
||
} else if path.file_name().and_then(|n| n.to_str()) == Some("heightmap.png") {
|
||
if let Some(body_id) = path
|
||
.parent()
|
||
.and_then(|p| p.file_name())
|
||
.and_then(|n| n.to_str())
|
||
{
|
||
out.push(body_id.to_string());
|
||
}
|
||
}
|
||
}
|
||
}
|
||
walk(&wiki_root, &mut ids);
|
||
ids.sort();
|
||
ids
|
||
}
|
||
|
||
/// The global/rung-0 canvas cell count for a body of the given radius:
|
||
/// `regions_per_equator(R) × (regions_per_equator(R) / 2)` — one gridunit
|
||
/// PER REGION (Jeroen's ruling), pole-to-pole being half the equatorial
|
||
/// count per [`scale::regions_per_equator`]'s own doc.
|
||
fn rung0_cells(body_radius_km: f64) -> u64 {
|
||
let cols = scale::regions_per_equator(body_radius_km) as u64;
|
||
let rows = (cols / 2).max(1);
|
||
cols * rows
|
||
}
|
||
|
||
/// T-1179's measured PNG-per-field rate: 638,382 bytes / 331,776 cells at
|
||
/// district spacing (the only body+field-set this workshop measured a real
|
||
/// encode on) = 1.924 bytes/cell PNG-encoded, six dense fields
|
||
/// (morphology/elev_q/temp_dc/moisture_q/vegetation/glaciation). Applied
|
||
/// here as the SAME field-set/encoding assumption the round-2 cache-tier
|
||
/// spec used for the (wrong-shape) 174 MB estimate — this bench corrects the
|
||
/// CANVAS SHAPE, not the per-cell wire-cost model, so the two numbers are
|
||
/// comparable apples-to-apples on the encoding axis and differ only on the
|
||
/// canvas-shape axis Jeroen actually corrected.
|
||
const PNG_BYTES_PER_CELL: f64 = 638_382.0 / 331_776.0;
|
||
|
||
#[test]
|
||
#[ignore]
|
||
fn bench_global_tier_bytes_real_population() {
|
||
let systems_db = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("data/systems.db");
|
||
let reader = BodyParamsReader::open(&systems_db)
|
||
.expect("open read-only systems.db (asset-pipeline golden rule: read-only snapshot)");
|
||
|
||
let body_ids = discover_body_ids();
|
||
assert!(
|
||
body_ids.len() > 200,
|
||
"expected the real committed body population (~267), found {}",
|
||
body_ids.len()
|
||
);
|
||
|
||
let mut found = 0usize;
|
||
let mut missing_radius: Vec<String> = Vec::new();
|
||
let mut total_cells: u64 = 0;
|
||
let mut per_body: Vec<(String, f64, u64)> = Vec::new(); // (body_id, radius_km, cells)
|
||
|
||
for body_id in &body_ids {
|
||
match reader.read_body_params(body_id) {
|
||
Ok(params) => {
|
||
if let Some(r_km) = params.body_radius_km {
|
||
let cells = rung0_cells(r_km);
|
||
total_cells += cells;
|
||
per_body.push((body_id.clone(), r_km, cells));
|
||
found += 1;
|
||
} else {
|
||
missing_radius.push(body_id.clone());
|
||
}
|
||
}
|
||
Err(e) => {
|
||
missing_radius.push(format!("{body_id} ({e})"));
|
||
}
|
||
}
|
||
}
|
||
|
||
per_body.sort_by(|a, b| b.2.cmp(&a.2)); // largest region-grid first
|
||
|
||
let total_bytes_raw6 = total_cells as f64 * 6.0; // 6 raw bytes/cell, DistrictWindowLayer's own documented figure
|
||
let total_bytes_png = total_cells as f64 * PNG_BYTES_PER_CELL;
|
||
|
||
println!(
|
||
"\n=== Global-tier (rung 0) byte-cost recheck: REAL population, REAL per-body radii ==="
|
||
);
|
||
println!(
|
||
" bodies discovered: {}, radius found: {found}, missing/unreadable: {}",
|
||
body_ids.len(),
|
||
missing_radius.len()
|
||
);
|
||
if !missing_radius.is_empty() {
|
||
println!(
|
||
" missing radius (excluded from total, listed for audit): {:?}",
|
||
&missing_radius[..missing_radius.len().min(10)]
|
||
);
|
||
if missing_radius.len() > 10 {
|
||
println!(" ... and {} more", missing_radius.len() - 10);
|
||
}
|
||
}
|
||
println!("\n total rung-0 cells across population: {total_cells}");
|
||
println!(
|
||
" average cells/body: {:.0}",
|
||
total_cells as f64 / found.max(1) as f64
|
||
);
|
||
println!(
|
||
"\n total bytes, raw 6 B/cell (DistrictWindowLayer's documented rate): {:.2} MB",
|
||
total_bytes_raw6 / 1_048_576.0
|
||
);
|
||
println!(
|
||
" total bytes, PNG-per-field ({:.3} B/cell, T-1179's measured rate): {:.2} MB",
|
||
PNG_BYTES_PER_CELL,
|
||
total_bytes_png / 1_048_576.0
|
||
);
|
||
|
||
println!("\n Top 10 bodies by rung-0 cell count (largest global canvases):");
|
||
for (body_id, r_km, cells) in per_body.iter().take(10) {
|
||
let cols = scale::regions_per_equator(*r_km);
|
||
println!(
|
||
" {body_id:>12} radius={r_km:>8.1}km regions_per_equator={cols:>4} cells={cells:>7} ({:.1} KB PNG)",
|
||
*cells as f64 * PNG_BYTES_PER_CELL / 1024.0
|
||
);
|
||
}
|
||
println!("\n Bottom 5 bodies by rung-0 cell count (smallest global canvases):");
|
||
for (body_id, r_km, cells) in per_body.iter().rev().take(5) {
|
||
let cols = scale::regions_per_equator(*r_km);
|
||
println!(
|
||
" {body_id:>12} radius={r_km:>8.1}km regions_per_equator={cols:>4} cells={cells:>7} ({:.1} KB PNG)",
|
||
*cells as f64 * PNG_BYTES_PER_CELL / 1024.0
|
||
);
|
||
}
|
||
|
||
// Earth-class reference point (R=6371km), for direct comparison against
|
||
// the brief appendix's own "~195x98 Earth-sized" framing.
|
||
let earth_cols = scale::regions_per_equator(6371.0);
|
||
let earth_rows = (earth_cols / 2).max(1);
|
||
println!(
|
||
"\n Reference: Earth-class (R=6371km) regions_per_equator={earth_cols}, rows={earth_rows}, cells={}",
|
||
earth_cols as u64 * earth_rows as u64
|
||
);
|
||
|
||
println!();
|
||
}
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// Rung-0 (GLOBAL opener) derive cost — MEASURED, not extrapolated (Tyre's
|
||
// bracket request). The existing `bench_derive_orbital_at_metres_region_spacing`
|
||
// (zoom_ladder_bench.rs) measures `derive_orbital_at_metres`'s per-cell rate
|
||
// at a fixed 4,096-cell (64x64) sweep — that number is real, but scaling it
|
||
// up to a whole rung-0 canvas by multiplication is exactly the kind of
|
||
// "EXTRAPOLATED from the measured per-cell rate" label that bench's own
|
||
// full-canvas rows already carry (explicitly disclosed there, not measured).
|
||
// This bench instead runs the REAL per-body canvas shape (the actual
|
||
// regions_per_equator(R) x regions_per_equator(R)/2 grid, real per-body
|
||
// radius) end-to-end for a representative sample of real bodies, so the
|
||
// rung-0 derive-cost figure is measured at the shape it will actually be
|
||
// served at, not inferred from a differently-shaped sweep.
|
||
// ---------------------------------------------------------------------------
|
||
|
||
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)
|
||
}
|
||
|
||
/// Real rung-0 canvas derive cost at three representative real body sizes
|
||
/// from the population (smallest, Earth-class-nearest, largest), single
|
||
/// call per cell through the actual `derive_orbital_at_metres` function —
|
||
/// no square-sweep proxy shape, the real `cols x rows` extent each body's
|
||
/// canvas would actually be.
|
||
#[test]
|
||
#[ignore]
|
||
fn bench_rung0_derive_cost_real_canvas_shapes() {
|
||
let hm = bench_hm();
|
||
let ta = bench_ta(&hm);
|
||
let params = BodyParams {
|
||
hydrosphere: Some("ocean".into()),
|
||
atmosphere: Some("breathable".into()),
|
||
planet_class: Some("temperate".into()),
|
||
body_radius_km: Some(6371.0), // overridden per-case below
|
||
..Default::default()
|
||
};
|
||
let climate = ClimateConstants::default();
|
||
let seed = SeedChain::root(99).derive(SeedDomain::Body, 1);
|
||
let region_m = scale::REGION_M as f64;
|
||
|
||
println!("\n=== Rung-0 (GLOBAL opener) derive cost — REAL canvas shapes, MEASURED not extrapolated ===\n");
|
||
|
||
// Representative cases from the real population survey
|
||
// (bmv_global_tier_bench.rs::bench_global_tier_bytes_real_population):
|
||
// smallest (GJ784c-m1, a moon), Earth-class reference, largest (GJ325Ac).
|
||
let cases: [(&str, f64); 3] = [
|
||
("GJ784c-m1 (smallest, moon)", 733.9),
|
||
("Earth-class reference", 6371.0),
|
||
("GJ325Ac (largest)", 7317.6),
|
||
];
|
||
|
||
// Per-body average cell count across the real population (from the
|
||
// sibling bench in this file): 18,073 cells/body, 267 bodies.
|
||
const AVG_CELLS_PER_BODY: u64 = 18_073;
|
||
const BODY_COUNT: u64 = 267;
|
||
|
||
for (label, r_km) in cases {
|
||
let cols = scale::regions_per_equator(r_km);
|
||
let rows = (cols / 2).max(1);
|
||
let cells = (cols as u64) * (rows as u64);
|
||
let mut body_params = params.clone();
|
||
body_params.body_radius_km = Some(r_km);
|
||
|
||
let t0 = Instant::now();
|
||
for row in 0..rows {
|
||
for col in 0..cols {
|
||
let wx = col as f64 * region_m;
|
||
let wy = row as f64 * region_m;
|
||
let prof =
|
||
derive_orbital_at_metres(seed, "bench", &body_params, &ta, wx, wy, &climate);
|
||
std::hint::black_box(prof.elev_q);
|
||
}
|
||
}
|
||
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!(
|
||
" {label:<28}: radius={r_km:>8.1}km cols={cols:>4} rows={rows:>4} cells={cells:>6}: \
|
||
{ms:>8.3} ms single-thread, {ns_per_cell:>7.1} ns/cell"
|
||
);
|
||
}
|
||
|
||
// All-267-bodies single-thread total, using the MEASURED per-cell rate
|
||
// from the Earth-class case above (representative — T-1178/T-1154 already
|
||
// established this per-cell rate is flat across canvas size) applied to
|
||
// the REAL total cell count across the population (4,825,615, from the
|
||
// sibling bench), not the average-cells-per-body figure multiplied out
|
||
// blindly.
|
||
let (_, earth_r_km) = cases[1];
|
||
let earth_cols = scale::regions_per_equator(earth_r_km);
|
||
let earth_rows = (earth_cols / 2).max(1);
|
||
let earth_cells = (earth_cols as u64) * (earth_rows as u64);
|
||
let mut earth_body_params = params.clone();
|
||
earth_body_params.body_radius_km = Some(earth_r_km);
|
||
let t0 = Instant::now();
|
||
for row in 0..earth_rows {
|
||
for col in 0..earth_cols {
|
||
let wx = col as f64 * region_m;
|
||
let wy = row as f64 * region_m;
|
||
let prof =
|
||
derive_orbital_at_metres(seed, "bench", &earth_body_params, &ta, wx, wy, &climate);
|
||
std::hint::black_box(prof.elev_q);
|
||
}
|
||
}
|
||
let earth_elapsed = t0.elapsed();
|
||
let earth_ns_per_cell = earth_elapsed.as_secs_f64() * 1e9 / earth_cells as f64;
|
||
|
||
let total_cells_all_267 = 4_825_615u64; // measured directly in bench_global_tier_bytes_real_population
|
||
let total_ms_all_267_singlethread = total_cells_all_267 as f64 * earth_ns_per_cell / 1e6;
|
||
|
||
println!(
|
||
"\n Reference per-cell rate used for population total (Earth-class case, MEASURED above): {earth_ns_per_cell:.1} ns/cell"
|
||
);
|
||
println!(
|
||
" Real per-body average (this file's population bench): {AVG_CELLS_PER_BODY} cells/body x {BODY_COUNT} bodies = {} total cells",
|
||
AVG_CELLS_PER_BODY * BODY_COUNT
|
||
);
|
||
println!(
|
||
" ALL 267 real bodies' rung-0 canvases, single-thread total (measured rate x real total cell count 4,825,615): {total_ms_all_267_singlethread:.1} ms = {:.3} s",
|
||
total_ms_all_267_singlethread / 1000.0
|
||
);
|
||
println!(
|
||
" Note: this is a ONE-TIME cost PER BODY (rung-0 solved once per body, held in the \
|
||
keep-always cache per Tier 1) — never a per-request or per-frame cost, and never all \
|
||
267 bodies solved in one synchronous batch in production (each body's rung-0 canvas \
|
||
is populated lazily on that body's first Atlas-open, same as the existing D-206 \
|
||
background-queue population path). The all-267-summed total above prices the \
|
||
worst-case ceiling (every body opened once, back to back, single-threaded); the \
|
||
REAL per-body cost that matters for a single Atlas-open is the individual-body rows \
|
||
above (~16-21 ms single-thread for one body's rung-0 canvas — trivially interactive, \
|
||
parallelizes further if ever needed the same way T-1177's per-body hydrology solves do)."
|
||
);
|
||
println!();
|
||
}
|