SK

ENGINEERING BLOG / CHAPTER 06 OF 10

Fast Inference and Training at Scale

Saravan Kumar M KUpdated 2026-10-26~34 min read24 sections

The fundamental bottleneck: Autoregressive generation calls the full Transformer once per output token. A 500-token response requires 500 forward passes through a multi-billion-parameter model. This section covers engineering techniques that reduce the cost of each forward pass or reduce the number of forward passes needed.

Each optimization targets a different part of the cost:

Problem                               Solution
──────────────────────────────────    ─────────────────────────
Recomputing past K,V every step   →   KV Caching (5.1)
KV cache too large (too many heads) → GQA (5.2)
KV cache memory fragmentation     →   PagedAttention (5.3)
KV cache too large (high dimension) → MLA (5.4)
Too many forward passes of big model→ Speculative Decoding (5.5)
                                  →   Multi-Token Prediction (5.6)

6.1 — KV Caching

Paper: Standard practice (widely adopted by 2021–2022).

The Problem

In self-attention, every token must interact with every previous token. When generating token , the model computes:

  • — the hidden state of token
  • , — learned projection matrices
  • , — the key and value vectors for token

Then attention is computed between the current query and all past keys and values. Without caching, this entire computation repeats from scratch at every generation step. The total compute scales as:

for a response of tokens. Enormously wasteful.

The Solution: Cache and Reuse

Past tokens never change. Once computed, and for token are fixed for the rest of the generation. We store them:

Without a cache every step recomputes all keys and values; with a cache only the new token is processed.

At step , only three things need computation:

  1. — query for the new token
  2. — key for the new token
  3. — value for the new token

Then attend over the full cache plus the new token’s K,V. The cost per step is now instead of .

Why Not Cache Queries ()?

Queries are specific to the token asking the question. represents “what information does token need?” Future tokens have their own informational needs — they’ll produce their own and never reuse . Caching queries provides no benefit because they’re never reused.

Keys and values, by contrast, represent past information that every future token needs to attend to. They are reused at every subsequent step.

KV Cache Memory Cost

The KV cache grows linearly with sequence length:

  • — one for K, one for V
  • — sequence length (grows during generation)
  • — hidden dimension of the model
  • — number of Transformer layers (KV cache per layer)
  • — e.g., 2 bytes for float16

For LLaMA-2 70B with : approximately 9 GB of GPU memory just for the KV cache. This is what motivates the next three optimizations.

6.2 — Grouped Query Attention (GQA)

Paper: Ainslie et al., 2023.

The Problem with Standard Multi-Head Attention (MHA)

In standard MHA, there are attention heads. Each head has its own independent key matrix and value matrix. So the KV cache holds tensors per layer per token — it scales linearly with the number of heads.

For LLaMA-2 70B with heads: the KV cache is proportional to 64 key tensors plus 64 value tensors per layer.

Multi-Query Attention (MQA): Too Extreme

The simplest reduction: use just one shared KV head pair for all query heads.

Cache shrinks by . But quality degrades noticeably — sharing a single set of keys and values across all query heads limits what each query head can focus on.

Grouped Query Attention (GQA): The Sweet Spot

GQA divides the query heads into groups. All query heads within the same group share one KV head pair:

  • Each group of query heads shares one key head and one value head
  • KV cache shrinks by
  • Quality loss is minimal when is not too small

GQA interpolates between MHA and MQA:

MHA (standard):  G = h  (every query head has its own K,V)
GQA:             1 < G < h  (groups share K,V)
MQA:             G = 1  (all heads share one K,V)

Real example (LLaMA-3): query heads, KV heads. Cache is smaller than MHA with negligible quality loss.

GQA is now the industry standard. Nearly all modern LLMs (LLaMA-2, LLaMA-3, Mistral, Qwen, Falcon) use it.

6.3 — PagedAttention

Paper: Kwon et al., 2023. Efficient Memory Management for Large Language Model Serving with PagedAttention. (The core innovation of the vLLM inference engine.)

The Problem: Memory Waste from Pre-allocation

When serving inference requests, the system must allocate KV cache memory for each request. But the final sequence length is unknown at the start — the model keeps generating until it decides to stop.

Naive systems pre-allocate a contiguous block of memory for the maximum possible sequence length upfront. For a model with a 128K token context, every request reserves memory for 128K tokens — even if the actual response is 200 tokens.

Naive pre-allocation for one request:
[■■■■■■■■■■□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□]
 (tokens generated)   (pre-allocated but empty — wasted memory)

This is enormously wasteful, especially when serving many concurrent requests.

The Solution: Paging (Borrowed from OS Virtual Memory)

