How LLMs Are Implemented, Algorithmically

Pull apart a decoder-only large model and you find only five modules: attention, normalization, positional encoding, the feed-forward network, plus the prefill / decode scheduling that strings them into a two-phase inference loop. This structure has barely changed from the 2017 Transformer [1] to today, and every “new architecture” is a local replacement inside one of these five modules. This piece gives each module its own chapter, writes out its core formulas, and then uses two real, current models — the latest Qwen3.5 [24] and DeepSeek-V4 [25] — to see what choices each one makes across these modules.

The whole piece uses one consistent set of symbols:

SymbolMeaning
BBbatch size
SSsequence (prompt) length
LLnumber of layers
HHhidden dim (the model’s main dimension)
VVvocabulary size
nqn_qnumber of Q heads
nkvn_{kv}number of KV heads
ddper-head dim
IIFFN intermediate dim

Tensor shapes follow PyTorch convention, written as [B, ..., H]; weight matrices follow the “input dim × output dim” convention, written as WR[in,out]W \in \mathbb{R}^{[\text{in}, \text{out}]}.

Attention — Q/K/V · Scaled Dot-Product · MHA/MQA/GQA/MLA

Attention is the only module in a Transformer that lets different tokens exchange information; every other module operates on each token independently. It first projects each token’s hidden state XX into three copies — query, key, and value:

Q=XWQ,K=XWK,V=XWVQ = X W_Q, \quad K = X W_K, \quad V = X W_V
  • XRB×S×HX \in \mathbb{R}^{B \times S \times H} — the normalized input
  • WQRH×nqdW_Q \in \mathbb{R}^{H \times n_q d} — Q projection; WK,WVRH×nkvdW_K, W_V \in \mathbb{R}^{H \times n_{kv} d} — K, V projections
  • QRB×S×nqdQ \in \mathbb{R}^{B \times S \times n_q d}, K,VRB×S×nkvdK, V \in \mathbb{R}^{B \times S \times n_{kv} d}, then reshaped to expose the head dimension

Scaled Dot-Product Attention

Within a single head, attention is just “use the similarity between Q and all Ks as weights to average over V”:

Attention(Q,K,V)=softmax ⁣(QKd+M)V\text{Attention}(Q, K, V) = \text{softmax}\!\left(\frac{QK^{\top}}{\sqrt{d}} + M\right) V
  • QKRS×SQK^{\top} \in \mathbb{R}^{S \times S} — the similarity matrix for each pair (qi,kj)(q_i, k_j)
  • d\sqrt{d} — the scaling factor, keeping the inner products from growing so large that softmax is pushed into a saturated region with near-zero gradients
  • MRS×SM \in \mathbb{R}^{S \times S} — the causal mask, with the upper triangle set to -\infty (position ii can only see positions i\le i)
  • softmax normalizes along the K dimension into attention weights, which are then weighted-summed with VV back to [S,d][S, d]

Multi-Head attention replicates this operation nqn_q times, each in its own dd-dimensional subspace, then concatenates and projects back to HH:

MHA(X)=[head(1);;head(nq)]WO,WORnqd×H\text{MHA}(X) = [\text{head}^{(1)}; \ldots; \text{head}^{(n_q)}]\, W_O, \qquad W_O \in \mathbb{R}^{n_q d \times H}

Different heads learn different attention patterns (near-neighbor, syntactic, coreference, …), which is where attention’s expressiveness comes from.

Four variants: touching only the K, V side

At inference, QQ is used once and discarded, but the KK, VV of every historical position must be kept for later tokens — this is the KV Cache. It grows linearly with the sequence and is the main pressure on long-context memory and decode bandwidth. The difference among the four attention variants lies entirely in “how many independent sets of K, V there are, and whether K, V are low-rank compressed”; the Q side is always nqn_q independent heads.

MHA (Multi-Head Attention, the original): every Q head gets its own independent set of K, V, for nqn_q sets total. Most expressive, and the largest cache.

MQA (Multi-Query Attention) [2]: all nqn_q Q heads share the same single set of K, V, leaving just 1 set:

qi(h)=WQ(h)xi,ki=WKxi,vi=WVxi,h=1,,nq\mathbf{q}^{(h)}_i = W_Q^{(h)} \mathbf{x}_i, \quad \mathbf{k}_i = W_K \mathbf{x}_i, \quad \mathbf{v}_i = W_V \mathbf{x}_i, \qquad h = 1, \ldots, n_q

