Skip to main content

mjolnir-kernel

Mjolnir — a paged decode-attention kernel for NVIDIA Jetson AGX Thor (sm_110, 20 SMs), written in CuTe DSL on Blackwell tcgen05 tensor cores, and packaged as a vLLM attention backend.

vllm serve <model> --attention-backend MJOLNIR      # the mjolnir-vllm image
vllm serve <model> --attention-backend CUSTOM       # stock vLLM 0.31.0 + this package

This package is the kernel only. Serving, calibration and benchmarking are the mjolnir-cli package; benchmarks (Mjolnir vs FlashInfer vs FlashAttention 2 across context lengths), the design, and one chapter per kernel feature live in the project repository: thor-arena/mjolnir — numbers there are rendered from the raw results, so they are never stale.

What it does

The backend subclasses a stock vLLM backend — the fallback — and inherits its KV-cache allocation, cache write, prefill and validation:

  • on the image, FlashAttention 4 (FLASH_ATTN, resolved to FA4 on sm_110 by the image's patch — its prefill is ~40% faster than FlashInfer's on Thor). If FA4's one-time fp8-KV probe fails on the device, the backend logs it and uses FlashInfer instead;
  • on a stock vLLM, FlashInfer (stock FA on sm_110 is FA2, which cannot read an fp8 KV cache);
  • MJOLNIR_ATTN_FALLBACK picks another either way.

Steps that are decode only run on Mjolnir:

  • every request has the same q_len ≤ 1 + num_speculative_tokens (so MTP / n-gram speculative verification runs on Mjolnir too);
  • CUDA-graph padding requests (zero tokens) may trail the batch;
  • the step is causal — or the non-causal block of a parallel drafter (DSpark, DFlash: every query token sees every key) — and has no cascade prefix.

Every other step (prefill, mixed, cascade) is built and run by the base backend, unchanged.

A decode step:

  1. Plan — an occupancy-pinned split-KV plan (plan_split_kv): exactly one wave of 2 CTAs × 20 SMs. The plan lives in persistent device buffers padded to a fixed grid, so full CUDA graphs replay new plans without re-capture.
  2. Quantize Q — dynamic e4m3 per (sequence, KV head) by default: fp8 Q, fp8 KV and fp8 P go straight into the tensor cores, no conversion warps.
  3. Stream KV — TMA loads pages into a shared-memory ring (L2 evict-first: KV is read once), tcgen05 MMAs accumulate S and O in TMEM, softmax warpgroups run the online softmax.
  4. Merge — a small second kernel combines the split-KV partials by LSE.

The KV cache is read through runtime strides: vLLM's (B,H,N,2D), FlashInfer NHD/HND, or FlashAttention (2,B,…), uint8 or fp8, with per-tensor or per-head scales. Q and O are zero-copy for q_len == 1, even as strided views of the fused QKV buffer.

Install

On the image (narandill/mjolnir-vllm:v0.31.0) it is pre-installed and the MJOLNIR backend name is patched into vLLM — nothing to do.

On a stock vLLM 0.31.0 (inside a container that has CUDA 13 and sm_110):

pip install "mjolnir-kernel[dsl]"          # [dsl] pulls nvidia-cutlass-dsl
VLLM_KV_CACHE_LAYOUT=HND vllm serve Qwen/Qwen3-8B \
    --kv-cache-dtype fp8 --attention-backend CUSTOM

The package registers a vllm.general_plugins entry point (mjolnir:register) that maps AttentionBackendEnum.CUSTOM — and MJOLNIR, when the vLLM build has that member — to mjolnir.backend.MjolnirAttentionBackend in every vLLM process. The two-line enum patch for vLLM is thor-mjolnir-attention-backend.patch.

Look for this line in the server log:

Mjolnir decode enabled: h_q=… h_k=… d=… page=… q_len<=… Q=fp8 ctas/SM=2 …

Requirements

  • An fp8 (fp8 / fp8_e4m3) KV cache — or, on the image, a 6-bit fp6_e2m3 one with calibrated scales (see below); head-major pages (VLLM_KV_CACHE_LAYOUT=HND) are fastest.
  • head_size % 128 == 0; block size 16, 32, 64 or 128.
  • A bf16 or fp16 model.
  • No sliding window, soft-cap, sinks or ALiBi; no DCP or PCP.