PagedAttention divides the KV cache into fixed-size blocks (or pages). These blocks:

PagedAttention allocates the KV cache in small non-contiguous blocks and can share prefixes.
  • Are allocated on demand as the sequence grows (no pre-allocation)
  • Do not need to be contiguous in physical memory
  • Are managed via a page table that maps logical positions to physical blocks
With PagedAttention:
Request A: [Block 1][Block 2][Block 3]
Request B: [Block 4][Block 5]
Request C: [Block 6]

(blocks allocated only as needed; memory is fully utilized)

Bonus: KV cache sharing. When multiple requests share a common prefix (e.g., same system prompt), PagedAttention can point multiple requests’ page tables to the same physical blocks — no duplication needed. This is called prefix caching.

Real-world impact: vLLM, which uses PagedAttention, achieves 2–4× throughput improvement over naive memory allocation. It is now the dominant open-source LLM inference engine.

6.4 — Multi-Head Latent Attention (MLA)

Paper: DeepSeek-V2, 2024.

The Problem: KV Cache Dimension

Even after GQA reduces the number of KV heads, the dimension of each KV representation remains large. For a model with hidden dimension and KV heads, the KV cache for tokens is:

For long sequences (100K+ tokens), this is still massive.

The Solution: Compress First, Cache the Compression

Instead of caching the full-rank and tensors, cache a compressed latent representation :

Compression step (run once per token during generation):

  • — the token’s hidden state (large: -dimensional)
  • — a learned down-projection matrix
  • — the compressed latent (small: -dimensional, where )

Reconstruction step (run each time the cached token is accessed in attention):

  • , — learned up-projection matrices
  • , — the reconstructed full-rank key and value tensors (materialized on the fly)

What gets stored: Only (the small latent vector). The full and are reconstructed from whenever attention is computed — they’re never stored.

Tradeoff:

  • Benefit: Cache is times smaller — DeepSeek reports 5–13× memory reduction vs. standard MHA
  • Cost: A small reconstruction compute is needed at each attention step (the projection); this is much cheaper than the saved memory cost

Relationship to GQA:

  • GQA reduces the number of KV heads (from to )
  • MLA reduces the dimension of each KV representation (from to )
  • They are complementary — you can apply both

6.5 — Speculative Decoding

Paper: Chen et al., 2023. Accelerating Large Language Model Decoding with Speculative Sampling.

The Problem: The Big Model Is Slow

A large, high-quality model (the “target” model) is expensive per forward pass. But autoregressive generation requires one forward pass per token. Generating 500 tokens = 500 expensive forward passes.

The Key Insight

Most of the time, a large and a small model agree on the next token. The next word in “I love machine learning and” is probably “deep” or “artificial” — you don’t need GPT-4 to figure that out. A much smaller, faster model (the “draft” model) can predict these easy cases correctly.

How Speculative Decoding Works

Phase 1 — Draft (cheap):
The small draft model autoregressively generates candidate tokens:

Draft with a small model, verify all drafts in one pass of the large model, accept or reject.
[BOS] my teddy bear    →  is  cute  and  smart   (4 draft tokens from ℳ_Q)
                           P₁   P₂   P₃   P₄

Phase 2 — Verify (one target forward pass):
The large target model evaluates all draft tokens in parallel in a single forward pass:

Target model processes: [BOS] my teddy bear is cute and smart
                                              ↑   ↑    ↑    ↑
                                    Computes Q₁  Q₂   Q₃   Q₄  Q₅ simultaneously

This parallelism is the magic: instead of slow forward passes of , it takes just one.

Phase 3 — Accept or Reject:
For each draft token with draft probability and target probability :

  • — probability the target model would have assigned to token
  • — probability the draft model assigned to token
  • — acceptance probability

Interpretation:

  • If : the target model is at least as confident as the draft → accept for sure ().
  • If : the draft was overconfident → accept with probability . On rejection, sample a corrected token from the “residual” distribution , renormalized, and terminate this draft batch.

Distribution guarantee: The accepted token sequence is distributed exactly according to — the target distribution. Speculative decoding is lossless. It does not change output quality; it only changes speed.

Why it’s faster:
When and agree on most tokens (high acceptance rate), tokens are produced using approximately 1 target forward pass instead of . Reported speedups: 2–3× on standard text generation tasks.

Requirements for this to work:

  • must be significantly faster than (typically smaller)
  • must be sufficiently aligned with (similar training, same tokenizer) to have a high acceptance rate
  • Low acceptance rate → few tokens accepted per verify pass → no speedup

6.6 — Multi-Token Prediction (MTP)

Paper: Gloeckle et al., 2024. Better & Faster Large Language Models via Multi-Token Prediction.