The cache shrinks to 2d2d, nqn_q times smaller than MHA, but expressiveness is limited and large models applying it directly tend to lose accuracy.

GQA (Grouped-Query Attention) [3]: split the nqn_q Q heads into nkvn_{kv} groups sharing K, V within each group — a continuous interpolation between MHA and MQA:

head(h)=softmax ⁣(Q(h)K(g(h))d+M)V(g(h)),g(h)=hnkvnq\text{head}^{(h)} = \text{softmax}\!\left(\frac{Q^{(h)} {K^{(g(h))}}^{\top}}{\sqrt{d}} + M\right) V^{(g(h))}, \qquad g(h) = \left\lfloor \frac{h \cdot n_{kv}}{n_q} \right\rfloor

nkv=nqn_{kv} = n_q degenerates to MHA, nkv=1n_{kv} = 1 to MQA. The kernel only computes nkvn_{kv} sets of K, V and broadcasts along the group dimension when computing scores, without actually replicating tensors. This is the current default for dense and most MoE models (Llama 3, Qwen3, Mistral).

MLA (Multi-head Latent Attention) [4]: GQA only scales the cache linearly with head count; MLA instead compresses K and V jointly into a low-rank latent vector cKV\mathbf{c}^{KV}, then routes RoPE through a separate, shared, small branch. In four steps:

Content branch — K and V share one down-projection, and only the latent vector ciKV\mathbf{c}^{KV}_i enters the cache:

ciKV=WDKVxi,kiC,(h)=WKU,(h)ciKV,vi(h)=WVU,(h)ciKV\mathbf{c}^{KV}_i = W^{DKV} \mathbf{x}_i, \quad \mathbf{k}^{C,(h)}_i = W_K^{U,(h)} \mathbf{c}^{KV}_i, \quad \mathbf{v}^{(h)}_i = W_V^{U,(h)} \mathbf{c}^{KV}_i

The Q side is also low-rank (saving memory during training):

ciQ=WDQxi,qiC,(h)=WQU,(h)ciQ\mathbf{c}^{Q}_i = W^{DQ} \mathbf{x}_i, \quad \mathbf{q}^{C,(h)}_i = W_Q^{U,(h)} \mathbf{c}^{Q}_i

Decoupled RoPE branch — on the K side, only one shared drd_r-dim vector is computed, used by all heads:

qiR,(h)=RoPE(WQR,(h)ciQ),kiR=RoPE(WKRxi)\mathbf{q}^{R,(h)}_i = \text{RoPE}(W_Q^{R,(h)} \mathbf{c}^{Q}_i), \quad \mathbf{k}^{R}_i = \text{RoPE}(W^{KR} \mathbf{x}_i)

Concatenate and compute attention (the content part and the RoPE part concatenated along the head dimension):

qi(h)=[qiC,(h);qiR,(h)],ki(h)=[kiC,(h);kiR]\mathbf{q}^{(h)}_i = [\mathbf{q}^{C,(h)}_i; \mathbf{q}^{R,(h)}_i], \quad \mathbf{k}^{(h)}_i = [\mathbf{k}^{C,(h)}_i; \mathbf{k}^{R}_i]

The key point: MLA actually caches only ciKVRdc\mathbf{c}^{KV}_i \in \mathbb{R}^{d_c} and kiRRdr\mathbf{k}^{R}_i \in \mathbb{R}^{d_r}, for dc+drd_c + d_r numbers in total. At inference, the associativity of matrices folds the up-projection WKU,(h)W_K^{U,(h)} into WQU,(h)W_Q^{U,(h)}:

qiC,(h)kjC,(h)=ciQ(WQU,(h))WKU,(h)pre-multiplied before inferencecjKV{\mathbf{q}^{C,(h)}_i}^{\top} \mathbf{k}^{C,(h)}_j = {\mathbf{c}^{Q}_i}^{\top} \underbrace{(W_Q^{U,(h)})^{\top} W_K^{U,(h)}}_{\text{pre-multiplied before inference}} \mathbf{c}^{KV}_j

This way the K content part never has to be reconstructed; attention is computed directly on the cached latent vectors. But RoPE’s rotation angle depends on the absolute position and cannot be folded into fixed weights — once it is applied on the reconstructed K, the folding breaks. So the positional signal must be split into an independent drd_r-dim shallow branch.

KV dimension comparison

