Testing and tracing¶
Testing¶
How an operator declares the shapes it is tested at on a device, and the device test harness.
Draw vectors, run it, judge and time it.
iron/tests/operators/test_catalog.py runs every declaration against the
operator's reference(). Nothing here imports pytest, so an operator
module stays importable without it.
Case
dataclass
¶
Case(kwargs: dict = dict(), extensive: bool = False, id: str | None = None, bench: bool = False, lower: bool = False)
One construction of an operator.
Attributes:
| Name | Type | Description |
|---|---|---|
kwargs |
dict
|
The constructor's keywords. |
extensive |
bool
|
Keeps the case out of the default suite. |
id |
str | None
|
Its name in test output, else the arguments. |
bench |
bool
|
CI tracks it over time ( |
lower |
bool
|
The toolchain tests lower it to instructions on every device, as they do each operator's first default case. |
Testing
dataclass
¶
Testing(cases: Iterable[Case | _Cases] | _Cases, draw: Callable[[Any], dict[str, Any]] | None = None)
How an operator is checked against its reference on a device.
Attributes:
| Name | Type | Description |
|---|---|---|
cases |
Iterable[Case | _Cases] | _Cases
|
|
draw |
Callable[[Any], dict[str, Any]] | None
|
A callable of the operator returning extra |
resolve
¶
Return the cases for cls on dev, so an inherited sweep reads the subclass's caps.
Source code in iron/common/testing.py
Vectors
dataclass
¶
One operator's test vectors, keyed by its declared buffer names.
vectors
¶
vectors(op: Operator, *, seed: int = 42, scale: float = 4.0, normal: Iterable[Operand] = (), centered: Iterable[Operand] = (), given: Mapping[Operand, ndarray | tuple[int, ...]] | None = None) -> Vectors
Random inputs for op's declared buffers, and op.reference()'s outputs.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
op
|
Operator
|
The operator whose declared buffers are drawn for. |
required |
seed
|
int
|
Seeds the draw, for reproducible vectors. |
42
|
scale
|
float
|
A uniform draw's scale; an integer buffer draws on |
4.0
|
normal
|
Iterable[Operand]
|
Inputs drawn normal instead ( |
()
|
centered
|
Iterable[Operand]
|
Inputs shifted to centre on zero. |
()
|
given
|
Mapping[Operand, ndarray | tuple[int, ...]] | None
|
Per input, an array as it is, or a shape to draw in place of the declared one (an operand the sequence packs). |
None
|
Raises:
| Type | Description |
|---|---|
ValueError
|
An operand named is not one of |
Source code in iron/common/testing.py
time_call
¶
time_call(fn: Callable, *args: Any, bytes_moved: int = 0, ops: float = 0.0, warmup: int = 0, iters: int = 20, record: Callable[[str, float], object] | None = None, **kwargs: Any) -> None
Time any compiled callable (an Operator, a graph version, a shipped binary).
The median NPU latency of iters runs of fn(*args, **kwargs).
run_test is this plus the checked run and its judgement.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
fn
|
Callable
|
The compiled callable timed. |
required |
*args
|
Any
|
Passed to |
()
|
bytes_moved
|
int
|
Bytes moved a call, for the bandwidth figure; 0 records none. |
0
|
ops
|
float
|
Per call, for the throughput figure; 0 records none. |
0.0
|
warmup
|
int
|
Untimed runs before the |
0
|
iters
|
int
|
The timed runs the median latency is taken over. |
20
|
record
|
Callable[[str, float], object] | None
|
A test's |
None
|
**kwargs
|
Any
|
Passed to |
{}
|
Raises:
| Type | Description |
|---|---|
RuntimeError
|
|
Source code in iron/common/testing.py
run_test
¶
run_test(operator: 'Operator', inputs: Vectors | Mapping[str, ndarray], outputs: Mapping[str, ndarray] | None = None, *, tolerance: Tolerance, timed_iters: int = 20, record: Callable[[str, float], object] | None = None) -> dict[str, Verdict]
Compile operator, run it on the device, check its outputs, time it.
The first run is the one checked, before a timed run rewrites an
inout buffer.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
operator
|
'Operator'
|
The declared operator compiled and run. |
required |
inputs
|
Vectors | Mapping[str, ndarray]
|
A |
required |
outputs
|
Mapping[str, ndarray] | None
|
Each buffer the operator writes, its expected result by
name; |
None
|
tolerance
|
Tolerance
|
What a checked output is judged by. |
required |
timed_iters
|
int
|
The timed runs the latency is taken over. |
20
|
record
|
Callable[[str, float], object] | None
|
|
None
|
Returns:
| Type | Description |
|---|---|
dict[str, Verdict]
|
Each failing output's |
Raises:
| Type | Description |
|---|---|
ValueError
|
|
Source code in iron/common/testing.py
Tracing¶
NPU hardware tracing, read back after a run.
An operator built with trace=TraceConfig(...) (aie.utils.trace) traces the
workers its build() marks Worker(trace=), else its first. After run(),
dump_traces(run, path) writes mlir-aie's raw trace text and Perfetto JSON.
dump_traces
¶
dump_traces(run: ImageCallable, trace_file: str | Path, *, colshift: int | None = None, mlir: str | Path | None = None, summary: bool = True) -> list[Path]
Write a completed run's trace buffer as text and Perfetto JSON.
The text goes to trace_file and the JSON beside it, one per traced
design. An untraced build writes nothing, so a test can call it
unconditionally.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
run
|
ImageCallable
|
The completed run whose trace buffer is read. |
required |
trace_file
|
str | Path
|
The text trace's path; the JSON paths are derived from it. |
required |
colshift
|
int | None
|
None lets the parser align the columns, as a design may load into another column; override it when that picks the wrong ones. |
None
|
mlir
|
str | Path | None
|
The lowered module the parser reads, else the build's own. |
None
|
summary
|
bool
|
Also print mlir-aie's per-tile cycles summary for each. |
True
|
Returns:
| Type | Description |
|---|---|
list[Path]
|
The JSON paths, empty on an untraced build. |