The Standard Single-Token Objective

Traditional LLM training uses a single prediction head: given hidden state at position , predict the next token :

  • — hidden state produced by the Transformer at position
  • — probability assigned to the correct next token
  • — cross-entropy loss (we minimize this)

The Multi-Token Objective

MTP adds additional prediction heads, one for each future position up to steps ahead:

Multi-token prediction adds heads that predict several future tokens from the same hidden state.

  • — number of future tokens predicted simultaneously
  • — offset into the future (1 = next token, 2 = two steps ahead, etc.)
  • All heads share the same hidden state as input

Training benefit:
Predicting multiple future tokens forces the hidden state to encode richer, more forward-looking information about the sequence. The model must “plan ahead” — its representation needs to capture enough structure to predict not just the immediate next token but several subsequent ones. This enriches learned representations and has been shown to improve downstream task performance.

Inference benefit — self-speculative decoding:
At inference time, the additional heads produce draft tokens in a single forward pass. The main model’s token-1 head then verifies them as in speculative decoding — but no separate smaller model is needed. The model speculates on its own future outputs.

Standard inference:
  Forward pass → predict x_{t+1} only
  (repeat t times for t tokens)

MTP inference:
  Forward pass → predict x_{t+1}, x_{t+2}, x_{t+3}, x_{t+4} simultaneously
  (draft 4 tokens, verify with main head, accept if consistent)

Connection to speculative decoding:
MTP heads play the role of the draft model . They are cheaper than a full forward pass (they share the backbone computation) and tend to be well-aligned with the main model (because they are the main model, with an extra head). This typically yields higher acceptance rates than using a separate smaller draft model.

DeepSeek-V3 uses MTP with (predicts 2 tokens ahead), contributing to its high throughput.

6.7 — Optimization Stack Summary

Optimization         Problem It Solves                          Key Result
─────────────────    ────────────────────────────────────────   ─────────────────────
KV Caching           Redundant K,V recomputation every step     O(n²) → O(n)
GQA                  KV cache too large (too many KV heads)     h/G × memory reduction
PagedAttention       KV memory fragmentation & pre-allocation   2-4× throughput gain
MLA                  KV cache too large (high dimension)        5-13× memory reduction
Speculative Decoding Too many target model forward passes       2-3× latency reduction
MTP                  Self-drafting (no separate draft model)    Throughput improvement
Guided Decoding      Structurally invalid output format         Guaranteed format validity

These optimizations are composable — production systems like vLLM, SGLang, and TensorRT-LLM stack several simultaneously.

6.8 — Why the Old Way Was Wasteful

Before large language models, machine learning worked task-by-task. If a company needed spam detection, they trained a spam model. If they then needed sentiment analysis, they trained a separate sentiment model. Translation? Another model entirely. Each model was trained from random weights, on task-specific data, from scratch.

This is deeply inefficient, because all these tasks share the same underlying requirement: understanding language. Every model had to re-learn English grammar, vocabulary, and reasoning on its own, independently, from nothing.

Traditional ML — one model per task:

  Data for spam        → Spam detection model    (trained from scratch)
  Data for sentiment   → Sentiment model         (trained from scratch)
  Data for translation → Translation model       (trained from scratch)

6.9 — The LLM Training Paradigm

The LLM era introduced a two-stage approach:

Stage 1 — Pretraining:
  Train one massive model on the entire internet.
  It becomes a universal language machine.

Stage 2 — Tuning:
  Specialize that model for any task you want.
  Spam / QA / Code / Medicine / ...

The key insight: Language understanding is a separable foundation. You pay the enormous cost of learning it once, then amortize that cost across every downstream use case. A model that already understands English, code, math, and reasoning only needs a small additional push to become good at a specific task.

6.10 — What Pretraining Is and Why It Exists

What it is: Pretraining is the first and most expensive stage of building an LLM. The model is initialized with random weights and then trained on enormous amounts of raw text — books, websites, code, scientific papers — until it learns the patterns, facts, and structures that underlie language.

Why it exists: Because you need a model that already “knows” how language works before you can teach it to be useful. Without pretraining, every finetuning run would need to re-learn basic language from scratch. Pretraining builds the foundation that everything else stands on.

6.11 — The Training Objective: Next-Token Prediction

The objective function for pretraining is deceptively simple:

Given all previous tokens, predict the next token.

Formally:

Variable meanings:

  • — the token at position (the one the model must predict)
  • — all tokens before position (the context the model sees)
  • — the probability the model assigns to the correct next token
  • — log-probability; turns multiplication into summation; approaches zero when prediction is perfect
  • The minus sign — turns maximum probability into minimum loss (gradient descent minimizes)

