File size: 7,617 Bytes
eadc767 3dee2fa 04e022a eadc767 fe56ace eadc767 fe56ace eadc767 b3c0ea1 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 | ---
license: apache-2.0
library_name: kernels
tags:
- kernel
- neuron
- nki
- trainium
- trn2
- vjepa2
- video
- vision
- attention
- encoder
---
# V-JEPA2 ViT-L Encoder NKI Kernels for AWS Trainium2
**Version 1 (2026-09-25).** Four NKI kernels replacing the per-layer torch ops of the
[V-JEPA2](https://huggingface.co/facebook/vjepa2-vitl-fpc64-256) ViT-L video encoder on trn2:
**4.35x over the stock torch path (104.2 → 23.9 ms/clip), MFU 10.1% → 44.1%** (neuron-explorer
`mfu_estimated_percent`, device window). Output is **bitwise identical** to the reference
implementation this was verified against, and rel_l2 4.2e-2 vs the fp32 torch encoder (the
model's own bf16-matmul sensitivity).
> **See also: [`aws-neuron/vit-encoder-neuron-kernels`](https://huggingface.co/kernels/aws-neuron/vit-encoder-neuron-kernels)**,
> a generalized version of these kernels. It adds batching (any batch size), LNC=1 (8-core) support,
> fused residual + LayerNorm epilogues, a SwiGLU FFN kernel, both RoPE pairings, and a DINOv3 layer
> alongside a V-JEPA2 one. This repo is unchanged and keeps its verified 16-frame / batch-1 path.
## What ships
| Component | Purpose |
|---|---|
| `NeuronVJEPA2Encoder` (layer) | KernelConfig-mappable drop-in for `VJEPA2Encoder`: the 4-kernel layer loop, lazy one-time weight fold, stock fallback off-device/off-geometry |
| `res_layernorm_fold` | K1/K3: residual add + affine-free LayerNorm (fp32 stream in, bf16 normalized out); the LN affines are folded into the neighboring matmuls |
| `attention_block` | K2: fused QKV projection (LN-gain folded, lane-permuted) + 3D RoPE + flash-attention inner + fused out-projection → [2, N, H] per-core partials (LNC=2) |
| `mlp` | K4: fc1 + GELU + fc2, bf16 matmuls, fp32 boundary, prologue-prestaged row blocks |
| `pack_*`, `build_rope_tables` | host-side weight folding (used internally by the layer; exported for direct kernel callers) |
## Quick start
```python
import torch
from transformers import AutoModel, KernelConfig
kernel_config = KernelConfig({
"VJEPA2Encoder": (
"aws-neuron/vjepa2-neuron-kernels:NeuronVJEPA2Encoder",
{"version": 1, "trust_remote_code": True},
),
})
model = AutoModel.from_pretrained(
"facebook/vjepa2-vitl-fpc64-256",
dtype=torch.float32,
kernel_config=kernel_config,
device_map="neuron",
).eval()
enc = torch.compile(model.encoder, backend="neuron") # the published numbers are of this graph
video = your_clip # [1, 16, 3, 256, 256] fp32
with torch.no_grad():
embeddings = enc(video).last_hidden_state # [1, 2048, 1024]
```
> **`torch.compile(backend="neuron")` is part of the configuration.** Eager dispatch adds
> ~5.5 ms/forward. The layer sets `can_torch_compile`, so the kernels library will not silently
> swap it out under compile.
> **Resolving via `get_kernel` / `kernels`:** on a vLLM (0.24) venv where `torch` is a
> CUDA-tagged build, `import libtorch_neuronx_lite` (or `torch_xla`) **before** `get_kernel(...)`
> so `torch.neuron` is registered and the `torch-neuron` variant is selected. On a PyTorch Native
> container (`torch.neuron` already registered) this import is unnecessary.
## Performance (trn2.3xlarge, verified 2026-09-25)
| | stock torch encoder | these kernels |
|---|---|---|
| throughput, compiled graph | 104.18 ms/clip | **23.94 ms/clip (4.35x)** |
| device MFU (`mfu_estimated_percent`) | 10.1% | **44.08%** |
| end-to-end serving, preprocess on device + double-buffered uint8 H2D | — | 24.59 ms/clip = **42.8%** |
| end-to-end single stream, nothing overlapped | — | 29.86 ms = 35.7% |
MFU is neuron-explorer's own field: model FLOPs (1.6557e12, equal to the closed-form
QK+PV+projections+MLP+patch count to 0.000%) over 157.3 TFLOP/s (one logical NeuronCore =
2 physical cores, bf16) times the window. One logical core of four: a single stream uses ~11%
of the chip; four concurrent streams reach ~44% chip-wide.
Where the remaining 2.3x to physics goes (measured, not estimated): the softmax exp runs on the
Scalar engine at 1 element/lane/cycle (+2.6 ms over physics — unreachable without different
hardware), instruction issue overhead (~3.8 ms), and the queueing of three engines co-bound
within 0.5% (~3.7 ms). The exact-math kernel-level candidate classes are enumerated to
exhaustion; details in the source repository's cycle records.
## Correctness
* Full-forward output **bitwise identical** (`torch.equal`) to the verified reference
implementation, including through this repo's KernelConfig-style forward graft under
`torch.compile`.
* rel_l2 vs the fp32 torch encoder: 4.2028e-2 — identical to the stock-vs-fp32 difference
(both paths run bf16 matmuls; neither is "more approximate" than the other).
* K1/K3's GpSimd `rsqrt` is measurably *closer* to fp64 than the torch path's (6.9e-8 vs
3.9e-5 relative).
* The vendor attention inner is run-to-run nondeterministic on some layers at a few bf16
positions (fp64 agreement unchanged) — an upstream kernel property, reported to the Neuron
team. Bitwise comparisons must use a fixed captured sample.
## Requirements
* **Hardware**: trn2 (verified on trn2.3xlarge, LNC=2).
* **SDK**: neuronx-cc 2.27.2878.0; torch-neuronx 2.11.3 (PyTorch Native); nki 0.6.0;
nkilib (bundled with the SDK — `ModularAllocator` is imported from it).
* **Transformers** `>= 5.14` (tuple-form `KernelConfig`); `kernels >= 0.17`.
* `NKI_ENABLE_TRACE_CACHE=0` in the environment (the runtime defaults the cross-process trace
cache on; a stale NEFF silently captures a different instruction stream).
## Repository layout
```
build/torch-neuron/
├── __init__.py # public API re-exports
├── metadata.json # kernels-library metadata (backend: neuron)
├── layers.py # NeuronVJEPA2Encoder (KernelConfig target)
└── nki_kernels/
├── res_layernorm.py # K1/K3
├── attention_block.py # K2 outer (QKV + RoPE + out-proj) and weight packing
├── attention_inner.py # K2 inner: MODIFIED copy of the Neuron SDK's nkilib attention_cte
├── mlp.py # K4
└── rope_tables.py # host-side 3D RoPE tables
```
## Known limitations
* **16 frames / 2048 tokens / batch 1 / 256x256 only.** The model's native 64-frame clip
(8192 tokens) is NOT supported: three of the vendor attention kernel's SBUF buffers scale
with keys-per-section (347 KiB/lane needed vs the 192 KiB budget, device-confirmed), so 8192
keys require the flash-sectioning path, whose cross-section combine this integration does not
yet reproduce correctly. Off-geometry inputs fall back to the stock implementation.
* **Inference only** — no autograd rule; the layer refuses in training mode.
* The first device forward performs the one-time weight fold eagerly (~seconds); it is
deliberately kept outside `torch.compile` (compiled folding rounds differently and breaks
bitwise parity with the verified reference).
## Attribution and license
Apache-2.0 (see `LICENSE`, `NOTICE`). `nki_kernels/attention_inner.py` is a **modified copy of
the AWS Neuron SDK's `nkilib` `core/attention/attention_cte.py`** (Copyright Amazon.com, Inc.
or its affiliates, Apache-2.0); the file header lists the modifications (four engine-placement
pins worth ~1.19x end-to-end, and removal of code paths unreachable for this configuration).
The other three kernels, the layer glue and the packing are original work.
## Contact
Kernels developed by Antonio Mena ([@menaman123](https://huggingface.co/menaman123)). |