Variantnkvn_{kv}per-token cache (per layer, fp16)representative models
MHA=nq= n_q2nqd2B2 \cdot n_q \cdot d \cdot 2\text{B}GPT-2/3, Llama 1/2 7B
MQA=1= 12d2B2 \cdot d \cdot 2\text{B}PaLM, Falcon
GQAgrouped <nq< n_q2nkvd2B2 \cdot n_{kv} \cdot d \cdot 2\text{B}Llama 3, Qwen3, Mistral
MLAlow-rank compressed(dc+dr)2B(d_c + d_r) \cdot 2\text{B}DeepSeek V2/V3

In one line: MHA → MQA/GQA saves params, FLOPs, and cache all at once; MLA adds a few projection stages, trading params/FLOPs for cache — shrinking the per-token cache from the KB range down to ~1 KB.

Linear attention and hybrid stacking

All four variants above are still softmax attention: O(S2)O(S^2) in sequence length, with a cache that grows linearly. Linear attention replaces softmax with an associative feature map ϕ\phi, rewriting attention as a recurrence over a “state matrix”, dropping the complexity to O(S)O(S) and compressing the KV Cache into a state of fixed size, independent of sequence length:

St=St1+vtϕ(kt),ot=Stϕ(qt)S_t = S_{t-1} + \mathbf{v}_t\, \phi(\mathbf{k}_t)^{\top}, \qquad \mathbf{o}_t = S_t\, \phi(\mathbf{q}_t)
  • StRdv×dkS_t \in \mathbb{R}^{d_v \times d_k} — the state accumulated up to step tt, equivalent to “compressing” all historical KV into one matrix
  • each decode step only updates and reads StS_t once, no longer slowing down as the cache grows

Naive linear attention cannot remember long-range details (the state only grows and cannot be precisely rewritten). Gated DeltaNet [26] adds two things to the recurrence: a gated decay αt(0,1)\alpha_t \in (0,1) (borrowed from Mamba2, letting the state forget quickly) and a delta rule (βt\beta_t controls precise rewriting of a specific key rather than pure accumulation):

St=αtSt1(Iβtktkt)+βtvtktS_t = \alpha_t\, S_{t-1}\big(I - \beta_t\, \mathbf{k}_t \mathbf{k}_t^{\top}\big) + \beta_t\, \mathbf{v}_t \mathbf{k}_t^{\top}

In practice no one uses pure linear attention on its own: it is good at handling long sequences but weak at precise retrieval. The mainstream approach is hybrid stacking — most layers use linear attention to cut cost, and every few layers a full-attention layer is inserted to restore retrieval capability. The Qwen3.5 chapter below is exactly this “3 linear + 1 full” ratio.

Sparsification and compression: letting each query see only part of the KV

Full softmax lets every query see all historical KV; to save cache and FLOPs in ultra-long contexts, there are two orthogonal routes:

Compression — aggregate the KV of mm consecutive tokens into 1 “compressed entry” in advance, so attention runs over compressed entries and the effective sequence length shrinks to 1m\frac{1}{m}:

ci=j=mim(i+1)1wijkvj,i=0,1,,Sm1\mathbf{c}_i = \sum_{j=mi}^{m(i+1)-1} w_{ij}\, \mathbf{kv}_j, \qquad i = 0, 1, \ldots, \tfrac{S}{m}-1

Sparse selection — no compression, but for each query pick only the most relevant top-kk historical entries to attend to, skipping the rest. DeepSeek’s approach is a lightweight lightning indexer that first computes a set of cheap index scores It,sI_{t,s} per query, then takes the top-kk:

It,s=hwt,hIReLU ⁣(qt,hIksI),St=Top-k(It,:)I_{t,s} = \sum_{h} w^{I}_{t,h}\cdot \text{ReLU}\!\big({\mathbf{q}^{I}_{t,h}}^{\top} \mathbf{k}^{I}_{s}\big), \qquad \mathcal{S}_t = \text{Top-}k(I_{t,:})

The two routes stack: compress first, then do top-kk sparse selection over the compressed entries — that is DeepSeek-V4’s CSA below; push the compression rate to the max, drop sparse selection, and keep dense attention over the compressed entries — that is its HCA. Add a sliding-window branch for local detail and a set of attention sinks that let each head’s attention weights not sum to 1, and you have V4’s hybrid attention.

Normalization — LayerNorm vs RMSNorm · pre-norm

