Download CHANGELOG.md from aws-neuron/vit-encoder-neuron-kernels: direct link, hf CLI and curl.
- Browser
- Download file 11 kB
-
https://huggingface.co/kernels/aws-neuron/vit-encoder-neuron-kernels/resolve/v1/CHANGELOG.md
- Command line
-
hf download hf://aws-neuron/vit-encoder-neuron-kernels@v1/CHANGELOG.md
-
curl -L -o CHANGELOG.md https://huggingface.co/kernels/aws-neuron/vit-encoder-neuron-kernels/resolve/v1/CHANGELOG.md
Changelog
v1.6 (2026-10)
Two images per producer pass in
attention_blocktoo (newpack2argument; the encoder enables it for even batches up to 256 tokens per image, e.g. 224 px). This kernel serves every model at LNC=2 and single-head-group models such as ViT-S/16 at LNC=1. v1.5 had packing only in the LNC=1 all-group kernel. Q/K/V projections and RoPE run once over 2N columns; attention stays per image. Output bit-identical to v1.5 (same rel_l2 vs fp32 on all 20 cases). Kernel device cycles, ViT-L 224 px LNC=2: batch 4 -8.4% (resident weights) / -12.0% (restaged), batch 2 -8.4%. Per instance at 224 px, batch 8:model config v1.5 img/s v1.6 img/s change ViT-S/16 LNC=1 DP=8 8,592.8 9,365.6 +9.0% ViT-S/16 LNC=2 DP=4 4,667.6 5,061.6 +8.4% ViT-B/16 LNC=2 DP=4 3,578.3 3,856.8 +7.8% ViT-L/16 LNC=2 DP=4 1,514.8 1,589.7 +4.9% ViT-H+/16 LNC=2 DP=4 507.3 554.0 +9.2% Single core, batch 4: +5.9% to +10.8%. Batch 1 and 448 px unchanged (packing off).
VIT_ENC_PACK2=0disables it.
v1.5 (2026-10)
- Two images per producer pass (LNC=1 all-group attention,
attention_block_l1_ln, newpack2argument, on by default from the encoder when the batch is even and two images fit one 512-token tile, i.e. up to 256 tokens such as 224 px). The two images' Q/K/V projections and RoPE run as one pass over 2N columns; attention stays per image. Kernel device cycles, ViT-L 224 px, batch 4: -6.2% (resident weights) / -14.8% (restaged), batch 2: -8.5%. End to end (real HF weights, LNC=1): per instance (8 cores, batch 8) ViT-L/16 1,838 -> 1,906 img/s (+3.7%), ViT-H+/16 672 -> 743 (+10.6%), ViT-B/16 4,390 -> 4,738 (+7.9%). Single core batch 4: ViT-L +4.6%, ViT-H+ +12.6%, ViT-B +4.1%. Output bit-identical to v1.4 (same rel_l2 vs fp32 on all 12 cases).VIT_ENC_PACK2=0disables it. A fused-attention-block technique (two images per producer pass). - Fix: inputs of 128 tokens or fewer failed to compile in every attention kernel (
index 1 out of bounds). They now use a non-pipelined single-Q-group path (rel_l2 vs a float64 reference 1.9-2.5e-3). Longer inputs are unchanged (bit-identical). - EXPERIMENTAL bf16 RoPE (
VIT_ENC_ROPE_BF16=1, off by default): RoPE tables and products in bf16. +0.6% to +3.3% end to end on DINOv3; rel_l2 vs fp32 grows by up to 1.16x (V-JEPA2: 0.039 -> 0.041). - EXPERIMENTAL fused attention write-back (
VIT_ENC_WB_FUSED=1, off by default): one Vector multiply normalises and casts O. -3.4% at 256 tokens / LNC=2, but +6.6% at 2048 tokens (Vector is the attention bottleneck), so off.
v1.4 (2026-10)
- QK^T PSUM banks: for 1024 or fewer tokens per image (two key tiles or fewer), the QK^T matmul now rotates over
five PSUM banks across Q groups instead of reusing four, so it no longer waits for the Vector row-max evict of the
tile four back. Attention kernel device cycles vs v1.3: -2.1% (N=256 and N=1024, LNC=2), -1.1% (N=1024, LNC=1),
-4.7% (all-group kernel, N=256, LNC=1). At 2048 tokens the old layout is kept (the rotation collided with other banks
and measured +7%).
VIT_ENC_MM1_WIDE=0restores the v1.3 layout. End to end (single core, real HF weights) vs v1.3: DINOv3 ViT-L/16 -0.5% to +3.7%, ViT-H+/16 0.0% to +2.6%. V-JEPA2 (2048 tokens): 23.80 ms/clip vs 23.81, output identical to v1.3. All 24 DINOv3 cases pass (rel_l2 vs fp32 0.56-1.75e-2). - EXPERIMENTAL FP8 MLP (
VIT_ENC_MLP_FP8=1, GELU-MLP models only, off by default): fc1 and fc2 run as e4m3double_rowmatmuls, with power-of-two weight scaling and a per-layer GELU-output scale (calibrate_fp8). Measured on DINOv3 ViT-L/16, single core: +7.3% (224 px, LNC=2) to +10.5% (448 px, LNC=1) end to end. On imagenette (real weights), k-NN and linear-probe predictions were identical to fp32. But the CLS feature error rises to rel_l2 5.8e-2 (bf16 path: 0.84e-2). Calibrate before use: trn2's e4m3 saturates at 240, and DINOv3's first block produces GELU outputs near 2,000. Without calibration the default scale (1/32) is used, which is safe for DINOv3 but coarse. - Outputs are deterministic per compiled graph. Across separate compilations they can differ at bf16-ulp level (rel ~1e-5), as in v1.3.
v1.3 (2026-10)
Attention-path schedule improvements, plus a SwiGLU fallback fix. Output of every attention kernel is bitwise identical to v1.2. Verified on trn2.3xlarge at LNC=1 and LNC=2, PyTorch Native Beta 6.
Device-cycle A/B, v1.2 vs v1.3 (median of 3 captures, same instance, container and inputs):
| Kernel / shape | LNC | change |
|---|---|---|
attention_block, N=1024, B=2 |
2 | -1.9% |
attention_block, N=2048, B=1 (V-JEPA2 clip) |
2 | -5.2% |
attention_block, N=256, B=8 |
2 | -2.0% |
attention_block, N=1024, B=2 |
1 | -3.3% |
attention_block_l1, N=256, B=4 |
1 | -11.1% |
attention_block_l1_ln, N=256, B=4 |
1 | -10.2% |
attention_block_l1_ln, N=1024, B=2 |
1 | -4.5% |
End to end (real Hugging Face weights, single core, vs v1.0 on the same harness): DINOv3 ViT-L/16 +1.0% to +12.7%, ViT-H+/16 +0.6% to +17.3%. Largest gains are at 224 px and batch 1, where attention's fixed costs dominate. V-JEPA2 (16 frames, LNC=2): 24.78 -> 24.48 ms/clip vs v1.2, output identical to v1.2. All 16 DINOv3 cases pass rel_l2 <= 5e-2 vs fp32 (0.56-0.98e-2).
Changes:
- Q/K bias in one DMA.
pack_weightsnow returns the Q/K bias packed as[2, 128, Fp/128]fp32 (pack_qk_bias, exported). Each attention program loads its groups with one DMA per Q/K on the Scalar HW-DGE queue, replacing one[128, 1]DMA plus one cast per 128-row group. The values are rounded to bf16 first, which keeps the output bit-identical. The kernels still accept the legacy[2, Fp, 1]layout. - Dead work removed in the attention core (
attention_inner.py, single-section path, N <= 2048): themm2_sbandexp_partial_sumzero-fills (never observed), and thetensor_reduceover the chained exp-sum columns (the sum is already in one column; it is now read directly). - Chained row-max reduce: the QK^T row-max accumulates across the 512-key tiles of a large tile and is
read out once, instead of once per tile followed by a
tensor_reduce. Pattern from the upstream nkilibattention_cterow-max chaining, re-implemented for this fork. It adds -3.5% at N=2048 and is neutral at N <= 1024 (fewer key tiles to chain). - Fix (v1.2 regression): the SwiGLU torch fallback (taken below 256 rows per core, e.g. ViT-H+/16 at LNC=2, batch 1, 224 px) used the packed biases and failed to compile. It now uses unpacked copies. The kernel path was unaffected.
v1.2 (2026-10)
Applied the v1.1 packed-bias one-DMA technique to the SwiGLU FFN kernels (swiglu_stream,
swiglu_stream_ln). The silu bias b1 and the up-projection bias bg are now host-packed to
[128, I/128] (pack_b1) and each loaded in a single DMA on the Scalar engine's HW-DGE queue,
replacing I/128 [128, 1] column DMAs apiece. Output is bitwise identical to v1.1 at LNC=1 and
LNC=2 (torch.equal, verified on trn2.3xlarge). Device cycles (same stack, A/B): swiglu_stream
-2.5% (reproducible); swiglu_stream_ln within measurement noise (the LN epilogue dominates and the
bias DMAs already overlap compute). The wrappers still accept an unpacked [I] bias (packed on the
fly). Affects only the gated-MLP (SwiGLU) models; the GELU-MLP path is unchanged from v1.1.
v1.1 (2026-10)
Reworked DMA/engine schedule for mlp (K4), mlp_res_ln (K4 + next LN) and res_layernorm_fold
(K1/K3). Output is bitwise identical to v1 at both LNC=1 and LNC=2. Based on an external
contribution by jlongen-aws (PR #2 on
jburtoft/vjepa2-neuron-kernels), re-implemented here with the LNC-parametric path and the min()
remainder handling preserved.
Verified on trn2.3xlarge (A/B on one consistent stack: old vs new compiled and run in the same
container + inputs, device cycles from neuron-explorer show-session). LNC=2, V-JEPA2 ViT-L shape
(R=2048, H=1024, MLP hidden 4096):
- K4
mlp: -7.5% device cycles (reproducible, -7.8% +/- ~1% over 3 captures). - K4+LN
mlp_res_ln: -6.8%. - K1
res_layernorm_fold(1 branch): -16.2%; K3 (2 branches): -18.3%. - Full 24-layer encoder (synthetic ViT-L weights, includes the unchanged attention): -3.7% device
cycles, output
torch.equalto v1. - Real-weight V-JEPA2 (
facebook/vjepa2-vitl-fpc64-256, 16 frames x 256 px, LNC=2): output bitwise identical to v1 (new_vs_v1_rel0.0), rel_l2 0.0389 vs fp32 CPU (unchanged from v1), -4.0% ms/clip (speedup vs stock fp32 3.19x -> 3.33x). - Per-kernel output is
torch.equalto v1 at LNC=1 and LNC=2.
Changes:
mlp/mlp_res_ln: fc1 bias is host-packed to[128, H/128](pack_b1, now exported) and loaded in one DMA on the Scalar engine's HW-DGE queue instead ofH/128column DMAs; row block 0'sxis staged and transposed on the (idle) Tensor engine before the weights, later row blocks stream under row block 0's matmuls.mlpstill accepts an unpacked[H]bias (packed on the fly).res_layernorm_fold: every DMA usesdge_mode.none(the runtime pre-generates descriptors) and all token tiles are made SBUF-resident before any compute, replacing the depth-4 ring. Per-tile statistic columns ([128, n_t]) so one tile's stat chain does not wait on the previous tile's.pack_blocknow stores the GELU fc1 bias pre-packed;pack_b1is exported fromnki_kernels.
The compute instructions and their order are unchanged, so results are bit-identical to v1.
v1 (2026-10)
First release.
- Drop-in layers for Hugging Face Transformers:
NeuronDINOv3ViTModel(DINOv3 ViT-S/16, S+/16, B/16, L/16, H+/16) andNeuronVJEPA2Encoder(V-JEPA2 ViT-L, any batch size, up to 2048 tokens per clip). - Model-agnostic encoder loop:
BlockWeights,pack_block,rope_tables,EncoderRunner. - Kernels:
attention_block(per head group; batched; padded keys skipped; head_dim 64 or 128; any head count),attention_block_l1andattention_block_l1_ln(LNC=1, all head groups, optional residual + LayerNorm epilogue),res_layernorm_fold,mlpandmlp_res_ln(GELU MLP, optional residual + next LayerNorm epilogue),swiglu_streamandswiglu_stream_ln(weight-streaming SwiGLU FFN). - RoPE pairing selectable at packing time:
rope="half"(rotate-half) orrope="interleaved". - Verified on trn2.3xlarge, PyTorch Native Beta 6 (torch 2.13.0, torch-neuronx 2.13.3, NKI 0.7.0b1), Transformers 4.57.6, LNC=1 and LNC=2. Every measured case passes rel_l2 <= 5e-2 vs the fp32 CPU model.
Known limitations: encoder (bidirectional) attention with RoPE only; LayerNorm (not RMSNorm); <= 2048 tokens per image; inference only; hidden size 4096 (ViT-7B) not covered by the drop-in layer.