Intuition: The model is penalized for being surprised. If the correct next word is “Paris” and the model gives it 90% probability, the loss is small. If the model gives “Paris” only 1% probability (and was betting on “London”), the loss is large. Over trillions of predictions, the model learns to be less and less surprised — and to avoid surprise, it must internally model grammar, facts, reasoning, and style.

Context:         [BOS]   A    teddy   bear    is
                                                 ↓
Model predicts:                               "cute"  ← next token

Training signal: Was "cute" the actual next word? If not, adjust weights.

Why this simple objective creates powerful models: To correctly predict Paris in "The capital of France is ___", the model must have encoded geography. To predict return a + b in "def add(a, b):\n return ___", it must understand Python. Prediction forces the model to build rich internal representations of the world.

6.12 — Training Data

Pretraining requires massive, diverse text data. Modern LLMs are trained on mixtures of:

Source Why it is included
Common Crawl / web scrapes Sheer volume; covers every topic, domain, writing style
Wikipedia Clean, factual, encyclopedic text
GitHub Code in many programming languages
StackOverflow Code paired with explanations and reasoning
Books Long-form coherent reasoning and narrative
arXiv / research papers Scientific and technical knowledge

Scale benchmarks:

Model Pretraining tokens
GPT-3 ~300 billion
LLaMA 3 ~15 trillion

A useful approximation: 1 token ≈ ¾ of an English word. So 15 trillion tokens corresponds to roughly 11 trillion words — orders of magnitude more text than any human will ever read.

6.13 — FLOPs vs FLOPS — A Critical Distinction

These two terms are constantly confused. The distinction matters whenever you read about model training cost or hardware performance:

Term Full form What it measures Analogy
FLOPs (lowercase s) Floating-Point Operations Total arithmetic work a computation requires — a fixed quantity for a given job Distance of a journey
FLOPS (uppercase S) Floating-Point Operations per Second How fast a piece of hardware can do arithmetic — a rate Speed of a car

Example: Training GPT-4 may have required approximately FLOPs of total work. An H100 GPU runs at roughly 1,000 TFLOPS (= FLOPS). Dividing total work by hardware speed gives time. More hardware running in parallel reduces wall-clock time without changing total FLOPs required.

6.14 — Scaling Laws

In 2020, OpenAI researchers (Kaplan et al.) trained hundreds of models at different sizes and made a striking discovery: model performance improves predictably with scale, following smooth power laws along three independent axes.

Variable meanings:

  • — validation loss (lower is better; reflects how well the model predicts held-out text)
  • — number of model parameters (size of the model)
  • — number of training tokens (size of the dataset)
  • — total compute used, measured in FLOPs
  • — positive empirical exponents, each typically around 0.07–0.35

What this says: Double the parameters, and loss drops by a predictable amount. Double the data, loss drops again. Double the compute, it drops again. All three independently improve the model, and the improvements are reliable and smooth — not random.

The sample efficiency finding: Larger models learn faster per token. If you train a 10B-parameter model and a 1B-parameter model on the same 300B tokens:

Same 300 billion tokens of training:
   1B-parameter model  →  training loss: 2.8
  10B-parameter model  →  training loss: 2.4   (lower = better)

The 10B model extracts more knowledge from each token. This is called sample efficiency — larger models are more data-efficient. This counterintuitive result changed the industry’s strategy: when in doubt, scale up the model.

6.15 — The Chinchilla Scaling Law

Kaplan’s scaling laws told us that all three axes matter. But they left a critical question unanswered: if your compute budget is fixed, how should you split it between a larger model and more data?

The compute-optimal rule of roughly 20 training tokens per parameter, with well-known models marked.

DeepMind answered this in 2022 (Hoffmann et al.) by training many models across many size and data combinations, finding the combination that achieved the lowest loss for each compute budget. Their result, known as the Chinchilla Scaling Law:

What this means:

  • — the optimal number of training tokens
  • — the number of model parameters
  • For compute-optimal training: use approximately 20 tokens of training data per model parameter

Why “Chinchilla”: The paper’s Chinchilla model (70B parameters, 1.4T tokens) outperformed the much larger Gopher model (280B parameters, 300B tokens) trained by the same team at the same compute budget — proving that the balance between model size and data volume matters more than sheer size.

Concrete examples from the paper:

Parameters () Compute-optimal tokens ()
400 M 8.0 B
1 B 20.2 B
10 B 205 B
67 B 1.5 T
175 B 3.7 T

The GPT-3 revelation: GPT-3 had 175B parameters but was trained on only ~300B tokens — far less than the Chinchilla-optimal ~3.7T tokens. GPT-3 was massively undertrained on data. A smaller model trained on more tokens would have achieved better performance for the same compute budget.