Normalization stabilizes the numerical scale of each layer’s input so that deep networks stay trainable. Standard LayerNorm does zero-mean, unit-variance over the feature dimension, then applies an affine transform:

LN(x)=xμσ2+ϵγ+β,μ=1Hixi, σ2=1Hi(xiμ)2\text{LN}(\mathbf{x}) = \frac{\mathbf{x} - \mu}{\sqrt{\sigma^{2} + \epsilon}} \odot \boldsymbol{\gamma} + \boldsymbol{\beta}, \quad \mu = \tfrac{1}{H}\sum_i x_i,\ \sigma^{2} = \tfrac{1}{H}\sum_i (x_i - \mu)^{2}
  • γ,βRH\boldsymbol{\gamma}, \boldsymbol{\beta} \in \mathbb{R}^{H} — learnable scale / shift
  • ϵ\epsilon — small constant to avoid division by zero (typically 10510^{-5})
  • \odot — element-wise multiplication

RMSNorm [5] (the mainstream choice for Llama / Qwen / DeepSeek) drops the mean-centering and the bias, using only root-mean-square scaling:

RMSNorm(x)=x1Hi=1Hxi2+ϵγ\text{RMSNorm}(\mathbf{x}) = \frac{\mathbf{x}}{\sqrt{\frac{1}{H}\sum_{i=1}^{H} x_i^{2} + \epsilon}} \odot \boldsymbol{\gamma}

The denominator is the root mean square of x\mathbf{x}, hence the name. It drops the mean and also drops β\boldsymbol{\beta}, roughly halving both compute and parameters, and empirically this is almost lossless for quality.

Placement: pre-norm, not post-norm. The original Transformer was post-norm (norm after the residual addition), which tends to be gradient-unstable at depth; modern models all use pre-norm — the norm sits inside the residual branch, and the trunk jumps straight across:

h=x+Attn(Norm(x))pre-normvsh=Norm(x+Attn(x))post-norm\underbrace{\mathbf{h} = \mathbf{x} + \text{Attn}(\text{Norm}(\mathbf{x}))}_{\text{pre-norm}} \qquad \text{vs} \qquad \underbrace{\mathbf{h} = \text{Norm}(\mathbf{x} + \text{Attn}(\mathbf{x}))}_{\text{post-norm}}

Pre-norm makes the residual trunk a “highway” undisturbed by the norm, allowing stable training even at hundreds of layers.

Beyond the residual: hyper-connections. Pre-norm adds each layer’s output back to a single residual trunk. Hyper-Connections [27] widen this trunk into nhcn_{hc} parallel residual streams, mixing between layers with learnable (and input-dependent) coefficient matrices Al,Bl,ClA_l, B_l, C_l:

Xl+1=BlXl+ClFl(AlXl),XlRnhc×HX_{l+1} = B_l X_l + C_l\, \mathcal{F}_l(A_l X_l), \qquad X_l \in \mathbb{R}^{n_{hc} \times H}

This relaxes the “fixed depth order + single residual” constraint, easing the tug-of-war between vanishing gradients and representation collapse. DeepSeek-V4’s mHC further constrains the residual mapping BlB_l to the manifold of doubly stochastic matrices (spectral norm 1\le 1, non-expansive mapping) in exchange for numerical stability when stacking deep — see the DeepSeek chapter below.

Positional encoding — absolute position → RoPE → length extrapolation

Attention itself is order-insensitive (shuffle the tokens and the result is unchanged), so positional information must be injected explicitly. The original Transformer used absolute positional encoding: build a sinusoidal vector for each position mm and add it to the embedding. The downsides are that it only encodes absolute position and extrapolates poorly to lengths never seen in training.

RoPE (Rotary Positional Embedding) [6] takes a different tack: instead of adding a vector, it lets position act on Q and K as a rotation, so the inner product automatically retains only relative position. It slices each head’s dd dimensions into d/2d/2 two-dimensional subspaces and, at position mm, multiplies the kk-th coordinate pair by a 2D rotation matrix:

