add non-functional validation
Validate Operations / validate-operations (push) Has been cancelled

refactors
This commit is contained in:
NiccoloN
2026-07-27 10:54:59 +02:00
parent 4964e889da
commit 620e381cfb
30 changed files with 677 additions and 164 deletions
+34 -20
View File
@@ -44,13 +44,18 @@ a35bad96441efbee28699cb61d1656cca7f7281f14040cf01699c3d0cfd8b202 googlenet/goog
## Paper hardware profiles
The files in `configs/` encode Table V's explicit resource parameters.
The files in
[`../../pimsim_configs/pimcomp/`](../../pimsim_configs/pimcomp/)
encode Table V's explicit resource parameters.
Each profile subdirectory contains pre-generated latency and throughput
`pimsim-nn` configs plus its matching mesh; comparison and validation select
these checked-in artifacts without generating configs at runtime.
| Config | Cores | Crossbars/core | Crossbar | Cell | PIMCOMP layout |
| --- | ---: | ---: | --- | ---: | --- |
| `arch-a.json` | 168 | 96 | `128x128` | 2-bit | `12x14` |
| `arch-b.json` | 138 | 128 | `128x128` | 2-bit | `6x23` |
| `arch-c.json` | 64 (16 chips x 4) | 8 | `512x1024` | 2-bit | flattened `8x8` |
| `arch-a/latency_config.json` | 168 | 96 | `128x128` | 2-bit | `12x14` |
| `arch-b/latency_config.json` | 138 | 128 | `128x128` | 2-bit | `6x23` |
| `arch-c/latency_config.json` | 64 (16 chips x 4) | 8 | `512x1024` | 2-bit | flattened `8x8` |
`adc_count` is 16, matching the paper's 16-bit fixed-point weight precision.
The paper does not give a two-dimensional core topology for Arch-A/B, so the
@@ -67,7 +72,7 @@ reproducible, but absolute paper power and energy numbers are not.
From the Raptor repository root:
```bash
.venv/bin/python -m pip install numpy onnx onnxruntime onnxsim colorama
.venv/bin/python -m pip install -r requirements.txt
cmake --build ./build_release
cmake --build third_party/PIMCOMP-NN/build --target PIMCOMP-NN
@@ -85,11 +90,11 @@ select one paper profile, and restore it when the shell exits:
```bash
RAPTOR_ROOT=$PWD
PIMCOMP="$RAPTOR_ROOT/third_party/PIMCOMP-NN"
PAPER_MODELS="$RAPTOR_ROOT/validation/networks/pimcomp_models"
PIMCOMP_CONFIGS="$RAPTOR_ROOT/validation/pimsim_configs/pimcomp"
CONFIG_BACKUP=$(mktemp)
cp "$PIMCOMP/config.json" "$CONFIG_BACKUP"
trap 'cp "$CONFIG_BACKUP" "$PIMCOMP/config.json"' EXIT
cp "$PAPER_MODELS/configs/arch-a.json" "$PIMCOMP/config.json"
cp "$PIMCOMP_CONFIGS/arch-a/latency_config.json" "$PIMCOMP/config.json"
```
The Model Zoo files map exactly to PIMCOMP's bundled model names, so compile
@@ -124,7 +129,8 @@ cd third_party/PIMCOMP-NN/build
./PIMCOMP-NN -m=vgg8_paper_reconstructed -r=balance -p=element -o=YES -v=YES -s=YES
```
Repeat after selecting `arch-b.json` and `arch-c.json`. All four models were
Repeat after selecting `arch-b/latency_config.json` and
`arch-c/latency_config.json`. All four models were
compiled successfully in both modes with all three configs. The released
random placement code occasionally segfaults; an unchanged retry succeeded in
the observed cases.
@@ -140,8 +146,9 @@ insufficient.
## Compare Raptor and PIMCOMP
The comparison driver uses one random input and one native ONNX-MLIR reference,
compiles both instruction streams, validates both through Raptor's Rust
simulator, and writes Markdown and JSON reports.
compiles both instruction streams, runs both through `pimsim-nn`, validates
Raptor through the Rust simulator, and writes Markdown and JSON reports.
PIMCOMP Rust validation also runs when its optional exporter is available.
To reproduce the complete Arch-A latency experiment, use the serial experiment
runner. It creates an isolated PIMCOMP build with population 200 and 1000 GA
@@ -156,8 +163,7 @@ comparison driver for one model at a time:
Reports are written under `<out-dir>/<model>/comparison_report.{md,json}`.
Use `--models vgg8` to run one model, `--resume` after an interruption, or
`--dry-run` to inspect every command. The runner continues after a failed model
so all reports are produced, then returns a nonzero status if any comparison
failed.
so all reports are produced.
Arch-A low-latency example:
@@ -167,28 +173,34 @@ PIMCOMP="$RAPTOR_ROOT/third_party/PIMCOMP-NN"
CONFIG_BACKUP=$(mktemp)
cp "$PIMCOMP/config.json" "$CONFIG_BACKUP"
trap 'cp "$CONFIG_BACKUP" "$PIMCOMP/config.json"' EXIT
cp "$RAPTOR_ROOT/validation/networks/pimcomp_models/configs/arch-a.json" "$PIMCOMP/config.json"
cp "$RAPTOR_ROOT/validation/pimsim_configs/pimcomp/arch-a/latency_config.json" "$PIMCOMP/config.json"
"$RAPTOR_ROOT/.venv/bin/python" "$RAPTOR_ROOT/validation/tools/compare_raptor_pimcomp.py" \
--model "$RAPTOR_ROOT/validation/networks/pimcomp_models/vgg8/vgg8-mnist-reconstructed.onnx" \
--out-dir /tmp/compare-vgg8-arch-a-ll \
--model "$RAPTOR_ROOT/validation/networks/pimcomp_models/resnet34/resnet34-v1-7.onnx" \
--out-dir /tmp/compare-resnet34-arch-a-ll \
--core-count 168 \
--crossbar-count 96 \
--crossbar-size 128 \
--mesh-rows 12 \
--mesh-cols 14 \
--pimsim-mode latency \
--pimcomp-pipeline element \
--fail-on-error
--pimcomp-pipeline element
```
For Arch-A high throughput, use `--pimsim-mode throughput
--pimcomp-pipeline batch`. For Arch-B, use 138 cores, 128 crossbars, a
`6x23` mesh, and `configs/arch-b.json`.
`6x23` mesh, and
`validation/pimsim_configs/pimcomp/arch-b/latency_config.json`.
If only semantic and instruction comparison is required, add
`--skip-pimsim-nn`. This exact VGG-8 Arch-A LL smoke test passed both semantic
validations with maximum output differences below `5e-10`.
`--skip-pimsim-nn`. A VGG-8 run with the same Arch-A LL settings passed both
semantic validations with maximum output differences below `5e-10`.
The current PIMCOMP-NN submodule does not include the optional
`verification/export_to_pim_simulator.py` helper. The driver therefore records
PIMCOMP Rust semantic validation as skipped, while PIMCOMP compilation,
instruction reporting, and `pimsim-nn` latency still run. Use
`--fail-on-error` only when that semantic export helper is available.
Current Raptor status:
@@ -213,6 +225,8 @@ the canonical `resnetv2/depth_68` file may not exist remotely:
REMOTE_REPO=/home/gmagnani/Project/Raptor
rsync -azL validation/networks/pimcomp_models/ \
"monolith:$REMOTE_REPO/validation/networks/pimcomp_models/"
rsync -az validation/pimsim_configs/pimcomp/ \
"monolith:$REMOTE_REPO/validation/pimsim_configs/pimcomp/"
rsync -az validation/tools/compare_raptor_pimcomp.py \
"monolith:$REMOTE_REPO/validation/tools/compare_raptor_pimcomp.py"
rsync -az validation/tools/run_pimcomp_paper_latency.py \
-3
View File
@@ -32,9 +32,6 @@ Run the complete suite with deadlock detection:
--raptor-path build_release/Release/bin/onnx-mlir \
--onnx-include-dir onnx-mlir/include \
--operations-dir validation/operations \
--crossbar-count 64 \
--crossbar-size 128 \
--core-count 144 \
--raptor-extra-arg=--pim-detect-communication-deadlock \
--raptor-extra-arg=--pim-export-spatial-dataflow=none
```
@@ -0,0 +1,61 @@
{
"chip_config": {
"core_config": {
"period": 1,
"matrix_config": {
"xbar_array_count": 96,
"period": 1,
"pipeline_mode": true,
"dac_resolution": 1,
"dac_count": 128,
"xbar_size": [
128,
128
],
"cell_precision": 2,
"xbar_latency": 7,
"xbar_read_power": 22.24,
"sample_hold_latency_cycle": 1,
"adc_resolution": 8,
"adc_latency_cycle": 20,
"adc_static_power": 0.322,
"adc_dynamic_power": 1.135,
"adc_count": 16,
"shift_adder_latency_cycle": 1,
"output_buffer_latency_cycle": 1
},
"vector_width": 32,
"vector_latency_cycle": 4,
"local_memory_config": {
"data_width": 64,
"period": 1,
"write_latency_cycle": 30,
"read_latency_cycle": 20
},
"global_memory_switch_id": -10,
"rob_size": 1
},
"global_memory_config": {
"data_width": 32,
"period": 1,
"write_latency_cycle": 50,
"read_latency_cycle": 40
},
"network_config": {
"bus_topology": "mesh",
"bus_width": 32,
"layout": [
12,
14
],
"net_config_file_path": "network_mesh_168.json"
},
"core_cnt": 168,
"global_memory_switch_id": -10
},
"sim_config": {
"sim_mode": 1,
"sim_time": 1000,
"report_verbose_level": 0
}
}
File diff suppressed because one or more lines are too long
@@ -8,7 +8,10 @@
"pipeline_mode": true,
"dac_resolution": 1,
"dac_count": 128,
"xbar_size": [128, 128],
"xbar_size": [
128,
128
],
"cell_precision": 2,
"xbar_latency": 7,
"xbar_read_power": 22.24,
@@ -29,7 +32,8 @@
"write_latency_cycle": 30,
"read_latency_cycle": 20
},
"global_memory_switch_id": -10
"global_memory_switch_id": -10,
"rob_size": 1
},
"global_memory_config": {
"data_width": 32,
@@ -40,7 +44,10 @@
"network_config": {
"bus_topology": "mesh",
"bus_width": 32,
"layout": [12, 14],
"layout": [
12,
14
],
"net_config_file_path": "network_mesh_168.json"
},
"core_cnt": 168,
@@ -48,7 +55,7 @@
},
"sim_config": {
"sim_mode": 0,
"sim_time": 200,
"sim_time": 1000,
"report_verbose_level": 0
}
}
@@ -0,0 +1,61 @@
{
"chip_config": {
"core_config": {
"period": 1,
"matrix_config": {
"xbar_array_count": 128,
"period": 1,
"pipeline_mode": true,
"dac_resolution": 1,
"dac_count": 128,
"xbar_size": [
128,
128
],
"cell_precision": 2,
"xbar_latency": 7,
"xbar_read_power": 22.24,
"sample_hold_latency_cycle": 1,
"adc_resolution": 8,
"adc_latency_cycle": 20,
"adc_static_power": 0.322,
"adc_dynamic_power": 1.135,
"adc_count": 16,
"shift_adder_latency_cycle": 1,
"output_buffer_latency_cycle": 1
},
"vector_width": 32,
"vector_latency_cycle": 4,
"local_memory_config": {
"data_width": 64,
"period": 1,
"write_latency_cycle": 30,
"read_latency_cycle": 20
},
"global_memory_switch_id": -10,
"rob_size": 1
},
"global_memory_config": {
"data_width": 32,
"period": 1,
"write_latency_cycle": 50,
"read_latency_cycle": 40
},
"network_config": {
"bus_topology": "mesh",
"bus_width": 32,
"layout": [
6,
23
],
"net_config_file_path": "network_mesh_138.json"
},
"core_cnt": 138,
"global_memory_switch_id": -10
},
"sim_config": {
"sim_mode": 1,
"sim_time": 1000,
"report_verbose_level": 0
}
}
File diff suppressed because one or more lines are too long
@@ -8,7 +8,10 @@
"pipeline_mode": true,
"dac_resolution": 1,
"dac_count": 128,
"xbar_size": [128, 128],
"xbar_size": [
128,
128
],
"cell_precision": 2,
"xbar_latency": 7,
"xbar_read_power": 22.24,
@@ -29,7 +32,8 @@
"write_latency_cycle": 30,
"read_latency_cycle": 20
},
"global_memory_switch_id": -10
"global_memory_switch_id": -10,
"rob_size": 1
},
"global_memory_config": {
"data_width": 32,
@@ -40,7 +44,10 @@
"network_config": {
"bus_topology": "mesh",
"bus_width": 32,
"layout": [6, 23],
"layout": [
6,
23
],
"net_config_file_path": "network_mesh_138.json"
},
"core_cnt": 138,
@@ -48,7 +55,7 @@
},
"sim_config": {
"sim_mode": 0,
"sim_time": 200,
"sim_time": 1000,
"report_verbose_level": 0
}
}
@@ -0,0 +1,61 @@
{
"chip_config": {
"core_config": {
"period": 1,
"matrix_config": {
"xbar_array_count": 8,
"period": 1,
"pipeline_mode": true,
"dac_resolution": 1,
"dac_count": 128,
"xbar_size": [
512,
1024
],
"cell_precision": 2,
"xbar_latency": 7,
"xbar_read_power": 22.24,
"sample_hold_latency_cycle": 1,
"adc_resolution": 8,
"adc_latency_cycle": 20,
"adc_static_power": 0.322,
"adc_dynamic_power": 1.135,
"adc_count": 16,
"shift_adder_latency_cycle": 1,
"output_buffer_latency_cycle": 1
},
"vector_width": 32,
"vector_latency_cycle": 4,
"local_memory_config": {
"data_width": 64,
"period": 1,
"write_latency_cycle": 30,
"read_latency_cycle": 20
},
"global_memory_switch_id": -10,
"rob_size": 1
},
"global_memory_config": {
"data_width": 32,
"period": 1,
"write_latency_cycle": 50,
"read_latency_cycle": 40
},
"network_config": {
"bus_topology": "mesh",
"bus_width": 32,
"layout": [
8,
8
],
"net_config_file_path": "network_mesh_64.json"
},
"core_cnt": 64,
"global_memory_switch_id": -10
},
"sim_config": {
"sim_mode": 1,
"sim_time": 1000,
"report_verbose_level": 0
}
}
File diff suppressed because one or more lines are too long
@@ -8,7 +8,10 @@
"pipeline_mode": true,
"dac_resolution": 1,
"dac_count": 128,
"xbar_size": [512, 1024],
"xbar_size": [
512,
1024
],
"cell_precision": 2,
"xbar_latency": 7,
"xbar_read_power": 22.24,
@@ -29,7 +32,8 @@
"write_latency_cycle": 30,
"read_latency_cycle": 20
},
"global_memory_switch_id": -10
"global_memory_switch_id": -10,
"rob_size": 1
},
"global_memory_config": {
"data_width": 32,
@@ -40,7 +44,10 @@
"network_config": {
"bus_topology": "mesh",
"bus_width": 32,
"layout": [8, 8],
"layout": [
8,
8
],
"net_config_file_path": "network_mesh_64.json"
},
"core_cnt": 64,
@@ -48,7 +55,7 @@
},
"sim_config": {
"sim_mode": 0,
"sim_time": 200,
"sim_time": 1000,
"report_verbose_level": 0
}
}
+1
View File
@@ -0,0 +1 @@
"""Reusable Raptor validation support."""
@@ -1,7 +1,7 @@
#!/usr/bin/env python3
import argparse, os, pathlib, textwrap
from onnx_utils import onnx_io
from .onnx_utils import onnx_io
from onnx import TensorProto
# ONNX dtype -> (ctype, printf, ONNX_TYPE_*)
@@ -207,7 +207,7 @@ int main(int argc, char **argv) {{
}}
"""
def gen_network_runner(network_onnx, network_so, onnx_include_dir, entry="run_main_graph", out=None, verbose=True):
def gen_network_runner(network_onnx, network_so, onnx_include_dir, entry, out, verbose):
ins, outs = onnx_io(network_onnx)
out_c = out or "runner.c"
so_abs = os.path.abspath(network_so)
@@ -240,4 +240,4 @@ if __name__=="__main__":
ap.add_argument("--out", default=None)
a=ap.parse_args()
gen_network_runner(a.network_onnx, a.network_so, a.onnx_include_dir, a.entry, a.out)
gen_network_runner(a.network_onnx, a.network_so, a.onnx_include_dir, a.entry, a.out, True)
@@ -30,9 +30,12 @@ def onnx_io(path):
return s
ins, outs = [], []
for i, v in enumerate(g.input):
initializer_names = {initializer.name for initializer in g.initializer}
for v in g.input:
if v.name in initializer_names:
continue
t = v.type.tensor_type
ins.append((i, v.name, t.elem_type, shp(t)))
ins.append((len(ins), v.name, t.elem_type, shp(t)))
for i, v in enumerate(g.output):
t = v.type.tensor_type
outs.append((i, v.name, t.elem_type, shp(t)))
@@ -3,7 +3,7 @@ import shlex
import subprocess
from pathlib import Path
from colorama import Fore, Style
from subprocess_utils import run_command_with_reporter
from .subprocess_utils import run_command_with_reporter
PIM_PASS_LABELS = (
("ONNXToSpatialPass", "ONNX to Spatial"),
@@ -41,9 +41,8 @@ def _format_command(cmd):
def compile_with_raptor(network_path, raptor_onnx_path: Path, output_base: Path,
crossbar_size, crossbar_count, core_count=None,
raptor_extra_args=None, cwd=None, verbose=False,
reporter=None, timeout_sec=None):
crossbar_size, crossbar_count, core_count,
raptor_extra_args, cwd, verbose, reporter, timeout_sec):
# Define the arguments, with the possibility to set crossbar size and count
args = [
network_path,
@@ -1,15 +1,16 @@
import json
import os
import numpy as np
import re
import shutil
import sys
import numpy as np
from dataclasses import dataclass, field
from pathlib import Path
from colorama import Style, Fore
from onnx_utils import gen_random_inputs, save_inputs_to_files, onnx_io, write_inputs_to_memory_bin, _ONNX_TO_NP
from raptor import compile_with_raptor
from gen_network_runner import gen_network_runner
from subprocess_utils import run_command_with_reporter
from .gen_network_runner import gen_network_runner
from .onnx_utils import gen_random_inputs, save_inputs_to_files, onnx_io, write_inputs_to_memory_bin, _ONNX_TO_NP
from .raptor import compile_with_raptor
from .subprocess_utils import run_command_with_reporter
STAGE_TITLES = (
"Compile ONNX",
@@ -17,8 +18,9 @@ STAGE_TITLES = (
"Generate Inputs",
"Run Reference",
"Compile PIM",
"Run Simulator",
"Run Functional Simulation",
"Compare Outputs",
"Run Non-functional Simulation",
)
STAGE_COUNT = len(STAGE_TITLES)
GENERATED_DIR_NAMES = ("inputs", "outputs", "raptor", "runner", "simulation")
@@ -37,11 +39,19 @@ MODE_STAGE_TITLES = {
MODE_RUN_ONLY: (
"Generate Inputs",
"Run Reference",
"Run Simulator",
"Run Functional Simulation",
"Compare Outputs",
"Run Non-functional Simulation",
),
}
PIMSIM_DONE = "DONE"
PIMSIM_FAILED = "ERROR"
PIMSIM_SKIPPED = "SKIP"
PIMSIM_NOT_RUN = "-"
PIMSIM_LATENCY_RE = re.compile(r"\blatency:\s+([0-9.eE+-]+)\s+ms")
PIMSIM_POWER_RE = re.compile(r"\baverage power:\s+([0-9.eE+-]+)\s+mW")
def sanitize_output_name(name):
return "".join(ch if ch.isalnum() or ch in "_.-" else "_" for ch in name[:255])
@@ -51,6 +61,9 @@ def sanitize_output_name(name):
class ValidationResult:
passed: bool
pim_pass_timings: dict[str, float] = field(default_factory=dict)
pimsim_latency_ms: float | None = None
pimsim_power_mw: float | None = None
pimsim_status: str = PIMSIM_SKIPPED
class ProgressReporter:
@@ -180,8 +193,70 @@ class ProgressReporter:
self._clear()
def run_command(cmd, cwd=None, reporter=None, timeout_sec=None):
run_command_with_reporter(cmd, cwd=cwd, reporter=reporter, timeout_sec=timeout_sec)
def run_command(cmd, cwd=None, reporter=None, timeout_sec=None, capture_output=False):
return run_command_with_reporter(
cmd,
cwd=cwd,
reporter=reporter,
timeout_sec=timeout_sec,
capture_output=capture_output,
)
def load_pimcomp_hardware(config_path):
with open(config_path, encoding="utf-8") as f:
config = json.load(f)
matrix = config["chip_config"]["core_config"]["matrix_config"]
rows, cols = config["chip_config"]["network_config"]["layout"]
xbar_rows, xbar_cols = matrix["xbar_size"]
return {
"core_count": config["chip_config"]["core_cnt"],
"crossbar_count": matrix["xbar_array_count"],
"crossbar_rows": xbar_rows,
"crossbar_cols": xbar_cols,
"mesh_rows": rows,
"mesh_cols": cols,
}
def pimcomp_compatibility_errors(config_path, *, core_count, crossbar_count, crossbar_size):
hardware = load_pimcomp_hardware(config_path)
errors = []
if hardware["mesh_rows"] * hardware["mesh_cols"] != hardware["core_count"]:
errors.append(
f"config layout {hardware['mesh_rows']}x{hardware['mesh_cols']} does not match "
f"{hardware['core_count']} cores"
)
if core_count != hardware["core_count"]:
errors.append(f"--core-count={core_count}, config requires {hardware['core_count']}")
if crossbar_count != hardware["crossbar_count"]:
errors.append(
f"--crossbar-count={crossbar_count}, config requires {hardware['crossbar_count']}"
)
if (
hardware["crossbar_rows"] != hardware["crossbar_cols"]
or crossbar_size != hardware["crossbar_rows"]
):
errors.append(
f"--crossbar-size={crossbar_size}, config requires "
f"{hardware['crossbar_rows']}x{hardware['crossbar_cols']}"
)
return errors
def run_pimsim_nn(pimsim_nn_build_dir, pim_dir, config_path, reporter=None, timeout_sec=None):
output = run_command(
[pimsim_nn_build_dir / "ChipTest", pim_dir, config_path, "false"],
cwd=pimsim_nn_build_dir,
reporter=reporter,
timeout_sec=timeout_sec,
capture_output=True,
)
latency_match = PIMSIM_LATENCY_RE.search(output)
power_match = PIMSIM_POWER_RE.search(output)
if not latency_match or not power_match:
raise RuntimeError("pimsim-nn output did not contain latency and average power")
return float(latency_match.group(1)), float(power_match.group(1))
def clean_workspace_artifacts(workspace_dir, model_stem):
@@ -214,6 +289,7 @@ def print_stage(reporter, model_index, model_total, model_name, title):
STAGE_TITLES[4]: Fore.CYAN,
STAGE_TITLES[5]: Fore.MAGENTA,
STAGE_TITLES[6]: Fore.YELLOW,
STAGE_TITLES[7]: Fore.BLUE,
}
color = stage_colors.get(title, Fore.WHITE)
reporter.log(Style.BRIGHT + color + f"[{title}]" + Style.RESET_ALL)
@@ -278,7 +354,7 @@ def parse_pim_simulator_outputs(output_bin_path, outputs_descriptor):
return arrays
def validate_outputs(sim_arrays, runner_out_dir, outputs_descriptor, threshold=1e-3, rtol=1e-5, verbose=False):
def validate_outputs(sim_arrays, runner_out_dir, outputs_descriptor, threshold, rtol, verbose):
all_passed = True
rows = []
for sim_array, (oi, name, _, shape) in zip(sim_arrays, outputs_descriptor):
@@ -312,15 +388,23 @@ def validate_outputs(sim_arrays, runner_out_dir, outputs_descriptor, threshold=1
def validate_network(network_onnx_path, raptor_path, onnx_include_dir,
simulator_dir, crossbar_size=128, crossbar_count=64, core_count=144,
raptor_extra_args=None,
threshold=1e-3, rtol=1e-5,
seed=0, reporter=None, model_index=1, model_total=1, verbose=False,
command_timeout_seconds=60.0, mode=MODE_FULL):
simulator_dir, crossbar_size, crossbar_count, core_count,
raptor_extra_args,
pimsim_nn_build_dir, pimsim_config_path,
threshold, rtol,
seed, reporter, model_index, model_total, verbose,
command_timeout_seconds, mode):
network_onnx_path = Path(network_onnx_path).resolve()
raptor_path = Path(raptor_path).resolve()
onnx_include_dir = Path(onnx_include_dir).resolve()
simulator_dir = Path(simulator_dir).resolve()
pimsim_enabled = pimsim_nn_build_dir is not None and pimsim_config_path is not None
if pimsim_enabled:
pimsim_nn_build_dir = Path(pimsim_nn_build_dir).resolve()
pimsim_config_path = Path(pimsim_config_path).resolve()
compile_extra_args = list(raptor_extra_args or [])
if pimsim_enabled and "--pim-emit-json" not in compile_extra_args:
compile_extra_args.append("--pim-emit-json")
owns_reporter = reporter is None
reporter = reporter or ProgressReporter(model_total, stages_per_model=len(MODE_STAGE_TITLES[mode]), verbose=verbose)
@@ -356,8 +440,14 @@ def validate_network(network_onnx_path, raptor_path, onnx_include_dir,
reporter.advance()
print_stage(reporter, model_index, model_total, network_onnx_path.name, "Build Runner")
gen_network_runner(network_onnx_path, network_so_path, onnx_include_dir, out=runner_dir / "runner.c",
verbose=False)
gen_network_runner(
network_onnx_path,
network_so_path,
onnx_include_dir,
entry="run_main_graph",
out=runner_dir / "runner.c",
verbose=False,
)
runner_path = build_onnx_runner(runner_dir, runner_build_dir, reporter=reporter,
timeout_sec=command_timeout_seconds)
print_info(reporter, f"Runner built at {runner_path}")
@@ -368,7 +458,7 @@ def validate_network(network_onnx_path, raptor_path, onnx_include_dir,
pim_pass_timings = compile_with_raptor(
network_onnx_path, raptor_path, pim_output_base, crossbar_size, crossbar_count,
core_count=core_count,
raptor_extra_args=raptor_extra_args,
raptor_extra_args=compile_extra_args,
cwd=raptor_dir, verbose=verbose, reporter=reporter, timeout_sec=command_timeout_seconds)
print_info(reporter, f"PIM artifacts saved to {raptor_dir / 'pim'}")
reporter.advance()
@@ -408,12 +498,14 @@ def validate_network(network_onnx_path, raptor_path, onnx_include_dir,
pim_pass_timings = compile_with_raptor(
network_onnx_path, raptor_path, pim_output_base, crossbar_size, crossbar_count,
core_count=core_count,
raptor_extra_args=raptor_extra_args,
raptor_extra_args=compile_extra_args,
cwd=raptor_dir, verbose=verbose, reporter=reporter, timeout_sec=command_timeout_seconds)
print_info(reporter, f"PIM artifacts saved to {raptor_dir / 'pim'}")
reporter.advance()
print_stage(reporter, model_index, model_total, network_onnx_path.name, "Run Simulator")
print_stage(
reporter, model_index, model_total, network_onnx_path.name,
"Run Functional Simulation")
pim_dir = raptor_dir / "pim"
write_inputs_to_memory_bin(pim_dir / "memory.bin", pim_dir / "config.json", inputs_list)
simulation_dir = workspace_dir / "simulation"
@@ -422,7 +514,7 @@ def validate_network(network_onnx_path, raptor_path, onnx_include_dir,
output_bin_path = simulation_dir / "out.bin"
run_pim_simulator(simulator_dir, pim_dir, output_bin_path, dump_ranges, reporter=reporter,
timeout_sec=command_timeout_seconds)
print_info(reporter, f"Simulator output saved to {output_bin_path}")
print_info(reporter, f"Functional simulation output saved to {output_bin_path}")
reporter.advance()
print_stage(reporter, model_index, model_total, network_onnx_path.name, "Compare Outputs")
@@ -431,10 +523,53 @@ def validate_network(network_onnx_path, raptor_path, onnx_include_dir,
passed = validate_outputs(sim_arrays, out_dir, outputs_descriptor, threshold, rtol=rtol, verbose=verbose)
reporter.resume()
reporter.advance()
print_stage(
reporter, model_index, model_total, network_onnx_path.name,
"Run Non-functional Simulation")
pimsim_latency_ms = None
pimsim_power_mw = None
pimsim_status = PIMSIM_SKIPPED
if pimsim_enabled:
try:
pimsim_latency_ms, pimsim_power_mw = run_pimsim_nn(
pimsim_nn_build_dir,
pim_dir,
pimsim_config_path,
reporter=reporter,
timeout_sec=command_timeout_seconds,
)
pimsim_status = PIMSIM_DONE
print_info(
reporter,
f"Latency: {pimsim_latency_ms:.6f} ms, "
f"Power: {pimsim_power_mw:.6f} mW")
except Exception as exc:
pimsim_status = PIMSIM_FAILED
reporter.suspend()
print(
Fore.RED
+ f"pimsim-nn non-functional simulation failed: {type(exc).__name__}: {exc}"
+ Style.RESET_ALL,
file=sys.stderr,
flush=True,
)
reporter.resume()
else:
print_info(reporter, "pimsim-nn non-functional simulation skipped")
reporter.advance()
passed = passed and pimsim_status != PIMSIM_FAILED
reporter.record_result(passed)
status = Fore.GREEN + "PASS" + Style.RESET_ALL if passed else Fore.RED + "FAIL" + Style.RESET_ALL
reporter.log(Style.BRIGHT + f"Result: {status}" + Style.RESET_ALL)
return ValidationResult(passed=passed, pim_pass_timings=pim_pass_timings)
return ValidationResult(
passed=passed,
pim_pass_timings=pim_pass_timings,
pimsim_latency_ms=pimsim_latency_ms,
pimsim_power_mw=pimsim_power_mw,
pimsim_status=pimsim_status,
)
except Exception:
failed_with_exception = True
reporter.record_result(False)
@@ -15,8 +15,8 @@ REPO_ROOT = VALIDATION_DIR.parent
if str(VALIDATION_DIR) not in sys.path:
sys.path.insert(0, str(VALIDATION_DIR))
from onnx_utils import _ONNX_TO_NP, onnx_io, write_inputs_to_memory_bin
from validate_one import (
from raptor_validation.onnx_utils import _ONNX_TO_NP, onnx_io, write_inputs_to_memory_bin
from raptor_validation.validate_one import (
MODE_COMPILE_ONLY,
build_dump_ranges,
parse_pim_simulator_outputs,
@@ -59,6 +59,15 @@ def ensure_local_artifacts(args, model_path: Path):
crossbar_size=args.crossbar_size,
crossbar_count=args.crossbar_count,
core_count=args.core_count,
raptor_extra_args=[],
pimsim_nn_build_dir=None,
pimsim_config_path=None,
threshold=1e-3,
rtol=1e-5,
seed=0,
reporter=None,
model_index=1,
model_total=1,
command_timeout_seconds=args.command_timeout_seconds,
mode=MODE_COMPILE_ONLY,
verbose=args.verbose,
+44 -54
View File
@@ -25,12 +25,19 @@ import onnx
REPO = Path(__file__).resolve().parents[2]
VALIDATION_DIR = REPO / "validation"
PIMSIM_CONFIG_DIR = VALIDATION_DIR / "pimsim_configs/pimcomp"
sys.path.insert(0, str(VALIDATION_DIR))
from gen_network_runner import gen_network_runner # noqa: E402
from onnx_utils import _ONNX_TO_NP, gen_random_inputs, onnx_io, save_inputs_to_files, write_inputs_to_memory_bin # noqa: E402
from validate_one import build_dump_ranges, parse_pim_simulator_outputs # noqa: E402
from raptor import compile_with_raptor # noqa: E402
from raptor_validation.gen_network_runner import gen_network_runner # noqa: E402
from raptor_validation.onnx_utils import ( # noqa: E402
_ONNX_TO_NP,
gen_random_inputs,
onnx_io,
save_inputs_to_files,
write_inputs_to_memory_bin,
)
from raptor_validation.raptor import compile_with_raptor # noqa: E402
from raptor_validation.validate_one import build_dump_ranges, parse_pim_simulator_outputs # noqa: E402
@dataclass
@@ -62,15 +69,6 @@ def load_pimcomp_exporter():
return module
def load_mesh_builder():
path = REPO / "validation/pimsim-configs/generate_mesh_config.py"
spec = importlib.util.spec_from_file_location("mesh_builder", path)
module = importlib.util.module_from_spec(spec)
assert spec is not None and spec.loader is not None
spec.loader.exec_module(module)
return module
def shell_join(cmd: list[str]) -> str:
return shlex.join(str(arg) for arg in cmd)
@@ -278,41 +276,26 @@ def load_effective_hardware(args: argparse.Namespace) -> dict[str, int]:
return hardware
def write_pimsim_config(args: argparse.Namespace, out_dir: Path, hardware: dict[str, int]) -> Path:
mesh_builder = load_mesh_builder()
with open(args.pimcomp_dir / "config.json", "r", encoding="utf-8") as f:
config = json.load(f)
config["chip_config"]["core_config"].setdefault("rob_size", 1)
config["chip_config"]["core_config"]["matrix_config"]["xbar_array_count"] = hardware["crossbar_count"]
config["chip_config"]["core_config"]["matrix_config"]["xbar_size"] = [
hardware["crossbar_size"],
hardware["crossbar_size"],
]
config["chip_config"]["network_config"]["layout"] = [
hardware["mesh_rows"],
hardware["mesh_cols"],
]
config["chip_config"]["network_config"]["net_config_file_path"] = f"network_mesh_{hardware['core_count']}.json"
config["chip_config"]["core_cnt"] = hardware["core_count"]
config["sim_config"]["sim_mode"] = 1 if args.pimsim_mode == "latency" else 0
config["sim_config"]["sim_time"] = args.pimsim_time_ms
out_dir.mkdir(parents=True, exist_ok=True)
config_path = out_dir / f"{args.pimsim_mode}_config.json"
network_path = out_dir / f"network_mesh_{hardware['core_count']}.json"
with open(config_path, "w", encoding="utf-8") as f:
json.dump(config, f, indent=2)
f.write("\n")
with open(network_path, "w", encoding="utf-8") as f:
json.dump(
mesh_builder.build_network(
hardware["core_count"],
(hardware["mesh_rows"], hardware["mesh_cols"]),
),
f,
separators=(",", ":"),
)
f.write("\n")
return config_path
def select_pimsim_config(args: argparse.Namespace, hardware: dict[str, int]) -> Path:
for path in sorted(PIMSIM_CONFIG_DIR.glob(f"*/{args.pimsim_mode}_config.json")):
with open(path, encoding="utf-8") as f:
config = json.load(f)
chip = config["chip_config"]
matrix = chip["core_config"]["matrix_config"]
network = chip["network_config"]
if (
chip["core_cnt"] == hardware["core_count"]
and matrix["xbar_array_count"] == hardware["crossbar_count"]
and matrix["xbar_size"] == [hardware["crossbar_size"]] * 2
and network["layout"] == [hardware["mesh_rows"], hardware["mesh_cols"]]
and config["sim_config"]["sim_mode"] == (1 if args.pimsim_mode == "latency" else 0)
and config["sim_config"]["sim_time"] == args.pimsim_time_ms
):
return path
raise ValueError(
f"No pre-generated {args.pimsim_mode} pimsim-nn config matches "
f"{hardware} with sim_time={args.pimsim_time_ms}"
)
def compile_reference(
@@ -348,7 +331,14 @@ def compile_reference(
network_so = runner_base.with_suffix(".so")
print_step("Generate Runner Source")
gen_network_runner(model_path, network_so, args.onnx_include_dir, out=runner_dir / "runner.c", verbose=False)
gen_network_runner(
model_path,
network_so,
args.onnx_include_dir,
entry="run_main_graph",
out=runner_dir / "runner.c",
verbose=False,
)
run_logged(
"Configure Runner",
@@ -427,6 +417,7 @@ def compile_raptor_target(
raptor_extra_args=raptor_extra_args,
cwd=out_dir,
verbose=args.verbose_raptor_compile,
reporter=None,
timeout_sec=args.timeout_seconds,
)
except Exception as exc:
@@ -1453,18 +1444,17 @@ def main():
else:
pimcomp_validation = skipped_validation("Output descriptors are not available")
if hardware["core_count"] > 0:
if not args.skip_pimsim_nn and hardware["core_count"] > 0:
written_config = try_stage(
failures,
"Write pimsim-nn config",
write_pimsim_config,
"Select pimsim-nn config",
select_pimsim_config,
args,
out_dir / "pimsim_config",
hardware,
)
if written_config is not None:
pimsim_config = written_config
else:
elif not args.skip_pimsim_nn:
record_failure(
failures,
"Skip pimsim-nn config",
@@ -48,7 +48,10 @@ def prepare_pimcomp(work_dir: Path) -> None:
if replacements != 1:
raise RuntimeError("Could not set PIMCOMP GA max_iteration")
header.write_text(source, encoding="utf-8")
shutil.copy2(SUITE / "configs/arch-a.json", work_dir / "config.json")
shutil.copy2(
REPO / "validation/pimsim_configs/pimcomp/arch-a/latency_config.json",
work_dir / "config.json",
)
def comparison_command(model: Path, result_dir: Path, pimcomp_dir: Path, timeout: float) -> list[str]:
@@ -79,7 +82,6 @@ def comparison_command(model: Path, result_dir: Path, pimcomp_dir: Path, timeout
"GA",
"--timeout-seconds",
str(timeout),
"--fail-on-error",
]
@@ -21,8 +21,14 @@ if sys.version_info < (3, 10):
"Run it with a newer interpreter, for example your project venv Python."
)
from onnx_utils import _ONNX_TO_NP, onnx_io, write_inputs_to_memory_bin
from validate_one import MODE_COMPILE_ONLY, build_dump_ranges, run_pim_simulator, sanitize_output_name, validate_network
from raptor_validation.onnx_utils import _ONNX_TO_NP, onnx_io, write_inputs_to_memory_bin
from raptor_validation.validate_one import (
MODE_COMPILE_ONLY,
build_dump_ranges,
run_pim_simulator,
sanitize_output_name,
validate_network,
)
from yolo_real_image_validation import (
IMAGE_CASES,
decode_yolo_output,
@@ -80,6 +86,15 @@ def ensure_local_artifacts(args, network_onnx_path: Path):
crossbar_size=args.crossbar_size,
crossbar_count=args.crossbar_count,
core_count=args.core_count,
raptor_extra_args=[],
pimsim_nn_build_dir=None,
pimsim_config_path=None,
threshold=1e-3,
rtol=1e-5,
seed=0,
reporter=None,
model_index=1,
model_total=1,
command_timeout_seconds=args.command_timeout_seconds,
mode=MODE_COMPILE_ONLY,
verbose=args.verbose,
+122 -21
View File
@@ -6,16 +6,26 @@ import subprocess
import sys
from pathlib import Path
from colorama import Style, Fore
from validate_one import (
from raptor_validation.validate_one import (
MODE_COMPILE_ONLY,
MODE_FULL,
MODE_RUN_ONLY,
MODE_STAGE_TITLES,
PIMSIM_DONE,
PIMSIM_FAILED,
PIMSIM_NOT_RUN,
PIMSIM_SKIPPED,
ProgressReporter,
ValidationResult,
clean_workspace_artifacts,
load_pimcomp_hardware,
pimcomp_compatibility_errors,
validate_network,
)
from raptor import PIM_PASS_LABELS
from raptor_validation.raptor import PIM_PASS_LABELS
DEFAULT_PIMCOMP_CONFIG = "arch-a"
PIMCOMP_CONFIG_CHOICES = ("arch-a", "arch-b", "arch-c")
def format_return_status(returncode):
@@ -60,41 +70,70 @@ def print_average_pim_pass_timings(pass_timing_sums, pass_timing_counts, total_t
print(f" {'Total'.ljust(28)} {total_timing_sum / timed_benchmark_count:.4f}s")
def format_pimsim_metric(result, value, unit):
if result.pimsim_status == PIMSIM_DONE:
return f"{value:.6f} {unit}"
return result.pimsim_status
def main():
script_dir = Path(__file__).parent.resolve()
pimcomp_configs_dir = script_dir / "pimsim_configs" / "pimcomp"
default_pimsim_config = pimcomp_configs_dir / DEFAULT_PIMCOMP_CONFIG / "latency_config.json"
default_hardware = load_pimcomp_hardware(default_pimsim_config)
ap = argparse.ArgumentParser(description="Validate all ONNX operations under the operations/ directory.")
ap.add_argument("--raptor-path", help="Path to the Raptor compiler binary.")
ap.add_argument("--onnx-include-dir", help="Path to OnnxMlirRuntime include directory.")
ap.add_argument("--operations-dir", default=None, help="Root of the operations tree (default: operations).")
ap.add_argument("--simulator-dir", default=None,
help="Path to pim-simulator crate root (default: auto-detected relative to script).")
help="Path to the functional pim-simulator crate root "
"(default: auto-detected relative to script).")
ap.add_argument("--non-functional-simulator-build-dir", metavar="PATH", default=None,
help="Path to the non-functional simulator build directory "
"(default: auto-detected relative to script).")
ap.add_argument("--pimcomp-config", choices=PIMCOMP_CONFIG_CHOICES, default=DEFAULT_PIMCOMP_CONFIG,
help="Hardware/timing profile for non-functional simulation "
"(default: arch-a).")
ap.add_argument("--skip-non-functional-simulation", action="store_true",
help="Skip non-functional simulation.")
ap.add_argument("--threshold", type=float, default=1e-3,
help="Absolute tolerance for per-element output comparison.")
ap.add_argument("--relative-threshold", type=float, default=1e-5,
help="Relative tolerance for per-element output comparison.")
ap.add_argument("--seed", type=int, default=0, help="RNG seed for generated validation inputs.")
ap.add_argument("--crossbar-size", type=int, default=128)
ap.add_argument("--crossbar-count", type=int, default=64)
ap.add_argument("--core-count", type=int, default=144)
ap.add_argument("--crossbar-size", type=int, default=default_hardware["crossbar_rows"],
help=f"Crossbar size (default: {default_hardware['crossbar_rows']}).")
ap.add_argument("--crossbar-count", type=int, default=default_hardware["crossbar_count"],
help=f"Crossbars per core (default: {default_hardware['crossbar_count']}).")
ap.add_argument("--core-count", type=int, default=default_hardware["core_count"],
help=f"Core count (default: {default_hardware['core_count']}).")
ap.add_argument("--raptor-extra-arg", action="append", default=[],
help="Additional argument to pass through to the Raptor compiler. Repeat as needed.")
ap.add_argument("--command-timeout-seconds", type=float, default=1000000.0,
help="Per-subprocess timeout in seconds for compiler, runner, and simulator commands.")
help="Per-subprocess timeout in seconds for compiler, runner, and simulation commands.")
ap.add_argument("--clean", action="store_true",
help="Remove generated validation artifacts under each model workspace and exit.")
mode_group = ap.add_mutually_exclusive_group()
mode_group.add_argument("--compile-only", action="store_true",
help="Compile reference and PIM artifacts only; do not run reference, simulator, or compare.")
help="Compile reference and PIM artifacts only; do not run reference execution, "
"simulations, or comparison.")
mode_group.add_argument("--run-only", action="store_true",
help="Reuse existing compiled artifacts and only run inputs/reference/simulator/compare.")
help="Reuse existing compiled artifacts and only run inputs, reference execution, "
"simulations, and comparison.")
ap.add_argument("--verbose", action="store_true",
help="Print per-stage progress and subprocess logs for passing validations too.")
a = ap.parse_args()
script_dir = Path(__file__).parent.resolve()
operations_dir = Path(a.operations_dir).resolve() if a.operations_dir else script_dir / "operations"
simulator_dir = Path(a.simulator_dir).resolve() if a.simulator_dir else (
script_dir / ".." / "backend-simulators" / "pim" / "pim-simulator"
)
pimsim_nn_build_dir = (
Path(a.non_functional_simulator_build_dir).resolve()
if a.non_functional_simulator_build_dir else
script_dir / ".." / "backend-simulators" / "pim" / "pimsim-nn" / "build"
)
pimsim_config_path = pimcomp_configs_dir / a.pimcomp_config / "latency_config.json"
if not operations_dir.is_dir():
print(Fore.RED + f"Operations directory not found: {operations_dir}" + Style.RESET_ALL)
@@ -130,7 +169,26 @@ def main():
elif a.run_only:
mode = MODE_RUN_ONLY
results = {} # relative_path -> passed
selected_pimsim_config = None
if not a.skip_non_functional_simulation:
compatibility_errors = pimcomp_compatibility_errors(
pimsim_config_path,
core_count=a.core_count,
crossbar_count=a.crossbar_count,
crossbar_size=a.crossbar_size,
)
if compatibility_errors:
print(
Fore.RED
+ "Non-functional simulation disabled: "
+ "; ".join(compatibility_errors)
+ Style.RESET_ALL,
file=sys.stderr,
)
else:
selected_pimsim_config = pimsim_config_path
results = {} # relative_path -> ValidationResult
pass_timing_sums = {label: 0.0 for _, label in PIM_PASS_LABELS}
pass_timing_counts = {label: 0 for _, label in PIM_PASS_LABELS}
total_timing_sum = 0.0
@@ -143,6 +201,8 @@ def main():
onnx_path, a.raptor_path, a.onnx_include_dir, simulator_dir,
crossbar_size=a.crossbar_size, crossbar_count=a.crossbar_count, core_count=a.core_count,
raptor_extra_args=a.raptor_extra_arg,
pimsim_nn_build_dir=pimsim_nn_build_dir,
pimsim_config_path=selected_pimsim_config,
command_timeout_seconds=a.command_timeout_seconds,
threshold=a.threshold,
rtol=a.relative_threshold,
@@ -153,7 +213,7 @@ def main():
verbose=a.verbose,
mode=mode,
)
results[str(rel)] = result.passed
results[str(rel)] = result
if result.pim_pass_timings:
benchmark_total = 0.0
for label, duration in result.pim_pass_timings.items():
@@ -163,32 +223,73 @@ def main():
total_timing_sum += benchmark_total
timed_benchmark_count += 1
except subprocess.CalledProcessError as exc:
results[str(rel)] = False
results[str(rel)] = ValidationResult(False, pimsim_status=PIMSIM_NOT_RUN)
print_validation_error(reporter, rel, exc)
except Exception as exc:
results[str(rel)] = False
results[str(rel)] = ValidationResult(False, pimsim_status=PIMSIM_NOT_RUN)
print_validation_error(reporter, rel, exc)
reporter.finish()
# Summary
n_passed = sum(1 for passed in results.values() if passed)
n_passed = sum(1 for result in results.values() if result.passed)
n_total = len(results)
status_width = len("Result")
path_width = max(len("Operation"), *(len(rel) for rel in results))
separator = f"+-{'-' * path_width}-+-{'-' * status_width}-+"
formatted_metrics = {
rel: (
format_pimsim_metric(result, result.pimsim_latency_ms, "ms"),
format_pimsim_metric(result, result.pimsim_power_mw, "mW"),
)
for rel, result in results.items()
}
latency_width = max(len("Latency"), *(len(metrics[0]) for metrics in formatted_metrics.values()))
power_width = max(len("Power"), *(len(metrics[1]) for metrics in formatted_metrics.values()))
separator = (
f"+-{'-' * path_width}-+-{'-' * status_width}-+-{'-' * latency_width}"
f"-+-{'-' * power_width}-+")
print(separator)
print(f"| {'Operation'.ljust(path_width)} | {'Result'.ljust(status_width)} |")
print(
f"| {'Operation'.ljust(path_width)} | {'Result'.ljust(status_width)} | "
f"{'Latency'.ljust(latency_width)} | {'Power'.ljust(power_width)} |"
)
print(separator)
for rel, passed in results.items():
plain_status = "PASS" if passed else "FAIL"
status = Fore.GREEN + plain_status.ljust(status_width) + Style.RESET_ALL if passed else \
for rel, result in results.items():
plain_status = "PASS" if result.passed else "FAIL"
status = Fore.GREEN + plain_status.ljust(status_width) + Style.RESET_ALL if result.passed else \
Fore.RED + plain_status.ljust(status_width) + Style.RESET_ALL
print(f"| {rel.ljust(path_width)} | {status} |")
latency, power = formatted_metrics[rel]
print(
f"| {rel.ljust(path_width)} | {status} | {latency.ljust(latency_width)} | "
f"{power.ljust(power_width)} |")
print(separator)
print("\n" + Style.BRIGHT + Fore.CYAN + "Summary" + Style.RESET_ALL)
print(Style.BRIGHT + f"Passed: {n_passed}" + Style.RESET_ALL)
print(Style.BRIGHT + f"Failed: {n_total - n_passed}" + Style.RESET_ALL)
measured_latencies = [
result.pimsim_latency_ms
for result in results.values()
if result.pimsim_status == PIMSIM_DONE
]
pimsim_failed = sum(
result.pimsim_status == PIMSIM_FAILED for result in results.values()
)
pimsim_skipped = sum(
result.pimsim_status in (PIMSIM_SKIPPED, PIMSIM_NOT_RUN)
for result in results.values()
)
print(
Style.BRIGHT
+ f"pimsim-nn: {len(measured_latencies)} measured, "
f"{pimsim_failed} failed, {pimsim_skipped} skipped"
+ Style.RESET_ALL
)
if measured_latencies:
print(
Style.BRIGHT
+ f"Total latency: {sum(measured_latencies):.6f} ms"
+ Style.RESET_ALL
)
if a.verbose:
print_average_pim_pass_timings(
pass_timing_sums,