#!/usr/bin/env python3 """Summarize per-tile cache counters from the five L137 paired timing runs.""" from __future__ import annotations import json import re import statistics from pathlib import Path ROOT = Path(__file__).resolve().parents[4] SOURCE = ROOT / "Saved/Linux/l137/timing/final-design1" OUT = ROOT / "Saved/Linux/l138/prechange/column-cache-counts.json" STAMPS = { "assembler-world": "4499fb2a8a4ede7d6cbc68d1b794f104658cdaae93ed9cacbc91fe7ff3fc3621", "l137": "7913d019d968de2dbaea2282b90fa8ff41b53f996c022e9e6fed92712cb40ecf", } FIELDS = re.compile(r"([a-zA-Z0-9_]+)=([^ ]+)") METRICS = ( "column_point_calls", "column_local_hits", "column_local_misses", "column_conflict_misses", "column_shared_hits", "column_formula_evaluations", ) def read_run(strate: int, workers: int, version: str, replicate: int) -> dict: path = SOURCE / f"s{strate}-w{workers}-{version}-measured{replicate}.log" if not path.is_file(): raise SystemExit(f"missing pre-change L137 run: {path}") text = path.read_text(encoding="utf-8", errors="replace") stamp = re.search(r"^\[VoxelForgeSourceStamp\] sha256=([0-9a-f]+)$", text, re.M) if stamp is None or stamp.group(1) != STAMPS[version]: raise SystemExit(f"unexpected source stamp in {path}") rows = {} for line in text.splitlines(): if not line.startswith("[WorldAssemblerLODCostDetail] "): continue fields = dict(FIELDS.findall(line)) rows[int(fields["lod"])] = fields if set(rows) != set(range(9)): raise SystemExit(f"incomplete LOD counters in {path}: {sorted(rows)}") return {"path": str(path.relative_to(ROOT)), "rows": rows} def summarize(runs: list[dict], lods: range) -> dict: per_run = [] for run in runs: selected = [run["rows"][lod] for lod in lods] tile_count = sum(int(row["tiles"]) for row in selected) per_run.append({ "tiles": tile_count, **{metric: sum(int(row[metric]) for row in selected) / tile_count for metric in METRICS}, }) return { "tiles_per_run": statistics.mean(row["tiles"] for row in per_run), "mean_per_tile": { metric: statistics.mean(row[metric] for row in per_run) for metric in METRICS }, "run_range_per_tile": { metric: [min(row[metric] for row in per_run), max(row[metric] for row in per_run)] for metric in METRICS }, } def main() -> int: result = { "source": "five measured L137 L80 runs per version, worker count, and stratum", "pre_change_stamps": STAMPS, "cells": [], } for strate in range(4): for workers in (8, 12): versions = { version: [read_run(strate, workers, version, replicate) for replicate in range(1, 6)] for version in STAMPS } item = {"strate": strate, "workers": workers} for window, lods in (("near_lod0_2", range(3)), ("whole_lod0_8", range(9))): item[window] = { version: summarize(runs, lods) for version, runs in versions.items() } item[window]["candidate_minus_baseline_per_tile"] = { metric: (item[window]["l137"]["mean_per_tile"][metric] - item[window]["assembler-world"]["mean_per_tile"][metric]) for metric in METRICS } item["logs"] = { version: [run["path"] for run in runs] for version, runs in versions.items() } result["cells"].append(item) OUT.parent.mkdir(parents=True, exist_ok=True) OUT.write_text(json.dumps(result, indent=2) + "\n", encoding="utf-8") print(f"[L138PrechangeCacheCounts] cells={len(result['cells'])} output={OUT.relative_to(ROOT)}") return 0 if __name__ == "__main__": raise SystemExit(main())