(q2k(m)q2k+1(m))=(cos(mθk)sin(mθk)sin(mθk)cos(mθk))(q2kq2k+1),θk=base2k/d\begin{pmatrix} q'^{(m)}_{2k} \\ q'^{(m)}_{2k+1} \end{pmatrix} = \begin{pmatrix} \cos(m\theta_k) & -\sin(m\theta_k) \\ \sin(m\theta_k) & \phantom{-}\cos(m\theta_k) \end{pmatrix} \begin{pmatrix} q_{2k} \\ q_{2k+1} \end{pmatrix}, \quad \theta_k = \text{base}^{-2k/d}
  • mm — the token’s absolute position; k{0,,d/21}k \in \{0, \ldots, d/2 - 1\} — the 2D-subspace index
  • θk\theta_k — the angular velocity of the kk-th subspace, with base\text{base} the frequency-decay base (Llama takes 10410^4, Qwen3 takes 10610^6)

Why does rotation encode relative position? Denote the whole dd-dim rotation as the block-diagonal matrix Rm\mathbf{R}_m; it is orthogonal and satisfies RmRn=Rnm\mathbf{R}_m^{\top}\mathbf{R}_n = \mathbf{R}_{n-m}, hence:

Rmq,  Rnk=qRmRnk=qRnmk\langle \mathbf{R}_m \mathbf{q},\; \mathbf{R}_n \mathbf{k} \rangle = \mathbf{q}^{\top} \mathbf{R}_m^{\top} \mathbf{R}_n \mathbf{k} = \mathbf{q}^{\top} \mathbf{R}_{n-m} \mathbf{k}

When attention computes QKQK^{\top}, the score for each pair (qi,kj)(q_i, k_j) depends only on the difference jij - i — absolute position is canceled in the inner product, leaving relative position. The frequency spectrum θk=base2k/d\theta_k = \text{base}^{-2k/d} gives the d/2d/2 subspaces angular velocities from fast to slow: k=0k=0 has period about 2π2\pi tokens handling near-neighbors, k=d/21k=d/2-1 has period tens of thousands of tokens handling long range. RoPE acts only on Q and K, not on V.

Length extrapolation. Training only ever sees mLtrainm \le L_{\text{train}}; once inference length exceeds this, the low-frequency subspaces enter the out-of-distribution region and attention degrades. The common fixes all modify θk\theta_k:

  • Position Interpolation: mm/sm \to m/s, compressing all frequencies by the same ratio — simple, but it sacrifices high-frequency precision.
  • NTK-aware: scale only the low frequencies while preserving the high ones, equivalent to enlarging base\text{base}.
  • YaRN [7]: handle frequency bands separately — high frequencies unchanged, low frequencies scaled à la PI, with a smooth transition in between, plus a temperature correction to offset the attention-entropy drift. The long-context extension of Llama 3.1 and Qwen3 both use it.

Activation functions and the FFN — GELU · SwiGLU · GeGLU · MoE

After attention comes the per-token feed-forward network (FFN), responsible for nonlinear transformation and knowledge storage. The classic FFN first projects up, applies an activation, then projects down:

FFN(x)=ϕ(xW1)W2,W1RH×I, W2RI×H\text{FFN}(\mathbf{x}) = \phi(\mathbf{x} W_1) W_2, \qquad W_1 \in \mathbb{R}^{H \times I},\ W_2 \in \mathbb{R}^{I \times H}

The activation ϕ\phi is an element-wise scalar nonlinearity. The mainstream choices:

ReLU(x)=max(0,x),GELU(x)=xΦ(x),SiLU(x)=xσ(x)=x1+ex\text{ReLU}(x) = \max(0, x), \qquad \text{GELU}(x) = x\,\Phi(x), \qquad \text{SiLU}(x) = x\,\sigma(x) = \frac{x}{1 + e^{-x}}
  • Φ\Phi is the standard-normal CDF; GELU [9] is used in GPT-2/3 and BERT
  • SiLU (a.k.a. Swish) has a shape close to GELU but simpler, and is the gating activation of the Llama / PaLM line

Gated FFN: the GLU family

The real engineering inflection point is switching the activation from “wrapped on the projection” to “wrapped on the gate”. The GLU family [8] uses two independent up-projections, one as gate and one as value, multiplied element-wise:

GLU(x)=(ϕ(xWgate)(xWup))Wdown\text{GLU}(\mathbf{x}) = \big(\phi(\mathbf{x} W_{\text{gate}}) \odot (\mathbf{x} W_{\text{up}})\big) W_{\text{down}}

Whichever ϕ\phi you pick names the variant: ϕ=GELU\phi = \text{GELU} is GeGLU (T5 v1.1), ϕ=SiLU\phi = \text{SiLU} is the most common SwiGLU (PaLM, Llama, Qwen, DeepSeek):

