Files
Raptor/test/PIM/test_QueueSelection.py
T

146 lines
8.3 KiB
Python

"""Production selection must use static compiler inputs only."""
import argparse
import hashlib
import json
import os
from pathlib import Path
import shutil
import subprocess
import tempfile
REPO = Path(__file__).resolve().parents[2]
def check_selected_program(out):
report = json.loads((out / "reports/pipeline_selection.json").read_text())
program = json.loads((out / "pim/config.json").read_text())
digest = hashlib.sha256()
for core in range(program["core_cnt"]):
digest.update((out / f"pim/core_{core}.pim").read_bytes())
for count in program["array_group_map"][f"core{core}"]:
digest.update(f"{count},".encode())
digest.update(b";")
assert digest.hexdigest().upper() == report["candidates"][report["selection"]]["identity"], \
"emitted program differs from the analyzed winner"
assert report["reused_lowered_candidate"]
for name in ("spatial3_scheduled_no_comm.txt", "memory_report.txt"):
assert (out / "reports" / name).stat().st_size > 0, name
return report
def main():
parser = argparse.ArgumentParser()
parser.add_argument("--compiler", required=True)
args = parser.parse_args()
compiler = str(Path(args.compiler).resolve())
model = REPO / "validation/networks/pimcomp_models/googlenet/googlenet-12-pimsim-nn.onnx"
with tempfile.TemporaryDirectory(prefix="raptor-static-selection-") as temporary:
root = Path(temporary)
history = root / "validation/tools/pim/pimcomp/compare/results_comparison.csv"
history.parent.mkdir(parents=True)
profile = root / "profiles/latency.json"
profile.parent.mkdir()
trap = root / "bin"
trap.mkdir()
for name in ("ChipTest", "pimsim", "raptor-periodic", "perf"):
path = trap / name
path.write_text("#!/bin/sh\necho forbidden execution >> '" + str(root / "executed") + "'\nexit 99\n")
path.chmod(0o755)
guard = root / "static-audit.so"
subprocess.run([os.environ.get("CC", "cc"), "-shared", "-fPIC",
str(REPO / "test/PIM/StaticSchedulingAudit.c"), "-o", str(guard)], check=True)
env = dict(os.environ, LD_PRELOAD=str(guard), PATH=str(trap) + os.pathsep + os.environ["PATH"], OMP_NUM_THREADS="1")
decisions = []
for iteration in range(3):
if iteration == 0:
history.write_text("network,throughput\ngooglenet,1e99\n")
profile.write_text('{"candidate":999,"measured_latency":1}')
elif iteration == 1:
history.write_bytes(b"invalid historical data\x00")
profile.write_text('{"candidate":0,"measured_latency":1e99}')
else:
history.unlink()
shutil.rmtree(profile.parent)
out = root / f"run-{iteration}"
out.mkdir()
command = [compiler, str(model), "-o", str(out / "model"), "--maccel=PIM",
"--EmitPimCodegen", "--core-count=138", "--crossbar-count=128", "--crossbar-size=128",
"--pipeline=4", "--mlir-disable-threading", "--pim-detect-communication-deadlock",
f"--pim-target-config={REPO}/validation/pimsim_configs/pimcomp/arch-b/throughput_config_1000ms.json"]
result = subprocess.run(command, cwd=root, env=env, capture_output=True, text=True, timeout=180)
assert result.returncode == 0, result.stdout + result.stderr
report = json.loads((out / "reports/pipeline_selection.json").read_text())
assert report["static_analysis"] and report["temporary_disk_bytes"] == 0
assert report["analyzed_candidates"] > 1, report
check_selected_program(out)
assert not report["reconstructed_spatial_artifacts"]
assert not (out / "dialects").exists()
assert not list(out.glob("candidate*"))
decisions.append((report["selection"], [(c["identity"], c.get("interval_ns"), c.get("alternative_tie_interval_ns"))
for c in report["candidates"]]))
# Keep the test's disk peak to one selected program.
shutil.rmtree(out)
assert decisions[0] == decisions[1] == decisions[2], "historical files or address allocation changed static selection"
# Exercise the same winner with and without explicit Spatial artifacts.
vgg = REPO / "validation/networks/pimcomp_models/vgg8/vgg8-mnist-reconstructed.onnx"
for diagnostics in (False, True):
out = root / f"vgg-{diagnostics}"
out.mkdir()
command = [compiler, str(vgg), "-o", str(out / "model"), "--maccel=PIM", "--EmitPimCodegen",
"--pipeline=2", "--core-count=138", "--crossbar-count=128", "--crossbar-size=128",
"--mlir-disable-threading", "--pim-detect-communication-deadlock",
f"--pim-target-config={REPO}/validation/pimsim_configs/pimcomp/arch-b/throughput_config_1000ms.json"]
if diagnostics:
command += ["--pim-export-spatial-dataflow=all", "--pim-dump-ir", "--pim-periodic-diagnostics"]
result = subprocess.run(command, cwd=root, env=env, capture_output=True, text=True, timeout=90)
assert result.returncode == 0, result.stdout + result.stderr
report = check_selected_program(out)
assert report["selection"] != 0 and report["reconstructed_spatial_artifacts"] == diagnostics
identity = report["candidates"][report["selection"]]["identity"]
schedule = (out / "reports/spatial3_scheduled_no_comm.txt").read_bytes()
if diagnostics:
assert identity == previous_identity and schedule == previous_schedule
for stage in ("spatial3_scheduled_no_comm", "spatial4_scheduled"):
for suffix in ("nodes.csv", "edges.csv"):
assert (out / f"reports/{stage}.{suffix}").stat().st_size > 0
assert (out / "reports/pipeline_periodic.json").exists()
assert (out / "dialects/spatial4_scheduled.mlir").exists()
previous_identity, previous_schedule = identity, schedule
shutil.rmtree(out)
config_path = REPO / "validation/pimsim_configs/pimcomp/arch-a/throughput_config_1000ms.json"
unsupported = json.loads(config_path.read_text())
unsupported["chip_config"]["core_config"]["rob_size"] = 2
network = unsupported["chip_config"]["network_config"]
network["net_config_file_path"] = str((config_path.parent / network["net_config_file_path"]).resolve())
hardware = root / "unsupported-target.json"
hardware.write_text(json.dumps(unsupported))
out = root / "fallback"
out.mkdir()
command = [compiler, str(REPO / "validation/networks/pimcomp_models/vgg8/vgg8-mnist-reconstructed.onnx"),
"-o", str(out / "model"), "--maccel=PIM", "--EmitPimCodegen", "--pipeline=4",
"--core-count=168", "--crossbar-count=96", "--crossbar-size=128", "--mlir-disable-threading",
f"--pim-target-config={hardware}"]
result = subprocess.run(command, cwd=root, env=env, capture_output=True, text=True, timeout=60)
assert result.returncode == 0, result.stdout + result.stderr
report = json.loads((out / "reports/pipeline_selection.json").read_text())
assert report["selection"] == 0 and report["analyzed_candidates"] == 0
assert report["generated_candidates"] == 1 and report["lowered_candidates"] == 0
assert "ROB capacity one" in report["reason"] and report["unavailable_estimates"] > 0
assert report["temporary_disk_bytes"] == 0 and not list(root.glob("candidate*"))
shutil.rmtree(out)
out.mkdir()
command[-1] = f"--pim-target-config={config_path}"
command += ["--pim-disable-synchronization"]
result = subprocess.run(command, cwd=root, env=env, capture_output=True, text=True, timeout=60)
assert result.returncode == 0, result.stdout + result.stderr
report = json.loads((out / "reports/pipeline_selection.json").read_text())
assert report["selection"] == 0 and report["analyzed_candidates"] == 0
assert report["generated_candidates"] == 1 and report["lowered_candidates"] == 0
assert "synchronization ablation" in report["reason"]
assert not (root / "executed").exists(), "production compiler executed a diagnostic or simulator"
if __name__ == "__main__":
main()