dflash-ops

CUDA ops for DFlash block-diffusion drafting (arXiv:2602.06036), the speculative-decoding scheme Meta ships with Muse Glimmer 30B: a 5-layer drafter denoises a 16-token noise block in one forward pass against a sliding-window KV cache injected with fused target-model features, and the target verifies the proposed block in parallel. The ops cover everything in the drafter's block step except the GEMMs, which stay in cuBLAS: feature injection into the ring (RMS norm, NEOX rope, scatter, one launch per layer), the per-head norm+rope of the noise block, the non-causal sliding-window GQA attention of the block against ring plus itself, and the first-mismatch acceptance scan. A DFlashDrafter class assembles the full step and captures it into a CUDA graph.

The package also ships an eager fp32 reference (DFlashRef) transcribed from llama.cpp's llama_model_dflash graph, and a GGUF loader for the released drafter weights. The reference defines the semantics; the ops are certified against it.

Usage

import torch
from kernels import get_kernel

dfo = get_kernel("phanerozoic/dflash-ops", version=1, trust_remote_code=True)

cfg, w = dfo.load_drafter("dflash-kquant.gguf")   # 58 tensors, metadata-derived config
w["tok_embd"] = target_tok_embd                   # the drafter borrows both from
w["lm_head"] = target_lm_head                     # the target model

m = dfo.DFlashDrafter(cfg, w, dtype="bf16", ring_dtype="bf16")
m.inject(m.encode(target_features), positions)    # features: [T, 5 * n_embd]
step = m.capture_step()                           # CUDA-graphed block step
logits = step(id_last, n_past)                    # [16, n_vocab]
drafts = logits[1:].argmax(-1)                    # greedy block, positions 1..15
n_ok = m.accept(drafts, target_tokens)            # device-side, no host round trip

version selects the release branch; trust_remote_code is required by kernels for publishers without the trusted-publisher mark.

API

Symbol Purpose
DFlashDrafter(cfg, w, dtype, ring_dtype) ring-cached drafter; dtype sets GEMM weights and activations (fp32 or bf16), ring_dtype the KV ring storage, independently
.encode(feats) fuse per-position target features (five extract-layer slabs, interleaved) through fc and the encoder RMS norm
.inject(g, pos) project fused features through every layer's wk/wv, norm+rope K, scatter into the ring at pos % window; chunked at window so no two tokens race for a slot
.block_step(id_last, n_past) eager block forward: [id_last, mask x 15] denoised non-causally against ring + block; returns [16, n_vocab] logits
.capture_step() the same step captured into a CUDA graph over static buffers; returns step(id_last, n_past) whose replay is bitwise-equal to block_step
.accept(draft, target) leading-agreement count, computed on device
ops.dflash_inject / dflash_qk_rope / dflash_block_attn / dflash_accept the raw registered ops
DFlashRef, load_drafter, DFlashConfig eager reference, GGUF weight loader, metadata-derived config

Method

Injection follows the llama.cpp graph exactly: fused features go through wk/wv with no attn_norm in front, K is per-head RMS-normalized and roped, V is stored raw. The noise block attends non-causally, so later mask positions inform earlier ones and the denoising is joint; the sliding window masks only the deep past (0 <= qpos - kpos < window), and drafts are read greedily at block positions 1..15 with position 0 unused. Rope angles come from a host-computed fp64 inverse-frequency table passed to the kernels, so no device pow is involved and the table is identical on every device. Arithmetic is fp32 throughout regardless of storage dtype.

capture_step allocates nothing inside the capture region: every intermediate is a held static buffer written with out= and in-place ops, so the graph owns no memory the allocator could later hand to eager work running between replays, which is the normal traffic of speculative decoding (the target verifies between drafter blocks). The test suite replays the captured step interleaved with eager forwards and requires bitwise equality throughout.

Measured

RTX 6000 Ada (sm89), real drafter dimensions (5 layers, n_embd 6656, heads 32/8, head_dim 128, n_ff 19968, window 2048, block 16), ring filled to 2048, vocabulary reduced to 8192 for the benchmark (tok_embd and lm_head belong to the target model and touch none of the ops). Instrument: metakernel.

Per block step:

configuration eager torch fused ops graphed
fp32 weights 20.3 ms 18.8 ms 17.7 ms
bf16 weights 15.0 ms (projected) 12.0 ms 11.4 ms

Phase decomposition at fp32, per step (5 layers):

phase eager torch fused op
GEMMs (8.70 GiB weight streaming) 14.3 ms unchanged (cuBLAS)
block attention vs full ring 3.42 ms 2.65 ms
per-head norm + rope 5.09 ms 0.18 ms

The step is weight-bandwidth bound: measured GEMM streaming runs at 720-736 GB/s of the card's 771 GB/s read bandwidth, so the fused work is 18% of the fp32 step, 30% at bf16, and grows as the weights shrink; with a ~4.6-bit quantized drafter the GEMM floor is ~3.2 ms and the same fused savings project to a ~1.9x step.

Acceptance, measured with the released quantized drafter under llama.cpp against Muse Glimmer Q4_K_XL on the same device: 0.25-0.70 per block with a mean accepted run of ~2.8 from a 15-token draft; end-to-end decode reaches 1.44x over no drafter at a draft length of 4, which outperforms lengths 8 and 15 (54.9 vs 54.7 vs 51.8 tok/s) because acceptance saturates well below the block size.

Correctness

The eager reference is compared against structural invariants (ring wraparound, window masking proven by perturbing out-of-window slots, non-causal joint denoising, determinism), and the CUDA ops against the reference: logits to tight tolerance, greedy draft tokens exactly, bf16 paths against a reference rounded the same way, and the graphed step bitwise against the eager step before and after interleaved eager traffic. The suite runs on sm86 and sm89.