Why modern LLMs over-train beyond Chinchilla: LLaMA 3 (8B parameters) was trained on 15 trillion tokens — roughly 94× the Chinchilla minimum of ~160B. The reason is economics: training is a one-time cost, but inference runs continuously in production. A model trained on more data is better at inference time, making it cheaper to serve at scale. Over-training beyond the compute-optimal point trades extra training cost for lower inference cost.

6.16 — Challenges of Pretraining

Pretraining is not without serious drawbacks:

Challenge Explanation
Cost Frontier models cost millions to hundreds of millions of dollars and take months of GPU cluster time
Energy The electricity and cooling required carry a significant carbon footprint
Knowledge cutoff The model only knows information that existed before training ended; it is blind to anything newer
Hard to edit You cannot surgically remove a false belief; fixing learned misinformation requires retraining or post-hoc correction
Memorization / copyright The model can reproduce chunks of training text verbatim, creating legal and ethical risks

6.17 — The Training Loop and Why Memory Is the Bottleneck

Before discussing solutions, we need to understand what happens during each training step and why GPU memory — not compute speed — is typically the limiting factor.

Parameters, gradients and Adam moments of a 7B FP32 model already exceed an 80 GB GPU.

Every training step has four phases:

┌──────────────────────────────────────────────────────────────┐
│  1. FORWARD PASS                                             │
│     Input tokens → embeddings → transformer layers          │
│     → output probability distribution → compute loss        │
│                                                              │
│  2. BACKWARD PASS (backpropagation)                         │
│     Walk back through every layer.                          │
│     Compute ∂Loss/∂W for every weight W in the model.      │
│     These derivatives are called "gradients".               │
│                                                              │
│  3. OPTIMIZER UPDATE (Adam)                                  │
│     Use gradients to update weights:                         │
│     W ← W − η · m̂ / (√v̂ + ε)                             │
│     where m̂, v̂ are running estimates of gradient           │
│     statistics (first and second moments).                   │
│                                                              │
│  4. REPEAT                                                   │
└──────────────────────────────────────────────────────────────┘

What must live in GPU memory at any moment:

Component Why it is needed Memory (for 7B params in FP32)
Parameters The model itself ~28 GB
Activations Intermediate outputs stored during forward pass for use in backward pass Depends on batch size × sequence length
Gradients One gradient per parameter, computed during backward pass ~28 GB
Adam first moment Running mean of gradients, one entry per parameter ~28 GB
Adam second moment Running variance of gradients, one entry per parameter ~28 GB
Total (params + optimizer state) ~112 GB

A single NVIDIA H100 GPU has 80 GB of HBM. A 7B model’s training state already requires ~112 GB — it does not fit. This is the fundamental problem that the following optimizations address.

6.18 — Data Parallelism

Problem: Training is too slow on a single GPU, even when the model fits.

Data parallelism replicates the model on every GPU and averages gradients with AllReduce.

Idea: Replicate the entire model on each of GPUs. Split each batch of data so every GPU processes a different chunk simultaneously. After all GPUs compute their gradients, average them together using an operation called AllReduce, then update weights identically on all GPUs.

          Batch of 1024 sequences
         ┌──────────┬──────────┬──────────┐
         │  256 seq │  256 seq │  256 seq │ ...
         └────┬─────┴────┬─────┴────┬─────┘
              ▼          ▼          ▼
         [GPU 1]      [GPU 2]    [GPU 3]      ...
         full model   full model  full model
         forward+back forward+back forward+back
              ↓          ↓          ↓
              └──────── AllReduce ─────────────┘
                    (average gradients across GPUs)
                         ▼
               identical weight update on all GPUs

What it improves: Throughput — tokens processed per second. With GPUs, you effectively process times more data per step, so training finishes faster.

What it does NOT improve: Memory per GPU. Every GPU still holds the complete model, complete gradients, and complete optimizer state. If the model doesn’t fit on one GPU, data parallelism alone won’t help.

6.19 — ZeRO — Zero Redundancy Optimization

Problem: In vanilla data parallelism with 8 GPUs, you store 8 identical copies of parameters, gradients, and optimizer states — a staggering 8× waste of memory.

ZeRO partitions optimizer states, then gradients, then parameters across GPUs.

Solution: ZeRO (Zero Redundancy Optimization, Rajbhandari et al., 2019), implemented in Microsoft’s DeepSpeed library, eliminates this redundancy by partitioning these components across GPUs instead of replicating them. Each GPU only stores its assigned shard. When it needs another GPU’s shard during computation, it fetches it over the high-speed GPU interconnect (NVLink or InfiniBand).

