Skip to content

[WIP] Alternative GEMM Design - #195

Draft
hunhoffe wants to merge 26 commits into
develfrom
port-flm-gemm
Draft

[WIP] Alternative GEMM Design#195
hunhoffe wants to merge 26 commits into
develfrom
port-flm-gemm

Conversation

@hunhoffe

@hunhoffe hunhoffe commented Sep 9, 2026

Copy link
Copy Markdown
Collaborator

Describe the intent of your PR here.

Added

Changed

Removed

PR Merge Checklist

  1. The PR is rebased on the latest devel commit and pointing to devel.
  2. Your PR has been reviewed and approved.
  3. All checks are passing.

hunhoffe and others added 16 commits September 8, 2026 14:34
Adds a second GEMM design, distinct from the existing gemm operator rather
than a retuning of it: A is broadcast along each compute row from four shim
columns, C is joined at the memtile, the mmul keeps A in a single ObjectFifo
object, and an activation + clamp are fused into the C drain.

Ported from an inert-overlay design whose host wrote shapes into an RTP
buffer per dispatch. With M/K/N static per IRON operator that machinery is
gone: the trip counts are compile-time constants, so the counts kernel, the
RTP buffer and its sync lock, and the N-remainder drain path all disappear.
The ObjectFifo dims are carried over verbatim and verified to match the
original's MLIR exactly.

Three things had to change to make it correct here:

