K4 (`mlp`) and K1/K3 (`res_layernorm_fold`): -4.8% end-to-end encoder, bitwise identical

#2

Reworks the fused MLP kernel and the residual + LayerNorm kernel. K2, the weight folds and the
layer are unchanged apart from one line in layers.py that packs the folded fc1 bias.

Results (trn2.3xlarge, LNC=2, all outputs torch.equal to v1.1)

Full encoder (torch.compile(model.encoder, backend="neuron"), 24 layers, one 16-frame clip):

v1.1 this PR change
Wall time, ms/clip (3 rounds, 20 iters each) 26.632 / 26.635 / 26.639 25.356 / 25.357 / 25.347 -1.28 ms (-4.8%)
Device time (one profiled forward) 26.83 ms 25.50 ms -5.0%
mfu_estimated_percent 39.5% 41.7% +2.2 points

Standalone at the encoder shape (2 interleaved rounds each):

Kernel v1.1 this PR change efficiency
K4 mlp ([2048, 1024] -> 4096 -> 1024) 295.3 us 248.6 us -15.8% MFU 70.3% -> 82.8%
K1 res_layernorm_fold, 1 branch 48.0 us 41.9 us -12.7% HBM 73% -> 84% of 716 GB/s
K3 res_layernorm_fold, 2 branches 57.7 us 48.2 us -16.3% HBM 71% -> 85% of 716 GB/s

K1/K3 do no matmuls, so the profiler reports MFU 0 for them. Achieved HBM bandwidth
(bytes moved / device time) is their roofline metric.

Absolute times are on torch-neuronx 2.14.3 + nki 0.7.0b1. The same v1.1 code measures 26.63 ms/clip
there, against 23.94 in the README, so compare the relative change.

What changed in K4 (found from the device profile)

  1. x before weights. v1.1 issued b1 -> all of W1 -> all of W2 -> x, so the first matmul waited
    for ~16 MB of weights it didn't need yet (first matmul at ~57 us). Order is now
    b1 -> x(rb0) -> W1 -> x(rb1) -> W2.
  2. b1 in one DMA. v1.1 loaded fc1 bias as 32 DMAs of [128, 1] fp32 (4 B per partition), which
    trickled in until ~69 us. Each gelu waited on its bias tile, and through its PSUM bank the fc1
    matmuls behind it. b1 is now host-packed to [128, H/128] (pack_b1) and lands in one 16 KB DMA on
    the Scalar engine's HW-DGE queue, so gelu's wait no longer counts W1/W2 traffic.
  3. Row block 0's x transposed on the Tensor engine, which is idle in the prologue.
  4. Rewrite: 3D SBUF tiles instead of tile lists, nl.tile_size.pmax / gemm_moving_fmax
    instead of literals, and exact-multiple shapes asserted instead of min() remainder handling.
    b2 stays a per-(row block, output chunk) partition-broadcast load; hoisting it into one load was
    neutral standalone but cost ~0.7 ms/clip in the encoder.

What changed in K1/K3

  1. dge_mode.none on every DMA. In v1.1 each of the ~40 DMAs had its descriptors generated by
    GpSimd (software DGE) at ~0.75 us apiece, one after another; the first add waited ~8 us for them.
    With dge_mode.none the runtime generates descriptors before execution. Hardware DGE on the Sync
    queue (dge_mode.hwdge, engine=sync) was as fast standalone but 0.1 ms/clip slower in the
    encoder, so it is not used.
  2. All token tiles resident in SBUF, every load issued before any compute, so a tile's loads
    never wait behind the previous tile's stores.

The compute instructions and their order are unchanged, so results are bitwise identical.

Compatibility

  • mlp still accepts b1 as [H] (packed on the fly) as well as the pre-packed [128, H/128];
    pack_b1 is exported. Op names unchanged.
  • New K4 requirement: K % 512 == 0, H % 128 == 0, R % 1024 == 0. The encoder shape (R=2048, K=1024,
    H=4096) satisfies these.
  • K1/K3 SBUF use is ~116 KB/partition at H=1024 with 2 branches (all tiles resident).
  • Toolchain: all numbers above are torch-neuronx 2.14.3 + nki 0.7.0b1. Standalone K4 and K1/K3
    were also verified on the latest public NKI release, nki 0.6.0 + neuronx-cc 2.27.5334: K4 248.6 /
    247.7 us vs 291.6 / 294.0 us for v1.1, K1 42.6 us, K3 48.1 us, all bitwise identical. The full
    encoder could not be run on nki 0.6.0 with torch-neuronx 2.14.3: v1.1 itself fails there
    (CPU fallback failed for 'model_default'). nki 0.6.0 rejects list comprehensions inside kernels,
    so the kernels use explicit loops.

image

Ready to merge
This branch is ready to get merged automatically.

Sign up or log in to comment