ZeRO comes in three increasingly aggressive stages:

┌──────────────────────────────────────────────────────────────────┐
│ ZeRO Stage 1 — Partition optimizer states                        │
│   Each GPU holds optimizer states (m, v) for a slice of params. │
│   Parameters and gradients are still fully replicated.           │
│   Memory saving: ~4× on optimizer states alone.                  │
├──────────────────────────────────────────────────────────────────┤
│ ZeRO Stage 2 — Partition optimizer states + gradients            │
│   After each backward pass, each GPU only retains gradients for  │
│   its own parameter slice; the rest are discarded.               │
│   Memory saving: ~8× total.                                      │
├──────────────────────────────────────────────────────────────────┤
│ ZeRO Stage 3 — Partition optimizer states + gradients + params   │
│   Each GPU stores only a slice of the model weights themselves.  │
│   Weights are gathered on-demand during forward/backward pass.   │
│   Memory scales as 1/N with N GPUs — enables trillion-param      │
│   models on clusters where no single GPU fits the full model.    │
└──────────────────────────────────────────────────────────────────┘

Trade-off: The memory savings come at the cost of communication. Fetching parameter shards from other GPUs during computation adds latency. In practice, this overhead is acceptable because it unlocks training of models that would otherwise be completely impossible.

Key point: ZeRO-3 is the standard technique for training 70B+ parameter models. The memory per GPU scales as with GPUs — the more GPUs you add, the more each GPU’s memory demand drops.

6.20 — Model Parallelism

An alternative family of approaches: instead of replicating the model and splitting data, split the model itself across GPUs. Different GPUs run different parts of the model for the same data.

Strategy How it works Best for
Tensor Parallelism (TP) Splits individual weight matrices column-wise or row-wise across GPUs. A single matrix multiply is distributed across several GPUs simultaneously. Models with very wide layers (large hidden dimension)
Pipeline Parallelism (PP) Assigns consecutive transformer blocks to consecutive GPUs. GPU 1 runs layers 1–10, GPU 2 runs 11–20, etc. Data flows through like an assembly line, with micro-batches pipelined to reduce idle time. Very deep models; each GPU holds fewer layers
Sequence Parallelism (SP) Splits long input sequences across GPUs, each processing a segment. Reduces activation memory proportionally. Extremely long context windows
Expert Parallelism (EP) For Mixture-of-Experts models: different expert sub-networks live on different GPUs. Tokens are routed to the GPU holding their assigned expert. MoE architectures (DeepSeek, Mixtral)

In production, all of these are typically combined simultaneously — so-called 3D or 4D parallelism. For example: data parallelism across nodes, tensor parallelism within a node, pipeline parallelism across nodes.

6.21 — FlashAttention

The Root Problem: Memory Bandwidth, Not Compute

Standard self-attention computes:

Variable meanings:

  • — Query matrix; each of the sequence positions has a -dimensional query vector representing “what am I looking for?”
  • — Key matrix; each position has a key vector representing “what do I offer?”
  • — Value matrix; the actual information content each position will share if attended to
  • — dimensionality of queries and keys; the scaling prevents dot products from growing too large in magnitude
  • — raw attention score matrix; every token’s compatibility score with every other token
  • — converts raw scores to probabilities (each row sums to 1)
  • Final output — weighted sum of values, where weights come from the attention probabilities

Most people assume attention is slow because the matrix multiplication is expensive math. This is wrong. Modern GPUs can perform matrix multiplications at extraordinary speed. The real bottleneck is something different: moving data between the GPU’s two types of memory.

The GPU Memory Hierarchy

Every GPU has two very different types of memory with very different characteristics:

HBM is large but slow; SRAM is tiny but fast, and compute units only touch SRAM.
┌───────────────────────────────────────────────────────────┐
│                         GPU                               │
│                                                           │
│   HBM  (High-Bandwidth Memory)                           │
│   • Full form: High-Bandwidth Memory                      │
│   • Size:  ~80 GB on H100                                │
│   • Bandwidth: ~3 TB/s (fast for DRAM, slow for GPU)     │
│   • Think: a large warehouse — stores everything but far  │
│                                                           │
│   SRAM  (Static Random-Access Memory, on-chip)           │
│   • Full form: Static Random-Access Memory                │
│   • Size:  ~20 MB on H100  (20,000× smaller than HBM)   │
│   • Bandwidth: ~20 TB/s  (10–100× faster than HBM)      │
│   • Think: the workbench — tiny but right next to you    │
│                                                           │
│   Compute Units (CUDA Cores / Tensor Cores)               │
│   • Perform the actual arithmetic                         │
│   • Can only work on data currently in SRAM               │
└───────────────────────────────────────────────────────────┘

