AI Grimoire
Sheet
statusstandard
difficultyintermediate
timeO(n²·d)
memoryO(g·n·d_h)
described2023
revised7d ago

Grouped-Query Attention

Multi-query attention shares one key–value head across all queries and loses quality; grouped-query shares one across each group of h/g queries and mostly does not.

Standing

Current standardPart of the default recipe. Present in most frontier models trained today, and the thing a new design departs from rather than argues for.

The default KV-cache arrangement in essentially every open-weight model released since 2024.

judged as of 2026-09 · what the labels mean

Theory

Autoregressive decoding is bound by memory bandwidth, and the memory in question is the KV cache. Multi-query attention shrinks it by a factor of hh — one key–value head for every query head — which is a large win and a measurable quality loss. Grouped-query attention puts a dial between the two.

headi=Attention ⁣(XWiQ,  XWg(i)K,  XWg(i)V),g(i)=igh\mathrm{head}_i = \mathrm{Attention}\!\left( X W^Q_i,\; X W^K_{g(i)},\; X W^V_{g(i)} \right), \qquad g(i) = \left\lfloor \frac{i \, g}{h} \right\rfloor
eq. 1 — query head i reads the KV head of its group

The query projections are untouched: there are still hh of them, and each head still computes its own attention pattern. What is shared is the thing that gets cached. With gg groups the cache is 2gndh2 \cdot g \cdot n \cdot d_h per layer instead of 2hndh2 \cdot h \cdot n \cdot d_h, a factor of h/gh/g.

Why the loss is small

A head’s attention pattern is set by WiQWg(i)KW^Q_i {W^K_{g(i)}}^{\top}. Sharing WKW^K within a group does not force the patterns to agree, because each head keeps its own query projection and can still rotate its queries into a different part of the shared key space. What is genuinely lost is the ability of two heads in a group to read different value subspaces of the same position — the OVOV paths are constrained in a way the QKQK paths are not.

Ainslie et al. report that uptrained GQA at g=8g = 8 recovers most of the quality gap to MHA while retaining most of the MQA speedup, which is the shape of result that made it the default. The comparison is at fixed hh: GQA is not free, it is cheap.

Interaction with the cache

Because a group’s keys and values are one tensor, decoding becomes a batched matmul with a broadcast rather than a gather. That matters for paged caches, where the page is now shared by h/gh/g query heads and the eviction unit is correspondingly coarser.

Implementation

python · torch ≥ 2.1
import torch
from torch import Tensor, nn


class GroupedQueryAttention(nn.Module):
    def __init__(self, d_model: int, n_heads: int, n_kv_heads: int) -> None:
        super().__init__()
        assert n_heads % n_kv_heads == 0
        self.h, self.g = n_heads, n_kv_heads
        self.d_head = d_model // n_heads
        self.q = nn.Linear(d_model, n_heads * self.d_head, bias=False)
        self.kv = nn.Linear(d_model, 2 * n_kv_heads * self.d_head, bias=False)
        self.proj = nn.Linear(d_model, d_model, bias=False)

    def forward(self, x: Tensor, causal: bool = True) -> Tensor:
        b, n, _ = x.shape
        q = self.q(x).view(b, n, self.h, self.d_head).transpose(1, 2)
        k, v = (
            t.view(b, n, self.g, self.d_head).transpose(1, 2)
            for t in self.kv(x).chunk(2, dim=-1)
        )

        # Broadcast each KV head to the query heads of its group.
        k = k.repeat_interleave(self.h // self.g, dim=1)
        v = v.repeat_interleave(self.h // self.g, dim=1)

        out = torch.nn.functional.scaled_dot_product_attention(
            q, k, v, is_causal=causal
        )
        out = out.transpose(1, 2).reshape(b, n, self.h * self.d_head)
        return self.proj(out)

repeat_interleave allocates, which defeats the purpose during decoding. The version that matters lives inside the attention kernel, where the KV head index is computed from the query head index and the cache is read once rather than copied — the code above is the readable statement of what that kernel does.

KV cache, MHA
128 MB
KV cache, g = 8
32 MB
KV cache, MQA
4 MB
d = 4096, h = 32, n = 8192, fp16, per layer

Related

References

[1]Ainslie et al. — GQA: Training Generalized Multi-Query Transformer Checkpoints (2023)arXiv:2305.13245
[2]Shazeer — Fast Transformer Decoding: One Write-Head is All You Need (2019)arXiv:1911.02150