SwiGLU(x)=(SiLU(xWgate)(xWup))Wdown\text{SwiGLU}(\mathbf{x}) = \big(\text{SiLU}(\mathbf{x} W_{\text{gate}}) \odot (\mathbf{x} W_{\text{up}})\big) W_{\text{down}}

SwiGLU has one more projection than the classic FFN (three matrices vs two), and to keep the parameter budget aligned, implementations usually set the intermediate dim to I234HI \approx \tfrac{2}{3}\cdot 4H.

Mixture-of-Experts (MoE)

In a dense FFN, every token passes through the same pair of weights, paying for all parameters and all FLOPs. MoE [10] replicates the FFN into NN “experts”, with each token selecting only the top-kk to compute — total parameters scale linearly while the per-token activated parameters and FLOPs stay almost unchanged, trading capacity for knowledge, not for compute:

MoE(x)=iTk(x)gi(x)FFNi(x),s(x)=softmax(xWg),Tk=TopK(s,k)\text{MoE}(\mathbf{x}) = \sum_{i \in \mathcal{T}_k(\mathbf{x})} g_i(\mathbf{x}) \cdot \text{FFN}_i(\mathbf{x}), \qquad \mathbf{s}(\mathbf{x}) = \text{softmax}(\mathbf{x} W_g), \quad \mathcal{T}_k = \text{TopK}(\mathbf{s}, k)
  • WgRH×NW_g \in \mathbb{R}^{H \times N} — the router, mapping the hidden state to the logits of NN experts
  • Tk(x)\mathcal{T}_k(\mathbf{x}) — the selected top-kk expert set; gig_i is the normalized combine weight
  • each FFNi\text{FFN}_i is usually just a SwiGLU, with only the weights not shared

A naive router will “collapse” onto a few hot experts, requiring load balancing. GShard/Switch use an auxiliary loss Laux=αNifisˉi\mathcal{L}_{\text{aux}} = \alpha N \sum_i f_i \bar{s}_i (fif_i the routing fraction, sˉi\bar{s}_i the average probability) as a soft constraint; DeepSeek instead uses an auxiliary-loss-free scheme [12]: maintain a bias bib_i per expert, base TopK on si+bis_i + b_i, raise it for underused experts and lower it for overused ones — affecting only selection, without polluting the gradient. These two pieces (fine-grained MoE, loss-free balancing) are central to the DeepSeek chapter below.

Prefill and Decoding — GEMM vs GEMV · compute vs bandwidth

Autoregressive inference splits into two phases with sharply different dimensions and bottlenecks.

Prefill: ingest all SS prompt tokens at once, run through every layer in parallel, and produce the first output token plus the full KV Cache. The main tensor shape is [B,S,H][B, S, H], all matrix multiplications are matrix × matrix (GEMM), arithmetic intensity is high, and it is compute-bound, much like a single training forward pass.

Decoding: each step feeds only the 1 token generated last step, reads all historical K, V from the cache, and computes the next token. The main shape is [B,1,H][B, 1, H], matrix multiplication degenerates to matrix × vector (GEMV), most time is spent moving weights from memory into the compute units, and it is memory-bandwidth-bound.

PositionPrefillDecode (per step)
input_ids[B,S][B, S][B,1][B, 1]
Q[B,nq,S,d][B, n_q, S, d][B,nq,1,d][B, n_q, 1, d]
K/V (from cache)[B,nkv,S,d][B, n_{kv}, S, d][B,nkv,cache_len,d][B, n_{kv}, \text{cache\_len}, d]
attention scores[B,nq,S,S][B, n_q, S, S][B,nq,1,cache_len][B, n_q, 1, \text{cache\_len}]
operation typeGEMM (mat × mat)GEMV (mat × vec)
bottleneckcomputememory bandwidth

KV Cache shape and growth

Each layer caches a pair of tensors that keep growing as generation proceeds:

Kcache,VcacheRB×nkv×Smax×dK_{\text{cache}}, V_{\text{cache}} \in \mathbb{R}^{B \times n_{kv} \times S_{\max} \times d}

The per-token, per-layer cache size (fp16, GQA) is 2×nkv×d×2 bytes2 \times n_{kv} \times d \times 2\ \text{bytes}. With nkv=8,d=128n_{kv}=8, d=128 that is 4 KB/layer; a 32-layer, 4096-token request is about 512 MB — under long contexts this is the main memory occupant, and the battleground for all cache optimizations like MLA, KV quantization, and PagedAttention.