The critical constraint: compute units can only work on data in SRAM. Data sitting in HBM must be fetched into SRAM first, then results must be written back to HBM for storage. Each round-trip between HBM and SRAM takes time — and that is the bottleneck.

Standard Attention: The Wasteful Memory Traffic Pattern

Here is what standard attention actually does, step by step, in terms of memory movement:

1. LOAD Q, K from HBM into SRAM
        ↓
2. Compute S = QKᵀ / √d_k   (arithmetic in compute units)
        ↓
3. WRITE S → HBM   ← S is N×N; for N=4096, that is 64M entries. Slow.
        ↓
4. READ S ← HBM    ← immediately read it back just to apply softmax
        ↓
5. Compute P = softmax(S)   (arithmetic in compute units)
        ↓
6. WRITE P → HBM   ← another huge N×N round-trip
        ↓
7. READ P, V ← HBM
        ↓
8. Compute O = PV   (arithmetic in compute units)
        ↓
9. WRITE O → HBM

Steps 3–6 write the full matrix to HBM and then immediately read it back — just to apply a softmax. For a sequence of 4,096 tokens, contains over 16 million entries. This is wasteful: and are temporary intermediate results, not things that need to be stored. Forcing them through HBM creates a massive bandwidth bottleneck that limits training speed.

FlashAttention: Two Ideas That Fix This

Idea 1 — Tiling (never write the full score matrix to HBM):

Standard attention versus FlashAttention’s tiled computation that never writes the N×N matrix to HBM.

FlashAttention splits , , and into small tiles (blocks) that fit inside SRAM. It processes one tile at a time, entirely within SRAM, accumulates the output incrementally, and only writes the final output to HBM. The enormous intermediate and matrices never exist in their full form and never touch HBM.

Q split into row tiles:    K split into column tiles:
  ┌────┐                   ┌────┬────┬────┐
  │ Q₁ │                   │ K₁ │ K₂ │ K₃ │
  ├────┤                   └────┴────┴────┘
  │ Q₂ │
  ├────┤   For each tile pair (Qᵢ, Kⱼ):
  │ Q₃ │     1. Load Qᵢ and Kⱼ from HBM into SRAM
  └────┘     2. Compute attention scores for this tile (stays in SRAM)
              3. Accumulate into running output Oᵢ (using online softmax)
              4. Discard intermediate scores; move to next tile
  Only the final Oᵢ result is written to HBM. Intermediates stay in SRAM.

The online softmax trick — how tiling handles the normalization problem: Normally, to compute softmax for a row of , you need the entire row to compute the denominator . FlashAttention uses a running-maximum algorithm: as each tile is processed, it updates a running maximum and running sum. The final softmax values are exact — identical to what you would get computing the full matrix — but computed without ever having the full row in memory at once.

Idea 2 — Recomputation in the backward pass (trade arithmetic for memory traffic):

During a standard forward pass, the attention probabilities (shape ) are saved in HBM so that the backward pass can use them to compute gradients. FlashAttention instead recomputes during the backward pass using tiling, rather than loading it from HBM.

Standard:      Forward saves P → HBM.  Backward loads P ← HBM.
               Cost: 2 × N² extra HBM reads and writes.

FlashAttention: Forward does NOT save P.
                Backward recomputes P via tiling (stays in SRAM).
                Cost: Extra arithmetic FLOPs, but zero extra HBM traffic.

This trades arithmetic (more FLOPs) for memory bandwidth (fewer HBM accesses). Since HBM bandwidth is the bottleneck — not arithmetic — the trade is profitable. More FLOPs, lower wall-clock time. This is the counterintuitive but correct key insight.

Results of FlashAttention:

  • 2–4× speedup in end-to-end attention computation
  • Memory footprint for attention drops from to — enables much longer context windows
  • Exact attention — not an approximation. Output is bit-for-bit identical to standard attention.

6.22 — Floating-Point Formats and Mixed Precision Training

Why Number Representation Matters

Every weight in a neural network is a floating-point number stored in memory using a fixed number of bits. The choice of format determines both memory usage and compute speed — and critically, which operations are numerically stable during training.

FP32, BF16 and FP16 differ in how many bits go to exponent (range) and mantissa (precision).

A floating-point number uses three fields:

Field Bits Controls
Sign 1 Whether the number is positive or negative
Exponent bits The range of representable magnitudes
Mantissa bits The precision — how many significant digits

The four formats relevant to LLM training:

Format Sign Exponent Mantissa Max value Notes
FP64 1 11 52 Very high precision; rarely used in DL
FP32 1 8 23 Standard “full precision” baseline
FP16 1 5 10 65,504 Narrow range — risky for training
BF16 1 8 7 Same range as FP32 — preferred for training

