FI/TN LOD0-2 W12 worker CPU 13.9/12.2 s: passages 40/35%, far-shell refinement (HasDownwardHit/ HasUpwardHit ~1.7-1.9 s) in 'other' 27/30%, support 14/13%, dispatch 8%, base field 6%, bands 6%. Workers 28%/24% idle during LOD0-1. Near-ready W12: SW 0.77, FI 1.51, TN 1.38, VS 0.96 s. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016dhxM43dnWvUu2B5Ph1iG1
120 lines
4.7 KiB
Python
120 lines
4.7 KiB
Python
#!/usr/bin/env python3
|
|
"""Attribute FI/TN L77 sample PCs to current-source functions and work areas."""
|
|
from __future__ import annotations
|
|
|
|
import csv
|
|
import json
|
|
import re
|
|
import subprocess
|
|
from collections import Counter
|
|
from pathlib import Path
|
|
|
|
ROOT = Path(__file__).resolve().parents[4]
|
|
OUT = ROOT / "Saved/Linux/l103/census"
|
|
BINARY = ROOT / "Saved/Linux/l103/profile-bin/VoxelForgeCoreHeadless-l103-profile-clang"
|
|
SAMPLE = re.compile(
|
|
r"^\[L77Sample\] pc=(0x\w+) lod=(\d+) stage=(\S+) route=(\S+) "
|
|
r"phase=(\S+) halo=(\d+) weight=(\d+)$", re.M)
|
|
TARGETS = {
|
|
"sweep_base_prepare": "prepareSweptTubeBase",
|
|
"sweep_lookup": "sweptSample",
|
|
"exact_route_visit": "VisitNearestPassage",
|
|
"miss_reach_bound": "PassagePotentialWithinNode",
|
|
"far_shell_down_hit": "HasDownwardHit",
|
|
"far_shell_up_hit": "HasUpwardHit",
|
|
}
|
|
|
|
|
|
def load_samples() -> tuple[list[dict], set[str]]:
|
|
rows, pcs = [], set()
|
|
for stratum in ("FI", "TN"):
|
|
for replicate in (1, 2, 3):
|
|
log = OUT / f"{stratum.lower()}-r{replicate}.log"
|
|
content = log.read_text(encoding="utf-8", errors="replace")
|
|
for pc, lod, stage, route, phase, halo, weight in SAMPLE.findall(content):
|
|
if int(lod) > 2:
|
|
continue
|
|
pcs.add(pc)
|
|
rows.append({"stratum": stratum, "replicate": replicate, "pc": pc,
|
|
"lod": int(lod), "stage": stage, "route": route,
|
|
"phase": phase, "weight": int(weight)})
|
|
return rows, pcs
|
|
|
|
|
|
def symbolize(pcs: set[str]) -> dict[str, list[dict]]:
|
|
command = ["llvm-symbolizer-19", "--demangle", "--inlines", "--output-style=JSON",
|
|
f"--obj={BINARY}"]
|
|
result = subprocess.run(command, input="\n".join(sorted(pcs)) + "\n",
|
|
capture_output=True, text=True, check=True)
|
|
decoder = json.JSONDecoder()
|
|
offset = 0
|
|
output = {}
|
|
while offset < len(result.stdout):
|
|
while offset < len(result.stdout) and result.stdout[offset].isspace():
|
|
offset += 1
|
|
if offset == len(result.stdout):
|
|
break
|
|
item, offset = decoder.raw_decode(result.stdout, offset)
|
|
output[item["Address"]] = item.get("Symbol", [])
|
|
if output.keys() != pcs:
|
|
raise RuntimeError(f"symbolizer resolved {len(output)}/{len(pcs)} sampled PCs")
|
|
return output
|
|
|
|
|
|
def write_csv(path: Path, rows: list[dict]) -> None:
|
|
with path.open("w", encoding="utf-8", newline="") as stream:
|
|
writer = csv.DictWriter(stream, fieldnames=list(rows[0]))
|
|
writer.writeheader()
|
|
writer.writerows(rows)
|
|
|
|
|
|
def main() -> int:
|
|
if not BINARY.is_file():
|
|
raise SystemExit(f"missing profile binary: {BINARY}")
|
|
samples, pcs = load_samples()
|
|
symbols = symbolize(pcs)
|
|
function_totals = Counter()
|
|
target_totals = Counter()
|
|
for sample in samples:
|
|
frames = symbols[sample["pc"]]
|
|
top_function = frames[0].get("FunctionName", "?") if frames else "?"
|
|
function_totals[(sample["stratum"], sample["stage"], sample["phase"],
|
|
top_function)] += sample["weight"]
|
|
seen = set()
|
|
for name, token in TARGETS.items():
|
|
matches = [frame for frame in frames if token in frame.get("FunctionName", "")]
|
|
if not matches:
|
|
continue
|
|
frame = matches[0]
|
|
line = int(frame.get("Line", 0))
|
|
category = name
|
|
if name == "sweep_base_prepare":
|
|
category = ("sweep_prepare_base" if 14249 <= line <= 14435 else
|
|
"sweep_prepare_detail" if 14436 <= line <= 14521 else
|
|
"sweep_prepare_unlocated")
|
|
key = (sample["stratum"], sample["stage"], sample["phase"], category)
|
|
if key not in seen:
|
|
target_totals[key] += sample["weight"]
|
|
seen.add(key)
|
|
|
|
by_function = [
|
|
{"stratum": stratum, "stage": stage, "phase": phase,
|
|
"weighted_ticks": ticks, "top_function": function}
|
|
for (stratum, stage, phase, function), ticks in
|
|
sorted(function_totals.items(), key=lambda item: (item[0][:3], -item[1]))
|
|
]
|
|
by_target = [
|
|
{"stratum": stratum, "stage": stage, "phase": phase,
|
|
"work_area": target, "weighted_ticks": ticks}
|
|
for (stratum, stage, phase, target), ticks in sorted(target_totals.items())
|
|
]
|
|
write_csv(OUT / "symbol-function-totals.csv", by_function)
|
|
write_csv(OUT / "symbol-work-area-totals.csv", by_target)
|
|
print(f"[L103SymbolAnalysis] samples={len(samples)} unique_pcs={len(pcs)} "
|
|
f"functions={len(by_function)} work_areas={len(by_target)}")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|