If a layer does not qualify, the builder logs the reason and that KV-cache group runs on the fallback backend.

Configuration

variable default meaning
MJOLNIR_ATTN_DISABLE – 1: the plugin does not register; everything runs on the fallback
MJOLNIR_ATTN_FALLBACK FLASH_ATTN (FA4) on the image, FLASHINFER on stock vLLM base backend (FLASHINFER, TRITON_ATTN, …) for prefill and mixed steps
MJOLNIR_ATTN_Q_DTYPE fp8 fp8 (dynamic per-(seq, KV head) quantization) or bf16
MJOLNIR_ATTN_MAX_QLEN 1 + num_speculative_tokens the largest q_len Mjolnir takes
MJOLNIR_ATTN_CTAS 2 CTAs per SM (2 is fastest on Thor)
MJOLNIR_ATTN_MAX_SPLITS 16 max KV chunks per sequence (1 = never split, no merge kernel)
MJOLNIR_ATTN_KV_STAGES 0 KV ring depth override (0 = the tuned default)
MJOLNIR_ATTN_PDL 0 1: programmatic dependent launch
MJOLNIR_ATTN_L2_PREFETCH 0 TMA-prefetch K/V this many tiles ahead into L2
MJOLNIR_ATTN_KV_EVICT_FIRST 1 for fp8 Q, 0 for bf16 Q L2 evict-first policy on K/V loads
MJOLNIR_ATTN_PRECOMPILE all compile every variant at start-up; none compiles on first use
MJOLNIR_ATTN_KERNEL_CACHE on compiled variants are exported to $VLLM_CACHE_ROOT/mjolnir/kernels (else ~/.cache/mjolnir/kernels) and loaded on the next start instead of recompiling; 0 = off, a path = that directory. Keyed by variant, kernel source, CuTe DSL version and GPU
MJOLNIR_ATTN_DRAFT_T5 1 0: the drafter's layers (MTP / EAGLE) run on the fallback — isolates the draft-step cost (mjolnir verify profile). fp8 KV only: refused on fp6 KV, whose fallback cannot be CUDA-graphed
MJOLNIR_ATTN_NONCAUSAL 1 0: a parallel drafter's non-causal block steps (DSpark / DFlash) go to the fallback instead of the decode kernel — fp8 KV only for the same reason as above
MJOLNIR_ATTN_FUSED_DRAFT 1 0: tell vLLM the backend cannot advance draft metadata in place → one graph replay per draft step instead of the fused draft loop
MJOLNIR_LM_HEAD – fp8: quantize the lm_head to e4m3 per row at load, Triton fp8-weight GEMM for the verify and every draft (mjolnir.lmhead; config NVFP4_MJOLNIR_E2M3_LMH8); the bf16 copy is freed automatically (untied heads only, +2.5 GB KV)
MJOLNIR_DRAFT_VOCAB – path to a draft_vocab.json (mjolnir calibrate --draft-vocab): the drafter's logits only over those tokens (VocabTrim); MJOLNIR_DRAFT_VOCAB_SIZE keeps the first N (a shorter file is padded with the lowest unused ids, with a warning)
MJOLNIR_ATTN_VERBOSE – 1: print the kernel's compile-time notes (ring stages, TMEM columns) — the standalone harness always does
MJOLNIR_ATTN_BANNER 1 0: skip the Mjolnir hammer banner in the vLLM boot log (mjolnir.banner.emit, once per start, coloured with the server's formatter)
MJOLNIR_ATTN_KV_LAYOUT auto force the KV view: BHN2D BNH2D B2NHD B2HND 2BNHD 2BHND
MJOLNIR_ATTN_FUSED_QPACK 1 0: quantize fp8 Q with PyTorch ops (~10 launches) instead of one Triton kernel
MJOLNIR_ATTN_KV_SCALES – fp6_e2m3 KV: the per-(layer, KV head, channel) scales file from python -m mjolnir.calibrate
MJOLNIR_ATTN_KV_SCALE_HEADROOM 1.0 fp6_e2m3 KV: multiply every calibrated scale
MJOLNIR_ATTN_KV_CALIBRATE – 1: record the per-channel amax of every K/V written (what mjolnir.calibrate reads)

The pre-rename THOR_T5_* spellings are still honored. Each default is backed by a measurement — see the kernel chapters.

fp6 (e2m3) KV in vLLM

On the image, --kv-cache-dtype fp6_e2m3 stores K/V as packed 6-bit e2m3 codes (25% fewer bytes than fp8) with static per-(layer, KV head, channel) scales — per channel because K has outlier channels a head-wide scale would crush the rest under. Mjolnir writes that cache (mjolnir/kvfp6.py), decodes from it directly (K's channel scales folded into Q, V's into the output), and expands the pages a prefill / mixed step reads to e4m3 for the FA4 fallback (channel scales applied, one descale per head). The scales come from a calibration run on fp8 KV:

python -m mjolnir.calibrate --config <serve.yaml> --out kv_scales.json   # in the image
MJOLNIR_ATTN_KV_SCALES=kv_scales.json vllm serve ... --kv-cache-dtype fp6_e2m3 --attention-backend MJOLNIR

(mjolnir calibrate / mjolnir serve do both for you — docs/faq/fp6-kv.md.)

Beyond fp8: the harness

mjolnir/t5_decode.py is the kernel and its standalone harness (python -m mjolnir.t5_decode --help) with a bit-faithful emulator as the correctness reference. The harness can also read KV formats vLLM cannot write yet — fp6 (e2m3/e3m2) and fp4 (e2m1) read directly by tcgen05 kind::f8f6f4, and nvfp4 — and defaults to e2m3 on head-major pages for fp8 Q. How they compare in speed and accuracy: kvfmt and quant. Wiring a vLLM cache-write path for fp6 is an open contribution.

Tests

From a checkout, on a Thor:

mjolnir verify correctness      # kernel vs the emulator, the full case matrix
mjolnir verify vllm             # runner tests + greedy MJOLNIR-vs-FLASHINFER smoke runs

or directly in the container: python tests/kernel/test_runner.py, python tests/kernel/smoke_vllm.py [--eager] [--spec 2] (they find the package installed, via $MJOLNIR_SRC, or by walking up to src/mjolnir).

Mjolnir rounds P (and, by default, Q) to e4m3, so greedy outputs can flip on near-tie tokens and then drift apart — FlashInfer's own greedy output differs between CUDA-graph and eager mode. The smoke test therefore compares per-step logprobs, not text: the chosen tokens must get the same logprob from both backends on the shared prefix, and every divergence must be a near-tie (each token in the other's top-5, at most 1 nat apart). The matching prefix is printed as information.