The critical difference between FP16 and BF16:

FP16 has only 5 exponent bits, giving a maximum representable value of about 65,504. If any gradient or activation during training exceeds this (which happens easily with large models), it becomes inf or NaN. Training crashes. This makes FP16 fragile.

BF16 (“Brain Float 16”, developed at Google Brain) keeps FP32’s 8 exponent bits — the same representable range — while trimming the mantissa from 23 to 7 bits. You lose precision granularity but keep the range. For LLM training, BF16 is almost always preferred over FP16. On modern GPUs (A100, H100), both run at roughly 2× the throughput of FP32.

Mixed Precision Training — The Key Strategy

The core problem: You want FP16/BF16 speed (2× faster compute, 2× less memory), but FP32 stability for weight updates. Using only one format everywhere is suboptimal.

Mixed precision computes in 16 bits but updates an FP32 master copy.

The solution: Use different formats for different parts of the training loop based on their numerical sensitivity.

Operation Format Reason
Forward pass (activations) FP16 / BF16 Transient values; small rounding errors are acceptable and do not accumulate
Backward pass (gradients) FP16 / BF16 Also transient; acceptable precision loss
Stored master weights FP32 Weight updates can be as tiny as ; FP16 would round these to exactly zero
Adam optimizer moments (, ) FP32 Accumulate over thousands of steps; must not lose precision over time
  ┌──────────────── FP32 master weights (stored in HBM) ────────────────┐
  │                                                                      │
  │   Cast down to FP16/BF16 ↓              ↑ Update master weights     │
  │                                                                      │
  │   ┌──────────── Low-precision compute zone ──────────────────┐      │
  │   │  Forward pass: activations in FP16/BF16                  │      │
  │   │  Backward pass: gradients in FP16/BF16                   │      │
  │   └──────────────────────────────────────────────────────────┘      │
  │                                                                      │
  │   Master weight update: W_fp32 ← W_fp32 − η · gradient_fp16        │
  └──────────────────────────────────────────────────────────────────────┘

Why the FP32 master weight is essential: Suppose a weight is 0.500000 and the gradient update says to move by 0.000001. In FP16 (limited precision), 0.500001 rounds back to 0.500000 — the update completely vanishes. The weight stops learning. Accumulated over millions of steps, training stalls. The FP32 master weight captures the tiny cumulative movements correctly, even when they are too small for FP16 to represent.

6.23 — Key Equations Reference

Concept Equation Plain English
Router score Dot product between token embedding and expert direction
Router probability Softmax converts scores to routing probabilities
Sparse MoE output Weighted sum of top-k expert outputs
DeepSeek MoE output Shared expert always active + top-k routed experts
Load balance loss Penalizes dominance of any single expert
Noisy routing Random noise forces exploration of all experts
Bias-based balancing Bias adjusts selection without polluting gradients
Greedy decoding Pick the most probable token every step
Beam score Cumulative log-probability of a candidate sequence
Top-k sampling Sample from the most probable tokens
Top-p (nucleus) Smallest set of tokens covering fraction of probability
Temperature Dividing logits by reshapes distribution sharpness
Guided decoding Mask invalid tokens; renormalize over legal tokens only
Chain of Thought Answer conditioned on both question and reasoning chain
Self-Consistency Marginalize answer over multiple reasoning paths
KV cache memory Grows linearly with sequence length
Expert capacity Max tokens per expert per batch
Spec. decoding acceptance Accept draft token if target model agrees
MTP training loss Predict next tokens simultaneously

6.24 — Reference Papers

Topic Paper Year
Original Sparse MoE Shazeer et al. — Outrageously Large Neural Networks 2017
Switch Transformer (Top-1 + load balance loss) Fedus et al. — Switch Transformers 2021
Mixtral Jiang et al. — Mixtral of Experts 2024
DeepSeekMoE (shared + fine-grained experts) DeepSeek-AI — DeepSeekMoE 2024
Aux-loss-free balancing DeepSeek-V3 Technical Report 2024
PagedAttention / vLLM Kwon et al. 2023
Grouped Query Attention Ainslie et al. 2023
Speculative Decoding Chen et al. — Accelerating LLM Decoding with Speculative Sampling 2023
Multi-Token Prediction Gloeckle et al. — Better & Faster LLMs via MTP 2024
Chain of Thought Wei et al. — CoT Prompting Elicits Reasoning 2022
Self-Consistency Wang et al. — Self-Consistency Improves CoT 2022
Context Rot Hong et al. 2025
Few-Shot ICL Brown et al. — Language Models are Few-Shot Learners 2020