Cheat sheets

Attention Variants Compared

Full, sliding window, strided, BigBird, differential and native sparse attention. Cost, KV cache, code, fixes.

  • Concepts and terms
  • Tasks and commands
  • Annotated code
  • Diagrams
  • Comparisons
  • Defaults and numbers
  • Troubleshooting
  • Questions and tips

Source

Variants side by side#

VariantComputeKV cacheQuality vs full
Full attentionO(N^2)O(N) per layerbaseline
SWA, window 1024N times WO(W) per layer-0.1 ppl, good with global layers
Local + strided sparseN times sqrt Nmixedsimilar to SWA
BigBird, local + global + randomO(N) approxmixedmatches full at 2× context
Native Sparse (DeepSeek-V3.2)N times active fractionO(N)within 0.05 ppl
Differential2 times N squaredO(2N)-5 to -10% ppl

From Lesson 07.15

Use X when#

  • Full attention every layer up to ~16K context, or when retrieval quality is paramount.
  • SWA + global mix long context above 32K. The 2026 default above 32K.
  • Sparse block attention custom kernel, custom pattern. For retrieval and audio.
  • Differential attention attention sinks hurt, as in long-context RAG and needle-in-haystack.
  • DIFF from the start a new model that targets 64k-plus effective context.
  • NSA pre-training or continued training that targets 64k-plus context.
  • Not DIFF or NSA a pre-trained dense model, or context under 16k.

From Lesson 07.15, Lesson 10.16, Lesson 10.17

Mask builders#

def swa_mask(n, window):
M = [[NEG_INF] * n for _ in range(n)]
for i in range(n):
lo = max(0, i - window + 1)window >= n gives full causal
for j in range(lo, i + 1):
M[i][j] = 0.00.0 attends, NEG_INF masks
return M
def strided_mask(n, window, stride):local window, then:
for j in range(0, i + 1, stride):
M[i][j] = 0.0every stride-th token back to 0
render(swa_mask(8, window=4), "sliding window (W=4)")
render(strided_mask(8, window=2, stride=3), "local (W=2) + strided (stride=3)")

From 07.15 code/main.py, Lesson 07.15

SWA with FlexAttention#

from torch.nn.attention.flex_attention import flex_attention, create_block_mask
def swa_pattern(b, h, q_idx, kv_idx):
return (q_idx - kv_idx < 1024) & (q_idx >= kv_idx)window 1024, causal
mask = create_block_mask(swa_pattern, B=batch, H=heads, Q_LEN=n, KV_LEN=n)
out = flex_attention(q, k, v, block_mask=mask)compiles to a Triton kernel

From Lesson 07.15

Q&A#

Does Flash Attention cut compute?
No. It hides the activation memory. Every token still attends to every token.
Why is NSA trainable?
Gradients flow through the compressed-branch scores. top_k only picks blocks to load.
Why does DIFF help more at 128k?
The noise floor grows with context. At 4k it is small.
Is DIFF V2 GQA compatible?
Yes. V2 adds Q heads, not KV heads.
NSA keys at 1024 tokens, l 32, k 4, w 128?
32 + 128 + 128 = 288 keys, 3.5x fewer.

From Lesson 07.15, Lesson 10.16, Lesson 10.17, Lesson 07.12

Glossary#

SWA
sliding window attention. Each query attends to its last W tokens.
Receptive field
an L-layer SWA stack with window W sees up to L × W tokens.
Attention sink
rows sum to 1, so uninformative queries put weight on position 0.
NSA
Native Sparse Attention. Learned block sparsity, zero blocks skipped in the kernel.
DSA
Deepseek Sparse Attention, the successor of NSA.
HBM
High Bandwidth Memory. About 3 TB/s on H100.
SRAM
per-SM on-chip memory. About 30 TB/s.

From Lesson 07.15, Lesson 07.12, Lesson 10.17

NSA: three branches#

attendtop_k_blocksattendload blocksout_selout_cmpout_winclientQueryone per positionprocessCompressedbranchblock means, N/lkeysprocessSelectedbranchtop-k blocks, k*lkeysprocessSliding windowlast W tokensstoreKV cachefine-grainedtokensmodelGatesigmoid, 3weightsattendtop_k_blocksattendload blocksout_selout_cmpout_winclientQueryone perpositionprocessCompressedbranchblockmeans,N/l keysprocessSelectedbranchtop-kblocks,k*l keysprocessSlidingwindowlast WtokensstoreKV cachefine-grainedtokensmodelGatesigmoid,3 weights

From Lesson 10.17, 10.17 code/main.py

Flash Attention tile loop#

1Outer loopload one Q tile, about 128 × 128, into SRAM.2Inner loopload each K tile and V tile into SRAM.3SRAMcompute S_tile = Q_tile @ K_tile^T.4SRAMkeep a running (max, sum) across tiles.5SRAMrescale the old output by exp_old and add thenew block.6HBMwrite O_tile to HBM. One HBM trip per tile.7Resultbit-identical to standard attention, memory O(N)not O(N^2).

From Lesson 07.12, 07.12 code/main.py

Tasks in code#

TaskCode
SWA causal maskswa_mask(n, window)
Local plus strided maskstrided_mask(n, window, stride)
Count attended cellscount_nonmasked(M)
KV cache byteskv_cache_bytes(N, n_layers, n_heads_kv, d_head, dtype=2)
Flash style softmaxtiled_softmax_dot(q, Ks, Vs, tile=4)
Signal to noise of a rowsnr(weights_row, signal_pos)
DIFF V1 and V2 paramscompute_param_diff(hidden=4096, n_heads=32, kv_heads=8)
NSA keys per querycount_nsa(N, l, k, W)

From 07.15 code/main.py, 07.12 code/main.py, 10.16 code/main.py, 10.17 code/main.py

Numbers to know#

SettingDefaultNote
Llama 3 70B KV per token per layer4096 bytes80 layers, 8 KV heads, fp16
Llama 3 70B KV, 32K context10.4 GBGQA with 8 KV heads
Gemma 3 layer mix5:1 SWA:globalwindow=1024
SWA KV at 128K context128×window 1024, last W tokens
Gemma-3 mix KV~6xvs pure full attention
DIFF lambda_init0.8learned per head
NSA window W512l = 64, k = 16 at 64k, 25x
NSA at 128k3536 keys36x reduction

From Lesson 07.12, Lesson 07.15, 07.15 code/main.py, Lesson 10.16, Lesson 10.17

Troubleshooting#

SymptomCauseFix
SWA model fails long-range retrievalNo global layersInterleave full layers, 5:1
Weight bleeds to token 0Attention sink from softmaxDifferential attention
Lost in the middle past 32kSoftmax noise floor grows with NDIFF from the start, or LoRA on Q
DIFF V1 decode slower than baselineValue cache loaded twiceDIFF V2
Late training unstable at 70B-plusV1 per-head RMSNormV2 removes it
NSA on a dense pre-trained modelNo retrofitContinued training
No NSA gain under 16kThree-branch overheadFull attention
Inference-time sparsity gives small speedupModel never routed through itNatively trained sparsity

From Lesson 07.15, Lesson 10.16, Lesson 10.17, Lesson 07.12