better synchronization
better deadlock detection to also track wait/sync
This commit is contained in:
@@ -16,6 +16,7 @@ import sys
|
||||
import time
|
||||
import types
|
||||
from collections import Counter
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
from dataclasses import asdict, dataclass
|
||||
from pathlib import Path
|
||||
from tempfile import TemporaryDirectory
|
||||
@@ -242,7 +243,7 @@ def reference_inputs_exist(
|
||||
|
||||
|
||||
def reference_batch_dirs(root: Path, batch_size: int) -> list[Path]:
|
||||
return [root / f"batch_{index:06d}" for index in range(batch_size)]
|
||||
return [root / f"batch_{index:06d}/outputs" for index in range(batch_size)]
|
||||
|
||||
|
||||
def reference_batch_outputs_exist(
|
||||
@@ -442,6 +443,7 @@ def generate_reference_batch_outputs(
|
||||
runner_build_dir: Path,
|
||||
model_path: Path,
|
||||
input_batch: list[list[np.ndarray]],
|
||||
outputs_desc: list[tuple[int, str, int, list[int]]],
|
||||
steps: list[StepRecord],
|
||||
args: argparse.Namespace,
|
||||
out_dir: Path,
|
||||
@@ -450,20 +452,26 @@ def generate_reference_batch_outputs(
|
||||
) -> list[Path]:
|
||||
if print_header:
|
||||
print_step("Run reference")
|
||||
references = []
|
||||
for index, sample in enumerate(input_batch):
|
||||
references.append(
|
||||
generate_reference_outputs(
|
||||
runner_path,
|
||||
runner_build_dir,
|
||||
model_path,
|
||||
sample,
|
||||
steps,
|
||||
args,
|
||||
out_dir / f"batch_{index:06d}",
|
||||
print_header=False,
|
||||
)
|
||||
references = reference_batch_dirs(out_dir, len(input_batch))
|
||||
missing = [
|
||||
index for index, reference in enumerate(references)
|
||||
if not reference_outputs_exist(outputs_desc, reference)
|
||||
]
|
||||
|
||||
def generate(index: int) -> None:
|
||||
generate_reference_outputs(
|
||||
runner_path,
|
||||
runner_build_dir,
|
||||
model_path,
|
||||
input_batch[index],
|
||||
steps,
|
||||
args,
|
||||
out_dir / f"batch_{index:06d}",
|
||||
print_header=False,
|
||||
)
|
||||
|
||||
with ThreadPoolExecutor(max_workers=args.jobs) as executor:
|
||||
list(executor.map(generate, missing))
|
||||
return references
|
||||
|
||||
|
||||
@@ -490,6 +498,7 @@ def prepare_reference_batch_outputs(
|
||||
runner_build_dir,
|
||||
model_path,
|
||||
input_batch,
|
||||
outputs_desc,
|
||||
steps,
|
||||
args,
|
||||
out_dir,
|
||||
@@ -512,16 +521,29 @@ def prepare_common_artifacts(
|
||||
outputs_ready = reference_outputs_exist(outputs_desc, outputs_dir)
|
||||
if inputs_ready:
|
||||
arrays_in_order = load_saved_inputs(inputs_desc, inputs_dir)
|
||||
input_batch = generate_input_batch(
|
||||
inputs_desc, arrays_in_order, args.batch_size, args.seed)
|
||||
batch_dir = common_dir / f"reference_seed_{args.seed}"
|
||||
first_batch_dir = batch_dir / "batch_000000"
|
||||
if inputs_ready and outputs_ready and not reference_outputs_exist(
|
||||
outputs_desc, first_batch_dir / "outputs"
|
||||
):
|
||||
shutil.copytree(inputs_dir, first_batch_dir / "inputs", dirs_exist_ok=True)
|
||||
shutil.copytree(outputs_dir, first_batch_dir / "outputs", dirs_exist_ok=True)
|
||||
references = prepare_reference_batch_outputs(
|
||||
runner_path,
|
||||
runner_path.parent,
|
||||
model_path,
|
||||
input_batch,
|
||||
outputs_desc,
|
||||
steps,
|
||||
args,
|
||||
batch_dir,
|
||||
)
|
||||
if not (inputs_ready and outputs_ready):
|
||||
generate_reference_outputs(
|
||||
runner_path,
|
||||
runner_path.parent,
|
||||
model_path,
|
||||
arrays_in_order,
|
||||
steps,
|
||||
args,
|
||||
common_dir,
|
||||
)
|
||||
shutil.copytree(
|
||||
references[0].parent / "inputs", inputs_dir, dirs_exist_ok=True)
|
||||
shutil.copytree(references[0], outputs_dir, dirs_exist_ok=True)
|
||||
def compile_raptor_target(
|
||||
model_path: Path,
|
||||
out_dir: Path,
|
||||
@@ -1409,7 +1431,14 @@ def main():
|
||||
parser.add_argument("--mesh-cols", type=int)
|
||||
parser.add_argument("--pimsim-time-ms", type=int, default=1000)
|
||||
parser.add_argument("--pimsim-mode", choices=["latency", "throughput"], default="latency")
|
||||
parser.add_argument("--batch-size", type=int, default=128)
|
||||
parser.add_argument("--batch-size", type=int, default=64)
|
||||
parser.add_argument(
|
||||
"-j",
|
||||
"--jobs",
|
||||
type=int,
|
||||
default=4,
|
||||
help="Maximum parallel native reference runner processes (default: 4).",
|
||||
)
|
||||
parser.add_argument("--pimcomp-pipeline", choices=["element", "batch"])
|
||||
parser.add_argument("--pimcomp-model-name", help="Use a Pimcomp built-in model name such as vgg16.")
|
||||
parser.add_argument(
|
||||
@@ -1466,6 +1495,8 @@ def main():
|
||||
parser.error("--pimsim-time-ms must be positive")
|
||||
if args.batch_size <= 0:
|
||||
parser.error("--batch-size must be positive")
|
||||
if args.jobs <= 0:
|
||||
parser.error("--jobs must be positive")
|
||||
if args.pimsim_mode == "throughput" and args.batch_size < 2:
|
||||
parser.error("throughput mode requires batch size greater than 1")
|
||||
if args.timeout_seconds < 0:
|
||||
@@ -1674,7 +1705,7 @@ def main():
|
||||
write_input_batch_csv(out_dir / "inputs.csv", input_batch)
|
||||
raptor_input_bins = write_input_batch_binaries(input_batch, out_dir / "simulation/raptor_inputs")
|
||||
if args.pimsim_mode == "throughput":
|
||||
batch_reference_dir = common_dir / f"reference_batch_{args.batch_size}_seed_{args.seed}"
|
||||
batch_reference_dir = common_dir / f"reference_seed_{args.seed}"
|
||||
throughput_references = try_stage(
|
||||
failures,
|
||||
"Run reference",
|
||||
|
||||
@@ -261,23 +261,9 @@ def performance_values(performance: dict) -> dict[str, float | None]:
|
||||
}
|
||||
|
||||
|
||||
def comparison_passed(report: dict, compiler: str | None = None) -> bool:
|
||||
other_compiler = "PIMCOMP" if compiler == "raptor" else "RAPTOR"
|
||||
if any(
|
||||
compiler is None or other_compiler not in failure.get("stage", "").upper()
|
||||
for failure in report.get("failures", [])
|
||||
):
|
||||
return False
|
||||
compilers = (compiler,) if compiler is not None else ("raptor", "pimcomp")
|
||||
for name in compilers:
|
||||
result = report.get(f"{name}_validation") or {}
|
||||
if result.get("status") != "done" or not result.get("passed"):
|
||||
return False
|
||||
for name in compilers:
|
||||
performance = report.get(f"{name}_performance") or {}
|
||||
if performance.get("error") or performance.get("skipped"):
|
||||
return False
|
||||
return True
|
||||
def comparison_passed(report: dict) -> bool:
|
||||
result = report.get("raptor_validation") or {}
|
||||
return result.get("status") == "done" and bool(result.get("passed"))
|
||||
|
||||
|
||||
def functional_validation_status(result: dict | None) -> str:
|
||||
@@ -404,13 +390,18 @@ def comparison_command(
|
||||
*[f"--raptor-extra-arg={arg}" for arg in raptor_extra_args],
|
||||
"--timeout-seconds",
|
||||
str(timeout),
|
||||
"--fail-on-error",
|
||||
*([] if fast else ["--no-fast"]),
|
||||
*reuse_args,
|
||||
]
|
||||
|
||||
|
||||
def prepare_common_command(model: Path, common_dir: Path, timeout: float) -> list[str]:
|
||||
def prepare_common_command(
|
||||
model: Path,
|
||||
common_dir: Path,
|
||||
timeout: float,
|
||||
batch_size: int,
|
||||
jobs: int,
|
||||
) -> list[str]:
|
||||
return [
|
||||
sys.executable,
|
||||
str(COMPARE),
|
||||
@@ -421,6 +412,10 @@ def prepare_common_command(model: Path, common_dir: Path, timeout: float) -> lis
|
||||
"--common-dir",
|
||||
str(common_dir),
|
||||
"--prepare-common",
|
||||
"--batch-size",
|
||||
str(batch_size),
|
||||
"--jobs",
|
||||
str(jobs),
|
||||
"--timeout-seconds",
|
||||
str(timeout),
|
||||
]
|
||||
@@ -587,8 +582,8 @@ def main() -> int:
|
||||
parser.add_argument(
|
||||
"--batch-size",
|
||||
type=int,
|
||||
default=128,
|
||||
help="functional throughput batch size (default: 128).",
|
||||
default=64,
|
||||
help="functional throughput and shared reference batch size (default: 64).",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--timeout-seconds",
|
||||
@@ -664,6 +659,11 @@ def main() -> int:
|
||||
}
|
||||
comparisons_by_arch[arch] = comparisons
|
||||
configs_by_arch[arch] = configs
|
||||
reference_batch_size = max(
|
||||
1 if mode == "latency" else args.batch_size
|
||||
for comparisons in comparisons_by_arch.values()
|
||||
for mode, _, _ in comparisons
|
||||
)
|
||||
|
||||
missing = [
|
||||
str(path)
|
||||
@@ -706,6 +706,7 @@ def main() -> int:
|
||||
print(f"Modes: {', '.join(args.mode)}")
|
||||
print(f"Throughput Pimsim time: {args.pimsim_time_ms} ms")
|
||||
print(f"Max parallel jobs: {args.jobs}")
|
||||
print(f"Shared reference batch: {reference_batch_size}")
|
||||
print(
|
||||
f"Comparison jobs: "
|
||||
f"{sum(len(args.models) * len(comparisons) for comparisons in comparisons_by_arch.values())}"
|
||||
@@ -722,6 +723,8 @@ def main() -> int:
|
||||
FUNCTIONAL_MODELS[name],
|
||||
common_dir(out_dir, name, common_root),
|
||||
args.timeout_seconds,
|
||||
reference_batch_size,
|
||||
args.jobs,
|
||||
),
|
||||
dry_run=args.dry_run,
|
||||
)
|
||||
@@ -849,10 +852,8 @@ def main() -> int:
|
||||
report_path = result_dir(
|
||||
out_dir, name, arch, mode, pipeline, args.ablation_variant
|
||||
) / "pimcomp/comparison_report.json"
|
||||
compiler = "raptor" if args.raptor_only else args.only
|
||||
if not report_path.exists() or not comparison_passed(
|
||||
json.loads(report_path.read_text(encoding="utf-8")),
|
||||
compiler,
|
||||
):
|
||||
if label not in failed:
|
||||
failed.append(label)
|
||||
|
||||
Reference in New Issue
Block a user