Files

file what
mjolnir/t5_decode.py the CuTe-DSL kernel, the split-KV merge, the harness and the emulator
mjolnir/runner.py plan buffers, compile cache, launch, KV views (no vLLM import)
mjolnir/backend.py the vLLM backend, metadata builder, impl
mjolnir/__init__.py register() — the vLLM plugin entry point

Apache-2.0, except mjolnir/t5_decode.py, which is derived from an NVIDIA CUTLASS CuTe DSL example and stays under BSD-3-Clause — see LICENSE and NOTICE in this package.

Metadata

Release files for mjolnir-kernel 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for mjolnir-kernel 0.1.0
File Size Uploaded
mjolnir_kernel-0.1.0.tar.gz 86.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for mjolnir-kernel 0.1.0
File Interpreter ABI Platform
mjolnir_kernel-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 182.2 kB

Release files / mjolnir_kernel-0.1.0.tar.gz

Download URL mjolnir_kernel-0.1.0.tar.gz
Size 86.9 kB
Tags Source
SHA-256 checksum
How to use checksums
8a3fdf704f9f26a6fbf4d5710a1cc8af2530d06d8d762cb4420bd7a127fe5586
BLAKE2b-256 checksum
How to use checksums
08642f39a326c0b77d376ccb02e968f7df7399bb46e2837506b12fce0f7eacf0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 15, 2026.

Transparency log

Release files / mjolnir_kernel-0.1.0-py3-none-any.whl

Download URL mjolnir_kernel-0.1.0-py3-none-any.whl
Size 95.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
b9c1ce41fb28eb5882ffe760387e464eb17de294c1ebf375e138ef7321d37b35
BLAKE2b-256 checksum
How to use checksums
b3b3e724eb38f3b6b685f5ac75d9de9fe4f1be7560221adf82065203ecfdf1ec
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 15, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page