Navigation
🔧 CUDA & Kernel Engineering· 12 min read

FlashInfer

Page-level attention for serving engines — handling heterogeneous requests, variable-length KV cache, and the kernel interface behind vLLM and SGLang.

📄 FlashInfer (MLSys 2025)

cudaflashinferpaged-attentionservingvllmsglangkv-cache

FlashAttention was designed for training — fixed batch, uniform sequence lengths, no KV cache. FlashInfer extends flash attention for serving, where every request has a different prompt length, different generation progress, and KV cache stored in non-contiguous pages.


1. The Serving Problem

Training attention: all sequences same length, contiguous memory, batch processed together.

Serving attention: every request is different:

PropertyTrainingServing
Sequence lengthsUniform (padded)Variable (each request different)
KV cacheContiguous tensorsPaged (non-contiguous blocks, via PagedAttention)
BatchSame operation per sampleMix of prefill and decode requests
Memory layoutDense, predictableSparse, fragmented

Standard FlashAttention assumes contiguous K/V tensors. In serving with PagedAttention, KV cache is scattered across fixed-size pages — FlashAttention can’t handle this directly.


2. FlashInfer’s Approach

Page-Level Attention

FlashInfer natively understands paged KV cache. Each request has a page table mapping logical sequence positions to physical memory pages:

Request 0: page_table = [page_5, page_12, page_3, page_8]
  Tokens 0-63   → page 5
  Tokens 64-127  → page 12
  Tokens 128-191 → page 3
  Tokens 192-255 → page 8

Request 1: page_table = [page_1, page_7]
  Tokens 0-63   → page 1
  Tokens 64-100  → page 7

FlashInfer’s kernel follows the page table to gather KV cache from non-contiguous pages — similar to how a CPU follows page tables for virtual memory.

Ragged Batching

FlashInfer handles ragged tensors — batches where each element has a different length — natively. No padding needed:

import flashinfer

# Variable-length sequences, no padding
q_lens = [128, 256, 64, 512]  # different length per request
kv_lens = [1024, 2048, 512, 4096]  # different KV cache sizes

# FlashInfer handles the ragged batch directly
output = flashinfer.batch_decode_with_paged_kv_cache(
    q, kv_data, kv_indices, kv_indptr, kv_last_page_len
)

Prefill-Decode Mixing

In serving, the batch may contain both prefill requests (processing full prompts) and decode requests (generating one token). FlashInfer supports mixed batches with different kernels per request type.


3. Key Optimizations

Cascade Attention for Shared Prefixes

When multiple requests share a system prompt:

Request 0: [system prompt (2K tokens)] + [user message A (100 tokens)]
Request 1: [system prompt (2K tokens)] + [user message B (200 tokens)]
Request 2: [system prompt (2K tokens)] + [user message C (50 tokens)]

FlashInfer computes attention over the shared prefix once, then extends per-request:

# Step 1: Shared prefix attention (computed once)
shared_output = flashinfer.single_prefill(q_shared, k_prefix, v_prefix)

# Step 2: Per-request suffix attention (computed per request)
# Merges with shared output using online softmax correction
final_output = flashinfer.merge_state(shared_output, suffix_outputs)

This avoids redundant computation on the shared prefix — significant for chat applications where system prompts are long.

FlashDecoding: Parallel KV Split

For long-context decode (one new token, but 100K+ cached KV):

Standard decode: one thread block processes the entire KV sequence → serialized.

FlashDecoding: split KV across multiple thread blocks, each computes partial attention, then merge:

Thread Block 0: KV tokens 0 - 16K      → partial output + (max, sum)
Thread Block 1: KV tokens 16K - 32K    → partial output + (max, sum)
Thread Block 2: KV tokens 32K - 48K    → partial output + (max, sum)
...
Merge: combine partial outputs using online softmax correction

Benefit: Parallelism across the sequence dimension — critical for long-context decode where a single block would be too slow.


4. FlashInfer in the Serving Stack

FlashInfer is the attention backend for major serving engines:

EngineAttention backendHow FlashInfer is used
vLLMFlashInfer or FlashAttentionPaged KV cache attention, batch decode
SGLangFlashInfer (primary)RadixAttention for prefix caching
TensorRT-LLMCustom kernelsSimilar concepts, proprietary implementation

The Interface

# Typical serving engine integration:

# 1. Build page table for the batch
kv_indices, kv_indptr, kv_last_page_len = build_page_tables(requests)

# 2. Prefill requests
prefill_output = flashinfer.batch_prefill_with_paged_kv_cache(
    q_prefill, kv_data, kv_indices, kv_indptr, kv_last_page_len,
    causal=True
)

# 3. Decode requests (one new token per request)
decode_output = flashinfer.batch_decode_with_paged_kv_cache(
    q_decode, kv_data, kv_indices, kv_indptr, kv_last_page_len
)

5. FlashInfer vs FlashAttention

FeatureFlashAttentionFlashInfer
TargetTraining (and inference)Serving (production inference)
KV layoutContiguous tensorsPaged (non-contiguous)
Batch typeUniform lengthsRagged (variable lengths)
Prefix sharingNot supported✅ Cascade attention
Decode parallelismSingle-block✅ FlashDecoding (multi-block)
GQA/MQA support✅✅
MLA supportLimited✅ (DeepSeek-V2 compatible)

Use FlashAttention for training. Use FlashInfer for serving.


Self-Check

  • Explain why standard FlashAttention can’t handle paged KV cache directly
  • Explain how FlashInfer follows page tables to gather non-contiguous KV cache
  • Explain cascade attention for shared prefixes and its benefit
  • Explain FlashDecoding and why it’s needed for long-context decode
  • Name which serving engines use FlashInfer and how
  • Explain when to use FlashAttention vs FlashInfer

References