The physical ceiling on throughput

Every decode step must scan the entire model’s weights once. With parameter count PP in fp16, the weight bytes are about 2P2P, so the decode throughput ceiling for a single request (B=1B=1):

tokens/s    HBM bandwidthbytes read per token    bandwidth2P+KV read\text{tokens/s} \;\approx\; \frac{\text{HBM bandwidth}}{\text{bytes read per token}} \;\approx\; \frac{\text{bandwidth}}{2P + \text{KV read}}

An 8B model has about 16 GB of fp16 weights; on an H100 @ 3 TB/s, moving the weights alone for one token takes about 16/30005.316/3000 \approx 5.3 ms — the physical floor for single-request decode, almost independent of compute. The breakthrough is continuous batching: pack BB requests into one big GEMV so the same weights are shared by BB requests, multiplying arithmetic intensity by BB and raising throughput near-linearly until compute or attention bandwidth hits the wall first.

In computational complexity, having a KV Cache or not differs by three orders of magnitude. With the cache, the total for generating TT tokens:

FLOPstotalO ⁣(LTH2+LT(S+T)H)\text{FLOPs}_{\text{total}} \sim O\!\left(L \cdot T \cdot H^{2} + L \cdot T \cdot (S + T) \cdot H\right)

The first term is the per-token linear layers (constant per decode step), the second is attention (growing linearly with the cache). Without the cache it would be O(L(S+T)3)O(L(S+T)^3), recomputing all history each step. “The longer you generate, the slower it gets” comes precisely from cache_len\text{cache\_len} growing in the second term. In practice FlashAttention [13] (fusing softmax and matmul into one kernel, cutting memory from O(S2)O(S^2) to O(S)O(S)), PagedAttention [14] (paged cache management, eliminating fragmentation), and speculative decoding [15] (a small model drafts, the large model verifies kk at once) all do local surgery on this same skeleton without changing the formulas.

Case study: Qwen — Qwen3.5-397B-A17B

Qwen3.5 [24] (released 2026, a flagship natively multimodal model) is Alibaba’s latest open-source generation, with flagship Qwen3.5-397B-A17B (397B total parameters, ~17B activated per token). Compared with the previous Qwen3, it switches the attention backbone: no longer uniformly full attention, but a hybrid stack of linear attention (Gated DeltaNet) and full attention. Broken down into the five modules (language backbone only):

ModuleQwen3.5’s choice
Attentionhybrid: every 4 layers = 3 Gated DeltaNet (linear attention) + 1 gated full attention (GQA)
NormalizationRMSNorm, pre-norm, ϵ=106\epsilon = 10^{-6}
Positional encodingRoPE (base=107\text{base}=10^7), native context 262144262144
Activation / FFNSwiGLU
Sparsityfine-grained MoE, 512 routed experts, top-10 + 1 shared expert

Key architecture numbers (from the official config): 60 layers, hidden =4096=4096; full-attention layers with nq=32n_q=32, nkv=2n_{kv}=2, head_dim =256=256 (decoupled from hidden); linear-attention layers with 16 key heads, 64 value heads, head dim 128128, conv kernel width 44; expert intermediate dim =1024=1024, vocabulary =248320=248320 (byte-level BPE, expanded from Qwen3’s ~150k to ~250k).

Three module-level highlights:

  • Hybrid attention: 3 of every 4 layers use Gated DeltaNet — an O(S)O(S) state recurrence whose cache is a fixed-size state matrix, absorbing the cost of long sequences; every 4th layer inserts full attention to restore precise retrieval. The full-sequence attention cost thus grows near-linearly rather than quadratically. See the earlier “Linear attention and hybrid stacking” section for the formulas.
  • Gated attention: the full-attention layer’s output passes through a per-channel sigmoid gate o=oσ(xWg)\mathbf{o}' = \mathbf{o} \odot \sigma(\mathbf{x} W_g), suppressing attention sinks and massive activations to stabilize large-model training.
  • Fine-grained MoE + shared expert: each token selects 10 of 512 routed experts, plus 1 shared expert that every token passes through — compared with Qwen3’s “no shared expert”, Qwen3.5 shifts to a DeepSeek-style shared-expert configuration.

In one line: Qwen3.5 = “linear/full hybrid attention + gated attention + RoPE + SwiGLU + fine-grained MoE (with sharing)”, pushing the cost of long context down on the attention side.