Requirements and limits

  • NVIDIA GPU, compute capability 8.0+; torch with CUDA.
  • head_dim must be a multiple of 32 (it is the launch width of the norm and rope kernels); the shipped drafter's is 128.
  • The attention kernel stages one query head plus all scores in shared memory: (head_dim + window + block) * 4 bytes, so window <= ~12000 at the 48 KB static limit. The shipped drafter's window is 2048.
  • At most window tokens per dflash_inject call (slots are pos % window; callers chunk, later chunks overwrite earlier ones).
  • A 2048-slot ring costs 41.9 MB across 5 layers at fp32, 21.0 MB at bf16.
  • tok_embd and lm_head are not in the drafter GGUF; they come from the target model.
  • fp8 storage and a fused whole-block megakernel are not implemented.

References

Meta Superintelligence Lab, "DFlash: Block-Diffusion Drafting" (arXiv:2602.06036); Muse Glimmer 30B model card and released drafter (dflash-kquant.gguf); llama.cpp llama_model_dflash and common_speculative_impl_draft_dflash (the reference semantics).

License

Apache-2.0.

Downloads last month
2
apache-2.0
arxiv: 2602.06036
Supported hardwares new
CUDA
8.08.68.99.010.012.0
GPU
B300
288GB
NVIDIA SXM
B200
192GB
NVIDIA SXM
H200
141GB
NVIDIA SXM
H100
80GB
GPU
H800
80GB
GPU
H20
96GB
GPU
L40s
48GB
GPU
L40
48GB
GPU
L20
48GB
GPU
L4
24GB
DGX Spark
GB10
128GB
GPU
RTX PRO 6000 WS
96GB
GPU
RTX PRO 6000 Max-Q
96GB
GPU
RTX PRO 5000
48GB
GPU
RTX PRO 4500 WS
32GB
GPU
RTX PRO 4000
24GB
GPU
RTX PRO 4000 SFF
24GB
GPU
RTX PRO 2000
16GB
GPU
RTX 6000 Ada
48GB
GPU
RTX 5880 Ada
48GB
RTX
RTX 5000 Ada
32GB
GPU
RTX 4500 Ada
24GB
RTX
RTX 4000 Ada
20GB
RTX
RTX 4000 SFF Ada
20GB
GPU
RTX 3500 Ada Mobile
12GB
GPU
RTX 2000 Ada
16GB
GPU
RTX A6000
48GB
GPU
RTX A5000
8GB
GPU
RTX A5000 Max-Q
16GB
GPU
RTX A5000 Mobile
16GB
GPU
RTX A4000
16GB
GPU
RTX A4000 Max-Q
8GB
GPU
RTX A4000 Mobile
8GB
GPU
RTX A3000 Mobile
6GB
GPU
RTX A2000
6GB
GPU
RTX A2000 Embedded
4GB
GPU
RTX A2000 Max-Q
4GB
GPU
RTX A2000 Mobile
4GB
GPU
A800
40GB
GPU
A100
80GB
GPU
A40
48GB
GPU
A30
24GB
GPU
A10
24GB
GPU
A2
16GB
RTX
RTX 5090
32GB
RTX
RTX 5090 D
32GB
RTX
RTX 5090 Mobile
24GB
RTX
RTX 5080
16GB
RTX
RTX 5080 Mobile
16GB
RTX
RTX 5070
12GB
RTX
RTX 5070 Mobile
8GB
RTX
RTX 5070 Ti
16GB
RTX
RTX 5070 Ti Mobile
12GB
RTX
RTX 5060 Ti
16GB
RTX
RTX 5060
8GB
RTX
RTX 5060 Mobile
8GB
RTX
RTX 5050
8GB
RTX
RTX 5050 Mobile
8GB
RTX
RTX 4090
24GB
RTX
RTX 4090D
24GB
RTX
RTX 4090 Mobile
16GB
RTX
RTX 4080 SUPER
16GB
RTX
RTX 4080
16GB
RTX
RTX 4080 Mobile
12GB
RTX
RTX 4070
12GB
RTX
RTX 4070 Mobile
8GB
RTX
RTX 4070 Ti
12GB
RTX
RTX 4070 Super
12GB
RTX
RTX 4070 Ti Super
16GB
RTX
RTX 4060
8GB
RTX
RTX 4060 Ti
8GB
RTX
RTX 4090 Laptop
16GB
RTX
RTX 4080 Laptop
12GB
RTX
RTX 4070 Laptop
8GB
RTX
RTX 4060 Laptop
8GB
RTX
RTX 4050 Laptop
6GB
RTX
RTX 3090
24GB
RTX
RTX 3090 Ti
24GB
RTX
RTX 3080
12GB
RTX
RTX 3080 Ti
12GB
RTX
RTX 3080 Mobile
16GB
RTX
RTX 3070
8GB
RTX
RTX 3070 Ti
8GB
RTX
RTX 3070 Ti Mobile
8GB
RTX
RTX 3060 Ti
8GB
RTX
RTX 3060
12GB
RTX
RTX 3060 Mobile
6GB
RTX
RTX 3050 Mobile
4GB
GPU
RTX 2050 Mobile
4GB
Jetson
Jetson AGX Orin 64GB
64GB
Jetson
Jetson AGX Orin 32GB
32GB
Jetson
Jetson Orin NX 16GB
16GB
Jetson
Jetson Orin NX 8GB
8GB
Jetson
Jetson Orin Nano 8GB
8GB
Jetson
Jetson Orin Nano 4GB
4GB
OS
linux
Arch
x86_64
Kernel Builder
2c40e10