- B's fill descriptor reorders on the fly. The memtile expects B in
  t-block-major (n//T, k%S, k//S, n%T) order; the original relied on its host
  pre-packing the weights that way. Doing it in the descriptor keeps B an
  ordinary dense (K, N) tensor.

- The kernel now sets rounding to conv_even. The core powers up in floor, and
  truncation biases every conversion the same direction, so the error
  accumulates coherently over K instead of cancelling: 1% of accumulated mass
  versus 0.042%, a 24x difference. The original never set it.

- A is filled one k-block at a time and the fills are retired in batches. A
  single BD spanning every k-block stalls mid-transfer once k_iters exceeds
  the fifo depth while still holding its shim channel, and a shim tile allows
  only 16 active BDs -- which is why this only showed up at larger K.

Accuracy now matches the existing GEMM run in the same bfp16-emulated mode
(mean error 0.00042 of accumulated mass vs 0.00044). The r=8 mmul shape only
exists on that emulated path, so the test bounds error against the
accumulated mass rather than elementwise-relatively, which a K-term signed
sum cancelling ~sqrt(K) makes meaningless.

Co-Authored-By: Claude <noreply@anthropic.com>
The epilogue is short and runs once per C object, so leaving it as a call
costs real time against the per-object overhead the C ObjectFifo introduces;
the design this was ported from merges it into the core for exactly that
reason. Inlining the much larger mmul measures worse there, so only the
epilogue is merged.

aie.iron's ExternalFunction(inline=True) emits the right declaration
(link_with_mode = "merge") but its own compilation is driven by
CompilableDesign, which IRON's flow does not use -- so the .ll was never
built and aiecc failed to find it. Make .ll/.bc kernel artifacts
first-class instead: KernelCompiler infers the inline build from the
suffix and passes the symbol to mark alwaysinline, which
KernelObjectArtifact now carries. Both are rejected with a clear message
when misused (chess has no equivalent path, and an inline build with no
symbol is a silent no-op otherwise).

Verified inlined rather than silently falling back: the .ll carries
alwaysinline, the MLIR declares merge mode, and the core ELF exports
flm_gemm_acc_init and flm_gemm_k_step but no epilogue symbol.

inline_epilogue is a field so the two can be A/B'd, and it participates in
the operator name -- otherwise a cached xclbin from one satisfies the other
and the comparison measures a binary against itself.

Co-Authored-By: Claude <noreply@anthropic.com>
N previously had to be a multiple of N_TILE*COLS=1024, which excluded the
shapes this design exists to serve: a transformer's o and down projections
have N = model dim, so 8 of Gemma4's 14 projections could not run at all.

N now only has to tile to N_TILE. The trailing group of fewer than COLS
column-blocks is handled by giving each column its own trip counts rather
than a runtime branch: columns below the remainder width compute one block
more, and the rest run an A-only drain loop for it. That drain is not
optional -- A is broadcast along the whole compute row, so a column sitting
the block out must still consume its share or the columns that do have work
stall behind it. The runtime sequence matches, issuing A for every row but
B and C only for the active columns.

Also enforces a K ceiling instead of silently mis-lowering. A sweep's fills
have to go in ONE task group; splitting them across groups to fit more
k-iterations into a shim tile's 16 buffer descriptors returns wrong data,
reproducible at M=1024 K=2560 N=2560 where one group passes and two fail.
Undiagnosed, so K > 3584 now raises rather than producing bad results.

Tests cover N=1536 (4 of 8 columns active), N=128 (1 column, no full
sweep), and the real E4B o-projection and E2B down-projection shapes.
13/13 flm_gemm and 24/24 gemm pass.

Co-Authored-By: Claude <noreply@anthropic.com>
When N is smaller than the grid's COLS*N_TILE stride there is no full sweep,
so only the first rem_blocks columns participate. The remaining columns were
still getting B and C objectfifos, plus a consumer on the A broadcast, none
of which anything ever drains.

The pinned mlir-aie accepts that silently; a newer one rejects it outright
with "objectfifo.pool op segment 0 has no drainer", which is how it surfaced
-- N=128 failed to compile there while passing on the pin. It was latent
dead dataflow either way.

Those columns are now not built at all. Dropping them from the A broadcast
also removes their A-drain obligation, so the drain loop is needed only for
a column that exists and sits out the trailing block, which requires at
least one full sweep.

13/13 on both the pinned wheel and mlir_aie 1.4.3.dev60 (verified with a
clean build dir -- a shared one silently reuses the other toolchain's
xclbins and the second run proves nothing).

Co-Authored-By: Claude <noreply@anthropic.com>
B was kept as a plain row-major (K, N) tensor and reordered into the
memtile's expected layout by the fill descriptor. That is correct, and it
was a deliberate choice to spare the caller a packing step, but its
innermost run is T=8 bf16 = 16 bytes: every 128 KB B transfer became 8192
scattered bursts. B is ~70% of the bytes a dispatch moves, so the whole
operator ran at ~10 GB/s against the original overlay's ~47.

B is now consumed pre-packed via FLMGEMM.pack_B and each fill is one
contiguous read, which is exactly why the design this came from packs its
weights on the host. Weights are packed once and reused across dispatches,
so the cost belongs on the caller.

M=1024 K=1536 N=6144, same session:
  FLM peano mm.xclbin  2270 us
  shipped v1.0.4       2335 us
  flm_gemm  before    11509 us  ->  after 3446 us
  IRON GEMM (r8/f32)   3465 us
So 5.2x off the original becomes 1.4x, and level with the existing GEMM
operator while keeping 41x better accuracy.

Found by nulling the compute out: with no arithmetic at all the operator
still took 11 ms, which exonerated the kernel, the mmul geometry, the
epilogue and the L1 budget in one measurement, and pointed at the transfer
descriptors. An earlier test of this same hypothesis had reported only 5%
because it reused a cached xclbin from a build directory it had not
cleared.

The residual 1.4x is the missing depth-2 overlap: data movement alone is
2.07 ms and compute adds ~1.37 ms, so the two are running almost entirely
serialised.

Co-Authored-By: Claude <noreply@anthropic.com>
A single fill or drain may span many fifo objects -- the descriptor walks
them in the order the cores consume. The sequence was issuing one task per
object instead, so every row-block ended in a host-side await on its C
drain, and the next row-block's fills could not start until that C had come
all the way back from DDR. Those awaits were the serialisation.

Each of A, B and C now goes out as one task per column-block, with the
dimension order matching the core loop nest (mega_row, then k). For
M=1024 K=1536 N=6144 that is 120 tasks and 48 awaits where it was 1056 and
192.

  before  3446 us
  after   2534 us     FLM mm.xclbin 2241 us, shipped 2212 us
  IRON GEMM (r8/f32)  3331 us

So 1.13x off the original, and 1.3x faster than the existing GEMM operator
at this shape, still with ~40x better accuracy.

This also retires the K ceiling. The previous per-k-block tasks needed
2*k_iters + 1 buffer descriptors on a shim tile, which capped K at 3584 and
forced a batching path that silently corrupted results when it split a
sweep's fills across task groups. One task per leg needs three, so that
whole mechanism is gone: K=4096 now builds and runs (err/mass 1.5e-4).

Co-Authored-By: Claude <noreply@anthropic.com>
Issue column-block i+1's transfers before retiring i's, so they are already
moving while i computes. Retiring each block first serialises the whole
pipeline on its C await, and a C await waits for the cores.

This is the same depth-2 model the design was ported from, and it failed
twice before for a reason that had nothing to do with the idiom: when each
leg was issued per object a block cost 1 + 2*k_iters buffer descriptors on
a shim column, so two in flight blew the 16-BD limit and deadlocked. Now
that a leg is a single task a block costs three, and two in flight is six.
Collapsing the per-object tasks was the precondition, not an alternative.

M=1024 K=1536 N=6144, same session:
  flm_gemm             2248 us   8834 GFLOP/s
  FLM peano mm.xclbin  2173 us
  shipped v1.0.4       2314 us
  IRON GEMM (r8/f32)   3384 us

So the operator is now level with the original overlay -- between its two
builds, 1.5x faster than the existing GEMM operator -- while keeping ~40x
better accuracy (err/mass 2.4e-4 vs the original's 9.9e-3, which runs in
the core's default floor rounding).

Two collapses that do NOT work, for the record: B cannot fold the row-block
in because it does not vary with it and a 0-stride wrap of size > 1 is
rejected by the BD lowering; and draining C once for the whole dispatch
hangs.

37/37 pass from a clean build directory.

Co-Authored-By: Claude <noreply@anthropic.com>
The epilogue was compiled to alwaysinline LLVM IR and llvm-linked into the
core, on the strength of a comment in the design this was ported from
saying that is what pays back the per-object call overhead the C ObjectFifo
introduces. Measured here it buys nothing: 7 runs x 100 iterations,
2189 us inlined vs 2182 us as a plain call, against a ~5% run-to-run
spread. Indistinguishable.

That was the only thing requiring .ll kernel artifacts, so
iron/common/compilation/base.py goes back to upstream. Making .ll a
first-class artifact type is a reasonable capability -- aie.iron's
ExternalFunction(inline=True) emits the merge-mode declaration but does not
build anything under IRON's compilation flow, so nothing produced the file
-- but it should land on its own merits with a case that measurably needs
it, not as shared-infrastructure drift inside an operator change.

The epilogue is now an ordinary Kernel call, which also removes the
inline_epilogue field, its artifact-naming special case, and the
ExternalFunction branch in the design.

Diff is now confined to the operator and its kernels. 37/37 pass, and
performance is unchanged: min 2183 us vs the original overlay's 2175 us.

Co-Authored-By: Claude <noreply@anthropic.com>
…exactly

The kernels already had the #ifdef; nothing could reach it. rounding=
"floor" now selects the core's power-up mode, which is what the design this
was ported from runs in -- it never calls set_rounding.

With it, the operator is BIT-IDENTICAL to the shipped FastFlowLM v1.0.4
mm.xclbin: all 6291456 elements match on M=1024 K=1536 N=6144, maxdiff 0.
That pins the port as arithmetically faithful and isolates rounding as the
only numerical difference between the two.

  conv_even (default)  err/mass 0.000241
  floor                err/mass 0.009867
  shipped mm.xclbin    err/mass 0.009867

So the 41x accuracy gain really is the rounding mode alone, and users who
need to match the shipped overlay can ask for it.

The mode is part of the operator name and of both kernel object names: it
changes the emitted code, so a cached build of one mode must not satisfy
the other.

Co-Authored-By: Claude <noreply@anthropic.com>
…g in tests

Adds a README covering the parts a caller cannot guess: that B must be
pre-packed and why, the shape constraints, the accuracy budget of the
bfp16-emulated path (and why an elementwise relative tolerance is the wrong
instrument for it), and how to reproduce the shipped FastFlowLM overlay.

Verified against FastFlowLM v1.0.4's Gemma4-E2B mm.xclbin, driven directly
with the instruction stream from that project's own TXN generator, on
identical inputs:

  rounding="floor"      bit-identical, 6291456/6291456 elements, maxdiff 0
  rounding="conv_even"  differs everywhere (41x more accurate)

The activation path matches too. The shipped kernel selects its activation
from RTP word 4; this operator bakes it in at compile time with the same
0/1/2/3 mapping, and all four are bit-identical to the shipped kernel at
output_mode 0/1/2/3 -- 1048576/1048576 elements each. Inputs were scaled
down for that check so the activations sit where the curve is not flat, and
each mode's output was confirmed to differ from mode 0, so a silently
ignored mode could not pass.

clamp has no shipped counterpart: their generate_seq never writes the clamp
RTP words, so clamping is always off there. Noted in the README rather than
left implicit.

Tests now carry rounding as a parameter, with floor given its own error
bound -- holding truncation to the conv_even budget would simply fail.

Co-Authored-By: Claude <noreply@anthropic.com>
Nulling the mmul out showed this operator is COMPUTE bound -- it drops from
2178 us to 1481 us with no arithmetic -- while the GEMM operator does not
move at all under the same treatment (3374 vs 3353 us) and is therefore
entirely data-movement bound. The two want opposite fixes, and this one
wants a cheaper inner loop.

n=64 gives the mmul colA=8 instead of 4, halving accumulator traffic per
mac, at the cost of doubling A fetches. With compute on the critical path
that trades well:

  M/K/N              k_iters  tile_n=64  tile_n=128
  1024/512/4096          1      642 us     589 us
  1024/1024/4096         2      850 us    1034 us
  1024/1536/6144         3     1741 us    2178 us
  1024/2560/4096         5     1891 us    2371 us
  2048/2048/2048         4     1535 us    1924 us
  256/4096/1024          8      254 us     316 us

Only the single-k-iteration shape prefers 128: there is too little compute
there to hide the extra A traffic. tile_n now defaults to None and picks 128
when K == 512 and 64 otherwise, which is correct on every shape measured
including the K=1024 boundary. It stays overridable.

At 1741 us the operator is now 1.25x faster than the shipped overlay (2175
us) and 1.9x faster than GEMM (3353 us) on the reference shape.

pack_B becomes an instance method: the packing layout depends on tile_n, so
a static one silently mismatches the operator it feeds. tile_n also joins
the operator and kernel-object names.

This vindicates the CT_MAX_K hypothesis from early on, which had been
dismissed after testing it while the operator was still DMA bound at 11 ms
-- where compute could not matter. Right idea, wrong regime.

39/39 pass.

Co-Authored-By: Claude <noreply@anthropic.com>
The table claimed GEMM in the same emulated mode had 0.00044 mean error
against this operator's 0.00024. That was wrong: 0.00044 came from a
different configuration (prio_accuracy=False, a bf16 accumulator), quoted
from a separate experiment and mislabelled as the f32 one.

Measured on identical data, this operator and GEMM with
emulate_bf16_mmul_with_bfp16=True and prio_accuracy=True are numerically
indistinguishable -- err/mass 0.000241, signed bias +0.0871, max 15.44 for
both, and the same at either tile_n. Same mmul shape, same emulation, same
f32 accumulation, same rounding; there was never a reason for them to
differ.

The only real accuracy difference remains conv_even versus the shipped
overlay's floor, which stands at 41x and is separately verified bit-exact.

Co-Authored-By: Claude <noreply@anthropic.com>
Takes the Peano pin from mlir-aie's own utils/peano-requirements.txt at
c80b88c, so the two stay in step.

1.4.3 adds ObjectFifo DMA channel pinning (prod_dma_channel /
cons_dma_channels, exposed as prod(channel=)/cons(channel=)), which is
absent from 1.4.2.dev16.

39/39 of the flm_gemm and gemm iter0 tests pass on the new toolchain, and
flm_gemm's latency is unchanged within noise at M=1024 K=1536 N=6144
(1743 us, against 1741 before the bump).

Co-Authored-By: Claude <noreply@anthropic.com>
Hold a whole column-block's B in the memtile as one object and replay it
per row-block, so DDR reads it once rather than m_row_blocks times. B is
the dominant DDR leg -- 75 MB of the 126 MB moved at M=1024 K=1536
N=6144 -- so this is ~43% less traffic overall.

This operator is DDR-bandwidth bound, so that is a latency win as well as
a power one, and it grows with the height of the problem because B's
re-reads scale with m_row_blocks. At K=1024 N=4096, full builds, min of
interleaved rounds:

  M=512  (2 row-blocks)  470.8 -> 468.5 us   0.5%
  M=1024 (4 row-blocks)  860.4 -> 846.5 us   1.6%
  M=2048 (8 row-blocks) 1760.1 -> 1622.8 us  7.8%

With the mmul nulled out to isolate data movement, the non-resident floor
at M=2048 is 1739 us to move 118 MB -- 68 GB/s, against a memcpy-measured
63-70 GB/s roof -- and residency drops it to 1135 us.

Only 137 us of that 604 us is captured, because repeat_count restarts the
memtile BD chain at every replay boundary. Closing that gap is the
largest known remaining lever here; it is left for follow-up.

The replay is repeat_count on the forward(). Earlier attempts used
iter_count and hung on every shape with more than one column-block:
iter_count does not replay anything, it only bounds how many times an
end cycles through all of its buffers (objects = iter_count *
elemNumber * repeat_count), so it counts depth-cycles, not objects, and
a wrong value runs the BD chain out from under the cores.

Correct k ordering needs the memtile to hold ONE object spanning every
k-block; replaying a pool of k_iters smaller objects emits k0,k0,k1,k1,
... instead of the k0..kn sequence the cores accumulate in.

Residency is gated on the buffer fitting double-buffered, which admits
only k_iters <= 2 (K <= 1024 at tile_n=64); larger K falls back to the
previous behaviour unchanged.

Co-Authored-By: Claude <noreply@anthropic.com>
Three gaps in the README, all found by re-deriving its numbers.

Resident B was undocumented. It is gated on a whole column-block's B
fitting the memtile double-buffered (K <= 1024 at tile_n=64), so it is
inactive on the shape the performance table reports and easy to miss
entirely. Record what it does, where it applies, and that its benefit
grows with M -- 0.5% / 1.6% / 7.8% at M=512/1024/2048 -- along with the
604 us of DMA floor it exposes against the 137 us that currently reaches
the full build.

The measurement recipe was unsafe. "min of per-run medians" is only
sound if the runs are not all in the same place: dispatch latency here
is bimodal with modes ~6% apart, so a batch landing wholly in one mode
makes that statistic a mode selector. It reported a convincing 5% win
for a change subsequently shown to do nothing. Say to interleave the
configurations and to require the min and the median to agree.

The tile_n=64 DMA-only cell was blank. Filling it in (1692 of 1741 us)
shows the default configuration is itself data-movement bound, which the
surrounding prose implied only of tile_n=128 -- and which is what makes
"move fewer bytes" the right next lever rather than "write a faster
kernel".

Co-Authored-By: Claude <noreply@anthropic.com>
@github-actions

github-actions Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

CI Test Results

f8890d7 (2026_09_10_17_00_31)

IRON - CI Summary

Examples

iron/applications/llama_3.2_1b
Test Krackan Status Krackan Phoenix Status Phoenix
test_llama_3_2_1b[llama_3.2_1b_prompt_1024_tokens_1] - - -
test_llama_3_2_1b[llama_3.2_1b_prompt_1024_tokens_40] - - -
test_llama_3_2_1b[llama_3.2_1b_prompt_13_tokens_1] - - -
test_llama_3_2_1b[llama_3.2_1b_prompt_13_tokens_40] - - -

Small

iron/operators/axpy
Test Krackan Status Krackan Latency (mean) Phoenix Status Phoenix Latency (mean)
test_axpy[input_length_2048-num_aie_columns_1-tile_size_2048-scalar_factor_3.0] 178.76 312.86
test_axpy[input_length_2048-num_aie_columns_2-tile_size_1024-scalar_factor_3.0] 166.44 343.92
test_axpy[input_length_2048-num_aie_columns_4-tile_size_512-scalar_factor_3.0] 173.18 680.94
test_axpy[input_length_2048-num_aie_columns_8-tile_size_256-scalar_factor_3.0] 224.44 - -
iron/operators/dequant
Test Krackan Status Krackan Latency (mean) Phoenix Status Phoenix Latency (mean)
test_dequant[input_length_2048-num_aie_columns_1-num_channels_1-tile_size_2048-group_size_32] 167.38 313.18
test_dequant[input_length_2048-num_aie_columns_1-num_channels_2-tile_size_1024-group_size_32] 152.64 358.90
test_dequant[input_length_2048-num_aie_columns_2-num_channels_1-tile_size_1024-group_size_32] 181.18 367.78
test_dequant[input_length_2048-num_aie_columns_2-num_channels_2-tile_size_512-group_size_32] 191.06 317.58
test_dequant[input_length_2048-num_aie_columns_4-num_channels_1-tile_size_512-group_size_32] 169.24 381.44
test_dequant[input_length_2048-num_aie_columns_4-num_channels_2-tile_size_256-group_size_32] 205.68 460.22
test_dequant[input_length_2048-num_aie_columns_8-num_channels_1-tile_size_256-group_size_32] 176.28 - -
test_dequant[input_length_2048-num_aie_columns_8-num_channels_2-tile_size_128-group_size_32] 203.70 - -
iron/operators/elementwise_add
Test Krackan Status Krackan Latency (mean) Phoenix Status Phoenix Latency (mean)
test_elementwise_add[input_length_2048-num_aie_columns_1-tile_size_2048] 147.04 396.70
test_elementwise_add[input_length_2048-num_aie_columns_2-tile_size_1024] 163.78 390.24
test_elementwise_add[input_length_2048-num_aie_columns_4-tile_size_512] 160.88 445.76
test_elementwise_add[input_length_2048-num_aie_columns_8-tile_size_256] 180.00 - -
iron/operators/elementwise_mul
Test Krackan Status Krackan Latency (mean) Phoenix Status Phoenix Latency (mean)
test_elementwise_mul[input_length_2048-num_aie_columns_1-tile_size_2048] 152.80 383.26
test_elementwise_mul[input_length_2048-num_aie_columns_2-tile_size_1024] 159.30 486.84
test_elementwise_mul[input_length_2048-num_aie_columns_4-tile_size_512] 176.08 344.30
test_elementwise_mul[input_length_2048-num_aie_columns_8-tile_size_256] 182.70 - -
iron/operators/flm_gemm
Test Krackan Status Krackan Latency (mean) Phoenix Status Phoenix Latency (mean)
test_flm_gemm[M_256-K_512-N_1024-epilogue_gelu-clamp_None-rounding_conv_even] 381.32 - -
test_flm_gemm[M_256-K_512-N_1024-epilogue_none-clamp_(-2.0, 2.0)-rounding_conv_even] 309.74 - -
test_flm_gemm[M_256-K_512-N_1024-epilogue_none-clamp_None-rounding_conv_even] 315.80 - -
test_flm_gemm[M_256-K_512-N_1024-epilogue_none-clamp_None-rounding_floor] 360.06 - -
test_flm_gemm[M_256-K_512-N_1024-epilogue_silu-clamp_None-rounding_conv_even] 324.38 - -
test_flm_gemm[M_256-K_512-N_128-epilogue_none-clamp_None-rounding_conv_even] 224.08 - -
test_flm_gemm[M_256-K_512-N_1536-epilogue_none-clamp_None-rounding_conv_even] 356.66 - -
test_flm_gemm[M_512-K_1024-N_2048-epilogue_none-clamp_None-rounding_conv_even] 391.20 - -
test_flm_gemm_stride_overflow_rejected[M_1024-K_10240-N_2560] - -
test_flm_gemm_stride_overflow_rejected[M_1024-K_2560-N_10240] - -
iron/operators/gelu
Test Krackan Status Krackan Latency (mean) Phoenix Status Phoenix Latency (mean)
test_gelu[input_length_2048-num_aie_columns_1-num_channels_1-tile_size_2048] 149.84 479.50
test_gelu[input_length_2048-num_aie_columns_1-num_channels_2-tile_size_1024] 161.46 347.72
test_gelu[input_length_2048-num_aie_columns_2-num_channels_1-tile_size_1024] 182.88 347.60
test_gelu[input_length_2048-num_aie_columns_2-num_channels_2-tile_size_512] 165.38 363.06
test_gelu[input_length_2048-num_aie_columns_4-num_channels_1-tile_size_512] 173.30 408.78
test_gelu[input_length_2048-num_aie_columns_4-num_channels_2-tile_size_256] 190.00 444.98
test_gelu[input_length_2048-num_aie_columns_8-num_channels_1-tile_size_256] 180.56 - -
test_gelu[input_length_2048-num_aie_columns_8-num_channels_2-tile_size_128] 202.24 - -
iron/operators/gemm
Test Krackan Status Krackan Latency (mean) Phoenix Status Phoenix Latency (mean)
test_gemm[M_1792-K_896-N_1152-num_aie_columns_8-b_col_maj_False-c_col_maj_True-m_64-k_32-n_48-trace_size_0-partition_N_1] 2344.74 - -
test_gemm[M_192-K_384-N_64-num_aie_columns_4-b_col_maj_False-c_col_maj_False-m_48-k_96-n_16-trace_size_0-partition_N_1] 256.58 493.56
test_gemm[M_192-K_384-N_64-num_aie_columns_4-b_col_maj_True-c_col_maj_True-m_48-k_96-n_16-trace_size_0-partition_N_1] 337.98 513.22
test_gemm[M_2048-K_2048-N_2048-num_aie_columns_1-b_col_maj_False-c_col_maj_False-m_64-k_64-n_64-trace_size_0-partition_N_1] 48530.80 82311.12
test_gemm[M_2048-K_2048-N_2048-num_aie_columns_2-b_col_maj_True-c_col_maj_False-m_64-k_64-n_64-trace_size_0-partition_N_1] 28299.60 25155.10
test_gemm[M_2048-K_2048-N_2048-num_aie_columns_8-b_col_maj_True-c_col_maj_True-m_64-k_64-n_64-trace_size_0-partition_N_1] 7880.98 - -
test_gemm[M_384-K_1536-N_1792-num_aie_columns_4-b_col_maj_True-c_col_maj_False-m_32-k_48-n_64-trace_size_0-partition_N_1] 2410.18 3123.56
test_gemm[M_64-K_512-N_256-num_aie_columns_4-b_col_maj_True-c_col_maj_False-m_16-k_64-n_64-trace_size_0-partition_N_4] 3450.52 5946.64
test_gemm[M_896-K_1792-N_640-num_aie_columns_8-b_col_maj_False-c_col_maj_True-m_32-k_64-n_80-trace_size_0-partition_N_1] 1891.22 - -
iron/operators/gemv
Test Krackan Status Krackan Latency (mean) Phoenix Status Phoenix Latency (mean)
test_gemv[M_128-K_128-num_aie_columns_1-tile_size_input_32-tile_size_output_128] 0.17 0.11
test_gemv[M_2048-K_8192-num_aie_columns_1-tile_size_input_1-tile_size_output_2048] 12.40 3.58
test_gemv[M_2048-K_8192-num_aie_columns_2-tile_size_input_1-tile_size_output_1024] 22.64 5.88
test_gemv[M_2048-K_8192-num_aie_columns_4-tile_size_input_1-tile_size_output_512] 39.77 9.10
test_gemv[M_2048-K_8192-num_aie_columns_8-tile_size_input_1-tile_size_output_256] 41.32 - -
test_gemv[M_8192-K_2048-num_aie_columns_1-tile_size_input_4-tile_size_output_1024] 11.87 3.59
test_gemv[M_8192-K_2048-num_aie_columns_2-tile_size_input_4-tile_size_output_1024] 23.79 6.28
test_gemv[M_8192-K_2048-num_aie_columns_4-tile_size_input_4-tile_size_output_1024] 36.52 10.44
test_gemv[M_8192-K_2048-num_aie_columns_8-tile_size_input_4-tile_size_output_1024] 41.06 - -
test_gemv_batched[M_1024-K_1024-num_aie_columns_1-tile_size_input_1-tile_size_output_64-num_batches_2] 8.73 2.81
test_gemv_batched[M_1026-K_64-num_aie_columns_1-tile_size_input_1-tile_size_output_2-num_batches_2] 0.88 0.28
test_gemv_batched[M_256-K_128-num_aie_columns_1-tile_size_input_1-tile_size_output_256-num_batches_4] 1.16 0.45
test_gemv_batched[M_256-K_128-num_aie_columns_8-tile_size_input_1-tile_size_output_32-num_batches_100] 14.60 - -
test_gemv_batched[M_448-K_64-num_aie_columns_8-tile_size_input_1-tile_size_output_56-num_batches_192] 13.35 - -
test_gemv_batched[M_512-K_64-num_aie_columns_8-tile_size_input_4-tile_size_output_64-num_batches_32] 7.76 - -
test_gemv_batched[M_64-K_1536-num_aie_columns_1-tile_size_input_1-tile_size_output_64-num_batches_8] 5.10 1.06
test_gemv_gelu[M_128-K_128-num_aie_columns_1-tile_size_input_32-tile_size_output_128] 0.17 -
test_gemv_gelu[M_2048-K_8192-num_aie_columns_1-tile_size_input_1-tile_size_output_2048] 12.31 -
test_gemv_gelu[M_8192-K_2048-num_aie_columns_1-tile_size_input_4-tile_size_output_1024] 12.12 -
iron/operators/layer_norm
Test Krackan Status Krackan Latency (mean) Phoenix Status Phoenix Latency (mean)
test_layer_norm[input_length_2048-num_aie_columns_1-num_channels_1-tile_size_2048] 184.06 393.82
test_layer_norm[input_length_2048-num_aie_columns_1-num_channels_2-tile_size_1024] 176.86 411.52
test_layer_norm[input_length_2048-num_aie_columns_2-num_channels_1-tile_size_1024] 166.44 689.82
test_layer_norm[input_length_2048-num_aie_columns_2-num_channels_2-tile_size_512] 170.04 323.38
test_layer_norm[input_length_2048-num_aie_columns_4-num_channels_1-tile_size_512] 161.34 491.82
test_layer_norm[input_length_2048-num_aie_columns_4-num_channels_2-tile_size_256] 212.64 474.38
test_layer_norm[input_length_2048-num_aie_columns_8-num_channels_1-tile_size_256] 186.58 - -
test_layer_norm[input_length_2048-num_aie_columns_8-num_channels_2-tile_size_128] 242.78 - -
iron/operators/leaky_relu
Test Krackan Status Krackan Latency (mean) Phoenix Status Phoenix Latency (mean)
test_leaky_relu[input_length_2048-num_aie_columns_1-num_channels_1-tile_size_2048-alpha_0.01] 188.00 387.58
test_leaky_relu[input_length_2048-num_aie_columns_1-num_channels_1-tile_size_2048-alpha_0.1] 195.48 383.56
test_leaky_relu[input_length_2048-num_aie_columns_1-num_channels_1-tile_size_2048-alpha_0.25] 189.00 390.52
test_leaky_relu[input_length_2048-num_aie_columns_1-num_channels_2-tile_size_1024-alpha_0.01] 202.76 514.76
test_leaky_relu[input_length_2048-num_aie_columns_2-num_channels_1-tile_size_1024-alpha_0.01] 186.70 354.24
test_leaky_relu[input_length_2048-num_aie_columns_2-num_channels_2-tile_size_512-alpha_0.01] 186.02 550.72
test_leaky_relu[input_length_2048-num_aie_columns_4-num_channels_1-tile_size_512-alpha_0.01] 202.36 377.30
test_leaky_relu[input_length_2048-num_aie_columns_4-num_channels_2-tile_size_256-alpha_0.01] 190.38 441.46
test_leaky_relu[input_length_2048-num_aie_columns_8-num_channels_1-tile_size_256-alpha_0.01] 204.72 - -
test_leaky_relu[input_length_2048-num_aie_columns_8-num_channels_2-tile_size_128-alpha_0.01] 239.20 - -
iron/operators/mem_copy
Test Krackan Status Krackan Latency (mean) Phoenix Status Phoenix Latency (mean)
test_mem_copy[input_length_2048-num_cores_1-num_channels_1-bypass_False-tile_size_2048] 154.22 441.50
test_mem_copy[input_length_2048-num_cores_16-num_channels_2-bypass_False-tile_size_128] 219.04 - -
test_mem_copy[input_length_2048-num_cores_2-num_channels_1-bypass_False-tile_size_1024] 148.74 491.12
test_mem_copy[input_length_2048-num_cores_2-num_channels_2-bypass_False-tile_size_1024] 165.24 532.00
test_mem_copy[input_length_2048-num_cores_4-num_channels_1-bypass_False-tile_size_512] 178.30 557.12
test_mem_copy[input_length_2048-num_cores_4-num_channels_2-bypass_False-tile_size_512] 174.00 446.92
test_mem_copy[input_length_2048-num_cores_8-num_channels_1-bypass_False-tile_size_256] 190.96 - -
test_mem_copy[input_length_2048-num_cores_8-num_channels_2-bypass_False-tile_size_256] 185.70 499.62
iron/operators/mha
Test Krackan Status Krackan Latency (mean) Phoenix Status Phoenix Latency (mean)
test_mha[seq_len_16384-dim_64-num_heads_1-num_pipelines_8-num_kv_heads_0] 47556.90 - -
iron/operators/relu
Test Krackan Status Krackan Latency (mean) Phoenix Status Phoenix Latency (mean)
test_relu[input_length_2048-num_aie_columns_1-num_channels_1-tile_size_2048] 151.26 447.90
test_relu[input_length_2048-num_aie_columns_1-num_channels_2-tile_size_1024] 150.04 449.38
test_relu[input_length_2048-num_aie_columns_2-num_channels_1-tile_size_1024] 163.22 345.22
test_relu[input_length_2048-num_aie_columns_2-num_channels_2-tile_size_512] 152.32 399.60
test_relu[input_length_2048-num_aie_columns_4-num_channels_1-tile_size_512] 173.84 448.30
test_relu[input_length_2048-num_aie_columns_4-num_channels_2-tile_size_256] 172.92 422.80
test_relu[input_length_2048-num_aie_columns_8-num_channels_1-tile_size_256] 197.02 - -
test_relu[input_length_2048-num_aie_columns_8-num_channels_2-tile_size_128] 201.04 - -
iron/operators/repeat
Test Krackan Status Krackan Latency (mean) Phoenix Status Phoenix Latency (mean)
test_cols_without_a_legal_split_is_rejected[cols_1031-why_prime > 1023: the only divisors are 1 and cols, neither legal] - -
test_cols_without_a_legal_split_is_rejected[cols_2062-why_2 x 1031: the only word-aligned chunk leaves a 1031-wide chunk count] - -
test_cols_without_a_legal_split_is_rejected[cols_513-why_odd: every divisor is odd, so no chunk is a whole 32-bit word] - -
test_repeat[rows_4-cols_1024-repeat_2-transfer_size_None] 156.28 271.42
test_repeat[rows_8-cols_512-repeat_4-transfer_size_64] 187.70 436.26
test_repeat[rows_8-cols_64-repeat_4-transfer_size_None] 191.10 300.92
iron/operators/rms_norm
Test Krackan Status Krackan Latency (mean) Phoenix Status Phoenix Latency (mean)
test_rms_norm[input_length_2048-num_aie_columns_1-num_channels_1-tile_size_2048-weighted_False] 155.94 355.50
test_rms_norm[input_length_2048-num_aie_columns_1-num_channels_1-tile_size_2048-weighted_True] 166.46 383.60
test_rms_norm[input_length_2048-num_aie_columns_1-num_channels_2-tile_size_1024-weighted_False] 150.42 307.90
test_rms_norm[input_length_2048-num_aie_columns_1-num_channels_2-tile_size_1024-weighted_True] 162.82 255.96
test_rms_norm[input_length_2048-num_aie_columns_2-num_channels_1-tile_size_1024-weighted_False] 161.02 581.74
test_rms_norm[input_length_2048-num_aie_columns_2-num_channels_1-tile_size_1024-weighted_True] 182.32 476.56
test_rms_norm[input_length_2048-num_aie_columns_2-num_channels_2-tile_size_512-weighted_False] 188.30 391.84
test_rms_norm[input_length_2048-num_aie_columns_2-num_channels_2-tile_size_512-weighted_True] 178.98 379.42
test_rms_norm[input_length_2048-num_aie_columns_4-num_channels_1-tile_size_512-weighted_False] 173.30 424.02
test_rms_norm[input_length_2048-num_aie_columns_4-num_channels_1-tile_size_512-weighted_True] 185.62 426.98
test_rms_norm[input_length_2048-num_aie_columns_4-num_channels_2-tile_size_256-weighted_False] 180.04 734.92
test_rms_norm[input_length_2048-num_aie_columns_4-num_channels_2-tile_size_256-weighted_True] 195.06 - -
test_rms_norm[input_length_2048-num_aie_columns_8-num_channels_1-tile_size_256-weighted_False] 154.86 - -
test_rms_norm[input_length_2048-num_aie_columns_8-num_channels_1-tile_size_256-weighted_True] 195.06 - -
test_rms_norm[input_length_2048-num_aie_columns_8-num_channels_2-tile_size_128-weighted_False] 208.66 - -
iron/operators/rope
Test Krackan Status Krackan Latency (mean) Phoenix Status Phoenix Latency (mean)
test_rope[rows_32-cols_512-angle_rows_32-aie_columns_1-method_type_0] 157.40 496.84
test_rope[rows_32-cols_512-angle_rows_32-aie_columns_2-method_type_0] 183.94 344.06
test_rope[rows_32-cols_512-angle_rows_32-aie_columns_4-method_type_0] 177.26 498.02
test_rope[rows_32-cols_512-angle_rows_32-aie_columns_8-method_type_0] 179.48 - -
test_rope[rows_32-cols_512-angle_rows_8-aie_columns_1-method_type_0] 176.54 357.72
test_rope[rows_32-cols_512-angle_rows_8-aie_columns_2-method_type_0] 168.32 742.02
test_rope[rows_32-cols_512-angle_rows_8-aie_columns_4-method_type_0] 190.38 379.20
test_rope[rows_32-cols_512-angle_rows_8-aie_columns_8-method_type_0] 203.24 - -
iron/operators/sigmoid
Test Krackan Status Krackan Latency (mean) Phoenix Status Phoenix Latency (mean)
test_sigmoid[input_length_2048-num_aie_columns_1-num_channels_1-tile_size_2048] 179.38 327.42
test_sigmoid[input_length_2048-num_aie_columns_1-num_channels_2-tile_size_1024] 176.42 352.88
test_sigmoid[input_length_2048-num_aie_columns_2-num_channels_1-tile_size_1024] 182.72 410.66
test_sigmoid[input_length_2048-num_aie_columns_2-num_channels_2-tile_size_512] 165.38 328.46
test_sigmoid[input_length_2048-num_aie_columns_4-num_channels_1-tile_size_512] 185.46 281.24
test_sigmoid[input_length_2048-num_aie_columns_4-num_channels_2-tile_size_256] 188.02 417.00
test_sigmoid[input_length_2048-num_aie_columns_8-num_channels_1-tile_size_256] 173.92 - -
test_sigmoid[input_length_2048-num_aie_columns_8-num_channels_2-tile_size_128] 219.76 - -
iron/operators/silu
Test Krackan Status Krackan Latency (mean) Phoenix Status Phoenix Latency (mean)
test_silu[input_length_2048-num_aie_columns_1-num_channels_1-tile_size_2048] 189.44 419.80
test_silu[input_length_2048-num_aie_columns_2-num_channels_1-tile_size_1024] 174.76 614.32
test_silu[input_length_2048-num_aie_columns_4-num_channels_1-tile_size_512] 174.74 364.50
test_silu[input_length_2048-num_aie_columns_8-num_channels_1-tile_size_256] 185.52 - -
iron/operators/softmax
Test Krackan Status Krackan Latency (mean) Phoenix Status Phoenix Latency (mean)
test_softmax[input_length_32768-num_aie_columns_2-num_channels_2-tile_size_1024] 186.62 503.36
test_softmax[input_length_32768-num_aie_columns_2-num_channels_2-tile_size_2048] 181.10 362.54
test_softmax[input_length_32768-num_aie_columns_2-num_channels_2-tile_size_512] 189.82 709.06
iron/operators/strided_copy
Test Krackan Status Krackan Latency (mean) Phoenix Status Phoenix Latency (mean)
test_strided_copy[chunked_transfer] 181.40 333.14
test_strided_copy[contiguous] 171.88 281.12
test_strided_copy[four_channels] 193.92 493.18
test_strided_copy[kv_slot0] 201.80 365.18
test_strided_copy[kv_slot5] 169.24 564.86
test_strided_copy[kv_slot5_four_channels] 156.26 415.44
test_strided_copy[kv_slot5_two_channels] 176.54 478.10
test_strided_copy[kv_slot_last] 183.20 397.20
test_strided_copy[two_channels] 181.32 281.34
test_strided_copy[two_channels_chunked] 175.30 344.48
test_transfer_size_not_dividing_per_channel_share_is_rejected[iter0] - -
test_transfer_size_not_dividing_per_channel_share_is_rejected[iter1] - -
test_transfer_size_not_dividing_per_channel_share_is_rejected[iter2] - -
test_transfer_size_not_dividing_per_channel_share_is_rejected[iter3] - -
test_transfer_size_not_dividing_per_channel_share_is_rejected[iter4] - -
iron/operators/swiglu_decode
Test Krackan Status Krackan Latency (mean) Phoenix Status Phoenix Latency (mean)
test_swiglu_decode[embedding_dim_1024-hidden_dim_3584] 981.96 12807.62
test_swiglu_decode[embedding_dim_2048-hidden_dim_2048] 1007.02 16940.08
iron/operators/swiglu_prefill
Test Krackan Status Krackan Latency (mean) Phoenix Status Phoenix Latency (mean)
test_swiglu_prefill[seq_len_256-embedding_dim_2048-hidden_dim_2048-prio_accuracy_False] 2174.90 23430.19
iron/operators/tanh
Test Krackan Status Krackan Latency (mean) Phoenix Status Phoenix Latency (mean)
test_tanh[input_length_2048-num_aie_columns_1-num_channels_1-tile_size_2048] 155.88 291.46
test_tanh[input_length_2048-num_aie_columns_1-num_channels_2-tile_size_1024] 180.84 480.04
test_tanh[input_length_2048-num_aie_columns_2-num_channels_1-tile_size_1024] 177.08 373.98
test_tanh[input_length_2048-num_aie_columns_2-num_channels_2-tile_size_512] 205.98 467.92
test_tanh[input_length_2048-num_aie_columns_4-num_channels_1-tile_size_512] 181.36 472.94
test_tanh[input_length_2048-num_aie_columns_4-num_channels_2-tile_size_256] 190.92 471.36
test_tanh[input_length_2048-num_aie_columns_8-num_channels_1-tile_size_256] 186.58 - -
test_tanh[input_length_2048-num_aie_columns_8-num_channels_2-tile_size_128] 208.08 - -
iron/operators/transpose
Test Krackan Status Krackan Latency (mean) Phoenix Status Phoenix Latency (mean)
test_transpose[M_2048-N_64-aie_columns_1-channels_1-m_64-n_64-s_8-num_batches_1] 191.06 445.18
test_transpose[M_2048-N_64-aie_columns_1-channels_1-m_64-n_64-s_8-num_batches_2] 233.20 653.10
test_transpose[M_2048-N_64-aie_columns_1-channels_2-m_64-n_64-s_8-num_batches_1] 215.48 700.36
Krackan - Small

IRON

Tested on 2026_09_10_17_00_31 at commit f8890d7.

iron/operators/axpy
TestChecksLatency (mean)Bandwidth (mean)Throughput (mean)
test_axpy[input_length_2048-num_aie_columns_1-tile_size_2048-scalar_factor_3.0]✅ 5/5178.760.07n/a
test_axpy[input_length_2048-num_aie_columns_2-tile_size_1024-scalar_factor_3.0]✅ 5/5166.440.07n/a
test_axpy[input_length_2048-num_aie_columns_4-tile_size_512-scalar_factor_3.0]✅ 5/5173.180.08n/a
test_axpy[input_length_2048-num_aie_columns_8-tile_size_256-scalar_factor_3.0]✅ 5/5224.440.06n/a
iron/operators/dequant
TestChecksLatency (mean)Bandwidth (mean)Throughput (mean)
test_dequant[input_length_2048-num_aie_columns_1-num_channels_1-tile_size_2048-group_size_32]✅ 5/5167.380.03n/a
test_dequant[input_length_2048-num_aie_columns_1-num_channels_2-tile_size_1024-group_size_32]✅ 5/5152.640.03n/a
test_dequant[input_length_2048-num_aie_columns_2-num_channels_1-tile_size_1024-group_size_32]✅ 5/5181.180.03n/a
test_dequant[input_length_2048-num_aie_columns_2-num_channels_2-tile_size_512-group_size_32]✅ 5/5191.060.03n/a
test_dequant[input_length_2048-num_aie_columns_4-num_channels_1-tile_size_512-group_size_32]✅ 5/5169.240.03n/a
test_dequant[input_length_2048-num_aie_columns_4-num_channels_2-tile_size_256-group_size_32]✅ 5/5205.680.03n/a
test_dequant[input_length_2048-num_aie_columns_8-num_channels_1-tile_size_256-group_size_32]✅ 5/5176.280.03n/a
test_dequant[input_length_2048-num_aie_columns_8-num_channels_2-tile_size_128-group_size_32]✅ 5/5203.700.03n/a
iron/operators/elementwise_add
TestChecksLatency (mean)Bandwidth (mean)Throughput (mean)
test_elementwise_add[input_length_2048-num_aie_columns_1-tile_size_2048]✅ 5/5147.040.09n/a
test_elementwise_add[input_length_2048-num_aie_columns_2-tile_size_1024]✅ 5/5163.780.08n/a
test_elementwise_add[input_length_2048-num_aie_columns_4-tile_size_512]✅ 5/5160.880.08n/a
test_elementwise_add[input_length_2048-num_aie_columns_8-tile_size_256]✅ 5/5180.000.07n/a
iron/operators/elementwise_mul
TestChecksLatency (mean)Bandwidth (mean)Throughput (mean)
test_elementwise_mul[input_length_2048-num_aie_columns_1-tile_size_2048]✅ 5/5152.800.08n/a
test_elementwise_mul[input_length_2048-num_aie_columns_2-tile_size_1024]✅ 5/5159.300.08n/a
test_elementwise_mul[input_length_2048-num_aie_columns_4-tile_size_512]✅ 5/5176.080.07n/a
test_elementwise_mul[input_length_2048-num_aie_columns_8-tile_size_256]✅ 5/5182.700.07n/a
iron/operators/flm_gemm
TestChecksLatency (mean)Bandwidth (mean)Throughput (mean)
test_flm_gemm[M_256-K_512-N_1024-epilogue_gelu-clamp_None-rounding_conv_even]✅ 5/5381.323.61704.18
test_flm_gemm[M_256-K_512-N_1024-epilogue_none-clamp_(-2.0, 2.0)-rounding_conv_even]✅ 5/5309.744.56890.17
test_flm_gemm[M_256-K_512-N_1024-epilogue_none-clamp_None-rounding_conv_even]✅ 5/5315.804.67911.49
test_flm_gemm[M_256-K_512-N_1024-epilogue_none-clamp_None-rounding_floor]✅ 5/5360.063.84749.54
test_flm_gemm[M_256-K_512-N_1024-epilogue_silu-clamp_None-rounding_conv_even]✅ 5/5324.384.52881.68
test_flm_gemm[M_256-K_512-N_128-epilogue_none-clamp_None-rounding_conv_even]✅ 5/5224.081.81151.42
test_flm_gemm[M_256-K_512-N_1536-epilogue_none-clamp_None-rounding_conv_even]✅ 5/5356.665.681182.69
test_flm_gemm[M_512-K_1024-N_2048-epilogue_none-clamp_None-rounding_conv_even]✅ 5/5391.2014.625704.18
test_flm_gemm_stride_overflow_rejected[M_1024-K_10240-N_2560]✅ 5/5n/an/an/a
test_flm_gemm_stride_overflow_rejected[M_1024-K_2560-N_10240]✅ 5/5n/an/an/a
iron/operators/gelu
TestChecksLatency (mean)Bandwidth (mean)Throughput (mean)
test_gelu[input_length_2048-num_aie_columns_1-num_channels_1-tile_size_2048]✅ 5/5149.840.06n/a
test_gelu[input_length_2048-num_aie_columns_1-num_channels_2-tile_size_1024]✅ 5/5161.460.05n/a
test_gelu[input_length_2048-num_aie_columns_2-num_channels_1-tile_size_1024]✅ 5/5182.880.05n/a
test_gelu[input_length_2048-num_aie_columns_2-num_channels_2-tile_size_512]✅ 5/5165.380.05n/a
test_gelu[input_length_2048-num_aie_columns_4-num_channels_1-tile_size_512]✅ 5/5173.300.05n/a
test_gelu[input_length_2048-num_aie_columns_4-num_channels_2-tile_size_256]✅ 5/5190.000.05n/a
test_gelu[input_length_2048-num_aie_columns_8-num_channels_1-tile_size_256]✅ 5/5180.560.05n/a
test_gelu[input_length_2048-num_aie_columns_8-num_channels_2-tile_size_128]✅ 5/5202.240.04n/a
iron/operators/gemm
TestChecksLatency (mean)Bandwidth (mean)Throughput (mean)
test_gemm[M_1792-K_896-N_1152-num_aie_columns_8-b_col_maj_False-c_col_maj_True-m_64-k_32-n_48-trace_size_0-partition_N_1]✅ 5/52344.744.051594.40
test_gemm[M_192-K_384-N_64-num_aie_columns_4-b_col_maj_False-c_col_maj_False-m_48-k_96-n_16-trace_size_0-partition_N_1]✅ 5/5256.580.8937.87
test_gemm[M_192-K_384-N_64-num_aie_columns_4-b_col_maj_True-c_col_maj_True-m_48-k_96-n_16-trace_size_0-partition_N_1]✅ 5/5337.980.6628.09
test_gemm[M_2048-K_2048-N_2048-num_aie_columns_1-b_col_maj_False-c_col_maj_False-m_64-k_64-n_64-trace_size_0-partition_N_1]✅ 5/548530.800.52354.00
test_gemm[M_2048-K_2048-N_2048-num_aie_columns_2-b_col_maj_True-c_col_maj_False-m_64-k_64-n_64-trace_size_0-partition_N_1]✅ 5/528299.600.89607.10
test_gemm[M_2048-K_2048-N_2048-num_aie_columns_8-b_col_maj_True-c_col_maj_True-m_64-k_64-n_64-trace_size_0-partition_N_1]✅ 5/57880.983.192180.01
test_gemm[M_384-K_1536-N_1792-num_aie_columns_4-b_col_maj_True-c_col_maj_False-m_32-k_48-n_64-trace_size_0-partition_N_1]✅ 5/52410.183.38886.21
test_gemm[M_64-K_512-N_256-num_aie_columns_4-b_col_maj_True-c_col_maj_False-m_16-k_64-n_64-trace_size_0-partition_N_4]✅ 5/53450.520.3820.42
test_gemm[M_896-K_1792-N_640-num_aie_columns_8-b_col_maj_False-c_col_maj_True-m_32-k_64-n_80-trace_size_0-partition_N_1]✅ 5/51891.223.531090.24
iron/operators/gemv
TestChecksLatency (mean)Bandwidth (mean)Throughput (mean)
test_gemv[M_128-K_128-num_aie_columns_1-tile_size_input_32-tile_size_output_128]✅ 5/5n/a0.170.17
test_gemv[M_2048-K_8192-num_aie_columns_1-tile_size_input_1-tile_size_output_2048]✅ 5/5n/a12.4012.39
test_gemv[M_2048-K_8192-num_aie_columns_2-tile_size_input_1-tile_size_output_1024]✅ 5/5n/a22.6422.63
test_gemv[M_2048-K_8192-num_aie_columns_4-tile_size_input_1-tile_size_output_512]✅ 5/5n/a39.7739.75
test_gemv[M_2048-K_8192-num_aie_columns_8-tile_size_input_1-tile_size_output_256]✅ 5/5n/a41.3241.29
test_gemv[M_8192-K_2048-num_aie_columns_1-tile_size_input_4-tile_size_output_1024]✅ 5/5n/a11.8711.86
test_gemv[M_8192-K_2048-num_aie_columns_2-tile_size_input_4-tile_size_output_1024]✅ 5/5n/a23.7923.77
test_gemv[M_8192-K_2048-num_aie_columns_4-tile_size_input_4-tile_size_output_1024]✅ 5/5n/a36.5236.50
test_gemv[M_8192-K_2048-num_aie_columns_8-tile_size_input_4-tile_size_output_1024]✅ 5/5n/a41.0641.04
test_gemv_batched[M_1024-K_1024-num_aie_columns_1-tile_size_input_1-tile_size_output_64-num_batches_2]✅ 5/5n/a8.738.71
test_gemv_batched[M_1026-K_64-num_aie_columns_1-tile_size_input_1-tile_size_output_2-num_batches_2]✅ 5/5n/a0.880.87
test_gemv_batched[M_256-K_128-num_aie_columns_1-tile_size_input_1-tile_size_output_256-num_batches_4]✅ 5/5n/a1.161.15
test_gemv_batched[M_256-K_128-num_aie_columns_8-tile_size_input_1-tile_size_output_32-num_batches_100]✅ 5/5n/a14.6014.44
test_gemv_batched[M_448-K_64-num_aie_columns_8-tile_size_input_1-tile_size_output_56-num_batches_192]✅ 5/5n/a13.3513.12
test_gemv_batched[M_512-K_64-num_aie_columns_8-tile_size_input_4-tile_size_output_64-num_batches_32]✅ 5/5n/a7.767.63
test_gemv_batched[M_64-K_1536-num_aie_columns_1-tile_size_input_1-tile_size_output_64-num_batches_8]✅ 5/5n/a5.105.02
test_gemv_gelu[M_128-K_128-num_aie_columns_1-tile_size_input_32-tile_size_output_128]✅ 5/5n/a0.170.17
test_gemv_gelu[M_2048-K_8192-num_aie_columns_1-tile_size_input_1-tile_size_output_2048]✅ 5/5n/a12.3112.30
test_gemv_gelu[M_8192-K_2048-num_aie_columns_1-tile_size_input_4-tile_size_output_1024]✅ 5/5n/a12.1212.11
iron/operators/layer_norm
TestChecksLatency (mean)Bandwidth (mean)Throughput (mean)
test_layer_norm[input_length_2048-num_aie_columns_1-num_channels_1-tile_size_2048]✅ 5/5184.060.05n/a
test_layer_norm[input_length_2048-num_aie_columns_1-num_channels_2-tile_size_1024]✅ 5/5176.860.05n/a
test_layer_norm[input_length_2048-num_aie_columns_2-num_channels_1-tile_size_1024]✅ 5/5166.440.05n/a
test_layer_norm[input_length_2048-num_aie_columns_2-num_channels_2-tile_size_512]✅ 5/5170.040.05n/a
test_layer_norm[input_length_2048-num_aie_columns_4-num_channels_1-tile_size_512]✅ 5/5161.340.05n/a
test_layer_norm[input_length_2048-num_aie_columns_4-num_channels_2-tile_size_256]✅ 5/5212.640.04n/a
test_layer_norm[input_length_2048-num_aie_columns_8-num_channels_1-tile_size_256]✅ 5/5186.580.04n/a
test_layer_norm[input_length_2048-num_aie_columns_8-num_channels_2-tile_size_128]✅ 5/5242.780.03n/a
iron/operators/leaky_relu
TestChecksLatency (mean)Bandwidth (mean)Throughput (mean)
test_leaky_relu[input_length_2048-num_aie_columns_1-num_channels_1-tile_size_2048-alpha_0.01]✅ 5/5188.000.04n/a
test_leaky_relu[input_length_2048-num_aie_columns_1-num_channels_1-tile_size_2048-alpha_0.1]✅ 5/5195.480.04n/a
test_leaky_relu[input_length_2048-num_aie_columns_1-num_channels_1-tile_size_2048-alpha_0.25]✅ 5/5189.000.04n/a
test_leaky_relu[input_length_2048-num_aie_columns_1-num_channels_2-tile_size_1024-alpha_0.01]✅ 5/5202.760.04n/a
test_leaky_relu[input_length_2048-num_aie_columns_2-num_channels_1-tile_size_1024-alpha_0.01]✅ 5/5186.700.04n/a
test_leaky_relu[input_length_2048-num_aie_columns_2-num_channels_2-tile_size_512-alpha_0.01]✅ 5/5186.020.05n/a
test_leaky_relu[input_length_2048-num_aie_columns_4-num_channels_1-tile_size_512-alpha_0.01]✅ 5/5202.360.04n/a
test_leaky_relu[input_length_2048-num_aie_columns_4-num_channels_2-tile_size_256-alpha_0.01]✅ 5/5190.380.05n/a
test_leaky_relu[input_length_2048-num_aie_columns_8-num_channels_1-tile_size_256-alpha_0.01]✅ 5/5204.720.04n/a
test_leaky_relu[input_length_2048-num_aie_columns_8-num_channels_2-tile_size_128-alpha_0.01]✅ 5/5239.200.04n/a
iron/operators/mem_copy
TestChecksLatency (mean)Bandwidth (mean)Throughput (mean)
test_mem_copy[input_length_2048-num_cores_1-num_channels_1-bypass_False-tile_size_2048]✅ 5/5154.220.06n/a
test_mem_copy[input_length_2048-num_cores_16-num_channels_2-bypass_False-tile_size_128]✅ 5/5219.040.04n/a
test_mem_copy[input_length_2048-num_cores_2-num_channels_1-bypass_False-tile_size_1024]✅ 5/5148.740.06n/a
test_mem_copy[input_length_2048-num_cores_2-num_channels_2-bypass_False-tile_size_1024]✅ 5/5165.240.05n/a
test_mem_copy[input_length_2048-num_cores_4-num_channels_1-bypass_False-tile_size_512]✅ 5/5178.300.05n/a
test_mem_copy[input_length_2048-num_cores_4-num_channels_2-bypass_False-tile_size_512]✅ 5/5174.000.05n/a
test_mem_copy[input_length_2048-num_cores_8-num_channels_1-bypass_False-tile_size_256]✅ 5/5190.960.04n/a
test_mem_copy[input_length_2048-num_cores_8-num_channels_2-bypass_False-tile_size_256]✅ 5/5185.700.05n/a
iron/operators/mha
TestChecksLatency (mean)Bandwidth (mean)Throughput (mean)
test_mha[seq_len_16384-dim_64-num_heads_1-num_pipelines_8-num_kv_heads_0]✅ 5/547556.900.18n/a
iron/operators/relu
TestChecksLatency (mean)Bandwidth (mean)Throughput (mean)
test_relu[input_length_2048-num_aie_columns_1-num_channels_1-tile_size_2048]✅ 5/5151.260.05n/a
test_relu[input_length_2048-num_aie_columns_1-num_channels_2-tile_size_1024]✅ 5/5150.040.06n/a
test_relu[input_length_2048-num_aie_columns_2-num_channels_1-tile_size_1024]✅ 5/5163.220.05n/a
test_relu[input_length_2048-num_aie_columns_2-num_channels_2-tile_size_512]✅ 5/5152.320.06n/a
test_relu[input_length_2048-num_aie_columns_4-num_channels_1-tile_size_512]✅ 5/5173.840.05n/a
test_relu[input_length_2048-num_aie_columns_4-num_channels_2-tile_size_256]✅ 5/5172.920.05n/a
test_relu[input_length_2048-num_aie_columns_8-num_channels_1-tile_size_256]✅ 5/5197.020.05n/a
test_relu[input_length_2048-num_aie_columns_8-num_channels_2-tile_size_128]✅ 5/5201.040.04n/a
iron/operators/repeat
TestChecksLatency (mean)Bandwidth (mean)Throughput (mean)
test_cols_without_a_legal_split_is_rejected[cols_1031-why_prime > 1023: the only divisors are 1 and cols, neither legal]✅ 5/5n/an/an/a
test_cols_without_a_legal_split_is_rejected[cols_2062-why_2 x 1031: the only word-aligned chunk leaves a 1031-wide chunk count]✅ 5/5n/an/an/a
test_cols_without_a_legal_split_is_rejected[cols_513-why_odd: every divisor is odd, so no chunk is a whole 32-bit word]✅ 5/5n/an/an/a
test_repeat[rows_4-cols_1024-repeat_2-transfer_size_None]✅ 5/5156.280.16n/a
test_repeat[rows_8-cols_512-repeat_4-transfer_size_64]✅ 5/5187.700.22n/a
test_repeat[rows_8-cols_64-repeat_4-transfer_size_None]✅ 5/5191.100.03n/a
iron/operators/rms_norm
TestChecksLatency (mean)Bandwidth (mean)Throughput (mean)
test_rms_norm[input_length_2048-num_aie_columns_1-num_channels_1-tile_size_2048-weighted_False]✅ 5/5155.940.05n/a
test_rms_norm[input_length_2048-num_aie_columns_1-num_channels_1-tile_size_2048-weighted_True]✅ 5/5166.460.08n/a
test_rms_norm[input_length_2048-num_aie_columns_1-num_channels_2-tile_size_1024-weighted_False]✅ 5/5150.420.06n/a
test_rms_norm[input_length_2048-num_aie_columns_1-num_channels_2-tile_size_1024-weighted_True]✅ 5/5162.820.07n/a
test_rms_norm[input_length_2048-num_aie_columns_2-num_channels_1-tile_size_1024-weighted_False]✅ 5/5161.020.05n/a
test_rms_norm[input_length_2048-num_aie_columns_2-num_channels_1-tile_size_1024-weighted_True]✅ 5/5182.320.06n/a
test_rms_norm[input_length_2048-num_aie_columns_2-num_channels_2-tile_size_512-weighted_False]✅ 5/5188.300.05n/a
test_rms_norm[input_length_2048-num_aie_columns_2-num_channels_2-tile_size_512-weighted_True]✅ 5/5178.980.05n/a
test_rms_norm[input_length_2048-num_aie_columns_4-num_channels_1-tile_size_512-weighted_False]✅ 5/5173.300.05n/a
test_rms_norm[input_length_2048-num_aie_columns_4-num_channels_1-tile_size_512-weighted_True]✅ 5/5185.620.05n/a
test_rms_norm[input_length_2048-num_aie_columns_4-num_channels_2-tile_size_256-weighted_False]✅ 5/5180.040.05n/a
test_rms_norm[input_length_2048-num_aie_columns_4-num_channels_2-tile_size_256-weighted_True]✅ 5/5195.060.04n/a
test_rms_norm[input_length_2048-num_aie_columns_8-num_channels_1-tile_size_256-weighted_False]✅ 5/5154.860.06n/a
test_rms_norm[input_length_2048-num_aie_columns_8-num_channels_1-tile_size_256-weighted_True]✅ 5/5195.060.05n/a
test_rms_norm[input_length_2048-num_aie_columns_8-num_channels_2-tile_size_128-weighted_False]✅ 5/5208.660.04n/a
iron/operators/rope
TestChecksLatency (mean)Bandwidth (mean)Throughput (mean)
test_rope[rows_32-cols_512-angle_rows_32-aie_columns_1-method_type_0]✅ 5/5157.400.65n/a
test_rope[rows_32-cols_512-angle_rows_32-aie_columns_2-method_type_0]✅ 5/5183.940.57n/a
test_rope[rows_32-cols_512-angle_rows_32-aie_columns_4-method_type_0]✅ 5/5177.260.57n/a
test_rope[rows_32-cols_512-angle_rows_32-aie_columns_8-method_type_0]✅ 5/5179.480.62n/a
test_rope[rows_32-cols_512-angle_rows_8-aie_columns_1-method_type_0]✅ 5/5176.540.43n/a
test_rope[rows_32-cols_512-angle_rows_8-aie_columns_2-method_type_0]✅ 5/5168.320.44n/a
test_rope[rows_32-cols_512-angle_rows_8-aie_columns_4-method_type_0]✅ 5/5190.380.40n/a
test_rope[rows_32-cols_512-angle_rows_8-aie_columns_8-method_type_0]✅ 5/5203.240.37n/a
iron/operators/sigmoid
TestChecksLatency (mean)Bandwidth (mean)Throughput (mean)
test_sigmoid[input_length_2048-num_aie_columns_1-num_channels_1-tile_size_2048]✅ 5/5179.380.05n/a
test_sigmoid[input_length_2048-num_aie_columns_1-num_channels_2-tile_size_1024]✅ 5/5176.420.05n/a
test_sigmoid[input_length_2048-num_aie_columns_2-num_channels_1-tile_size_1024]✅ 5/5182.720.05n/a
test_sigmoid[input_length_2048-num_aie_columns_2-num_channels_2-tile_size_512]✅ 5/5165.380.05n/a
test_sigmoid[input_length_2048-num_aie_columns_4-num_channels_1-tile_size_512]✅ 5/5185.460.05n/a
test_sigmoid[input_length_2048-num_aie_columns_4-num_channels_2-tile_size_256]✅ 5/5188.020.04n/a
test_sigmoid[input_length_2048-num_aie_columns_8-num_channels_1-tile_size_256]✅ 5/5173.920.05n/a
test_sigmoid[input_length_2048-num_aie_columns_8-num_channels_2-tile_size_128]✅ 5/5219.760.04n/a
iron/operators/silu
TestChecksLatency (mean)Bandwidth (mean)Throughput (mean)
test_silu[input_length_2048-num_aie_columns_1-num_channels_1-tile_size_2048]✅ 5/5189.440.04n/a
test_silu[input_length_2048-num_aie_columns_2-num_channels_1-tile_size_1024]✅ 5/5174.760.05n/a
test_silu[input_length_2048-num_aie_columns_4-num_channels_1-tile_size_512]✅ 5/5174.740.05n/a
test_silu[input_length_2048-num_aie_columns_8-num_channels_1-tile_size_256]✅ 5/5185.520.05n/a
iron/operators/softmax
TestChecksLatency (mean)Bandwidth (mean)Throughput (mean)
test_softmax[input_length_32768-num_aie_columns_2-num_channels_2-tile_size_1024]✅ 5/5186.620.73n/a
test_softmax[input_length_32768-num_aie_columns_2-num_channels_2-tile_size_2048]✅ 5/5181.100.74n/a
test_softmax[input_length_32768-num_aie_columns_2-num_channels_2-tile_size_512]✅ 5/5189.820.71n/a
iron/operators/strided_copy
TestChecksLatency (mean)Bandwidth (mean)Throughput (mean)
test_strided_copy[chunked_transfer]✅ 5/5181.400.02n/a
test_strided_copy[contiguous]✅ 5/5171.880.02n/a
test_strided_copy[four_channels]✅ 5/5193.920.02n/a
test_strided_copy[kv_slot0]✅ 5/5201.800.67n/a
test_strided_copy[kv_slot5]✅ 5/5169.240.80n/a
test_strided_copy[kv_slot5_four_channels]✅ 5/5156.260.88n/a
test_strided_copy[kv_slot5_two_channels]✅ 5/5176.540.79n/a
test_strided_copy[kv_slot_last]✅ 5/5183.200.74n/a
test_strided_copy[two_channels]✅ 5/5181.320.02n/a
test_strided_copy[two_channels_chunked]✅ 5/5175.300.02n/a
test_transfer_size_not_dividing_per_channel_share_is_rejected[iter0]✅ 1/1n/an/an/a
test_transfer_size_not_dividing_per_channel_share_is_rejected[iter1]✅ 1/1n/an/an/a
test_transfer_size_not_dividing_per_channel_share_is_rejected[iter2]✅ 1/1n/an/an/a
test_transfer_size_not_dividing_per_channel_share_is_rejected[iter3]✅ 1/1n/an/an/a
test_transfer_size_not_dividing_per_channel_share_is_rejected[iter4]✅ 1/1n/an/an/a
iron/operators/swiglu_decode
TestChecksLatency (mean)Bandwidth (mean)Throughput (mean)
test_swiglu_decode[embedding_dim_1024-hidden_dim_3584]✅ 5/5981.960.00n/a
test_swiglu_decode[embedding_dim_2048-hidden_dim_2048]✅ 5/51007.020.01n/a
iron/operators/swiglu_prefill
TestChecksLatency (mean)Bandwidth (mean)Throughput (mean)
test_swiglu_prefill[seq_len_256-embedding_dim_2048-hidden_dim_2048-prio_accuracy_False]✅ 5/52174.900.96n/a
iron/operators/tanh
TestChecksLatency (mean)Bandwidth (mean)Throughput (mean)
test_tanh[input_length_2048-num_aie_columns_1-num_channels_1-tile_size_2048]✅ 5/5155.880.05n/a
test_tanh[input_length_2048-num_aie_columns_1-num_channels_2-tile_size_1024]✅ 5/5180.840.05n/a
test_tanh[input_length_2048-num_aie_columns_2-num_channels_1-tile_size_1024]✅ 5/5177.080.05n/a
test_tanh[input_length_2048-num_aie_columns_2-num_channels_2-tile_size_512]✅ 5/5205.980.04n/a
test_tanh[input_length_2048-num_aie_columns_4-num_channels_1-tile_size_512]✅ 5/5181.360.05n/a
test_tanh[input_length_2048-num_aie_columns_4-num_channels_2-tile_size_256]✅ 5/5190.920.05n/a
test_tanh[input_length_2048-num_aie_columns_8-num_channels_1-tile_size_256]✅ 5/5186.580.05n/a
test_tanh[input_length_2048-num_aie_columns_8-num_channels_2-tile_size_128]✅ 5/5208.080.04n/a
iron/operators/transpose
TestChecksLatency (mean)Bandwidth (mean)Throughput (mean)
test_transpose[M_2048-N_64-aie_columns_1-channels_1-m_64-n_64-s_8-num_batches_1]✅ 5/5191.062.81n/a
test_transpose[M_2048-N_64-aie_columns_1-channels_1-m_64-n_64-s_8-num_batches_2]✅ 5/5233.204.57n/a
test_transpose[M_2048-N_64-aie_columns_1-channels_2-m_64-n_64-s_8-num_batches_1]✅ 5/5215.482.47n/a
Krackan - Examples

IRON

Tested on 2026_09_10_16_51_35 at commit f8890d7.

iron/applications/llama_3.2_1b
TestChecksTTFT (mean)TPS (mean)
test_llama_3_2_1b[llama_3.2_1b_prompt_1024_tokens_1]✅ 5/52.05n/a
test_llama_3_2_1b[llama_3.2_1b_prompt_1024_tokens_40]✅ 5/52.097.85
test_llama_3_2_1b[llama_3.2_1b_prompt_13_tokens_1]✅ 5/52.00n/a
test_llama_3_2_1b[llama_3.2_1b_prompt_13_tokens_40]✅ 5/52.027.63
Phoenix - Small

IRON

Tested on 2026_09_10_16_52_25 at commit f8890d7.

iron/operators/axpy
TestChecksLatency (mean)Bandwidth (mean)Throughput (mean)
test_axpy[input_length_2048-num_aie_columns_1-tile_size_2048-scalar_factor_3.0]✅ 5/5312.860.04n/a
test_axpy[input_length_2048-num_aie_columns_2-tile_size_1024-scalar_factor_3.0]✅ 5/5343.920.04n/a
test_axpy[input_length_2048-num_aie_columns_4-tile_size_512-scalar_factor_3.0]✅ 5/5680.940.03n/a
iron/operators/dequant
TestChecksLatency (mean)Bandwidth (mean)Throughput (mean)
test_dequant[input_length_2048-num_aie_columns_1-num_channels_1-tile_size_2048-group_size_32]✅ 5/5313.180.02n/a
test_dequant[input_length_2048-num_aie_columns_1-num_channels_2-tile_size_1024-group_size_32]✅ 5/5358.900.02n/a
test_dequant[input_length_2048-num_aie_columns_2-num_channels_1-tile_size_1024-group_size_32]✅ 5/5367.780.02n/a
test_dequant[input_length_2048-num_aie_columns_2-num_channels_2-tile_size_512-group_size_32]✅ 5/5317.580.02n/a
test_dequant[input_length_2048-num_aie_columns_4-num_channels_1-tile_size_512-group_size_32]✅ 5/5381.440.02n/a
test_dequant[input_length_2048-num_aie_columns_4-num_channels_2-tile_size_256-group_size_32]✅ 5/5460.220.01n/a
iron/operators/elementwise_add
TestChecksLatency (mean)Bandwidth (mean)Throughput (mean)
test_elementwise_add[input_length_2048-num_aie_columns_1-tile_size_2048]✅ 5/5396.700.03n/a
test_elementwise_add[input_length_2048-num_aie_columns_2-tile_size_1024]✅ 5/5390.240.03n/a
test_elementwise_add[input_length_2048-num_aie_columns_4-tile_size_512]✅ 5/5445.760.03n/a
iron/operators/elementwise_mul
TestChecksLatency (mean)Bandwidth (mean)Throughput (mean)
test_elementwise_mul[input_length_2048-num_aie_columns_1-tile_size_2048]✅ 5/5383.260.04n/a
test_elementwise_mul[input_length_2048-num_aie_columns_2-tile_size_1024]✅ 5/5486.840.03n/a
test_elementwise_mul[input_length_2048-num_aie_columns_4-tile_size_512]✅ 5/5344.300.04n/a
iron/operators/flm_gemm
TestChecksLatency (mean)Bandwidth (mean)Throughput (mean)
test_flm_gemm_stride_overflow_rejected[M_1024-K_10240-N_2560]❌ 0/5n/an/an/a
test_flm_gemm_stride_overflow_rejected[M_1024-K_2560-N_10240]❌ 0/5n/an/an/a
iron/operators/gelu
TestChecksLatency (mean)Bandwidth (mean)Throughput (mean)
test_gelu[input_length_2048-num_aie_columns_1-num_channels_1-tile_size_2048]✅ 5/5479.500.02n/a
test_gelu[input_length_2048-num_aie_columns_1-num_channels_2-tile_size_1024]✅ 5/5347.720.03n/a
test_gelu[input_length_2048-num_aie_columns_2-num_channels_1-tile_size_1024]✅ 5/5347.600.03n/a
test_gelu[input_length_2048-num_aie_columns_2-num_channels_2-tile_size_512]✅ 5/5363.060.03n/a
test_gelu[input_length_2048-num_aie_columns_4-num_channels_1-tile_size_512]✅ 5/5408.780.02n/a
test_gelu[input_length_2048-num_aie_columns_4-num_channels_2-tile_size_256]✅ 5/5444.980.02n/a
iron/operators/gemm
TestChecksLatency (mean)Bandwidth (mean)Throughput (mean)
test_gemm[M_192-K_384-N_64-num_aie_columns_4-b_col_maj_False-c_col_maj_False-m_48-k_96-n_16-trace_size_0-partition_N_1]✅ 5/5493.560.4719.86
test_gemm[M_192-K_384-N_64-num_aie_columns_4-b_col_maj_True-c_col_maj_True-m_48-k_96-n_16-trace_size_0-partition_N_1]✅ 5/5513.220.4720.01
test_gemm[M_2048-K_2048-N_2048-num_aie_columns_1-b_col_maj_False-c_col_maj_False-m_64-k_64-n_64-trace_size_0-partition_N_1]✅ 5/582311.120.31208.77
test_gemm[M_2048-K_2048-N_2048-num_aie_columns_2-b_col_maj_True-c_col_maj_False-m_64-k_64-n_64-trace_size_0-partition_N_1]✅ 5/525155.101.00683.24
test_gemm[M_384-K_1536-N_1792-num_aie_columns_4-b_col_maj_True-c_col_maj_False-m_32-k_48-n_64-trace_size_0-partition_N_1]✅ 5/53123.562.84743.65
test_gemm[M_64-K_512-N_256-num_aie_columns_4-b_col_maj_True-c_col_maj_False-m_16-k_64-n_64-trace_size_0-partition_N_4]✅ 5/55946.640.2212.06
iron/operators/gemv
TestChecksLatency (mean)Bandwidth (mean)Throughput (mean)
test_gemv[M_128-K_128-num_aie_columns_1-tile_size_input_32-tile_size_output_128]✅ 5/5n/a0.110.11
test_gemv[M_2048-K_8192-num_aie_columns_1-tile_size_input_1-tile_size_output_2048]✅ 5/5n/a3.583.58
test_gemv[M_2048-K_8192-num_aie_columns_2-tile_size_input_1-tile_size_output_1024]✅ 5/5n/a5.885.88
test_gemv[M_2048-K_8192-num_aie_columns_4-tile_size_input_1-tile_size_output_512]✅ 5/5n/a9.109.09
test_gemv[M_8192-K_2048-num_aie_columns_1-tile_size_input_4-tile_size_output_1024]✅ 5/5n/a3.593.59
test_gemv[M_8192-K_2048-num_aie_columns_2-tile_size_input_4-tile_size_output_1024]✅ 5/5n/a6.286.27
test_gemv[M_8192-K_2048-num_aie_columns_4-tile_size_input_4-tile_size_output_1024]✅ 5/5n/a10.4410.43
test_gemv_batched[M_1024-K_1024-num_aie_columns_1-tile_size_input_1-tile_size_output_64-num_batches_2]✅ 5/5n/a2.812.80
test_gemv_batched[M_1026-K_64-num_aie_columns_1-tile_size_input_1-tile_size_output_2-num_batches_2]✅ 5/5n/a0.280.28
test_gemv_batched[M_256-K_128-num_aie_columns_1-tile_size_input_1-tile_size_output_256-num_batches_4]✅ 5/5n/a0.450.45
test_gemv_batched[M_64-K_1536-num_aie_columns_1-tile_size_input_1-tile_size_output_64-num_batches_8]✅ 5/5n/a1.061.04
test_gemv_gelu[M_128-K_128-num_aie_columns_1-tile_size_input_32-tile_size_output_128]❌ 0/5n/an/an/a
test_gemv_gelu[M_2048-K_8192-num_aie_columns_1-tile_size_input_1-tile_size_output_2048]❌ 0/5n/an/an/a
test_gemv_gelu[M_8192-K_2048-num_aie_columns_1-tile_size_input_4-tile_size_output_1024]❌ 0/5n/an/an/a
iron/operators/layer_norm
TestChecksLatency (mean)Bandwidth (mean)Throughput (mean)
test_layer_norm[input_length_2048-num_aie_columns_1-num_channels_1-tile_size_2048]✅ 5/5393.820.02n/a
test_layer_norm[input_length_2048-num_aie_columns_1-num_channels_2-tile_size_1024]✅ 5/5411.520.02n/a
test_layer_norm[input_length_2048-num_aie_columns_2-num_channels_1-tile_size_1024]✅ 5/5689.820.02n/a
test_layer_norm[input_length_2048-num_aie_columns_2-num_channels_2-tile_size_512]✅ 5/5323.380.03n/a
test_layer_norm[input_length_2048-num_aie_columns_4-num_channels_1-tile_size_512]✅ 5/5491.820.02n/a
test_layer_norm[input_length_2048-num_aie_columns_4-num_channels_2-tile_size_256]✅ 5/5474.380.02n/a
iron/operators/leaky_relu
TestChecksLatency (mean)Bandwidth (mean)Throughput (mean)
test_leaky_relu[input_length_2048-num_aie_columns_1-num_channels_1-tile_size_2048-alpha_0.01]✅ 5/5387.580.02n/a
test_leaky_relu[input_length_2048-num_aie_columns_1-num_channels_1-tile_size_2048-alpha_0.1]✅ 5/5383.560.02n/a
test_leaky_relu[input_length_2048-num_aie_columns_1-num_channels_1-tile_size_2048-alpha_0.25]✅ 5/5390.520.02n/a
test_leaky_relu[input_length_2048-num_aie_columns_1-num_channels_2-tile_size_1024-alpha_0.01]✅ 5/5514.760.02n/a
test_leaky_relu[input_length_2048-num_aie_columns_2-num_channels_1-tile_size_1024-alpha_0.01]✅ 5/5354.240.03n/a
test_leaky_relu[input_length_2048-num_aie_columns_2-num_channels_2-tile_size_512-alpha_0.01]✅ 5/5550.720.02n/a
test_leaky_relu[input_length_2048-num_aie_columns_4-num_channels_1-tile_size_512-alpha_0.01]✅ 5/5377.300.02n/a
test_leaky_relu[input_length_2048-num_aie_columns_4-num_channels_2-tile_size_256-alpha_0.01]✅ 5/5441.460.02n/a
iron/operators/mem_copy
TestChecksLatency (mean)Bandwidth (mean)Throughput (mean)
test_mem_copy[input_length_2048-num_cores_1-num_channels_1-bypass_False-tile_size_2048]✅ 5/5441.500.02n/a
test_mem_copy[input_length_2048-num_cores_2-num_channels_1-bypass_False-tile_size_1024]✅ 5/5491.120.02n/a
test_mem_copy[input_length_2048-num_cores_2-num_channels_2-bypass_False-tile_size_1024]✅ 5/5532.000.02n/a
test_mem_copy[input_length_2048-num_cores_4-num_channels_1-bypass_False-tile_size_512]✅ 5/5557.120.02n/a
test_mem_copy[input_length_2048-num_cores_4-num_channels_2-bypass_False-tile_size_512]✅ 5/5446.920.02n/a
test_mem_copy[input_length_2048-num_cores_8-num_channels_2-bypass_False-tile_size_256]✅ 5/5499.620.02n/a
iron/operators/relu
TestChecksLatency (mean)Bandwidth (mean)Throughput (mean)
test_relu[input_length_2048-num_aie_columns_1-num_channels_1-tile_size_2048]✅ 5/5447.900.02n/a
test_relu[input_length_2048-num_aie_columns_1-num_channels_2-tile_size_1024]✅ 5/5449.380.02n/a
test_relu[input_length_2048-num_aie_columns_2-num_channels_1-tile_size_1024]✅ 5/5345.220.03n/a
test_relu[input_length_2048-num_aie_columns_2-num_channels_2-tile_size_512]✅ 5/5399.600.02n/a
test_relu[input_length_2048-num_aie_columns_4-num_channels_1-tile_size_512]✅ 5/5448.300.02n/a
test_relu[input_length_2048-num_aie_columns_4-num_channels_2-tile_size_256]✅ 5/5422.800.02n/a
iron/operators/repeat
TestChecksLatency (mean)Bandwidth (mean)Throughput (mean)
test_cols_without_a_legal_split_is_rejected[cols_1031-why_prime > 1023: the only divisors are 1 and cols, neither legal]✅ 5/5n/an/an/a
test_cols_without_a_legal_split_is_rejected[cols_2062-why_2 x 1031: the only word-aligned chunk leaves a 1031-wide chunk count]✅ 5/5n/an/an/a
test_cols_without_a_legal_split_is_rejected[cols_513-why_odd: every divisor is odd, so no chunk is a whole 32-bit word]✅ 5/5n/an/an/a
test_repeat[rows_4-cols_1024-repeat_2-transfer_size_None]✅ 5/5271.420.09n/a
test_repeat[rows_8-cols_512-repeat_4-transfer_size_64]✅ 5/5436.260.10n/a
test_repeat[rows_8-cols_64-repeat_4-transfer_size_None]✅ 5/5300.920.02n/a
iron/operators/rms_norm
TestChecksLatency (mean)Bandwidth (mean)Throughput (mean)
test_rms_norm[input_length_2048-num_aie_columns_1-num_channels_1-tile_size_2048-weighted_False]✅ 5/5355.500.02n/a
test_rms_norm[input_length_2048-num_aie_columns_1-num_channels_1-tile_size_2048-weighted_True]✅ 5/5383.600.04n/a
test_rms_norm[input_length_2048-num_aie_columns_1-num_channels_2-tile_size_1024-weighted_False]✅ 5/5307.900.03n/a
test_rms_norm[input_length_2048-num_aie_columns_1-num_channels_2-tile_size_1024-weighted_True]✅ 5/5255.960.05n/a
test_rms_norm[input_length_2048-num_aie_columns_2-num_channels_1-tile_size_1024-weighted_False]✅ 5/5581.740.02n/a
test_rms_norm[input_length_2048-num_aie_columns_2-num_channels_1-tile_size_1024-weighted_True]✅ 5/5476.560.03n/a
test_rms_norm[input_length_2048-num_aie_columns_2-num_channels_2-tile_size_512-weighted_False]✅ 5/5391.840.02n/a
test_rms_norm[input_length_2048-num_aie_columns_2-num_channels_2-tile_size_512-weighted_True]✅ 5/5379.420.03n/a
test_rms_norm[input_length_2048-num_aie_columns_4-num_channels_1-tile_size_512-weighted_False]✅ 5/5424.020.02n/a
test_rms_norm[input_length_2048-num_aie_columns_4-num_channels_1-tile_size_512-weighted_True]✅ 5/5426.980.02n/a
test_rms_norm[input_length_2048-num_aie_columns_4-num_channels_2-tile_size_256-weighted_False]✅ 5/5734.920.02n/a
iron/operators/rope
TestChecksLatency (mean)Bandwidth (mean)Throughput (mean)
test_rope[rows_32-cols_512-angle_rows_32-aie_columns_1-method_type_0]✅ 5/5496.840.23n/a
test_rope[rows_32-cols_512-angle_rows_32-aie_columns_2-method_type_0]✅ 5/5344.060.32n/a
test_rope[rows_32-cols_512-angle_rows_32-aie_columns_4-method_type_0]✅ 5/5498.020.20n/a
test_rope[rows_32-cols_512-angle_rows_8-aie_columns_1-method_type_0]✅ 5/5357.720.23n/a
test_rope[rows_32-cols_512-angle_rows_8-aie_columns_2-method_type_0]✅ 5/5742.020.17n/a
test_rope[rows_32-cols_512-angle_rows_8-aie_columns_4-method_type_0]✅ 5/5379.200.22n/a
iron/operators/sigmoid
TestChecksLatency (mean)Bandwidth (mean)Throughput (mean)
test_sigmoid[input_length_2048-num_aie_columns_1-num_channels_1-tile_size_2048]✅ 5/5327.420.03n/a
test_sigmoid[input_length_2048-num_aie_columns_1-num_channels_2-tile_size_1024]✅ 5/5352.880.03n/a
test_sigmoid[input_length_2048-num_aie_columns_2-num_channels_1-tile_size_1024]✅ 5/5410.660.02n/a
test_sigmoid[input_length_2048-num_aie_columns_2-num_channels_2-tile_size_512]✅ 5/5328.460.03n/a
test_sigmoid[input_length_2048-num_aie_columns_4-num_channels_1-tile_size_512]✅ 5/5281.240.03n/a
test_sigmoid[input_length_2048-num_aie_columns_4-num_channels_2-tile_size_256]✅ 5/5417.000.02n/a
iron/operators/silu
TestChecksLatency (mean)Bandwidth (mean)Throughput (mean)
test_silu[input_length_2048-num_aie_columns_1-num_channels_1-tile_size_2048]✅ 5/5419.800.02n/a
test_silu[input_length_2048-num_aie_columns_2-num_channels_1-tile_size_1024]✅ 5/5614.320.02n/a
test_silu[input_length_2048-num_aie_columns_4-num_channels_1-tile_size_512]✅ 5/5364.500.02n/a
iron/operators/softmax
TestChecksLatency (mean)Bandwidth (mean)Throughput (mean)
test_softmax[input_length_32768-num_aie_columns_2-num_channels_2-tile_size_1024]✅ 5/5503.360.29n/a
test_softmax[input_length_32768-num_aie_columns_2-num_channels_2-tile_size_2048]✅ 5/5362.540.39n/a
test_softmax[input_length_32768-num_aie_columns_2-num_channels_2-tile_size_512]✅ 5/5709.060.31n/a
iron/operators/strided_copy
TestChecksLatency (mean)Bandwidth (mean)Throughput (mean)
test_strided_copy[chunked_transfer]✅ 5/5333.140.01n/a
test_strided_copy[contiguous]✅ 5/5281.120.01n/a
test_strided_copy[four_channels]✅ 5/5493.180.01n/a
test_strided_copy[kv_slot0]✅ 5/5365.180.39n/a
test_strided_copy[kv_slot5]✅ 5/5564.860.41n/a
test_strided_copy[kv_slot5_four_channels]✅ 5/5415.440.35n/a
test_strided_copy[kv_slot5_two_channels]✅ 5/5478.100.30n/a
test_strided_copy[kv_slot_last]✅ 5/5397.200.38n/a
test_strided_copy[two_channels]✅ 5/5281.340.01n/a
test_strided_copy[two_channels_chunked]✅ 5/5344.480.01n/a
test_transfer_size_not_dividing_per_channel_share_is_rejected[iter0]✅ 1/1n/an/an/a
test_transfer_size_not_dividing_per_channel_share_is_rejected[iter1]✅ 1/1n/an/an/a
test_transfer_size_not_dividing_per_channel_share_is_rejected[iter2]✅ 1/1n/an/an/a
test_transfer_size_not_dividing_per_channel_share_is_rejected[iter3]✅ 1/1n/an/an/a
test_transfer_size_not_dividing_per_channel_share_is_rejected[iter4]✅ 1/1n/an/an/a
iron/operators/swiglu_decode
TestChecksLatency (mean)Bandwidth (mean)Throughput (mean)
test_swiglu_decode[embedding_dim_1024-hidden_dim_3584]✅ 5/512807.620.00n/a
test_swiglu_decode[embedding_dim_2048-hidden_dim_2048]✅ 5/516940.080.00n/a
iron/operators/swiglu_prefill
TestChecksLatency (mean)Bandwidth (mean)Throughput (mean)
test_swiglu_prefill[seq_len_256-embedding_dim_2048-hidden_dim_2048-prio_accuracy_False]✅ 5/523430.190.09n/a
iron/operators/tanh
TestChecksLatency (mean)Bandwidth (mean)Throughput (mean)
test_tanh[input_length_2048-num_aie_columns_1-num_channels_1-tile_size_2048]✅ 5/5291.460.03n/a
test_tanh[input_length_2048-num_aie_columns_1-num_channels_2-tile_size_1024]✅ 5/5480.040.02n/a
test_tanh[input_length_2048-num_aie_columns_2-num_channels_1-tile_size_1024]✅ 5/5373.980.03n/a
test_tanh[input_length_2048-num_aie_columns_2-num_channels_2-tile_size_512]✅ 5/5467.920.02n/a
test_tanh[input_length_2048-num_aie_columns_4-num_channels_1-tile_size_512]✅ 5/5472.940.02n/a
test_tanh[input_length_2048-num_aie_columns_4-num_channels_2-tile_size_256]✅ 5/5471.360.02n/a
iron/operators/transpose
TestChecksLatency (mean)Bandwidth (mean)Throughput (mean)
test_transpose[M_2048-N_64-aie_columns_1-channels_1-m_64-n_64-s_8-num_batches_1]✅ 5/5445.181.34n/a
test_transpose[M_2048-N_64-aie_columns_1-channels_1-m_64-n_64-s_8-num_batches_2]✅ 5/5653.101.87n/a
test_transpose[M_2048-N_64-aie_columns_1-channels_2-m_64-n_64-s_8-num_batches_1]✅ 5/5700.361.20n/a
Phoenix - Examples

IRON

Tested on 2026_09_10_16_46_24 at commit f8890d7.

Trend tables omitted, the comment hit GitHub's size limit. Full report in the workflow run.

hunhoffe and others added 10 commits September 9, 2026 12:40
Two changes that only pay together, taking M=1024 K=1536 N=6144 from
1741 us to 1434 us against the shipped overlay's 2175 us -- 1.25x to
1.52x -- at bit-identical arithmetic (err/mass 2.41e-04 either way).

The mmul's colA loop was hand-unrolled 2x for the Peano path, on the
premise that "Peano schedules the 2x-unrolled body better than it
schedules the rolled one". That is no longer true, and the unroll had
become a 23% pessimization: it halves the inner trip count to 4, too
few to amortize the software pipeline's fill and drain. Measured cycles
per mmul call, by hardware trace of the event0/event1 pair the kernel
already emits: rolled 1875, hand-unrolled 2x 2446, compiler unroll 4
and 8 3291 and 3221. Chess always took the rolled path, so the branch
is simply deleted rather than re-gated.

The rolled loop is a sharp optimum -- anything adding live state across
it loses more than it gains, which is the signature of llvm-aie#1066,
where Peano's pipelining-unaware register allocator manufactures false
loop-carried anti-dependencies. Loop hints, hoisting the bfp16 operand
conversion, and prefetching the accumulator all measured neutral or
worse.

Widening the residency gate is what banks the win. It only counts C's
64 KB, so it admits k_iters <= 3 (K <= 1536) rather than 2,
overcommitting the A-carrying memtiles by 64 KB and relying on
aie-objectfifo-allocate to spill one buffer to the adjacent memtile.
That packs all eight to exactly 512 KB, with zero slack -- see the
comment in design.py before changing any buffer size.

Neither change is worth much alone: residency was measured
latency-neutral while the mmul was the critical path, and the re-rolled
mmul gained little while the non-resident DMA floor was. Total latency
is max(compute, DMA), so testing them separately scores both as zero.

39/39 green.
Takes M=1024 K=1536 N=6144 from 1435 us to 1252 us against the shipped
overlay's 2175 us -- 1.52x to 1.74x -- at unchanged arithmetic (err/mass
2.41e-04). Three changes that only pay together.

Asymmetric tile buffering decouples the A tile's height from the
accumulator's. A is dead as soon as it is consumed while C must live
across the whole K reduction, so sizing both to M_TILE pays the peak L1
cost twice. Giving A 16 rows against the accumulator's 64 (rho = 4)
frees enough L1 for a 128-deep k slice, which halves the accumulator
bytes per mac and doubles the inner loop's trip count: 3.67 -> 2.64
cycles per 8x8x8 mac by hardware trace. Idea from "Can Asymmetric Tile
Buffering Be Beneficial?" (arXiv:2511.16041) and mlir-aie's
gemm_asymmetric_tile_buffering examples, though those spend the freed L1
on a larger C tile, which an f32 accumulator cannot afford.

That k slice was not reachable before. At CT_MAX_K=128 B's innermost
contiguous run is 1024, one over the buffer descriptor's 10-bit size
field, so it needs a second dimension to encode -- and residency already
spends one walking k. Five dimensions against the hardware's four.
pack_B now emits B in the order the cores consume it rather than an
intermediate blocked order, so both B hops are linear and neither spends
a dimension. On its own that is worth nothing; it is what makes the rest
fit.

tile_ma is chosen by fitting the L1 working set rather than fixed, so
tile_n=128 (whose accumulator is already 32 KB) stays symmetric.

Note the resolved tile_ma is in the operator name as well as the kernel
object name. It changes both the emitted MLIR and the kernel, and the
build cache is keyed on filename -- a dir holding another value's
artifacts silently produced NaNs before the name encoded it.

39/39 green.
…sion

Takes M=1024 K=1536 N=6144 from 1436 us to 1143 us against the shipped
overlay's 2175 us -- 1.74x to 1.90x. DDR drops 69 -> 47 MB because
bfp16ebs8 packs 8 values in 9 bytes where bf16 needs 16, and B is the
leg that residency keeps but cannot shrink.

DO NOT SHIP AS IS. Accuracy moves from 2.4133e-04 to 2.6923e-04, about
12% worse, and 81% of output elements change. That contradicts the
premise this was built on -- that quantizing B on the host merely hoists
a rounding the mmul already performs on every mac call, and so is free.
It is not free, and the residual is not yet explained.

For scale: 2.69e-04 is still 37x better than the shipped overlay's
9.87e-03. But it breaks an exact tie -- flm_gemm at 2.4133e-04 matched
IRON GEMM's emulated mode to five digits, and GEMM's accurate mode
(emulate_bf16_mmul_with_bfp16=False) reaches 4.1321e-05, which this
moves further away from.

What has been ruled out as the cause, each by measurement:

  * Block grouping. The shared exponent covers 8 consecutive k for one
    n; grouping over n instead gives 1.95e-02.
  * Rounding. Truncation is correct; round-to-nearest gives 6.18e-03.
  * The A widening path. Using mul_elem_64 for one operand and direct
    assignment for the other, as mm_bfp_mixed.cc does, is byte for byte
    identical to using direct assignment for both.
  * The packer itself. It is byte-identical to mlir-aie's reference
    floatToBfp16, and single-value probes decode exactly on hardware
    across magnitude, sign and shared-exponent crushing.
  * Layout and accumulation. Single-element (k,n) probes map correctly,
    and partial sums over k are exact to 512 terms.

Also fixes a bug found on the way in: the bfp16 mmul never advanced pC1
and pC2 across the j loop, so every j iteration accumulated into the
same C slot. Every single-element probe passed anyway because they all
used columns that j=0 owns -- the error only showed as a period-64
column pattern in full data.

39/39 green (the test budget is 4e-3, well above both figures).
Removes the 12% accuracy regression from the previous commit. B in bfp16
is now numerically free, as it was supposed to be: 2.3912e-04 against the
bf16 build's 2.4133e-04, i.e. very slightly BETTER, and better on every
shape measured (2.900 vs 2.93e-04, 2.068 vs 2.09e-04, 4.173 vs 4.20e-04,
1.846 vs 1.86e-04). 1.90x the shipped overlay, 41x more accurate than it.

Two bugs, both in the host-side quantizer.

The conversion obeys the core's ROUNDING MODE. mlir-aie's reference
floatToBfp16 hardcodes truncation and comments that AIE2P always
truncates; that is true only of the power-up floor mode. flm_gemm calls
set_rounding(conv_even), so the kernel's own conversion rounds to nearest
with ties to even. Reading one block back off the hardware a slot at a
time shows it plainly: 14.9375 -> 15 while 106.5 -> 106 and 94.5 -> 94.
Truncation cannot produce that. pack_B now follows self.rounding, so
rounding="floor" still truncates.

Rounding then overflows the mantissa. A block's largest magnitude sits at
127 before rounding and can carry to 128, which does not fit the signed
8-bit field and wrapped to -128. B is drawn from rand() in the tests, so
values sit near their block maximum often, and the wrap alone cost
6.18e-03. Saturate instead.

Found by probing a single 8-value block: A[0,ki]=1 selects slot ki so
C[0,0] is exactly that slot's decoded value, which makes the kernel's
conversion directly readable and comparable against the packer's. Worth
keeping in mind -- the earlier single-value probes all passed because a
lone nonzero owns its block's exponent and never exercises the shift.

39/39 green.
pack_B returns bfp16ebs8 -- 9 bytes per 8 values -- but the arg spec still
described B as (K, N) bf16. #179 made buffer sizing follow spec.dtype
(prod(shape) * dtype.itemsize in calculate_buffer_layout), so that
declaration over-allocates this operator's LARGEST buffer by 1.78x: at
K=1536 N=6144 it asks for 18.9 MB where pack_B produces 10.6 MB.

Harmless before the merge because the spec's dtype was not consulted;
worth fixing now rather than leaving a caller-visible size lie in place.

39/39 green.
All three are env-gated and inert by default; 15/15 green with nothing
set. They exist because the operator is now DMA-rate bound rather than
byte bound, and that is not visible from wall clock alone.

  FLM_NULL_MMUL=1  skip the multiply, keep every DMA and handshake
  FLM_C_LINEAR=1   drain C as one contiguous run per column-block
  FLM_OVERLAP=N    column-blocks in flight (default 2, unchanged)

What they establish, at M=1024 K=1536 N=6144:

Compute is entirely hidden. Nulling the mmul does not make the operator
faster (1125 us full against 1152 nulled), so the ~14% of compute still
above its ResMII bound is worth nothing until data movement improves.

The operator moves 60.9 MB in ~1140 us, i.e. 53 GB/s against a measured
63-70 GB/s roof -- 79%. The gap is not per-core ingress: the trace shows
the cores' two input DMA ports only 45% and 25% busy, so they are starved
rather than saturated. Nor is it pipelining: overlap depth 3 is worth
under 1%, and 4 and 5 are worse as shim buffer descriptors run out.

It is C's write pattern. C is drained as ROWS*M_TILE runs of N_TILE
elements -- 128 bytes -- strided by N, and 128 B sits below the ~256 B
cliff where DDR bursts fall apart. Draining the same bytes contiguously
saves 114 us on the min and 86 us on the median over 6 interleaved
rounds. That is ~8% of the whole operator, and recovering it would put
it near 2.1x the shipped overlay.

FLM_C_LINEAR writes C to the wrong place -- it is a measurement, not a
fix. The fix needs 256 B runs with C still row-major, which means either
a 128-wide n tile (but that caps the k slice at 64, making compute the
wall again) or joining C across pairs of adjacent columns so one drain
covers 128 columns.
The gate compared B's resident footprint against (512 - 64) KB, which
ignores A's 128 KB entirely and hardcodes C's size. It happened to be
conservative enough at tile_n=64, but at tile_n=128 -- where C doubles
to 128 KB and resident B is 432 KB -- it admitted a configuration
totalling 688 KB of a 512 KB memtile, which failed address assignment
outright rather than falling back to non-resident.

Found while measuring whether tile_n=128 could pay for itself now that
ATB and bfp16 B changed the L1 and L2 budgets. It cannot: with the gate
honest it correctly drops to non-resident, and B's DDR then quadruples
to 42.5 MB for a total of 74 MB against tile_n=64's 60.9. Measured
2283 us against 1154. So tile_n=64 stays the default, and the sweep
table's preference is confirmed for the current balance even though its
absolute numbers are stale.

39/39 green.
_default_tile_ma picked the A-tile height against a fixed double-buffered
B. That is one axis of a two-axis budget, and it cannot express the
tradeoff that matters at wider n tiles: there, B's L1 object is large
enough that a double-buffered pair does not fit, and giving up B's
prefetch is what buys a deeper k slice -- colA 8 -> 16 is worth 3.67 ->
2.28 cycles per mac, far more than B's L1 prefetch.

_default_l1 now returns both, deepest B first and then the tallest A
that fits, so tile_n=64 is unchanged at (T_MA=32, B_DEPTH=2).

Also lets the resident-B gate fall back to a single-buffered memtile B
rather than giving up residency: at tile_n=128 B is 432 KB
double-buffered and does not fit beside A and C, but 216 KB does, and
there residency is worth much more than the prefetch because it is also
what stops B's DDR traffic quadrupling.

Neither changes the shipped configuration. 39/39 green.
…atter costs

The sweep table had been marked stale since cf270ed -- it predated the rolled
mmul, resident B, ATB and bfp16 B, which between them took 1024/1536/6144 from
1741 to 1141 us. Re-measured all six shapes against the current design, min of
per-run medians over 6 rounds with the two tile_n builds interleaved
round-robin (this box is bimodal ~6%, so running all of one and then all of the
other measures drift rather than design).

The default rule is unchanged in sign -- tile_n=128 still wins only at
k_iters=1 -- but both edges moved: its margin there narrowed 8% -> 3%, and its
penalty elsewhere grew from ~1.2-1.25x to 1.2-1.7x. Both follow from
tile_n=128 giving up resident B, which costs more the more k there is.

FLM_C_RUN2 is a new ablation next to FLM_C_LINEAR. C leaves each core as a
64-element (128 B) run, and the guess was that this sits below a ~256 B DDR
write-efficiency cliff, making a column-pair join worth ~100 us. It is not:
matched for byte count and footprint, halving the number of runs and doubling
their length buys 14.9 us min / 13.0 us median, against 98.2 / 96.9 for
removing the scatter entirely. So there is no cliff at 256 B -- the cost tracks
the NUMBER of scattered runs and keeps paying well past it, and one doubling
recovers only 15%. Both knobs write C to the wrong place; they are timing
probes, not modes.

That measurement is why the column-pair join is not being built: it would have
taken 1148 -> ~1134 us (1.90x -> 1.92x vs the shipped overlay) in exchange for
replacing the whole C leg with explicit Buffer/Lock/Flow/TileDma -- 8 core-side
DMA programs plus 2 memtile ones -- because ObjectFifo.join() cannot place its
sources interleaved and cannot reorder on the way out either. A hardware-
verified reproducer for that limitation lives in /scratch/ehunhoff/objectfifo_repro.

Co-Authored-By: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant