wip(assembler): round L3 -- Core forces FP contraction off and refuses to generate if it can't
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>
This commit is contained in:
1 parent
303b7b9f24
commit
046f2af495
10 files changed
+1051
-2
No files matched your search
@@ -0,0 +1,175 @@
|
||||
#!/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())
|
||||
Reference in new issue
Block a user