CoreNamespacePreamble.h applies per-compiler contraction-off pragmas (Clang, MSVC, GCC; unknown compiler = #error) and a fused-multiply-add self-test that World::compile runs before building. Clang -ffp-contract=on and GCC's default (fast) now reproduce the L1/Windows hashes (they diverged in L2); Clang -ffp-contract=fast ignores the pragma and is refused at startup (negative control). Seed sweep with every cube on real terrain: 48 seeds x 2 recipes x 3 builds x W1/W12, 576 runs, 0 mismatches, 0 empty meshes. Cost +0.5% (2.326 -> 2.337 s, interleaved). World unchanged. MSVC/UBT not compiled here: Windows to-do list in Linux3/REPORT.md. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
176 lines
9.5 KiB
Python
176 lines
9.5 KiB
Python
#!/usr/bin/env python3
|
|
"""Run the corrected L3 seed/compiler/worker matrix with per-seed mesh coverage."""
|
|
import csv
|
|
import re
|
|
import subprocess
|
|
import sys
|
|
import time
|
|
from pathlib import Path
|
|
|
|
ROOT = Path.cwd()
|
|
ROUND = ROOT / "Standalone/Research/WorldAssemblerM3/Linux3"
|
|
SEEDS = ROOT / "Standalone/Research/WorldAssemblerM3/Linux2/seeds.txt"
|
|
LOG_DIR = ROOT / "Saved/Linux"
|
|
MEASURE = ROOT / "Standalone/Research/WorldAssemblerM3/Linux2/MeasureProcess.py"
|
|
DATA = ROOT / "Standalone/data/owner_world.json"
|
|
BINARIES = {
|
|
"clang19-off": ROOT / "Standalone/bin/linux/VoxelForgeCoreHeadless-clang19-l3-off",
|
|
"clang19-on": ROOT / "Standalone/bin/linux/VoxelForgeCoreHeadless-clang19-l3-on",
|
|
"gcc14-default": ROOT / "Standalone/bin/linux/VoxelForgeCoreHeadless-gcc14-l3-default",
|
|
}
|
|
CAMERAS = {
|
|
"surface-world": "0,-35,-487.94,0,-22",
|
|
"floating-islands": "0,0,-32,0,-22",
|
|
}
|
|
HASH_RE = re.compile(
|
|
r"\[PipelineWholeViewHash\] tiles=(\d+) density=0x([0-9a-f]+) "
|
|
r"mesh=0x([0-9a-f]+) payload=0x([0-9a-f]+) topology=0x([0-9a-f]+)"
|
|
)
|
|
MESH_RE = re.compile(
|
|
r"\[PipelineMeshCoverage\] tiles=(\d+) nonempty_mesh_tiles=(\d+) indexed_triangles=(\d+)"
|
|
)
|
|
PLACEMENT_RE = re.compile(
|
|
r"\[SeedSurfacePlacement\].*camera_voxel=\(([-0-9.]+),([-0-9.]+),([-0-9.]+)\) status=PASS"
|
|
)
|
|
READY_RE = re.compile(r"\[StreamPipeline\].*whole_view_ready=([0-9.]+)s")
|
|
RESOURCE_RE = re.compile(
|
|
r"\[ProcessResources\] exit_code=(-?\d+) wall_s=([0-9.]+) peak_rss_kib=(\d+)"
|
|
)
|
|
|
|
|
|
def main() -> int:
|
|
if len(sys.argv) > 2:
|
|
raise SystemExit("usage: RunSeedSweep.py [budget-seconds; default 1800]")
|
|
budget = int(sys.argv[1]) if len(sys.argv) == 2 else 1800
|
|
seeds = [int(line.strip()) for line in SEEDS.read_text().splitlines() if line.strip()]
|
|
if len(seeds) != 48 or len(set(seeds)) != 48:
|
|
raise SystemExit("expected the 48 unique signed int32 L2 seeds")
|
|
missing = [str(path) for path in [MEASURE, DATA, *BINARIES.values()] if not path.is_file()]
|
|
if missing:
|
|
raise SystemExit("missing sweep input/build: " + ", ".join(missing))
|
|
|
|
LOG_DIR.mkdir(parents=True, exist_ok=True)
|
|
output_path = ROUND / "seed_mesh_coverage.csv"
|
|
fields = [
|
|
"seed", "recipe", "compiler_setting", "workers", "tiles",
|
|
"nonempty_mesh_tiles", "indexed_triangles", "camera_x_vox", "camera_y_vox", "camera_z_vox",
|
|
"density_hash", "mesh_hash", "payload_hash", "topology_hash",
|
|
"whole_view_ready_s", "wall_s", "peak_rss_kib", "exit_code", "status",
|
|
]
|
|
started = time.monotonic()
|
|
total = len(seeds) * len(CAMERAS) * len(BINARIES) * 2
|
|
print(f"L3 sweep: {len(seeds)} seeds x 2 recipes x 3 builds x W1/W12 = {total}; budget={budget}s", flush=True)
|
|
with output_path.open("w", newline="") as csv_file:
|
|
writer = csv.DictWriter(csv_file, fieldnames=fields)
|
|
writer.writeheader()
|
|
completed = 0
|
|
for seed in seeds:
|
|
for recipe, camera in CAMERAS.items():
|
|
for compiler, binary in BINARIES.items():
|
|
for workers in (1, 12):
|
|
remaining = budget - (time.monotonic() - started)
|
|
if remaining <= 0:
|
|
print("L3 sweep exceeded its 30-minute budget; stopping.", file=sys.stderr, flush=True)
|
|
return 124
|
|
stem = f"linux3-seed-{seed}-{recipe}-{compiler}-w{workers}"
|
|
log_path = LOG_DIR / f"{stem}.log"
|
|
args = [
|
|
str(binary), str(DATA), f"--seed={seed}", f"--recipe={recipe}",
|
|
"--render-distance-chunks=0", f"--workers={workers}",
|
|
"--world-assembler-prototype", "--stream-benchmark", "--stream-pipeline",
|
|
"--stream-hash", "--mesh-coverage", "--auto-surface-camera", "--force-generate",
|
|
"--clip-radius=1", "--max-clip-level=0", f"--camera={camera}",
|
|
"--far-horizon-lod8-step=0",
|
|
]
|
|
command = [
|
|
"taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--",
|
|
"python3", str(MEASURE), *args,
|
|
]
|
|
with log_path.open("w") as log_file:
|
|
log_file.write("[SweepCommand] " + " ".join(command) + "\n")
|
|
log_file.flush()
|
|
try:
|
|
result = subprocess.run(
|
|
command, cwd=ROOT, stdout=log_file, stderr=subprocess.STDOUT,
|
|
timeout=min(900, max(1, remaining)), check=False,
|
|
)
|
|
exit_code = result.returncode
|
|
except subprocess.TimeoutExpired:
|
|
exit_code = 124
|
|
log_file.write("\n[SweepTimeout] row reached its allotted limit\n")
|
|
|
|
output = log_path.read_text(errors="replace")
|
|
hashes = HASH_RE.search(output)
|
|
mesh = MESH_RE.search(output)
|
|
placement = PLACEMENT_RE.search(output)
|
|
ready = READY_RE.search(output)
|
|
resources = RESOURCE_RE.search(output)
|
|
passed = bool(
|
|
exit_code == 0 and hashes and mesh and placement and ready and resources
|
|
and int(hashes.group(1)) == 27 and int(mesh.group(1)) == 27
|
|
and int(mesh.group(2)) > 0 and int(mesh.group(3)) > 0
|
|
and int(resources.group(1)) == 0
|
|
)
|
|
row = {
|
|
"seed": seed, "recipe": recipe, "compiler_setting": compiler, "workers": workers,
|
|
"tiles": mesh.group(1) if mesh else "",
|
|
"nonempty_mesh_tiles": mesh.group(2) if mesh else "",
|
|
"indexed_triangles": mesh.group(3) if mesh else "",
|
|
"camera_x_vox": placement.group(1) if placement else "",
|
|
"camera_y_vox": placement.group(2) if placement else "",
|
|
"camera_z_vox": placement.group(3) if placement else "",
|
|
"density_hash": "0x" + hashes.group(2) if hashes else "",
|
|
"mesh_hash": "0x" + hashes.group(3) if hashes else "",
|
|
"payload_hash": "0x" + hashes.group(4) if hashes else "",
|
|
"topology_hash": "0x" + hashes.group(5) if hashes else "",
|
|
"whole_view_ready_s": ready.group(1) if ready else "",
|
|
"wall_s": resources.group(2) if resources else "",
|
|
"peak_rss_kib": resources.group(3) if resources else "",
|
|
"exit_code": exit_code, "status": "PASS" if passed else "FAIL",
|
|
}
|
|
writer.writerow(row)
|
|
csv_file.flush()
|
|
completed += 1
|
|
print(
|
|
f"[{completed}/{total}] seed={seed} recipe={recipe} build={compiler} W{workers} "
|
|
f"nonempty={row['nonempty_mesh_tiles']}/27 triangles={row['indexed_triangles']} "
|
|
f"wall={row['wall_s']}s status={row['status']} elapsed={time.monotonic()-started:.1f}s",
|
|
flush=True,
|
|
)
|
|
if not passed:
|
|
print(f"Failed row log: {log_path}", file=sys.stderr, flush=True)
|
|
return exit_code or 1
|
|
|
|
rows = list(csv.DictReader(output_path.open(newline="")))
|
|
hash_columns = ("density_hash", "mesh_hash", "payload_hash", "topology_hash")
|
|
compiler_checks = []
|
|
worker_checks = []
|
|
for seed in seeds:
|
|
for recipe in CAMERAS:
|
|
for workers in (1, 12):
|
|
pair = [r for r in rows if int(r["seed"]) == seed and r["recipe"] == recipe
|
|
and int(r["workers"]) == workers]
|
|
compiler_checks.append(len(pair) == len(BINARIES)
|
|
and all(all(row[key] == pair[0][key] for key in hash_columns) for row in pair))
|
|
for compiler in BINARIES:
|
|
pair = [r for r in rows if int(r["seed"]) == seed and r["recipe"] == recipe
|
|
and r["compiler_setting"] == compiler]
|
|
worker_checks.append(len(pair) == 2
|
|
and all(all(row[key] == pair[0][key] for key in hash_columns) for row in pair))
|
|
elapsed = time.monotonic() - started
|
|
maximum_rss = max(int(row["peak_rss_kib"]) for row in rows)
|
|
empty_rows = sum(int(row["nonempty_mesh_tiles"]) == 0 or int(row["indexed_triangles"]) == 0 for row in rows)
|
|
summary = (
|
|
f"runs={len(rows)} seeds={len(seeds)} recipes={len(CAMERAS)} builds={len(BINARIES)} workers=1,12 "
|
|
f"cross_build_checks={len(compiler_checks)} cross_build_mismatches={sum(not x for x in compiler_checks)} "
|
|
f"worker_checks={len(worker_checks)} worker_mismatches={sum(not x for x in worker_checks)} "
|
|
f"empty_mesh_rows={empty_rows} max_peak_rss_kib={maximum_rss} elapsed_s={elapsed:.3f}\n"
|
|
)
|
|
(ROUND / "seed_mesh_coverage_summary.txt").write_text(summary)
|
|
print(summary, end="", flush=True)
|
|
return 0 if all(compiler_checks) and all(worker_checks) and empty_rows == 0 and elapsed <= budget else 3
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|