40 layers › 2 groups of 6 layers › Source layer, pairs pooled › Compressed sparse attention
Index key cache
×state · kept between callsshared_attn.index_k · model.py 537, 548, 554
The first 500 rows of layer 20’s index keys: what this layer’s indexer scores against on a step with even P.
- shape
- k [B batch, 500 compressed positions, 128 index width]
This is what the pinned reference code does on this step, and it differs from what the code documents: its docstring says every source writes before its consumers read (model.py 1167–1170), and here this layer’s indexer reads keys its own layer did not write in this call. A prefill of the same sequence scores the same query against this layer’s own keys (model.py 537–548, 554). Whether the model is meant to do this cannot be read from the code. With no pair completed, this layer publishes no index keys on this step, and the shared object still points to layer 20’s, published last in the previous call (model.py 537, 548, 554). They are keys of single tokens 0…499, where this layer’s own describe pairs. While there are at most 512 compressed positions the choice is the same, since all are kept.
Codemodel.py 488–580, 1166–1180
class Indexer(torch.nn.Module):
"""Keeps the `index_topk` best compressed positions per query.
A small side attention: fp4 query heads against one shared key per compressed position, scores
rectified then combined by `weights_proj`. With a candidate source this is the second of two
levels; `select_candidate_blocks` is the first.
"""
def __init__(self, args: ModelArgs, layer_id: int):
super().__init__()
# the index keys are derived from the compressor's latent, so only a layer that compresses
# its own KV can produce them; every other indexer reads them from that layer's cache
self.owns_k = layer_id in args.kv_source_layers
self.compress_ratio = args.compress_ratios[layer_id]
self.is_candidate_source = layer_id == args.candidate_source_layer
self.uses_candidates = 0 <= args.candidate_source_layer < layer_id
self.candidate_topk_blocks = args.candidate_topk_blocks
self.candidate_block_size = args.candidate_block_size
self.dim = args.dim
self.n_heads = args.index_n_heads
self.n_local_heads = args.index_n_heads // world_size
self.index_head_dim = args.index_head_dim
self.rope_head_dim = args.rope_head_dim
self.index_topk = args.index_topk
self.q_lora_rank = args.q_lora_rank
self.softmax_scale = self.index_head_dim**-0.5
self.wq_b = ColumnParallelLinear(self.q_lora_rank, self.n_heads * self.index_head_dim)
self.weights_proj = ColumnParallelLinear(self.dim, self.n_heads, dtype=torch.bfloat16)
self.freqs_cis: torch.Tensor | None = None
if self.owns_k:
self.wk = Linear(args.head_dim, self.index_head_dim, dtype=torch.bfloat16)
self.k_norm = RMSNorm(self.index_head_dim, args.norm_eps)
self.k_cache: torch.Tensor
self.register_buffer(
"k_cache",
torch.zeros(args.max_batch_size, args.max_seq_len // self.compress_ratio, args.index_head_dim),
persistent=False,
)
def forward(self, x: torch.Tensor, qr: torch.Tensor, latent: torch.Tensor, start_pos: int, offset: int):
"""`latent` is this layer's RoPE-free compressed latent, None when this layer does not
compress or when its current group is still incomplete. An index-key owner turns it into
index keys here, which has to happen before Attention overwrites that same storage with
the RoPE'd, quantized values."""
assert self.freqs_cis is not None
bsz, seqlen, _ = x.size()
ratio, rd, end_pos = self.compress_ratio, self.rope_head_dim, start_pos + seqlen
# latent is None while a group is still filling up, so there is nothing to publish yet
if self.owns_k and latent is not None:
# a latent stands for the first token of its group, so group j takes position j * ratio
freqs = (
self.freqs_cis[: seqlen - seqlen % ratio : ratio]
if start_pos == 0
else self.freqs_cis[start_pos + 1 - ratio].unsqueeze(0)
)
k = self.k_norm(self.wk(latent))
apply_rotary_emb(k[..., -rd:], freqs)
fp4_act_quant(k, fp4_block_size, True)
self.k_cache[:bsz, start_pos // ratio : start_pos // ratio + k.size(1)] = k
shared_attn.index_k = self.k_cache
q = self.wq_b(qr).unflatten(-1, (self.n_local_heads, self.index_head_dim))
apply_rotary_emb(q[..., -rd:], self.freqs_cis[start_pos:end_pos])
fp4_act_quant(q, fp4_block_size, True)
index_k = shared_attn.index_k[:bsz, : end_pos // ratio]
weights = self.weights_proj(x) * (self.softmax_scale * self.n_heads**-0.5)
index_score = torch.einsum("bshd,btd->bsht", q, index_k)
index_score = (index_score.relu_() * weights.unsqueeze(-1)).sum(dim=2)
if world_size > 1:
dist.all_reduce(index_score)
# how many compressed positions each query can see: a block becomes visible once the query
# has passed its last token. One query per decode step, so there it is just a number.
if start_pos == 0:
compress_lens = (torch.arange(1, seqlen + 1, device=x.device) // ratio).unsqueeze(-1)
index_score.masked_fill_(torch.arange(seqlen // ratio, device=x.device) >= compress_lens, -torch.inf)
else:
compress_lens = end_pos // ratio
if self.is_candidate_source:
shared_attn.candidates = select_candidate_blocks(
index_score, compress_lens, self.candidate_topk_blocks, self.candidate_block_size
)
elif self.uses_candidates:
# level two: score with our own weights, but only inside the source's candidate blocks
index_score = index_score.masked_fill(~shared_attn.candidates, -torch.inf)
# top-k by score, re-sorted into position order; unreachable -> -1, rest shifted by offset
topk = min(self.index_topk, end_pos // ratio)
idxs = index_score.topk(topk, dim=-1, sorted=False).indices.sort(dim=-1).values
return torch.where(idxs < compress_lens, idxs + offset, -1).int()class SharedAttentionRuntime:
"""What attention layers hand down the stack instead of recomputing. Layers run in order and
every source writes before its consumers read, so one slot each is enough and nothing needs
resetting between forwards. Sources: compress_kv and index_k from kv_source_layers, topk_idxs
from index_source_layers, candidates from candidate_source_layer."""
def __init__(self):
self.compress_kv: torch.Tensor | None = None
self.index_k: torch.Tensor | None = None
self.topk_idxs: torch.Tensor | None = None
self.candidates: torch.Tensor | None = None
# Only ever one model per process, same as world_size / rank / default_dtype above.
shared_attn = SharedAttentionRuntime()Sources2
Contextual evidence
The indexer scores every compressed position for every query with few small heads against one shared key, masks what the query may not see, and keeps the best 512; the candidate source also publishes its candidate blocks, and later indexers score inside them.
indexer · deepseek-ai/DeepSeek-V4.1-Flash / inference/model.py
Pinned revision 2cba9e42aa02 ↗ · lines 488–580
Read code excerpt
class Indexer(torch.nn.Module):
"""Keeps the `index_topk` best compressed positions per query.
A small side attention: fp4 query heads against one shared key per compressed position, scores
rectified then combined by `weights_proj`. With a candidate source this is the second of two
levels; `select_candidate_blocks` is the first.
"""
def __init__(self, args: ModelArgs, layer_id: int):
super().__init__()
# the index keys are derived from the compressor's latent, so only a layer that compresses
# its own KV can produce them; every other indexer reads them from that layer's cache
self.owns_k = layer_id in args.kv_source_layers
self.compress_ratio = args.compress_ratios[layer_id]
self.is_candidate_source = layer_id == args.candidate_source_layer
self.uses_candidates = 0 <= args.candidate_source_layer < layer_id
self.candidate_topk_blocks = args.candidate_topk_blocks
self.candidate_block_size = args.candidate_block_size
self.dim = args.dim
self.n_heads = args.index_n_heads
self.n_local_heads = args.index_n_heads // world_size
self.index_head_dim = args.index_head_dim
self.rope_head_dim = args.rope_head_dim
self.index_topk = args.index_topk
self.q_lora_rank = args.q_lora_rank
self.softmax_scale = self.index_head_dim**-0.5
self.wq_b = ColumnParallelLinear(self.q_lora_rank, self.n_heads * self.index_head_dim)
self.weights_proj = ColumnParallelLinear(self.dim, self.n_heads, dtype=torch.bfloat16)
self.freqs_cis: torch.Tensor | None = None
if self.owns_k:
self.wk = Linear(args.head_dim, self.index_head_dim, dtype=torch.bfloat16)
self.k_norm = RMSNorm(self.index_head_dim, args.norm_eps)
self.k_cache: torch.Tensor
self.register_buffer(
"k_cache",
torch.zeros(args.max_batch_size, args.max_seq_len // self.compress_ratio, args.index_head_dim),
persistent=False,
)
def forward(self, x: torch.Tensor, qr: torch.Tensor, latent: torch.Tensor, start_pos: int, offset: int):
"""`latent` is this layer's RoPE-free compressed latent, None when this layer does not
compress or when its current group is still incomplete. An index-key owner turns it into
index keys here, which has to happen before Attention overwrites that same storage with
the RoPE'd, quantized values."""
assert self.freqs_cis is not None
bsz, seqlen, _ = x.size()
ratio, rd, end_pos = self.compress_ratio, self.rope_head_dim, start_pos + seqlen
# latent is None while a group is still filling up, so there is nothing to publish yet
if self.owns_k and latent is not None:
# a latent stands for the first token of its group, so group j takes position j * ratio
freqs = (
self.freqs_cis[: seqlen - seqlen % ratio : ratio]
if start_pos == 0
else self.freqs_cis[start_pos + 1 - ratio].unsqueeze(0)
)
k = self.k_norm(self.wk(latent))
apply_rotary_emb(k[..., -rd:], freqs)
fp4_act_quant(k, fp4_block_size, True)
self.k_cache[:bsz, start_pos // ratio : start_pos // ratio + k.size(1)] = k
shared_attn.index_k = self.k_cache
q = self.wq_b(qr).unflatten(-1, (self.n_local_heads, self.index_head_dim))
apply_rotary_emb(q[..., -rd:], self.freqs_cis[start_pos:end_pos])
fp4_act_quant(q, fp4_block_size, True)
index_k = shared_attn.index_k[:bsz, : end_pos // ratio]
weights = self.weights_proj(x) * (self.softmax_scale * self.n_heads**-0.5)
index_score = torch.einsum("bshd,btd->bsht", q, index_k)
index_score = (index_score.relu_() * weights.unsqueeze(-1)).sum(dim=2)
if world_size > 1:
dist.all_reduce(index_score)
# how many compressed positions each query can see: a block becomes visible once the query
# has passed its last token. One query per decode step, so there it is just a number.
if start_pos == 0:
compress_lens = (torch.arange(1, seqlen + 1, device=x.device) // ratio).unsqueeze(-1)
index_score.masked_fill_(torch.arange(seqlen // ratio, device=x.device) >= compress_lens, -torch.inf)
else:
compress_lens = end_pos // ratio
if self.is_candidate_source:
shared_attn.candidates = select_candidate_blocks(
index_score, compress_lens, self.candidate_topk_blocks, self.candidate_block_size
)
elif self.uses_candidates:
# level two: score with our own weights, but only inside the source's candidate blocks
index_score = index_score.masked_fill(~shared_attn.candidates, -torch.inf)
# top-k by score, re-sorted into position order; unreachable -> -1, rest shifted by offset
topk = min(self.index_topk, end_pos // ratio)
idxs = index_score.topk(topk, dim=-1, sorted=False).indices.sort(dim=-1).values
return torch.where(idxs < compress_lens, idxs + offset, -1).int()Contextual evidence
One object hands compressed keys and values, index keys, selected positions and candidates from a source layer to the layers after it.
shared · deepseek-ai/DeepSeek-V4.1-Flash / inference/model.py
Pinned revision 2cba9e42aa02 ↗ · lines 1166–1180
Read code excerpt
class SharedAttentionRuntime:
"""What attention layers hand down the stack instead of recomputing. Layers run in order and
every source writes before its consumers read, so one slot each is enough and nothing needs
resetting between forwards. Sources: compress_kv and index_k from kv_source_layers, topk_idxs
from index_source_layers, candidates from candidate_source_layer."""
def __init__(self):
self.compress_kv: torch.Tensor | None = None
self.index_k: torch.Tensor | None = None
self.topk_idxs: torch.Tensor | None = None
self.candidates: torch.Tensor | None = None
# Only ever one model per process, same as world_size / rank / default_dtype above.
shared_attn = SharedAttentionRuntime()