Case study: DeepSeek — DeepSeek-V4-Pro / Flash

DeepSeek-V4 [25] (April 2026, preview) gives two MoE models: V4-Pro (1.6T total / 49B activated) and V4-Flash (284B / 13B activated), both natively supporting a 1-million-token context. Compared with V3 [18] / V3.2 [19], it keeps DeepSeekMoE and MTP but switches to new designs in attention and residuals.

Hybrid attention: CSA + HCA (replacing MLA). V3’s signature was MLA (compressing K, V into low-rank latent vectors); V4 goes further, dropping MLA and instead twisting the “compression” and “sparsity” routes (the earlier “Sparsification and compression” section) into two interleavable attention layer types:

  • CSA (Compressed Sparse Attention): first compress every m=4m=4 tokens’ KV into 1 entry, then use the lightning indexer to select only the top-kk compressed entries per query for attention (Pro k=1024k=1024, Flash k=512k=512) — compression × sparsity stacked.
  • HCA (Heavily Compressed Attention): push the compression rate to m=128m'=128 (far above CSA), but no longer sparse-select, keeping dense attention over the compressed entries.

The core attention uses shared-KV MQA (compressed entries serve as both K and V), the Q side goes through low-rank compression (dcd_c: Pro 15361536, Flash 10241024), plus an nwin=128n_{\text{win}}=128 sliding-window branch for local dependencies and a set of learnable attention sinks that let each head’s attention weights not sum to 1. RoPE acts only on the last 64 dims of each vector. KV storage uses mixed precision: BF16 for the RoPE dims, FP8 for the rest, cutting the cache nearly in half again. The result: at 1M context V4-Pro needs only ~27% of V3.2’s per-token FLOPs and 10% of its KV cache, and Flash drops further to ~10% FLOPs and 7% KV cache; relative to a BF16 GQA (head_dim 128) baseline, KV cache is squeezed to about 2%.

DeepSeekMoE (fine-grained + shared experts) [11] follows V3’s approach with tweaked configuration: MoE now fills every Transformer layer (no more dense FFNs), the first 3 MoE layers switch to hash routing (assigning experts by a fixed hash of the token id); the routing scoring function changes from V3’s sigmoid to Sqrt(Softplus())\text{Sqrt}(\text{Softplus}(\cdot)); load balancing still uses the auxiliary-loss-free bias method [12], plus a weak sequence-level balancing loss. Pro has 1 shared + 384 routed experts per layer (intermediate dim 30723072), Flash has 1 shared + 256 routed experts (intermediate dim 20482048), both activating 6 routed experts per token.

Two more modules:

  • mHC (Manifold-Constrained Hyper-Connections): widen the single residual trunk into nhc=4n_{hc}=4 parallel residual streams, and constrain the residual-mixing matrix to the manifold of doubly stochastic matrices (spectral norm 1\le 1, non-expansive), trading for numerical stability when stacking deep. See the earlier “Beyond the residual” passage for the principle.
  • MTP (Multi-Token Prediction): a depth-1 shallow module predicts the “token after next”, giving the backbone a denser supervision signal during training and serving directly as a speculative-decoding draft at inference to boost throughput — same as V3.

Architecture numbers: V4-Pro has 61 layers, hidden =7168=7168; V4-Flash has 43 layers, hidden =4096=4096; vocabulary 128K128\text{K}; both trained in FP8, with post-training FP4 quantization of the MoE expert weights and indexer QK. Placing the three models side by side: Qwen3.5 takes “linear/full hybrid attention + fine-grained MoE”, DeepSeek-V4 takes “compression+sparsity hybrid attention (CSA/HCA) + fine-grained MoE + mHC + MTP” — the same skeleton, different trade-offs.

Summary — five modules, one skeleton

Decoder-only LLM inference is, at bottom, the combination of five modules: attention (who sees whom), normalization (stabilizing values), positional encoding (encoding order), FFN/MoE (storing knowledge), and prefill/decode (scheduling). Each module has one main formula, and the variants just swap in a replacement at one spot — GQA/MLA changes the attention KV structure, RMSNorm changes normalization, RoPE/YaRN changes positional encoding, SwiGLU/MoE changes the FFN. Once you understand these five formulas, the difference between Qwen3.5 and DeepSeek-V4 is nothing but a different selection table on the same skeleton; and any inference-optimization paper you read afterward can be located to whichever module it touches.

References — papers · technical reports