http://zhifeiding.github.io 2026-11-03T15:39:19+00:00 dzhf21@hotmail.com 投机解码综述 http://zhifeiding.github.io/programming/2026/11/02/%E6%8A%95%E6%9C%BA%E8%A7%A3%E7%A0%81%E7%BB%BC%E8%BF%B0/ 2026-11-02T00:00:00+00:00 http://zhifeiding.github.io/programming/2026/11/02/投机解码综述 1 概述

投机解码(Speculative Decoding)是一类面向自回归大语言模型(LLM)推理加速的无损解码技术。其基本思想是:以一个轻量的草稿模型(draft model)在低计算开销下并行预测未来 $k$ 个候选 token,再由目标模型(target model)在单次前向传播中并行验证全部候选,并保留通过验证的最长前缀。由此,解码过程由”逐 token 串行”转变为”逐块并行”,在保持输出分布严格不变的前提下显著提升生成吞吐。

0.png 图 1:投机解码的基本流程(Leviathan et al., 2022, Figure 1)。绿色为草稿模型提出的候选 token,红色为验证失败后由目标模型分布改写的 token;每轮迭代中目标模型仅需一次前向即可提交多个 token。

1.1 问题定义与计算瓶颈

给定上下文序列 $x_{<t} = (x_1, \dots, x_{t-1})$,标准自回归解码在每一步从目标模型 $p_\theta$ 的输出分布中采样一个 token:

\[x_t \sim p_\theta(\cdot \mid x_{<t}),\]

并将 $x_t$ 追加至上下文后重复该过程。该范式的瓶颈在于:每生成一个 token 都需要完整执行一次前向传播。在 batch size 为 1 的解码阶段,计算访存比(arithmetic intensity)极低,GPU 算力大部分时间消耗于权重搬运(memory-bandwidth bound)而非矩阵运算。因此解码吞吐的实际上限由显存带宽决定,单步延迟近似等于权重自显存读入计算单元的时间。

投机解码正是利用了这一特性:在一次前向中验证 $k$ 个 token 的额外计算开销近似可忽略,但每轮的有效产出可由 1 个 token 提升至 $\mathbb{E}[\tau] > 1$ 个,其中 $\tau$ 表示接受长度(acceptance length)。

1.2 投机解码的基本流程

记草稿模块为 $q_\phi$(既可以是独立的小型语言模型,也可以是附着于目标模型的轻量草稿头)。每轮解码包含三个阶段:

  1. 草稿生成(Drafting):草稿模块以较低开销生成候选序列 $y_{1:k}$,其参数量与计算量远小于目标模型;
  2. 并行验证(Verification):目标模型以 $(x_{<t}, y_{1:k})$ 为输入执行一次前向,同时获得各位置的条件分布 $p_\theta(\cdot \mid x_{<t}, y_{<i})$;
  3. 接受与修正(Accept & Correct):自左向右逐位验证,接受与目标分布一致的最长前缀;于首个被拒绝位置处,由 $p_\theta$ 采样一个修正 token 作为该位置的输出,并以该位置为新起点进入下一轮。

1.3 无损性与加速比

无损性(Losslessness)。标准验证机制采用拒绝采样(rejection sampling):候选 token $y_i$ 以概率

\[\min\left\{1,\ \frac{p_\theta(y_i \mid x_{<t}, y_{<i})}{q_\phi(y_i \mid x_{<t}, y_{<i})}\right\}\]

被接受;若被拒绝,则从 $p_\theta$ 的(截断)分布中重采样。该机制保证最终输出序列的边缘分布与目标模型独立解码严格一致(Leviathan et al., 2022; Chen et al., 2023),即加速不以牺牲生成质量为代价。在贪心解码(temperature $= 0$)下,验证退化为逐位 argmax 比对。

加速比。设草稿耗时为 $t_d$、验证耗时为 $t_v$、目标模型单步解码耗时为 $t_0 \approx t_v$,则理论加速比为

\[S = \frac{\mathbb{E}[\tau] \cdot t_0}{t_d + t_v},\]

其中 $\tau \in {1, \dots, k{+}1}$ 为每轮接受长度。由此可导出各方法的统一优化目标:

  1. 降低草稿耗时 $t_d$;
  2. 提高草稿质量,即增大接受长度 $\mathbb{E}[\tau]$;
  3. 避免将验证预算浪费于高拒绝风险的候选位置。

1.4 技术路线演进

技术路线 代表方法 核心机制
独立草稿模型 Leviathan 2022 / Chen 2023 独立小模型自回归生成草稿,目标模型并行验证
多头并行预测 Medusa 目标模型挂载多个独立预测头,一次前向并行产出多个候选
自回归草稿头 MTP、EAGLE 复用目标模型的特征与输出权重,训练轻量草稿头执行自回归外推
并行/半自回归草稿 DFlash、DSpark 以块扩散或半自回归结构在单次前向中并行产出整块草稿

2 Draft

草稿生成(Drafting)是投机解码框架中决定加速上限的核心组件。由 1.3 节的加速比公式可知,草稿器同时决定分子 $\mathbb{E}[\tau]$(草稿质量)与分母中的 $t_d$(草稿开销)。本章对草稿生成的设计空间、结构选择与长度调度进行系统梳理;第 3–6 章介绍的具体方法均可视为该设计空间中的代表性实例。

1.png 图 2:草稿头的典型结构(Cai et al., 2024, Figure 1, Medusa)。在目标模型最后隐藏状态上并行挂载多个轻量解码头(Medusa Heads),每个头预测一个未来位置的 top-$k$ 候选;候选组合经树式验证后单步提交最长正确前缀。

2.1 草稿器的形式化角色

定义位置 $i$ 的条件接受率(conditional acceptance rate)为

\[a_i = \Pr\big(y_i \text{ 通过验证} \mid y_{<i} \text{ 全部通过验证}\big).\]

对链式草稿(chain drafting),每轮提交的 token 数为 $\tau = N + 1$,其中 $N$ 为通过验证的草稿 token 数,$+1$ 为首个被拒绝位置处由目标模型提交的修正 token。于是

\[\mathbb{E}[\tau] = 1 + \sum_{i=1}^{k} \prod_{j=1}^{i} a_j.\]

实践中 $a_i$ 通常随位置 $i$ 递增而衰减(后缀衰减,suffix decay),因此单纯增大草稿长度 $k$ 的边际收益递减。草稿器设计面临两个基本矛盾:

  1. 精度–开销矛盾:自回归草稿器的接受率高,但草稿耗时随长度线性增长($t_d = O(k)$);
  2. 依赖–并行矛盾:完全并行草稿使 $t_d = O(1)$,但各位置预测相互独立、缺乏块内依赖建模,后缀衰减严重。

2.2 草稿器的设计空间

  1. 独立草稿模型(Independent draft model):$q_\phi$ 为独立训练的小型语言模型,通常经序列级蒸馏(sequence-level distillation)从 $p_\theta$ 蒸馏而来(图 1 即属此类)。优点是与目标模型完全解耦、易于替换;缺点是草稿分布与目标分布存在错位(distribution mismatch),压低 $a_i$,且引入额外的显存与部署开销。
  2. 共享主干草稿头(Self-drafting heads):在目标模型上挂载轻量草稿模块,复用其主干特征或输出权重,包括 Medusa(并行独立头)、MTP(联合训练的辅助头,见第 3 章)与 EAGLE(特征层自回归草稿头,见第 4 章)。草稿分布与目标分布天然对齐,且无需独立模型。
  3. 并行/半自回归草稿(Parallel & semi-autoregressive drafting):以块扩散(DFlash,见第 5 章)或半自回归结构(DSpark,见第 6 章)在单次前向中并行产出整块草稿,使 $t_d$ 与 $k$ 解耦,其核心挑战在于抑制后缀衰减。
  4. 免训练草稿(Training-free draft sources):Lookahead/Jacobi 解码、$n$-gram / prompt-lookup、检索与缓存复用等。无需任何训练,但接受率高度依赖生成内容的重复结构,适合特定场景。

2.3 草稿结构:链与树

链式草稿每轮仅提出一条候选序列;树式草稿(tree drafting)则在每个节点扩展 top-$w$ 个分支,并通过树形注意力掩码(tree attention)在单次前向中验证整棵候选树。设节点 $v$ 的路径置信度为沿根到 $v$ 各边条件接受率之积,则树结构在同等验证预算下显著提高命中概率,$\mathbb{E}[\tau]$ 相应提升。

2.png 图 3:树式草稿与树验证的总体结构(Miao et al., 2024, Figure 3, SpecInfer)。草稿端以扩展式或合并式构造 token 树(Speculated Token Tree),验证端将树线性化后由目标模型执行树状并行解码(Tree-based Parallel Decoding),最终同步通过验证的子树(Verified Subtree)。

树式草稿的代表工作包括:SpecInfer 的 token 树构造与树验证机制(Miao et al., 2024)、Medusa 的树注意力并发验证(Cai et al., 2024)、EAGLE-2 的置信度驱动动态树(见 4.2 节),以及面向块扩散草稿的 DDTree(见 5.3 节)。

2.4 草稿长度与验证预算

草稿长度 $k$ 的选择存在明确权衡:增大 $k$ 提高潜在的 $\mathbb{E}[\tau]$,但同时推高 $t_d$(自回归草稿)或加剧验证浪费(并行草稿中高拒绝风险候选占用批处理容量)。Leviathan et al. (2022) 给出最优草稿长度 $\gamma^*$ 随接受率 $\alpha$ 与草稿/验证代价比的闭式分析。实践中 $k$ 往往依系统负载自适应:低负载时延长草稿以利用空闲带宽,高负载时缩短草稿以降低验证浪费——DSpark 的置信度调度验证即为该思路的生产级实现(见 6.1 节)。


3 MTP

多 token 预测(Multi-Token Prediction, MTP)将语言模型的训练目标由单一的下一 token 预测扩展为对未来 $D$ 个 token 的联合预测。其辅助预测头在推理阶段可直接充当草稿模块,构成自投机解码(Self-Speculative Decoding)范式——无需独立部署草稿模型。

3.png 图 4:MTP 原始论文结构图(Gloeckle et al., 2024, Figure 1)。共享主干(shared trunk)与 $D=4$ 个专用输出头:训练时每个位置同时预测 4 个未来 token;推理时辅助头可被丢弃,亦可用于投机解码加速(最高约 $3\times$)。

3.1 训练目标

记预测深度为 $D$(常见取值 1–4)。MTP 在每个位置 $i$ 上除主损失外附加 $D$ 个辅助损失,总训练目标为

\[\mathcal{L}_{\mathrm{MTP}} = \sum_{d=1}^{D} \lambda_d \, \mathcal{L}_{\mathrm{CE}}^{(d)}, \qquad \mathcal{L}_{\mathrm{CE}}^{(d)} = -\log p_\theta^{(d)}(x_{i+d} \mid x_{\le i}),\]

其中 $\lambda_d$ 为各深度的损失权重($\lambda_1$ 对应主任务)。权重过大将干扰主目标收敛,过小则辅助头无法达到可用精度,实践中需对 ${\lambda_d}$ 做超参搜索。依据辅助头的条件化方式,实现分为两类:

  • 并行独立头(Gloeckle et al., 2024):$D$ 个输出头共享主干、相互独立,各深度的监督信号同时稠密化,提升样本效率与表征质量;
  • 顺序式 MTP(DeepSeek-AI, 2024):各深度预测保持完整因果链 $p_\theta^{(d)}(x_{i+d} \mid x_{\le i},\, \hat x_{i+1:i+d-1})$,深层预测以浅层预测为条件,草稿质量更高。DeepSeek-V3 采用 $D=1$ 的 MTP 模块(约 14B 额外参数)。

4.svg 图 5:DeepSeek-V3 的顺序式 MTP 实现(DeepSeek-AI, 2024, Figure 3)。各深度的预测保持完整因果链,MTP 模块的输出即作为投机解码的草稿 token。

3.2 推理机制

推理阶段,MTP 模块以主干隐藏状态为输入生成草稿序列,后续流程与标准投机解码一致:草稿生成 $\rightarrow$ 目标模型单次前向并行验证 $\rightarrow$ 接受最长正确前缀,并在首个错误位置回退至主模型采样。由于草稿头与主模型联合训练,二者分布高度对齐,第二 token 的接受率可达 85%–90%。

3.3 实验结果与讨论

  • 加速效果:DeepSeek-V3 报告吞吐约 $1.8\times$;在代码、结构化文本等低熵场景中接受率更高,加速比相应增大;
  • 稠密监督效应:MTP 目标可视为对主任务的正则化与表征增强,通常带来约 0.5–1.5 个 benchmark 点的质量提升与更快的收敛速度;
  • 无损性:输出分布由验证机制保证与主模型单独解码一致;
  • 局限:MTP 头须在预训练阶段引入并联合训练,无法对既有 checkpoint 事后扩展(未训练的辅助头输出近似随机);推理引擎对自投机的支持仍在完善中(vLLM、SGLang、llama.cpp 等陆续支持)。

4 EAGLE

EAGLE(Extrapolation Algorithm for Greater Language-model Efficiency)是目前部署最广的无损投机解码方法家族之一。其核心假设为特征不确定性假设(feature-uncertainty hypothesis):设 $\mathbf{f}_t$ 为目标模型倒数第二层的隐藏特征,则特征序列 ${\mathbf{f}_t}$ 的条件演化较 token 序列更为平滑、更易被轻量模型拟合。据此,EAGLE 将草稿建模由 token 空间转移至特征空间,在特征层执行自回归外推。

5.png 图 6:EAGLE 原始论文结构图(Li et al., 2024, Figure 6, Pipeline of EAGLE)。上半部分为计算流程:目标 LLM 前向得到特征 $f$ 与 token,草稿模型以”特征 + 移位 token embedding”为输入,经单一自回归头外推后续特征,再由共享 LM Head 采样候选 token;下半部分为对应的草稿树生成过程。

4.1 EAGLE-1:特征层自回归(ICML 2024)

架构。 草稿头由一个线性投影 $W_c$ 与一个 Transformer 解码层构成,且共享目标模型的 embedding 矩阵与 LM head,新增参数仅约占目标模型的 2%–5%(如 LLaMA-3.1-8B 约 0.24B)。单步外推的递推式为

\[\mathbf{h}_{t+1} = \mathrm{DecLayer}\big(W_c\,[\mathbf{f}_t\,;\,\mathbf{e}_{t+1}]\big), \qquad \hat{x}_{t+2} \sim \mathrm{softmax}\big(\mathrm{LMHead}(\mathbf{h}_{t+1})\big),\]

其中 $\mathbf{e}_{t+1}$ 为已采样 token 的 embedding。拼接离散 token 信息以锚定预测,结合连续特征的平滑性以降低不确定性;滚动 $\gamma$ 步即得长度为 $\gamma$ 的草稿链。

训练。 冻结目标模型全部参数,仅训练草稿头,以目标模型的真实特征为回归目标。训练数据规模约数万条对话样本,单卡数小时即可完成。为缓解 exposure bias(训练时输入真实特征、推理时输入自身预测特征所导致的分布漂移),训练时对输入特征注入噪声。

验证。 采用标准拒绝采样,输出分布与目标模型严格一致。MT-bench 上约 $3\times$ 加速,曾居 Spec-Bench 无损方法首位。

4.2 EAGLE-2:动态草稿树(EMNLP 2024)

将静态草稿树替换为动态草稿树:节点权重取根至该节点路径上草稿置信度之积,作为整段前缀被接受概率的估计;每步仅扩展权重最高的叶节点,并对展开结果重排(expand & rerank)。相对 EAGLE-1 提速 20%–40%。

4.3 EAGLE-3:训练时测试(NeurIPS 2025)

放弃显式特征回归,改为直接预测 token,并以三层特征融合(tri-layer feature fusion)聚合目标模型多层表示;引入训练时测试(Training-Time Test, TTT):训练阶段即令草稿头自由滚动生成、以最终接受长度为优化目标,从根源上消除 exposure bias。加速比达 $3\times$–$6.5\times$,约为 EAGLE-2 的 $1.4$ 倍。该系列已集成于 vLLM、SGLang、TensorRT-LLM 等推理框架;Google Vertex AI 报告在 Llama 70B 上取得 $2$–$3\times$ 的生产环境加速。


5 DFlash

DFlash(Block Diffusion for Flash Speculative Decoding, ICML 2026, UCSD Z Lab, arXiv:2602.06036)以块扩散(block diffusion)模型替换自回归草稿器,使草稿生成由”逐 token 串行”转变为”整块并行去噪”,解除了草稿深度 $k$ 与草稿耗时 $t_d$ 之间的线性耦合。

6.svg 图 7:DFlash 原始论文结构图(Chen et al., 2026, Figure 2, DFlash Inference Design)。轻量块扩散草稿器以全 [MASK] 块为起点,单次前向联合去噪出整块候选 token;目标模型抽取的隐藏上下文特征经融合后注入草稿器各层的 KV cache(KV Injection),无需重新编码上下文即可维持高接受率。

5.1 动机

自回归草稿器(MTP、EAGLE)的草稿成本随 $k$ 线性增长($t_d = O(k)$),迫使草稿深度受限;完全并行的多头草稿(如 Medusa)虽使 $t_d = O(1)$,但各位置预测相互独立、缺乏块内依赖建模,接受率随位置迅速衰减(后缀衰减,suffix decay)。DFlash 的设计目标是:在保持 $t_d$ 对 $k$ 近似不敏感的同时,生成高质量的长草稿。

5.2 方法

  1. 块扩散草稿(Block-diffusion drafting):以长度 $k$ 的全 [MASK] 序列为起点,草稿器在单次前向中联合去噪全部 $k$ 个位置。草稿 token 数与草稿前向次数解耦,$t_d$ 随 $k$ 近似不变;
  2. 目标特征条件化(Target hidden-state conditioning):草稿器以目标模型抽取的上下文隐藏特征为条件,继承目标模型对上下文的表征;
  3. KV 注入(KV Injection):将目标模型的隐藏表示直接写入草稿模型各层的 key–value 缓存,草稿器无需重新编码上下文即可利用目标模型的特征,同时维持高接受率。

验证阶段仍由目标模型执行,输出分布严格不变(无损)。

5.3 实验结果

  • 多模型多任务上取得 $5$–$6\times$ 无损加速;经 DDTree 优化后 HumanEval 上最高 $8.22\times$;较同期最优的自回归方法 EAGLE-3 最高提升 $2.5\times$;
  • NVIDIA Blackwell Ultra 上 gpt-oss-120b 在同等交互性约束下吞吐最高 $15\times$;对 Llama 3.1 8B 的交互性接近 EAGLE-3 的 $2$ 倍;
  • 接受长度随草稿预算 $k$ 增大而持续增长,而 MTP 类草稿在预算超过 8 后趋于饱和;
  • 与权重量化正交、可叠加:目标模型 mxfp4 量化降低验证开销且不损害接受率,可再获得 10%–20% 的吞吐增益;
  • 已集成于 SGLang、vLLM、TensorRT-LLM、MLX;官方发布 20 个 DFlash checkpoint(覆盖 Qwen、Kimi K2.6、Llama、Gemma、gpt-oss 等模型族)。

6 DSpark

DSpark(Confidence-Scheduled Speculative Decoding with Semi-Autoregressive Generation, arXiv:2607.05147)由 DeepSeek 与北京大学联合提出(2026 年 6 月),面向高并发生产服务场景,统一优化草稿质量与验证效率。该框架已部署于 DeepSeek-V4 在线服务系统(V4-Pro / V4-Flash)。

7.svg 图 8:DSpark 原始论文结构图(Cheng et al., 2026, Figure 1, DSpark architecture and decoding cycle)。给定 prompt token ABC,目标模型先生成锚点 token D;半自回归草稿器(并行主干 + 轻量串行模块)并行产出候选块,置信度头估计各位置前缀生存概率并裁剪低置信后缀,最终由目标模型单次前向验证。

6.1 方法

  1. 半自回归草稿器(Semi-Autoregressive Generation):并行主干在单次前向中产出整块候选,再由轻量串行模块(Markov 头)按位置顺序条件化重写,以近似自回归的精度建模块内依赖。设位置 $i$ 的条件接受率为 $a_i$,该结构显著缓解后缀衰减,使平均接受长度 $\mathbb{E}[\tau]$ 全面优于纯自回归与纯并行草稿基线;
  2. 置信度调度验证(Confidence-Scheduled Verification):置信度头估计各位置的前缀生存概率 $s_i = \prod_{j \le i} \hat{a}_j$;低于阈值的低置信后缀在验证前即被裁剪,回退至更短的草稿。结合推理引擎的吞吐特性曲线,为每条请求动态确定验证长度 $k^*$,将验证预算集中于期望收益最高的候选位置,避免批处理容量被高拒绝风险的 token 占用;
  3. 硬件感知调度(Hardware-Aware Scheduling):调度器依据实时 GPU 负载自适应草稿长度——低负载时延长草稿以利用空闲带宽,高负载时缩短草稿以降低验证浪费,使系统在严格交互性约束下维持高吞吐,整体外移服务的 Pareto 前沿。

6.2 实验结果与生态

  • 线上部署效果:DeepSeek-V4 真实用户流量下,同吞吐水平相比 MTP-1 生产基线,单用户生成速度提升 60%–85%(V4-Flash)与 57%–78%(V4-Pro);在 120 tok/s 的延迟地板下总吞吐最高约 $6.6\times$;
  • 无损性:拒绝采样保证输出与目标模型严格一致;目标模型参数不更新,仅训练草稿头;
  • 开源生态:DSpark 与配套训练评测库 DeepSpec 以 MIT 协议开源;官方发布面向 Qwen、Gemma 等模型的草稿 checkpoint,训练配方公开,可为任意目标模型训练草稿器;
  • 2026 年 8 月,Liquid AI 与 Hugging Face 发布了基于 DSpark 的 LFM2.5 系列草稿 checkpoint。

7 参考文献

  1. Leviathan Y, Kalman M, Matias Y. Fast Inference from Transformers via Speculative Decoding. ICML 2023. arXiv:2211.17192.
  2. Chen C, et al. Accelerating Large Language Model Decoding with Speculative Sampling. DeepMind, 2023. arXiv:2302.01318.
  3. Cai T, et al. Medusa: Simple LLM Inference Acceleration Framework with Multiple Decoding Heads. ICML 2024. arXiv:2401.10774.
  4. Miao X, et al. SpecInfer: Accelerating Generative Large Language Model Serving with Tree-based Speculative Inference and Verification. OSDI 2024. arXiv:2305.09781.
  5. Gloeckle F, et al. Better & Faster Large Language Models via Multi-token Prediction. NeurIPS 2024. arXiv:2404.19737.
  6. DeepSeek-AI. DeepSeek-V3 Technical Report. 2024. arXiv:2412.19437.
  7. Li Y, Wei F, Zhang C, Zhang H. EAGLE: Speculative Sampling Requires Rethinking Feature Uncertainty. ICML 2024. arXiv:2401.15077.
  8. Li Y, et al. EAGLE-2: Faster Inference of Language Models with Dynamic Draft Trees. EMNLP 2024. arXiv:2406.16858.
  9. Li Y, et al. EAGLE-3: Scaling up Inference Acceleration of Large Language Models via Training-Time Test. NeurIPS 2025. arXiv:2503.01840.
  10. Chen J, Liang Y, Liu Z. DFlash: Block Diffusion for Flash Speculative Decoding. ICML 2026. arXiv:2602.06036.
  11. Cheng X, et al. DSpark: Confidence-Scheduled Speculative Decoding with Semi-Autoregressive Generation. DeepSeek & Peking University, 2026. arXiv:2607.05147.
]]>
Deepseek推理系统 http://zhifeiding.github.io/programming/2026/11/01/Deepseek%E6%8E%A8%E7%90%86%E7%B3%BB%E7%BB%9F/ 2026-11-01T00:00:00+00:00 http://zhifeiding.github.io/programming/2026/11/01/Deepseek推理系统 DeepSeek 大模型推理系统综述:从 V3 到 DeepInfer

摘要

DeepSeek 系列模型以低于同代前沿模型的单位 token 成本提供服务,其竞争力来自模型结构与推理系统的协同设计。本文以 DeepSeek-V3、V3.2、V4、V4.1-Flash 及开源推理引擎 DeepInfer 为对象,梳理四代系统的并行方案、缓存分层与调度机制,并汇总官方披露的时延与吞吐数据。效率提升集中在四个方向:键值缓存(KV cache)的表达逐代压缩,多头潜在注意力(MLA)将其压至约 70 KB/token,稀疏注意力(DSA)把注意力复杂度由 $O(L^2)$ 降至 $O(Lk)$,压缩稀疏注意力(CSA)与重度压缩注意力(HCA)在 token 维实现 4×–128× 压缩,V4.1 进一步降至 890 B/token;计算稀疏化由专家稀疏扩展到位置稀疏;部署由 Prefill/Decode 分离演进为三级分离与跨超节点的宽专家并行(Wide-EP);存储由单机 HBM 扩展至约 100 PiB 的共享缓存集群。1M 上下文下,V4 单 token 推理 FLOPs 较 V3.2 降至 27%,KV cache 降至 10%。本文以帕累托前沿刻画吞吐与单用户速度的取舍,并讨论硬件可移植性、确定性推理代价与评测口径统一三个未解决问题。

1 引言

大模型推理的成本结构由两个不对称性决定。第一个是参数总量与激活参数量之间的不对称:DeepSeek-V3 的 671B 参数中每个 token 仅激活 37B,计算量按激活参数量计,但显存须容纳全部专家权重 [1]。第二个是两类阶段之间的负载不对称:Prefill 阶段一次性处理全部输入 token,属计算密集型任务,决定首 token 时延(time to first token,TTFT);Decode 阶段逐 token 生成,每步需读取完整 KV cache,属显存带宽密集型任务,决定吐字间隔(time per output token,TPOT)[2]。

围绕这两组不对称,DeepSeek 的推理系统形成了相对稳定的设计范式:以专家并行(expert parallelism,EP)换取批量规模与显存占用,以阶段分离让两类负载各自匹配硬件,以缓存分层把重复计算移出关键路径。四代系统之间的差异,主要体现为注意力压缩程度、并行域宽度、缓存层级深度三个维度上推进幅度的不同。

本文以公开的技术报告、模型卡、官方仓库与第三方基准为依据,对上述四个维度做统一口径的梳理。第 2 节给出硬件基座、存储层级与评测指标;第 3–6 节依次分析 V3、V3.2、V4、V4.1-Flash 的架构、集群与性能;第 7 节介绍 DeepInfer 的组成与基准;第 8 节作横向比较并讨论开放问题;第 9 节为结论。附录 A 汇总四代模型的结构超参数。

2 硬件基座、存储层级与评测指标

2.1 关键加速器与互联

推理系统的形态取决于芯片的算力、显存容量、显存带宽与互联能力四项参数。表 1 汇总本文涉及的主要加速器。从 Hopper 到 Blackwell Ultra,单卡显存容量由 80 GB 增至 288 GB(3.6×),显存带宽由 3.35 TB/s 增至 8 TB/s(2.4×),FP8 稠密算力由 1.98 PFLOPS 提升至 5 PFLOPS(2.5×),并新增 FP4 计算路径(B300 达 15 PFLOPS)。互联侧,GB300 NVL72 将 72 颗 GPU 置于单一 NVLink 域(聚合带宽 130 TB/s),专家并行的 all-to-all 通信得以完全留在机架内 [3]。

表 1 主要加速器关键参数

芯片 架构 低精度算力(稠密) 显存容量 显存带宽 卡间互联 节点间互联 典型功耗
NVIDIA H800 SXM Hopper FP8 1.98 PFLOPS;BF16 0.99 PFLOPS 80 GB HBM3 3.35 TB/s NVLink 400 GB/s IB 400 Gb/s 700 W
NVIDIA H100 SXM Hopper FP8 1.98 PFLOPS;BF16 0.99 PFLOPS 80 GB HBM3 3.35 TB/s NVLink 900 GB/s IB 400 Gb/s 700 W
NVIDIA H20 Hopper FP8 296 TFLOPS;BF16 148 TFLOPS 96 GB HBM3 4.0 TB/s NVLink 900 GB/s IB 400 Gb/s 400 W
NVIDIA B200 Blackwell FP4 9 PFLOPS;FP8 4.5 PFLOPS;BF16 2.25 PFLOPS 192 GB HBM3e 8 TB/s NVLink 5,1.8 TB/s IB 400/800 Gb/s ≈1 000 W
NVIDIA B300(GB300 NVL72) Blackwell Ultra FP4 15 PFLOPS;FP8 5 PFLOPS 288 GB HBM3e 8 TB/s NVLink 5,1.8 TB/s(NVL72 单域 130 TB/s) CX-8 800 Gb/s ≤1 400 W
昇腾 950PR 达芬奇 FP8 1 PFLOPS;MXFP4 2 PFLOPS 128 GB 自研 HBM(Atlas 350 实配 112 GB) 1.6 TB/s(实配 1.4 TB/s) 灵衢 UB,2 TB/s(卡级 424 GB/s) UB-Mesh 600 W
昇腾 950DT 达芬奇 FP8 2 PFLOPS;FP4 4 PFLOPS 144 GB 自研 HBM 4 TB/s 灵衢 UB,2 TB/s UB-Mesh —

注:算力均为稠密(dense)口径。昇腾 950 为芯片级标称值,量产卡 Atlas 350 因分档存在差异 [4]。H800 相对 H100 的主要差异在于 NVLink 带宽由 900 GB/s 降至 400 GB/s、FP64 算力大幅削减,该限制直接影响 V3 的并行策略选择 [1][5]。

昇腾 950 系列采用”一芯双构”设计:同一颗 950 Die 分别与低成本高容量的 HiBL 1.0、高带宽的 HiZQ 2.0 合封,形成面向 Prefill(算力受限)的 950PR 与面向 Decode(带宽受限)的 950DT,阶段分离的思路由此下沉到芯片选型层面 [4]。

2.2 存储层级与集群规模

推理系统的存储不止 HBM 一级。以 V4.1-Flash 为例,线上系统形成三层结构(表 2):运行中的 Block/State KVCache 驻留推理卡显存;滑动窗口注意力(sliding window attention,SWA)状态保存在产生它的 Prefill/Decode 机器的主机内存;持久化的 Block KVCache 写入独立的 3FS 存储集群,容量约 100 PiB 全固态,峰值聚合读 10 TiB/s、写约 850 GiB/s,并针对约 112 KiB 粒度的 Block 随机读写作了专门优化 [6][7]。计算节点与存储节点独立扩缩容,计算侧故障不会导致持久缓存丢失,这是把 KV 缓存从单机缓存扩展为集群级共享数据层的前提。

表 2 推理系统的典型存储层级(以 V4.1-Flash 线上系统为例)

层级 介质 容量口径 带宽/时延特征 存放内容
L1 显存 HBM3e / 自研 HBM 80–288 GB/卡 3.35–8 TB/s 权重、运行中 Block/State KVCache
L2 主机内存 DDR5 单机 TB 级 百 GB/s 量级 SWA KVCache(134 token ring × 20 层,单副本)
L3 集群存储 3FS + SSD ≈100 PiB 读 10 TiB/s、写 850 GiB/s 持久化 Block KVCache(128 token/block)

集群规模上,V3 时代的训练集群为 2 048 张 H800 [1],推理侧以 4 节点(32 卡)与 18 节点(144 卡)为部署单元 [2];V4.1-Flash 时代则由 8 卡服务器承担 Prefill、64–128 卡超节点承担 Decode,官方给出的最小高效配置为 64 卡 Prefill + 64 卡 Decode,单集群日吞吐达 155 万亿 token [8][6]。

2.3 评测指标定义

为避免跨代比较时的口径差异,本文统一采用如下定义。TTFT 为请求发出至首个输出 token 返回的时间,受排队、调度与 Prefill 共同影响;TPOT 为相邻输出 token 的间隔,满足 TPOT ≈ 1/TPS;TPS 既可用于单请求输出速度(tok/s/user),也可用于单卡或集群吞吐(tok/s/GPU);吞吐–时延帕累托前沿指在不牺牲一项指标的前提下无法改进另一项的配置集合,是评价推理系统综合效率的常用口径 [8]。

3 DeepSeek-V3 推理系统

3.1 模型架构

DeepSeek-V3 为 671B 总参、37B 激活的 MoE 模型,共 61 层:第 0–2 层为稠密前馈网络(feed-forward network,FFN)(中间维 18 432),第 3–60 层为 MoE,每层 1 个共享专家加 256 个路由专家、每 token 激活 8 个(专家中间维 2 048),词表 129 280 [1]。其结构可拆为三个部件(图 1(a))。

  • MLA:hidden 为 7 168,128 个注意力头。查询经 1 536 维低秩路径(W_DQ)升维后拆为 128 维 Nope 与 64 维旋转位置编码(rotary positional encoding,RoPE)两部分;键值方向先由 W_DKV 压成 512 维潜在向量 c_KV,再由 W_UKV 上投影为各头 128 维的 k、v,位置信息由独立的 W_KR 逐头生成 64 维 RoPE 键。解码时每 token 只需保存 c_KV(512)与 k_R(64),合计约 70 KB/token(61 层),相对 LLaMA-3.1-405B 减少约 7.28 倍。
  • DeepSeekMoE:门控先算 s_i,t = Sigmoid(u_tᵀe_i) 并归一化,取 top-8。无辅助损失的均衡通过仅用于选择的偏置 b_i 实现,b_i 每步按专家负载以步长 γ 增减,损失函数中不含均衡项。
  • 多 token 预测(multi-token prediction,MTP):主干之后接 1 个深度的多 token 预测模块,与主干共享 embedding 与输出头,官方口径下带来约 1.8× 的 TPS 提升。

0.png

图 1(a) DeepSeek-V3 的基本架构:Transformer 块、DeepSeekMoE 与多头潜在注意力(引自 DeepSeek-V3 技术报告 Fig. 2)

3.2 服务拓扑:Prefill/Decode 分离与跨节点专家并行

V3 的线上系统采用 Prefill/Decode 分离架构,两阶段使用不同并行度(表 3、图 1(b))。Prefill 以 4 节点 32 卡为部署单元,路由专家 EP32、MLA 与共享专家 DP32,含 32 个冗余路由专家,每卡承担 9 个路由专家与 1 个共享专家;Decode 以 18 节点 144 卡为单元,EP144/DP144,每卡仅 2 个路由专家与 1 个共享专家 [2]。每层 256 选 8 的稀疏比意味着单个专家需累积足够批量才能使矩阵计算效率与显存访问效率同时达到可接受水平,跨节点 EP 的动机源于此。

表 3 V3 线上系统的部署形态与并行配置

项目 Prefill 阶段 Decode 阶段
部署单元 4 节点 / 32 GPU 18 节点 / 144 GPU
路由专家并行 EP32(含 32 个冗余专家) EP144(含 32 个冗余专家)
MLA / 共享专家并行 DP32 DP144
单卡专家负载 9 路由专家 + 1 共享专家 2 路由专家 + 1 共享专家
计算精度 矩阵乘/dispatch:FP8;MLA 核心/combine:BF16 同左
通信优化 双 microbatch 交替,隐藏 all-to-all attention 拆两步,5 级流水线重叠
flowchart LR
    G["请求网关<br/>路由与调度"] --> P["Prefill 单元<br/>4 节点 × 8 卡 = 32 GPU<br/>EP32 + DP32<br/>每卡 9 路由专家"]
    P -- "KV cache 传输(IB 400 Gb/s)" --> D["Decode 单元<br/>18 节点 × 8 卡 = 144 GPU<br/>EP144 + DP144<br/>每卡 2 路由专家"]
    D --> R["流式返回 token"]

图 1(b) V3 的 Prefill/Decode 分离服务拓扑

3.3 通信–计算重叠与三级负载均衡

跨节点 EP 的主要代价是 all-to-all 通信。V3 采用两种重叠策略:Prefill 阶段把请求批次拆为两个 microbatch 交替执行,用一个的计算隐藏另一个的通信;Decode 阶段各子阶段耗时不均,遂将 attention 拆为两步并用 5 级流水线实现重叠。此外,每 token 最多分发至 4 个节点(node-limited routing),把跨节点 IB 流量约束在可控范围,节点内则走 NVLink;配套的 DeepEP 通信库实现单卡 all-to-all 带宽超过 40 GB/s,接近打满 400 Gb/s 网卡 [1][2]。

负载均衡被拆为三个相互独立的问题:Prefill 侧均衡各 DP 实例的 core-attention 计算量与 dispatch 发送量;Decode 侧均衡 KV cache 占用与请求数;专家并行侧为高负载专家设置冗余副本,并按线上统计周期性重排专家放置(约每 10 分钟一次),以最小化单卡最大接收负载 [2]。

3.4 线上性能

表 4 给出官方披露的 24 小时统计。按 $2/GPU·h 的租价测算,理论日收入约 56.2 万美元、成本利润率约 545%,实际值受定价策略、夜间折扣与免费流量影响而低于该数 [2][9]。生成速度方面,V3 相较 V2.5 由约 20 TPS 提升至 60 TPS。

表 4 V3 线上服务 24 小时统计与性能指标

指标 数值
输入 token 608B / 天(其中 342B 命中 KV cache,命中率约 56%)
输出 token 168B / 天
单节点 Prefill 吞吐 ≈73.7k input tok/s(8×H800)
单节点 Decode 吞吐 ≈14.8k output tok/s(8×H800)
单请求平均输出速度 20–22 TPS

4 DeepSeek-V3.2:稀疏注意力驱动的成本下探

4.1 DSA 架构

V3.2-Exp(2025-09-29)与正式版 V3.2(2025-12-01)在架构上仅有一处改动,即通过持续训练引入 DeepSeek 稀疏注意力(DSA),参数规模与 MoE 配置保持不变(671B/37B,256 路由 + 1 共享专家,激活 8),上下文 128K [10][11]。DSA 是”轻量索引器 + 稀疏 MLA”的两段式结构(图 2)。

  • Lightning Indexer:查询由 1 536 维 query 潜向量经 W_qI 映射为 64 个头 × 128 维,并施加与主 MLA 不同的非交错 RoPE;键由 W_kI 压成单头 128 维,经 LayerNorm 与 Hadamard 旋转后以 FP8 缓存。全部 64 个头共享同一键(multi-query attention,MQA 模式),每 token 每层仅 128 B,加 4 B 缩放共 132 B,约为主 MLA 缓存的 1/5。打分采用 $I(t,s) = \Sigma_j w_{t,j}\cdot\mathrm{ReLU}(q_{t,j}\cdot k_s)$,以 ReLU 替代 softmax 换取低精度下的吞吐。
  • 细粒度选择:每个 query 取 $I(t,\cdot)$ 最大的 k = 2 048 个位置,在 128K 上下文下只访问约 1.6% 的键;复杂度由 $O(L^2)$ 降至 $O(Lk)$,128K 下单层注意力 FLOPs 降约 80%。
  • 稀疏 MLA:仅对选中位置加载完整潜在向量(c_KV 512 + k_R 64 = 656 B/token/层)执行 192 维注意力,未选中位置在 softmax 前置 −inf 归零。每层各带独立索引器,不做跨层 top-k 共享。

1.png

图 2 DeepSeek-V3.2 的注意力架构:DSA 在 MLA 之下实例化,绿色部分为 Lightning Indexer 选出 top-k 键值条目的路径(引自 DeepSeek-V3.2 技术报告 Fig. 2)

4.2 工程实现与部署

DSA 的落地伴随算子重写:DeepSeek 同时开源了 TileLang 与 CUDA 两版 GPU 算子,TileLang 版作为精度基线与快速迭代载体,CUDA 版作为高效实现 [10]。部署侧沿用 V3 的 PD 分离与跨节点 EP 架构,变化体现在负载结构上:core-attention 的计算与访存压力随上下文长度近似线性化,同等显存可容纳更长上下文与更大并发。主流引擎(SGLang、vLLM)提供 day-0 支持,典型配置为 DSA attention backend + topk 2048 + indexer FP8 + KV cache FP8(e4m3)+ page size 64 [12]。

4.3 性能与成本

DSA 的直接经济效果是 API 价格下调超过 50% [10]。能力侧,V3.2 在推理类基准上达到 GPT-5 水平(略低于 Gemini-3.0-Pro),输出长度较同代长思考模型明显缩短,等效降低了用户等待时间与单位任务成本;长思考增强版 V3.2-Speciale 在 IMO 2025、CMO 2025、ICPC World Finals 2025、IOI 2025 均获金牌,但 token 消耗显著更高 [10][11]。结合 V3 时期以单 token 成本核算的定价方式(表 4)可以观察到,推理系统的优化目标已从单 token 成本转向单任务成本。

5 DeepSeek-V4:百万上下文下的混合注意力

5.1 架构

V4(2026-04-24 预览并开源,MIT 许可)包含两个规格:V4-Pro(1.6T 总参 / 49B 激活)与 V4-Flash(约 285B / 13B),上下文 1M(1 048 576 token,按 95% 可用),最大输出 384K [13][14]。其架构改动可归纳为四点。

  1. 混合注意力 CSA + HCA。系列整体架构见图 3(a)。config 以逐层的 compress_ratios 列表声明每个注意力层的类型:4 表示 CSA、128 表示 HCA、0 表示纯滑动窗口。CSA 先用带学习位置偏置的 softmax 门控池化把连续 m = 4 个 token 压成 1 个 KV 条目(序列维 4× 压缩),再用 FP4 的 Lightning Indexer(64 头 × 128 维)选出 top-k = 1 024 个压缩块做稀疏注意力,单元内部结构见图 3(b);HCA 把 m′ = 128 个 token 压成 1 条后做稠密注意力,以较低成本获得全程视角,见图 3(c)。两类层均附带 128 token 的滑动窗口分支。V4-Pro 的 61 层按”层 0–1 仅 HCA → 层 2–60 的 CSA/HCA 交替(30 层 CSA + 31 层 HCA)→ MTP 块仅滑动窗口”排布;V4-Flash 为 43 层,前两层为纯滑动窗口,其后 21 层 CSA 与 20 层 HCA 交替。系列的整体架构见图 3(a)。
  2. 流形约束超连接(manifold-constrained hyper-connections,mHC)。残差流宽度扩为 4 路(hc_mult = 4),残差混合矩阵约束在 Birkhoff 多面体(双随机矩阵)上,训练时用 Sinkhorn-Knopp 迭代(不超过 20 次)投影,使谱范数不超过 1,抑制深层信号放大。
  3. Muon 优化器与 FP4 量化感知训练。主体参数采用 Newton-Schulz 正交化的 Muon(8 次收敛 + 2 次稳定),embedding、预测头、偏置与 RMSNorm 保留 AdamW;MoE 专家权重降为 MXFP4,其余保持 FP8。
  4. 其余继承与微调。注意力形状为 128 头、KV 头 1、head_dim 512、q_lora 1 536、o_lora 1 024、qk_rope 64,hidden 7 168;MoE 扩为 384 路由 + 1 共享、每 token 激活 6 个、专家中间维 3 072,门控由 Sigmoid 改为 Softplus;层 0–2 使用哈希路由;上下文 1M 由 65 536 的原始训练长度经 YaRN(16×)外推得到。

2.png

图 3(a) DeepSeek-V4 系列的整体架构:混合 CSA/HCA 注意力、Pre-Block 与 Post-Block 混合及 MTP 模块(引自 DeepSeek-V4 技术报告 Fig. 2)

3.png

图 3(b) CSA 的核心结构:token 级压缩器、Lightning Indexer 与 top-k 选择器(引自 DeepSeek-V4 技术报告 Fig. 3)

4.png

图 3(c) HCA 的核心结构:重度压缩键值与稠密注意力(引自 DeepSeek-V4 技术报告 Fig. 4)

5.2 推理效率

1M 上下文下的效率提升见表 5。以 V3.2 为基准,V4-Pro 的单 token 推理 FLOPs 降至 27%、KV cache 降至 10%;V4-Flash 进一步降至 10% 与 7%。对比 GQA-8 基线,V4 的 KV cache 约为其 2%。同为 100 万 token 上下文,Llama-3.1 需约 274 GB、GLM-5 约 60 GB,而 V4 仅约 5.48 GB;各代模型的 KV 占用对比见图 4。

表 5 V4 系列模型规格与 1M 上下文下的推理效率

模型 总参/激活 单 token FLOPs(vs V3.2) KV cache(vs V3.2) 1M 上下文 KV 权重落盘
V4-Pro 1.6T / 49B 27% 10% ≈5.48 GB ≈865 GB(MXFP4 专家 + FP8)
V4-Flash 约 285B / 13B 10% 7% ≈3.8 GB(按比例推算) ≈160 GB

5.svg

图 4 同一 1M token 上下文下各代模型 KV cache 占用的对比

5.3 部署形态与性能

V4 提供 Non-think、Think High、Think Max 三种推理模式(后者需 ≥384K 上下文),API 同时兼容 OpenAI 与 Anthropic 协议。由于权重与注意力结构发生改变,社区部署普遍以 Wide-EP 为核心:GB300 NVL72 上把 Decode 扩至 EP16、并发推到 21 504,配合 W4A4(MXFP4)MegaMoE kernel 与 MTP,使 MoE 的 dispatch/combine 全部留在 72 卡 NVLink 域内,50 tok/s/用户、8K 输入/1K 输出下成本约 $0.156/百万输出 token [15]。B200 + vLLM 的单位供电吞吐由约 30 万 tok/s/MW 提升至约 50 万 tok/s/MW [15]。在算力更受限的 H20 上,场景化配置(Prefill 按上下文长度在 PP2/PP4 间切换,Decode 低时延用 PP2-TP8、高吞吐用 DP32-EP32)使 4K/32 并发下吞吐由 319.9 提升至 703.2 tok/s/GPU(2.2×),1M 单请求由 27.1 提升至 66.8 tok/s/GPU [16]。这些部署结果的差异表明,同一模型的最优部署剖面随上下文长度与并发目标变化,拓扑需按此选择。

6 DeepSeek-V4.1-Flash:非对称结构与集群级缓存

6.1 架构

V4.1-Flash(2026-09-10,API 名 deepseek-flash)是全新结构族中尺寸最小的模型(主干 552B,检查点约 511 GB),也是首个原生支持视觉理解的版本 [17][6]。其结构要点见图 5(a) 与图 5(b)。

  • 因果编码器–解码器(causal encoder–decoder,CED)非对称主干:40 层 Transformer 拆成 20 层因果编码器 + 20 层解码器,hidden 5 120;编码器带因果掩码、前两层仅滑动窗口、其后为 CSA2,Prefill 阶段每 token 激活 8B;解码器自回归生成、每 token 激活 16B。解码器的全局 KV 由编码器最终隐态投影而来,而非由每个解码层各自维护,这是激活量能够非对称的根本原因,也是 KV 体积下降的主要来源。
  • CSA2 跨层共享:每个注意力层被静态指派为 Full、Reindex、Reuse 三种模式之一,共享主 KV 与索引器键;分层稀疏索引器先把候选压缩块限定在前一个 Full 层建立的候选池(2 048 块 × 8 token),每个 query 只保留 512 块,使深层索引成本与上下文长度解耦。
  • MoE 与 Engram:每层 384 路由 + 1 共享专家、每 token 激活 6 个;另挂载约 196B 参数的 Engram 条件记忆,在第 1、14 层以两张哈希表按 token 组合稀疏查表,只占存储、不进入前向计算。
  • 视觉通路:自研 DeepSeek-ViT(32 层、hidden 1 024、patch 14、2D-RoPE、3×3 像素下采样)加 2 层 MLP 投影,图像以 span 形式进入提示,并在路由时对 span 内 token 施加单独偏置。
  • KV 与投机解码:主 KV 以 FP4 存储,单 token 全局 KV 压至 890 B,HBM 需求为上一代的 1/4、SSD 为 1/8,相对初代模型累计缩小 437 倍;生成侧由 DSpark 草稿头做定长 5 的投机解码。

6.png

图 5(a) DeepSeek-V4.1-Flash 的整体架构:因果编码器与解码器分离、全局 KV 由编码器末层投影、Engram 条件记忆与 DSpark 草稿头(引自 DeepSeek-V4.1-Flash 技术报告 Fig. 3)

7.png

图 5(b) CSA2 的三种运行模式:Full、Reindex 与 Reuse(引自 DeepSeek-V4.1-Flash 技术报告 Fig. 4)

6.2 部署形态:E/P/D 三级分离与自动扩缩容

V4.1-Flash 的线上系统在设计之初确立了三个目标:兼容不同厂商芯片、围绕常见硬件组织两阶段负载、让 KVCache 保留 72 小时以上以减少重复计算。部署上把视觉编码器(vision encoder,VE)、Prefill、Decode 三级分离,各自独立扩缩容并分别优化 TTFT 与吐字间隔(表 6)。Prefill 使用常规 8 卡服务器,MoE 采用 EP2–8 / DP2–8;Decode 使用 64–128 卡超节点,Wide-EP 扩至 EP64–128 / DP64–128,当前生产配置不使用张量并行与上下文并行 [8][6]。

表 6 V4.1-Flash 线上系统的部署与缓存参数

项目 配置
阶段划分 视觉编码器 / Prefill / Decode 三级分离
Prefill 8 卡服务器,EP2–8 + DP2–8;按输入长度分四档(<16K、16K–64K、64K–256K、>256K)调度
Decode 64–128 卡超节点,EP64–128 + DP64–128(Wide-EP),不使用 TP/CP
最小高效配置 64 卡 Prefill + 64 卡 Decode
持久缓存 3FS 集群约 100 PiB;Block KVCache 128 token/block
缓存 TTL Agent 流量 3 天;Chatbot 流量 7 天
KVCache 粒度 890 B/token;SWA 状态为 134 token ring × 20 层(128 窗口 + 6 投机回退余量)
扩缩容 Prefill 依据未命中 token 数、Decode 依据活跃计算请求数;配额占用 >0.8 禁止缩容;下线 1 分钟宽限期并转移请求与 KV

PD 分离后两类资源的需求比例随流量实时变化,固定配比会在部分时段出现一侧排队、另一侧空转。系统因此按未命中 token 数与活跃请求数分别估算实例需求,并设置上下界与调整速率限制;Decode 实例收到下线通知后进入 1 分钟宽限期,未完成请求连同其 KVCache 一次性转交其他实例;同时保留冗余配额以吸收突发,配额占用率高于 0.8 时禁止缩容。容错方面并未把重启时间压到分钟级以下,而是依靠冗余配额与请求级重路由的组合实现 [6]。

6.3 缓存命中路径与 SWA 有界重放

持久 Block KVCache 由独立 3FS 集群承载,任何 Prefill 机器都能命中任意前缀,因而不需要前缀亲和路由,也避免了缓存路由与负载均衡之间的冲突。命中路径分为三段,只有第一段位于请求关键路径:匹配阶段,入口在本机 kvcache-store 中以哈希链做前缀匹配,只查元数据,但命中长度必须在路由前获知,无法被掩盖;取 KV 阶段,独立进程异步地把数据从 3FS 搬入主机共享内存;换入阶段,引擎线程再把数据从共享内存搬入 HBM,并逐层与前向计算重叠。由于 CUDA Copy Engine 处理细粒度 Block 拷贝效率不佳,改用 GDR 让 RDMA 网卡跑满 PCIe 带宽且不占用流多处理单元(streaming multiprocessor,SM)[6]。

V4 时代曾把 SWA KVCache 一并持久化以便精确恢复,实测表明这类缓存通常只在单次会话的数分钟内被复用,与可跨天复用的 Block KVCache 性质不同,长期保存会占据近一半持久容量却难以带来命中收益。V4.1 因此改用 SWA 有界重放(bounded replay):不再持久化 SWA 状态,缺失时仅重算最近 128 个 token(部署配置保留后缀 140 token,覆盖窗口与投机回退余量),并把重算时的注意力范围截断在窗口内。重算成本不再随上下文长度与层数增长,而成为近似固定开销,官方给出的量级是 Decoder 路径的 Prefill 计算量接近减半 [8][6]。

6.4 调度与投机解码

DSpark 把推测解码的验证长度做成动态决策:每轮生成 5 个草稿 token,置信度头(confidence head)预测各位置的接受率,再结合预先测得的硬件吞吐曲线,选择”预期有效 token 数 / 预计执行时间”最高的连续前缀送交主模型验证。线上 EP64/DP64、每 rank 60 并发条件下,平均验证长度由 5 降至 3.58,平均接受长度由 3.30 变为 3.21,吞吐由 13 730 升至 14 466 tok/s/rank(+5.4%);16 并发时为 −1.5%,96 并发时为 +11.0%,收益依赖工作点 [8]。调度侧则让 GPU 执行第 N 步前向与采样时,CPU 并行完成第 N+1 步调度并回收上一轮结果,把调度开销移出关键等待路径。

6.5 线上性能

表 7 汇总官方披露的线上实测。平均输入长度 77K 与缓存命中率 96% 的组合,使每卡吞吐(含缓存命中)达到 1.5M tok/s;在此条件下,Agent 类负载的成本主要由缓存命中率而非算力决定。图 6 与图 7 分别给出 Decode 与 Prefill 阶段的帕累托前沿。

表 7 V4.1-Flash 线上实测指标

指标 数值
TTFT 平均 / P99 400 ms / 1 000 ms
平均输入长度 77K token
平均输入缓存命中率 96%
Decode 吐字速度 230 TPS(代码场景 300 TPS)
单卡吞吐(不含缓存命中 / 含命中) 60K tok/s / 1.5M tok/s
单集群日吞吐 155 万亿 token/天
谷时单位算力收入 $38 / GPU·h
推测解码收益 变长验证较固定 5 个草稿 +6% 吞吐
缓存收益 Bounded Replay 使 Decoder Prefill 计算量 ≈ 减半

8.svg

图 6 Decode 阶段吞吐–时延帕累托前沿(64×SM100,EP64/DP64,131K 上下文,nextn=3,接受率 75%)

9.svg

图 7 Prefill 阶段吞吐–时延帕累托前沿(4×SM100,EP4/DP4,131K 上下文,bounded replay 140 token)

7 DeepInfer:生产级推理引擎的开源

7.1 系统结构

2026-10-20,DeepSeek 以 Apache-2.0 协议开源其线上生产级推理引擎 DeepInfer,同步开源 token 编解码组件 deepseek-recipe [18]。首发版本面向 DeepSeek-V4.1-Flash-1017 单模型,提供离线批处理推理能力 [8][6][19]。其结构自上而下分为接入层、调度层、并行层、算子层与硬件层(图 8):算子层整合 FlashMLA(MLA 注意力核)、DeepGEMM(矩阵乘)、DeepEP(MoE all-to-all)、TileKernels/TileLang(分块与自定义核)与 DeepSelect;硬件层原生适配 NVIDIA Blackwell SM100 与华为昇腾 950 [8]。

flowchart TB
    A["接入层:结构化输出(正则 / JSON Schema / EBNF)· 批次不变推理 · 确定性采样"]
    B["调度层:零开销异步批调度器 · DSpark 置信度投机解码 · SWA 有界重放"]
    C["并行层:Attention DP · balanced CP · MoE EP + EPLB"]
    D["算子层:FlashMLA · DeepGEMM · DeepEP · TileKernels / TileLang · DeepSelect"]
    E["硬件层:NVIDIA SM100(Blackwell)· 华为昇腾 950"]
    A --> B --> C --> D --> E
    F["本次未开源:KVCache 存储系统 · VE/Prefill/Decode 分离部署 · 跨副本负载均衡"] -.-> B

图 8 DeepInfer 的系统分层

7.2 并行与负载均衡

DeepInfer 支持三种并行:Attention DP(注意力按请求切分)、balanced CP(均衡上下文并行,面向长输入)与 MoE EP + EPLB(专家并行与专家负载均衡)。生产布局为 Prefill EP2–8/DP2–8、Decode EP64–128/DP64–128。Wide-EP 的收益来自两个方面:单卡承担的专家权重减少,MoE 每步的显存流量随之下降;更多请求的 token 汇入同一专家,矩阵计算的批规模得以改善。EPLB 为热点专家保留冗余副本,依据最近 1 000 步的负载统计、每 10 000 步重排专家放置 [8]。

7.3 关键优化

除并行外,引擎层面有三项优化直接影响体验指标。异步批调度使 CPU 在设备执行当前批次时准备下一批次,把调度移出关键路径。DSpark 投机解码采用变长验证,官方口径下单 token 生成延迟下降 40% 以上。批不变与确定性采样固定 batch 组合下的浮点结果,使相同输入在相同参数下产生完全相同的输出,为回归测试与可复现评测提供基础;该能力当前仅覆盖 NVIDIA 路径,且需配合固定 seq_seed 并关闭变长 MTP [8]。

7.4 基准与帕累托前沿

官方给出两条吞吐–时延前沿(表 8,对应图 6、图 7)。Prefill 侧,批量由 4K 增至 64K token/rank/step,单卡吞吐由约 60K 升至约 89.5K tok/s,单步时延由 68 ms 增至 732 ms;超过 32K 后时延近似翻倍而吞吐仅增约 3%,拐点位于 16K–32K。Decode 侧,并发由 8 增至 256,单卡吞吐由约 2.6K 升至约 26.3K tok/s(约 10×),单用户速度由 325 降至 103 tok/s(约 1/3)。

表 8 DeepInfer 官方基准(SM100)

阶段 配置 扫描变量 时延 单卡吞吐
Prefill EP4/DP4,4 卡,131K 4 096 token/rank 68 ms/step ≈60.0K tok/s
Prefill 同上 16 384 token/rank 200 ms/step ≈81.5K tok/s
Prefill 同上 65 536 token/rank 732 ms/step ≈89.5K tok/s
Decode EP64/DP64,64 卡,131K,nextn=3 8 并发/rank 3.1 ms/token(325 tok/s/user) ≈2.6K tok/s
Decode 同上 64 并发/rank 4.1 ms/token(246 tok/s/user) ≈15.7K tok/s
Decode 同上 256 并发/rank 9.7 ms/token(103 tok/s/user) ≈26.3K tok/s

7.5 能力边界

开源版本不是线上系统的完整复刻。仓库标注了三项缺失:KVCache 存储系统(如 3FS 集群与 kvcache-store)、VE/Prefill/Decode 分离部署、跨数据并行副本的负载均衡;在线 HTTP 服务网关也需自行封装 [8][6]。硬件门槛同样不低:仅支持 SM100 与昇腾 950,软件栈要求 Linux、GCC 13 以上、Python 3.12、CUDA 13 或 CANN 9.2,需从源码构建 [8]。

8 讨论

8.1 四条演进主线

综合四代系统,效率提升集中在四个方向(表 9)。KV 表达的压缩幅度最大:MLA 把 KV 压为低秩潜在向量,DSA 稀疏化访问范围,CSA/HCA 在 token 维做 4×–128× 压缩,V4.1 将单 token 缓存压到 890 B,同一 1M 上下文下 KV 占用由数十 GB 级降至 GB 级(图 4)。计算的稀疏化贯穿四代:MoE 的专家稀疏(37B/671B)与注意力的位置稀疏(top-2048)使稀疏度成为可调的系统参数。部署形态的解耦从 PD 分离扩展到 E/P/D 三级分离,并行域由节点内扩展到超节点内(Wide-EP),代价是通信与负载均衡复杂度上升,收益是两类负载各自匹配硬件。缓存与调度的分层则由单机 KV 缓存发展为 100 PiB 集群共享缓存,投机解码由固定验证长度变为置信度调度的变长验证,调度由同步改为异步流水。

表 9 四代推理系统的横向对比

维度 V3(2024-12) V3.2(2025-12) V4(2026-04) V4.1-Flash(2026-09)
总参/激活 671B / 37B 671B / 37B 1.6T / 49B(Pro);285B / 13B(Flash) 552B / 8B(输入)+16B(输出)
注意力 MLA MLA + DSA CSA + HCA CSA2 + CED
KV 粒度 ≈70 KB/token ≈15–23 KB/token(128K 下降 3–5×) ≈5.5 KB/token(1M 时 5.48 GB) 890 B/token(1M 约 0.9 GB)
上下文 128K 128K 1M 1M 输入 / 384K 输出
部署形态 PD 分离 PD 分离(继承) Wide-EP(第三方引擎为主) VE/P/D 三级分离
集群缓存 单机 HBM 单机 HBM 显存 + 主机内存 显存 + 主机内存 + 3FS 100 PiB
代表性指标 73.7k in / 14.8k out tok/s·节点;20–22 TPS API 价格 −50% 以上 1M 下单卡 66.8 tok/s(H20) TTFT 400 ms;230 TPS;155 万亿 token/天

8.2 开放问题

公开指标之外仍有三个问题有待研究。异构与可移植性方面,DeepInfer 同时支持 SM100 与昇腾 950,但两者在算子指令集、互联协议与 HBM 规格上差异明显,跨平台性能是否可比尚无公开的第三方基准;文献 [15][16] 显示,同一模型在 GB300 NVL72、B200、H20 上的最优拓扑相差较大。确定性推理的代价方面,批不变性与变长投机解码在实现上互斥(前者要求关闭 varlen MTP),在生产环境中同时获得可复现性与加速仍需工程折中。缓存的经济学边界方面,96% 的命中率建立在 Agent 长会话与工具调用复用的流量特征上,若负载以一次性长文本为主,100 PiB 存储层的投入产出比将明显下降。此外,不同来源对吞吐的定义(是否计入缓存命中、是否含草稿 token、按卡还是按节点)差异很大,跨系统比较时须对齐口径 [8][15]。

9 结论

本文梳理了 DeepSeek-V3 至 V4.1-Flash 及 DeepInfer 的硬件基座、并行方案、缓存分层与性能表现。四代系统的成本下降不是单点突破的结果,而是模型结构、并行策略、存储层级与调度机制四层共同作用的结果:MLA、DSA 与 CSA 逐级压缩 KV 表达,长上下文由难以服务变为可负担;PD 分离与 Wide-EP 使两类负载分别匹配到适合的硬件;集群级共享缓存与有界重放把重复计算移出关键路径。DeepInfer 以 Apache-2.0 协议开源后,这套工程方法首次以可复现的形式对外公开,但其 KV 存储系统、分离部署与跨副本负载均衡未随源码发布,完整复现仍需较多自研工作。

声明

致谢

【待补:非作者贡献但需致谢的人员、机构与项目支持】

资助

【待补:资助机构全称与项目编号;若无资助,填”本研究未获得任何公共、商业或非营利机构的资助。”】

作者贡献

【待补:按 CRediT 分类填写,例如”甲:概念化、方法设计、初稿撰写与修订;乙:文献调研、数据整理、图表制作。”所有作者均已审阅并同意稿件最终版本。】

利益冲突

【待补:如无冲突,填”作者声明不存在利益冲突。”】

数据可得性

本文引用的全部数据来自公开发布的技术报告、模型卡、官方仓库与第三方基准,正文与附录已逐条标注来源。图表对应的原始数据可向通讯作者索取。

附录 A 模型结构超参数对照

表 A1 汇总四代模型的结构超参数。两代之间的差异值得注意。V3 到 V3.2 的 MoE 配置完全未动,唯一增量是 DSA 的索引器(多出 685B − 671B ≈ 14B 参数);V4 起注意力形状与 MoE 配置同时改写(KV 头数降为 1、专家数增至 384、门控换为 Softplus),注意力压缩与专家扩容在此处一并设计,压缩释放的显存与带宽被用于换取更大的专家池。

表 A1 四代模型的结构超参数(据各代技术报告、模型卡与官方 config)

项目 V3 V3.2 V4-Pro V4.1-Flash
层数 61(3 稠密 + 58 MoE) 61(同 V3) 61 + 1 MTP 40(20 编码器 + 20 解码器)
hidden 7 168 7 168 7 168 5 120
注意力 MLA MLA + DSA 逐层 CSA(4×)/HCA(128×)/SWA(128) CSA2(Full/Reindex/Reuse)+ SWA
头数 / KV 头 128 / 128 128 / 128 128 / 1 未公开
head_dim 128(Nope)+ 64(RoPE) 同左 512(qk_rope 64) 未公开
低秩维度 q 1 536 / kv 512 q 1 536 / kv 512 q 1 536 / o 1 024 未公开
索引器 — 64 头 × 128 维,FP8,k = 2 048 64 头 × 128 维,FP4,k = 1 024 块 分层索引器:候选 2 048 块 × 8 → 保留 512
路由 / 共享专家 256 / 1 256 / 1 384 / 1 384 / 1
每 token 激活专家 8 8 6 6
专家中间维 2 048 2 048 3 072 未公开
门控 Sigmoid + 选择偏置 b_i 同左 Softplus + noaux_tc 未公开
词表 129 280 129 280 129 280 未公开
上下文 128K 128K 1M 1M / 输出 384K
权重精度 FP8 FP8 专家 FP4 + 其余 FP8 主 KV FP4
结构附加件 MTP 深度 1 DSA 索引器 mHC 4×、层 0–2 哈希路由 Engram 196B、ViT 32 层、DSpark

注:V4.1-Flash 的部分超参官方尚未在模型卡中逐项披露,表中以”未公开”标注;V4-Pro 的 1.7T 参数口径见 0813 模型卡,本文正文采用技术报告的 1.6T。

参考文献

[1] DeepSeek-AI, DeepSeek-V3 technical report, arXiv:2412.19437, 2024.

[2] DeepSeek-AI, DeepSeek-V3/R1 inference system overview (open-source week, day 6), 2025. Available from: https://github.com/deepseek-ai/profile-data

[3] NVIDIA, GB300 NVL72 and Blackwell Ultra platform specifications, 2026. Available from: https://www.nvidia.com/en-eu/data-center/gb300-nvl72/

[4] 华为, 昇腾 950PR/950DT 芯片规划与 Atlas 350 加速卡规格, 2025-09/2026-03. Available from: https://www.hiascend.com/hardware/accelerator-card

[5] NVIDIA, H100/H800 Tensor Core GPU specifications and architecture whitepaper, 2022–2023.

[6] 褚杏娟, 刚刚,DeepSeek 开源最新推理框架:单集群日吞吐 155 万亿 Token,KVCache 可保存 7 天, InfoQ, 2026-10.

[7] DeepSeek-AI, 3FS (Fire-Flyer File System): a high-performance distributed file system for AI training and inference, 2025.

[8] DeepSeek-AI, DeepInfer: high-performance large-scale inference framework (repository, Q&A and configuration documentation), 2026-10. Available from: https://github.com/deepseek-ai/DeepInfer

[9] 中泰证券, DeepSeek 开启盈利新时代:545% 成本利润率测算, 2025-03.

[10] DeepSeek-AI, DeepSeek-V3.2: pushing the frontier of open large language models, arXiv:2512.02556, 2025. Available from: https://api-docs.deepseek.com/zh-cn/news/news251201

[11] DeepSeek-AI, DeepSeek-V3.2 model card, 2026-01. Available from: https://fe-static.deepseek.com/chat/transparency/deepseek-v3.2-model-card-0414-CN.pdf

[12] vLLM/SGLang, DeepSeek-V3, V3.2 and V4 deployment documentation (MoE, DeepEP, EPLB, PD separation), 2026.

[13] DeepSeek-AI, DeepSeek-V4: towards highly efficient million-token context intelligence, arXiv:2606.19348, 2026.

[14] DeepSeek-AI, DeepSeek-V4 model card, 2026-04. Available from: https://fe-static.deepseek.com/chat/transparency/deepSeek-V4-model-card-CN_3c2bd9.pdf

[15] SemiAnalysis, DeepSeek V4 1.6T: day 0 to day 43 performance over time (GB300 NVL72, B200, MI355X), 2026-06.

[16] Ant Group SCT and SGLang Community, Pushing the limits of serving DeepSeek-V4-Pro (H20 scenario deployment), 2026.

[17] DeepSeek-AI, DeepSeek-V4.1-Flash technical report (DeepSeek_V41_Tech_Report.pdf) and release announcement, 2026-09. Available from: https://huggingface.co/deepseek-ai/DeepSeek-V4.1-Flash

[18] DeepSeek-AI, deepseek-recipe: production-grade token encoding and decoding component, 2026-10. Available from: https://github.com/deepseek-ai/deepseek-recipe

[19] DeepSeek-AI, API changelog (V4.1-Flash-1017 etc.), 2026-10. Available from: https://api-docs.deepseek.com/zh-cn/updates/

]]>
Transformer的十年 变与不变 http://zhifeiding.github.io/programming/2026/10/31/Transformer%E7%9A%84%E5%8D%81%E5%B9%B4-%E5%8F%98%E4%B8%8E%E4%B8%8D%E5%8F%98/ 2026-10-31T00:00:00+00:00 http://zhifeiding.github.io/programming/2026/10/31/Transformer的十年 变与不变 1. 概述

自 2017 年 Transformer 提出以来,到 2026 年已近十年。这十年间,Transformer 的哪些部分发生了变化,哪些保持不变?它如今是什么形态,又为何演化成现在的样子,背后的驱动力是什么?预测未来并不容易,梳理历史却有助于理解现状。本文是对 Transformer 十年演进的一次系统回顾。

针对 Transformer 的不同组成部分,研究者分别进行了大量改进:

  • 注意力机制
  • 归一化方法
  • 位置编码
  • 前馈网络
  • 激活函数
  • 残差结构

本文先从 2017 年的奠基论文引出背景,随后依次从注意力、归一化、位置编码、前馈网络、激活函数与残差连接六个方面,梳理各部分的演化脉络。

0.png

图 1.1 Transformer 原始架构(Vaswani et al., 2017, Figure 1)。十年间,图中几乎每个模块都经历过改动,但“堆叠的残差子层 + 注意力 + FFN”这一整体骨架保持不变。

2. Attention is All You Need

2017年, Ashish Vaswani等人发表了一篇具有里程碑意义的论文《Attention Is All You Need》,该论文提出了Transformer架构,其核心观点是仅使用注意力机制,而不依赖循环神经网络(RNN)或卷积神经网络(CNN),也可以构建出高效、强大的模型。

Transformer模型结构由编码器(Encoder)和解码器(Decoder)组成,每个编码器和解码器由6层“自注意力层”和“前馈网络层”堆叠而成。自注意力层可以让模型在生成每个单词的时候考虑到序列中其他所有单词的影响,而前馈网络层则对每个位置的表示进行进一步的非线性变换。论文引入了“多头注意力”机制,通过将输入序列分割成多个“头”来同时进行自注意力计算,增强了模型的表达能力。由于Transformer模型本身不具有捕捉序列顺序的能力,论文提出了位置编码方法,用来给模型提供关于单词在序列中位置的信息。

1.png

图 2.1 Transformer 模型架构(Vaswani et al., 2017, Figure 1)

在标准的自注意力机制中,每个词元(token)通过一套查询(Query)、键(Key)、值(Value)矩阵来捕捉与序列中所有词元的关系。然而,单头注意力存在局限:它可能过度关注自身位置,或者只能捕捉单一类型的依赖关系。 2.png

图 2.2 缩放点积注意力(左)与多头注意力(右)(Vaswani et al., 2017, Figure 2)

3.png

图 2.3 缩放点积注意力公式(Vaswani et al., 2017, 式 (1))

缩放点积注意力(Scaled Dot-Product Attention)的公式为:

\[\text{Attention}(Q, K, V) = \text{softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right)V \tag{2.1}\]

其中 $Q, K, V \in \mathbb{R}^{n \times d_k}$,$d_k$ 是键向量的维度。除以 $\sqrt{d_k}$ 的原因:当 $d_k$ 较大时,点积结果的方差会随 $d_k$ 线性增长,softmax 容易被推向梯度极小的饱和区,缩放可以稳定训练。

多头注意力将输入序列的嵌入表示(通常是 512 维等高维向量)沿特征维度分割成多个子空间(即“头”),每个头在低维空间(如 64 维)中独立、并行地计算自注意力。 4.png

图 2.4 多头注意力公式(Vaswani et al., 2017, 式 (2))

多头注意力(Multi-Head Attention)公式:

\[\text{MultiHead}(Q, K, V) = \text{Concat}(\text{head}_1, \dots, \text{head}_h)W^O \tag{2.2}\] \[\text{where } \text{head}_i = \text{Attention}(QW_i^Q, KW_i^K, VW_i^V)\]

其中投影矩阵 $W_i^Q \in \mathbb{R}^{d_{\text{model}} \times d_k}$,$W_i^K \in \mathbb{R}^{d_{\text{model}} \times d_k}$,$W_i^V \in \mathbb{R}^{d_{\text{model}} \times d_v}$,$W^O \in \mathbb{R}^{hd_v \times d_{\text{model}}}$。原论文取 $h=8$,$d_k = d_v = d_{\text{model}}/h = 64$。

多头注意力的计算是一个标准化的四步流程:

  1. 线性投影与分割(切分): 输入序列 X 首先分别通过三组不同的可学习权重矩阵 W_q, W_k, W_v 进行线性变换,得到初始的 Q、K、V 矩阵。然后,这些矩阵被重塑(reshape)和转置(transpose),从形状 (batch_size, seq_len, d_model) 变为 (batch_size, num_heads, seq_len, d_k)。这里 d_k = d_model / num_heads,即完成了将高维特征分割到多个头的过程。
  2. 并行注意力计算: 每个头独立进行缩放点积注意力(Scaled Dot-Product Attention)计算。
  3. 拼接(Concat): 将 num_heads 个头的输出(每个形状为 (batch_size, seq_len, d_k))在最后一个维度(特征维度)上拼接起来,恢复为 (batch_size, seq_len, d_model) 的形状
  4. 最终线性投影(融合):拼接后的结果通过最终的可学习权重矩阵 $W_o$ 进行线性变换。这一步既把维度适配回原始输入大小以支持残差连接,又融合了来自不同头的信息,而非简单拼接。该融合过程由模型在训练中自动学习,会为更重要的头分配更高的权重。

实现一个多头注意力层:

import torch
import torch.nn as nn
import torch.nn.functional as F
import math


class MultiHeadAttention(nn.Module):
    """原始论文中的多头注意力 (Vaswani et al., 2017)"""
    def __init__(self, d_model, num_heads, dropout=0.0):
        super().__init__()
        assert d_model % num_heads == 0
        self.num_heads = num_heads
        self.d_k = d_model // num_heads  # 例如 d_model=512, h=8 -> d_k=64

        self.W_q = nn.Linear(d_model, d_model)
        self.W_k = nn.Linear(d_model, d_model)
        self.W_v = nn.Linear(d_model, d_model)
        self.W_o = nn.Linear(d_model, d_model)
        self.dropout = nn.Dropout(dropout)

    def forward(self, x, mask=None):
        B, L, _ = x.shape

        # 1. 线性投影 + 切分多头: (B, L, d_model) -> (B, h, L, d_k)
        q = self.W_q(x).view(B, L, self.num_heads, self.d_k).transpose(1, 2)
        k = self.W_k(x).view(B, L, self.num_heads, self.d_k).transpose(1, 2)
        v = self.W_v(x).view(B, L, self.num_heads, self.d_k).transpose(1, 2)

        # 2. 并行缩放点积注意力
        scores = (q @ k.transpose(-2, -1)) / math.sqrt(self.d_k)  # (B, h, L, L)
        if mask is not None:
            scores = scores.masked_fill(mask, float("-inf"))
        attn = self.dropout(F.softmax(scores, dim=-1))
        out = attn @ v                                    # (B, h, L, d_k)

        # 3. 拼接所有头 + 4. 输出投影
        out = out.transpose(1, 2).contiguous().view(B, L, -1)  # (B, L, d_model)
        return self.W_o(out)

3. Attention的改进

注意力机制的演进是深度学习领域过去十年间最核心的发展主线之一。它并非一蹴而就,而是一场在理论表达能力、计算效率与硬件资源三者之间持续博弈与突破的螺旋式上升过程。整个演进史始终围绕一个核心矛盾展开:注意力机制强大的全局建模能力($O(N^2)$ 复杂度)与随之而来的巨大计算和内存开销之间的冲突。

随着模型规模和应用场景(尤其是长序列处理)的增长,标准 MHA 的二次方复杂度和巨大的 KV Cache(推理时存储的键值对)开销成为主要瓶颈,演进也主要围绕几个方向展开:

标准自注意力的计算复杂度为 $O(n^2 \cdot d)$(序列长度 $n$,特征维度 $d$),推理时 KV Cache 显存占用为 $O(n \cdot h \cdot d_k)$。十年改进基本沿着三条主线:

表 3.1 注意力效率优化的三条主线

主线 目标 代表工作
减少 KV Cache 降低推理显存/带宽压力 MQA → GQA → MLA
减少计算量 打破 $O(n^2)$ 滑窗注意力、线性注意力、稀疏注意力
优化 IO 与调度 让算子贴合硬件 FlashAttention 1→4

1. 优化KV Cache,提升推理效率

这是为了缓解自回归解码时显存带宽的极端压力。

  • 效率优化变体:为了减少推理时缓存的键值对(KV Cache)大小,提升推理速度,出现了多查询注意力(MQA) 和分组查询注意力(GQA)。MQA让所有头共享一套K和V,极大减小缓存;GQA是MHA和MQA的折中,将头分组,组内共享K/V,在效率和效果间取得更好平衡

2. 优化计算与IO:FlashAttention

即使 KV Cache 降下来了,训练和预填充阶段的 $O(n^2)$ 注意力矩阵仍是主要瓶颈。FlashAttention(Tri Dao et al., 2022)则另辟蹊径:不改数学,改数据搬运——利用 GPU 存储层级(SRAM 快但小,HBM 大但慢)做 tiling 与 online softmax 分块计算,IO 复杂度从 $O(N^2 + Nd^2)$ 降至 $O(N^2 d^2 / M)$,数值上与标准注意力完全等价。其核心机制与 FA1→FA4 的软硬件协同演进详见附录 FlashAttention。

3.1 Multi-query attention

多查询注意力(Multi-Query Attention, MQA)由 Noam Shazeer 在 2019 年《Fast Transformer Decoding: One Write-Head is All You Need》中提出。其做法十分直接:保留 $h$ 个查询头,但所有头共享同一组 Key 和 Value 投影。

5.png

图 3.1 从 Multi-Head 到 Multi-Query 的转换——把 $h$ 个 Key 投影做 Mean Pool 合并为单头(GQA 论文 Figure 1)。

对输入序列 $X$,MQA 的投影形状为:

  • 查询(Q):$[b, n, h, d_k]$($h$ 个头)
  • 键(K):$[b, n, 1, d_k]$(1 个头)
  • 值(V):$[b, n, 1, d_k]$(1 个头)

KV Cache 从 $O(n \cdot h \cdot d_k)$ 降到 $O(n \cdot d_k)$,推理时内存带宽压力大幅下降(解码阶段本质是 memory-bound,KV 读取量决定吞吐)。代价是表达能力下降——所有查询头只能从同一套 K/V 里取信息,长尾任务上质量有可感知的损失。

# MQA 是 GQA 的特例:num_kv_groups = 1

mqa = GroupedQueryAttention(d_in=2048, num_heads=32, num_kv_groups=1)

3.2 Grouped query attention

分组查询注意力(Grouped Query Attention, GQA,Ainslie et al., 2023)在多头注意力(MHA)与多查询注意力(MQA)之间取得折中:将查询头分为若干组,每组共享同一组键、值投影。这样既保留了 MHA 的表达能力,又显著降低了推理时的 KV Cache 与内存占用,在长序列场景下收益尤为明显。

6.png

图 3.2 Multi-head / Grouped-query / Multi-query 三种结构对比(GQA 论文 Figure 2)。GQA 是 MHA 与 MQA 的折中:查询头分组,组内共享 K/V。

具体而言,GQA 将 $h$ 个查询头划分为 $g$ 个组($g < h$),每组包含 $h/g$ 个查询头,同一组内的查询头共享一组键、值投影,不同组之间相互独立。相比之下,标准 MHA 为每个查询头维护独立的键头与值头,MQA 则让所有头共享一组键、值。GQA 因此兼顾两者:既保持了一定的表达多样性,又将键值头数从 $h$ 降到 $g$。论文实验表明,GQA 在保持与 MHA 相近模型质量的同时,可将处理速度提高约 3 倍,接近 MQA 的水平。

具体来说,对于输入序列X,经过线性变换后得到:

  • 查询(Q):形状为[batch_size, seq_len, h, head_dim]

  • 键(K):形状为[batch_size, seq_len, g, head_dim]

  • 值(V):形状为[batch_size, seq_len, g, head_dim]

其中h是查询头的总数,g是分组的数量(键值头的数量),且h/g为整数。

表 3.2 MHA、GQA 与 MQA 的结构对比

特性 MHA(多头注意力) GQA(分组查询注意力) MQA(多查询注意力)
查询头数 h h h
键值头数 h g(1 < g < h) 1
参数数量 多 中等 少
表达能力 强 中等 相对较弱
内存使用 高 中等 低
计算速度 慢 较快 快

GQA的计算过程可以分解为以下几个步骤:

  1. 投影操作:将输入序列通过线性变换得到查询、键和值张量。需要注意的是,查询的投影维度为h×head_dim,而键和值的投影维度仅为g×head_dim。
  2. 查询分组:将查询张量沿头维度分割为g个组,每个组包含h/g个查询头。
  3. 键值复制:为了便于计算,将键和值张量沿着头维度复制h/g次,使其形状与查询张量匹配。
  4. 注意力计算:对每个组分别计算注意力分数。首先计算查询组和键的点积,然后通过softmax函数归一化得到注意力权重,最后使用这些权重对值进行加权求和。
  5. 输出拼接:将所有组的注意力输出拼接起来,形成最终的注意力输出。
class GroupedQueryAttention(nn.Module):
    """
    分组查询注意力 (Grouped Query Attention, GQA) 实现。
    
    核心思想:
    - 分组查询注意力(GQA): Q 有 num_heads 个头,K、V 有 num_kv_groups 个头(折中方案)
    
    参数说明:
    - d_in: 输入特征维度
    - num_heads: Query 的头数(总头数)
    - num_kv_groups: Key/Value 的组数(KV 头数)
    - head_dim: 每个头的维度(如果不指定则自动计算)
    - qk_norm: 是否对 Q 和 K 应用 RMSNorm(提高训练稳定性)
    - dtype: 参数数据类型
    
    示例配置(Qwen3 风格):
    - num_heads=32, num_kv_groups=8 -> group_size=4
    - 意味着每 4 个 Query 头共享 1 组 Key-Value 头
    """
    def __init__(
        self, d_in, num_heads, num_kv_groups, head_dim=None, qk_norm=False, dtype=None
    ):
        super().__init__()
        # 检查 Q 头数必须能被 KV 组数整除
        assert num_heads % num_kv_groups == 0, "num_heads must be divisible by num_kv_groups"

        self.num_heads = num_heads  # Query 头数(例如 32)
        self.num_kv_groups = num_kv_groups  # KV 组数(例如 8)
        self.group_size = num_heads // num_kv_groups  # 每组大小(例如 4)

        # 计算每个头的维度
        if head_dim is None:
            assert d_in % num_heads == 0, "`d_in` must be divisible by `num_heads` if `head_dim` is not set"
            head_dim = d_in // num_heads  # 例如:d_in=2048, num_heads=32 -> head_dim=64

        self.head_dim = head_dim
        self.d_out = num_heads * head_dim  # 输出维度

        # 投影层:注意 K、V 的输出维度是 num_kv_groups * head_dim
        # 示例:num_heads=32, num_kv_groups=8, head_dim=64
        # Q: d_in -> 32*64 = 2048
        # K: d_in -> 8*64 = 512(只有 Q 的 1/4)
        # V: d_in -> 8*64 = 512
        self.W_query = nn.Linear(d_in, self.d_out, bias=False, dtype=dtype)
        self.W_key = nn.Linear(d_in, num_kv_groups * head_dim, bias=False, dtype=dtype)
        self.W_value = nn.Linear(d_in, num_kv_groups * head_dim, bias=False, dtype=dtype)

        self.out_proj = nn.Linear(self.d_out, d_in, bias=False, dtype=dtype)

        # 可选的 QK 归一化(Qwen3 使用此技术提高稳定性)
        if qk_norm:
            self.q_norm = RMSNorm(head_dim, eps=1e-6)
            self.k_norm = RMSNorm(head_dim, eps=1e-6)
        else:
            self.q_norm = self.k_norm = None

    def forward(self, x, mask, cos, sin, start_pos=0, cache=None):
        """
        前向传播详解:
        
        输入:
        - x: (batch, num_tokens, d_in) 输入序列
        - mask: 注意力掩码,防止看到未来 token
        - cos, sin: RoPE 旋转位置编码的预计算值
        - start_pos: KV cache 的起始位置(推理时使用)
        - cache: 之前的 (keys, values) 缓存
        
        输出:
        - context: (batch, num_tokens, d_in) 注意力输出
        - next_cache: 更新后的 (keys, values) 缓存
        
        处理流程(以 num_heads=32, num_kv_groups=8 为例):
        1. Q 投影: (B, L, 2048) -> (B, 32, L, 64)
        2. K 投影: (B, L, 2048) -> (B, 8, L, 64)
        3. V 投影: (B, L, 2048) -> (B, 8, L, 64)
        4. K/V 扩展: (B, 8, L, 64) -> (B, 32, L, 64) 通过 repeat_interleave
        5. 注意力计算: Q @ K^T -> softmax -> @ V
        """
        b, num_tokens, _ = x.shape

        # ========== 步骤 1: 应用线性投影 ==========
        queries = self.W_query(x)  # (b, num_tokens, num_heads * head_dim)
        keys = self.W_key(x)       # (b, num_tokens, num_kv_groups * head_dim)
        values = self.W_value(x)   # (b, num_tokens, num_kv_groups * head_dim)

        # ========== 步骤 2: 重塑为多头格式 ==========
        # Query: (B, L, num_heads*head_dim) -> (B, L, num_heads, head_dim) -> (B, num_heads, L, head_dim)
        queries = queries.view(b, num_tokens, self.num_heads, self.head_dim).transpose(1, 2)
        
        # Key/Value: (B, L, num_kv_groups*head_dim) -> (B, L, num_kv_groups, head_dim) -> (B, num_kv_groups, L, head_dim)
        keys_new = keys.view(b, num_tokens, self.num_kv_groups, self.head_dim).transpose(1, 2)
        values_new = values.view(b, num_tokens, self.num_kv_groups, self.head_dim).transpose(1, 2)

        # ========== 步骤 3: 可选的 QK 归一化 ==========
        # 对每个头的特征维度进行归一化,提高训练稳定性
        # 这是 Qwen3 的一个关键技术点
        if self.q_norm:
            queries = self.q_norm(queries)
        if self.k_norm:
            keys_new = self.k_norm(keys_new)

        # ========== 步骤 4: 应用旋转位置编码 (RoPE) ==========
        # RoPE 通过旋转嵌入注入位置信息,不增加额外参数
        # offset=start_pos 用于 KV cache 场景,确保位置编码连续
        queries = apply_rope(queries, cos, sin, offset=start_pos)
        keys_new = apply_rope(keys_new, cos, sin, offset=start_pos)

        # ========== 步骤 5: KV Cache 处理(推理优化)==========
        # 推理时复用之前计算的 K、V,避免重复计算
        # 训练时 cache=None,每次计算完整序列
        if cache is not None:
            prev_k, prev_v = cache
            # 将新的 K、V 拼接到缓存后面
            # 例如:prev_k (B, 8, 100, 64) + keys_new (B, 8, 1, 64) -> (B, 8, 101, 64)
            keys = torch.cat([prev_k, keys_new], dim=2)
            values = torch.cat([prev_v, values_new], dim=2)
            next_cache = (keys, values)
        else:
            start_pos = 0  # 训练模式,重置位置
            keys, values = keys_new, values_new
            next_cache = (keys, values)

        # ========== 步骤 6: 扩展 K、V 以匹配 Q 的头数 ==========
        # 关键操作:将 num_kv_groups 个 KV 头扩展为 num_heads 个
        # repeat_interleave 确保每组连续重复,而不是整体重复
        # 例如:[K0, K1] -> [K0, K0, K0, K0, K1, K1, K1, K1](group_size=4)
        # 形状变化:(B, 8, L, 64) -> (B, 32, L, 64)
        keys = keys.repeat_interleave(self.group_size, dim=1)
        values = values.repeat_interleave(self.group_size, dim=1)

        # ========== 步骤 7: 计算注意力分数 ==========
        # Q @ K^T: (B, 32, L, 64) @ (B, 32, 64, L) -> (B, 32, L, L)
        attn_scores = queries @ keys.transpose(2, 3)
        
        # 应用掩码:将不应该看到的位置设为 -inf(softmax 后变为 0)
        # 例如:因果掩码防止看到未来 token
        attn_scores = attn_scores.masked_fill(mask, -torch.inf)
        
        # 缩放 + softmax:除以 sqrt(head_dim) 防止梯度消失
        # 例如:head_dim=64 -> 除以 8
        attn_weights = torch.softmax(attn_scores / self.head_dim**0.5, dim=-1)

        # ========== 步骤 8: 应用注意力权重 ==========
        # (B, 32, L, L) @ (B, 32, L, 64) -> (B, 32, L, 64)
        # 然后转置回 (B, L, 32, 64) 并合并头维度为 (B, L, 2048)
        context = (attn_weights @ values).transpose(1, 2).reshape(b, num_tokens, self.d_out)
        
        # ========== 步骤 9: 输出投影 ==========
        # 将多头输出投影回原始维度
        return self.out_proj(context), next_cache

注:GQA 的后续演进还包括 DeepSeek 的 MLA(Multi-head Latent Attention)——把 KV 压缩到低秩潜变量,推理时只缓存潜变量,进一步压缩 KV Cache;以及在 GQA 基础上加 QK-RMSNorm(如 Qwen3)提升训练稳定性。思路一脉相承:在“每个头独立”和“全部共享”之间找最优点。

3.3 MLA

GQA 把 KV 按头分组共享,缓存量仍是 $O(L \cdot n_h \cdot d_h)$——头数一多,KV Cache 仍是主要开销。MLA(Multi-head Latent Attention,DeepSeek-V2 提出、V3 沿用)则从另一个维度入手:不共享,而是把 KV 联合投影到一个低维潜变量,推理时只缓存这个潜变量。

7.png

图 3.3 DeepSeek-V2 架构与 MLA 细节(DeepSeek-V2 论文 Figure 2)。注意 “Cached During Inference” 标记的只有潜变量 $c_t^{KV}$ 和解耦的 $k_t^R$。

给定第 $t$ 个 token 的输入 $h_t \in \mathbb{R}^d$,MLA 的四步:

\[c_t^{KV} = W^{DKV} h_t, \qquad c_t^{KV} \in \mathbb{R}^{d_c}, \quad d_c \ll d_h n_h \tag{3.1}\] \[k_t^C = W^{UK} c_t^{KV}, \qquad v_t^C = W^{UV} c_t^{KV} \tag{3.2}\] \[k_t^R = \mathrm{RoPE}(W^{KR} h_t) \tag{3.3}\] \[k_{t,i} = [\,k_{t,i}^C \,;\, k_t^R\,], \qquad q_{t,i} = [\,q_{t,i}^C \,;\, q_{t,i}^R\,] \tag{3.4}\]

其中 $W^{DKV} \in \mathbb{R}^{d_c \times d}$ 是下投影,$W^{UK}, W^{UV} \in \mathbb{R}^{d_h n_h \times d_c}$ 是上投影。

为什么必须“解耦 RoPE”:RoPE 要作用在 K 上,但 K 是从潜变量解压出来的——一旦把旋转乘进 $W^{UK}$,就会破坏“潜变量不含位置信息”这一前提,位置信息会污染内容压缩。DeepSeek 的解法是另开一路共享的 $k^R$ 专门携带位置信息,内容部分 $k^C$ 保持“无位置”,两者拼接后再算注意力。这样压缩与解耦互不干扰。

矩阵吸收:推理时无需显式还原 $k^C$。因为

\[QK^T = (h W^Q)(W^{UK} c^{KV})^T = h \underbrace{(W^Q (W^{UK})^T)}_{\text{可预折叠}} (c^{KV})^T \tag{3.5}\]

把 $W^Q (W^{UK})^T$ 预计算成一个矩阵,就可以在潜变量上直接算注意力分数,$K$ 从头到尾不被物化。同理 $W^{UV}$ 可以吸收进输出投影。

缓存账本(每层每 token):

表 3.3 MHA、GQA 与 MLA 的每层每 token 缓存量对比

方案 缓存元素 相对 MHA
MHA $2 \cdot d_h n_h$ 基准
GQA $2 \cdot d_h n_g$ 随 KV 头数线性下降
MLA $d_c + d_h^R$ 约 1/20

DeepSeek-V2 取 $d_c = 512$、$d_{model} = 5120$,报告 KV Cache 相比 MHA 减少 93.3%。

import math
import torch
import torch.nn as nn


class MultiHeadLatentAttention(nn.Module):
    """MLA (DeepSeek-V2/V3):KV 低秩联合压缩,推理时只缓存潜变量 c^{KV}。"""

    def __init__(self, d_model, n_heads, d_head, kv_lora_rank=512, rope_head_dim=64,
                 q_lora_rank=0, dtype=None, bias=False):
        super().__init__()
        self.n_heads, self.d_head = n_heads, d_head
        self.kv_lora_rank, self.q_lora_rank = kv_lora_rank, q_lora_rank
        self.rope_head_dim = rope_head_dim
        d_nope = d_head - rope_head_dim          # 不带位置信息的部分

        # 1) KV 联合下投影:d_model -> kv_lora_rank(推理时唯一缓存的东西)
        self.wkv = nn.Linear(d_model, kv_lora_rank, bias=False, dtype=dtype)
        # 2) 解压回各头
        self.wuk = nn.Linear(kv_lora_rank, n_heads * d_nope, bias=False, dtype=dtype)
        self.wuv = nn.Linear(kv_lora_rank, n_heads * d_head, bias=False, dtype=dtype)
        # 3) 解耦 RoPE 分支:k^R 全头共享,q^R 每头独立
        self.wkr = nn.Linear(d_model, rope_head_dim, bias=False, dtype=dtype)
        self.wqr = nn.Linear(d_model, n_heads * rope_head_dim, bias=False, dtype=dtype)
        # 4) Query 内容部分(可用低秩压缩省训练显存)
        if q_lora_rank > 0:
            self.wq_a = nn.Linear(d_model, q_lora_rank, bias=False, dtype=dtype)
            self.wq_b = nn.Linear(q_lora_rank, n_heads * d_nope, bias=False, dtype=dtype)
        else:
            self.wq_a = nn.Linear(d_model, n_heads * d_nope, bias=False, dtype=dtype)
        self.wo = nn.Linear(n_heads * d_head, d_model, bias=False, dtype=dtype)

    def forward(self, x, cos, sin, cache=None):
        B, L, _ = x.shape
        h, dh, rd = self.n_heads, self.d_head, self.rope_head_dim
        d_nope = dh - rd

        # --- 压缩:整个网络里只有这一步的输出需要进 KV Cache ---
        c_kv = self.wkv(x)                                   # (B, L, kv_lora_rank)
        if cache is not None:
            c_kv = torch.cat([cache[0], c_kv], dim=1)         # 追加新 token
        next_cache = (c_kv,)

        # --- 解压 ---
        k_c = self.wuk(c_kv).view(B, -1, h, d_nope).transpose(1, 2)   # (B,h,L,d_nope)
        v = self.wuv(c_kv).view(B, -1, h, dh).transpose(1, 2)        # (B,h,L,d_head)

        # --- Query ---
        if self.q_lora_rank > 0:
            q_c = self.wq_b(self.wq_a(x)).view(B, L, h, d_nope).transpose(1, 2)
        else:
            q_c = self.wq_a(x).view(B, L, h, d_nope).transpose(1, 2)
        q_r = self.wqr(x).view(B, L, h, rd).transpose(1, 2)           # (B,h,L,rd)

        # --- 解耦 RoPE:只作用在 k^R / q^R 上 ---
        k_r = apply_rope(self.wkr(x).view(B, L, 1, rd).transpose(1, 2), cos, sin)
        q_r = apply_rope(q_r, cos, sin)

        # --- 拼接内容与位置 ---
        q = torch.cat([q_c, q_r], dim=-1)                   # (B,h,L,d_head)
        k = torch.cat([k_c, k_r.expand(-1, h, -1, -1)], dim=-1)   # (B,h,L,d_head)

        scores = (q @ k.transpose(-2, -1)) / math.sqrt(dh)
        out = torch.softmax(scores, dim=-1) @ v
        out = out.transpose(1, 2).reshape(B, L, h * dh)
        return self.wo(out), next_cache

MLA 把“共享 KV”换成了“压缩 KV”,本质是用训练期算力换推理期显存。代价是训练时多了一次投影,且质量依赖低秩瓶颈的容量——所以 DeepSeek 把 $d_c$ 设得并不激进(512),并用解耦 RoPE 把位置信息的自由度独立出来。

3.4 SWA

SWA(Sliding Window Attention,滑动窗口注意力)是最早也最直觉的稀疏化尝试:每个 token 只看最近 $w$ 个 token,而不是全部历史。

8.png

图 3.4 Vanilla Attention 与 Sliding Window Attention 的掩码对比,以及“有效上下文长度”(Mistral 7B 论文 Figure 1)。

形式化地,注意力只在窗口内计算:

\[M_{sw,ij} = \begin{cases} 0, & j \le i \ \text{且}\ i - j < w \\ -\infty, & \text{其他} \end{cases}, \qquad \text{Attention} = \mathrm{softmax}\!\left(\frac{QK^T}{\sqrt{d_k}} + M_{sw}\right) V \tag{3.6}\]

复杂度从 $O(n^2)$ 降到 $O(n \cdot w)$,当 $w \ll n$ 时近似线性;KV Cache 也从 $O(n)$ 降到 $O(w)$。

Mistral 7B 是第一个大规模验证 SWA 的开源模型,窗口 $w = 4096$,并配套了滚动缓冲缓存(Rolling Buffer Cache):缓存固定大小 $W=4$ 的环形缓冲,位置超出后旧的 K/V 被覆盖,在 32k 序列上把缓存显存降到 1/8。代价是跨窗口的信息只能靠层数逐层传递——SWA 层看不见远处,远处的信息必须靠中间层搬运。

后续演化:

  • Gemma 2:全局注意力与 SWA 交替(1:1),窗口 4096
  • Gemma 3:改成 5:1(每 5 层滑窗 + 1 层全局),窗口从 4096 缩到 1024
\[\text{Memory}_{\text{Gemma3}} = \frac{5 \cdot 1024 + 1 \cdot L}{1 \cdot 4096 + 1 \cdot L} \approx 0.26 \quad (L = 32K) \tag{3.7}\]

消融显示每 6 层放一个全局层就足以捕捉长程依赖,困惑度几乎不掉。

  • gpt-oss 在交替层中使用;小米 MiMo-V2-Flash 用 $w=128$ 的极端窗口
  • Mistral 3.1 Small 却放弃了 SWA:统一注意力模式对 FlashAttention-3 这类优化 kernel 更友好——这是“算法最优 vs 硬件最优”的经典权衡
import torch
import torch.nn as nn


class SlidingWindowAttention(nn.Module):
    """SWA:每个 query 只看最近 window_size 个 key。"""

    def __init__(self, d_model, n_heads, window_size=4096, dtype=None):
        super().__init__()
        self.n_heads = n_heads
        self.d_head = d_model // n_heads
        self.window = window_size
        self.wq = nn.Linear(d_model, d_model, bias=False, dtype=dtype)
        self.wk = nn.Linear(d_model, d_model, bias=False, dtype=dtype)
        self.wv = nn.Linear(d_model, d_model, bias=False, dtype=dtype)
        self.wo = nn.Linear(d_model, d_model, bias=False, dtype=dtype)

    def forward(self, x, cache=None):
        B, L, _ = x.shape
        h, dh = self.n_heads, self.d_head
        q = self.wq(x).view(B, L, h, dh).transpose(1, 2)
        k = self.wk(x).view(B, L, h, dh).transpose(1, 2)
        v = self.wv(x).view(B, L, h, dh).transpose(1, 2)

        if cache is not None:
            k = torch.cat([cache[0], k], dim=2)      # (B,h,L_total,dh)
            v = torch.cat([cache[1], v], dim=2)
            start = cache[0].shape[2]
        else:
            start = 0
        next_cache = (k, v)

        scores = (q @ k.transpose(-2, -1)) / dh**0.5   # (B,h,L,L_total)
        # 滑动窗口掩码:只允许看到 [i-window+1, i]
        qpos = torch.arange(start, start + L, device=x.device).view(-1, 1)
        kpos = torch.arange(0, k.shape[2], device=x.device).view(1, -1)
        mask = (kpos <= qpos) & (kpos > qpos - self.window)
        scores = scores.masked_fill(~mask, float("-inf"))

        out = torch.softmax(scores, dim=-1) @ v
        out = out.transpose(1, 2).reshape(B, L, -1)
        return self.wo(out), next_cache

SWA 是“纯局部”的稀疏化,靠层数堆叠换取长程能力。它给后世稀疏注意力留下的最重要遗产是滑动窗口几乎成了标配——NSA、DSA、CSA、QSA 全都保留了滑动窗口分支。

3.5 Sparse Attention

如果说 FlashAttention 是“把同样的数学算得更快”,那稀疏注意力就是“少算一部分”。

两者的区别是根本性的:

表 3.4 FlashAttention 与稀疏注意力的技术路线对比

  FlashAttention 稀疏注意力
交互次数 全部 $n^2$ 对,一个不少 只保留 $n \cdot k$ 对
$n \times n$ 矩阵 不物化到 HBM,但仍然要算 根本不构造大部分
精确性 数值上完全等价 近似,有质量损失
省的是 显存带宽 算力 + 带宽 + KV 读取量

稀疏注意力的一般框架是:用一个廉价的选择器(indexer / scorer)判断“哪些历史 token 值得看”,然后只在这些 token 上跑精确注意力。难点从来不是“怎么少看”,而是“怎么在不看错的前提下少看”——选择器一旦漏掉关键信息,模型就会退化。

2025 年之后,这一分支密集产出了 DeepSeek 的 NSA / DSA / V4 的 CSA-HCA / V4.1 的 CSA2、Qwen 的 QSA、MiniMax 的 MSA、美团 LongCat 的 LSA 等工作。它们在三个维度上分化:

  1. 选择粒度:token 级(NSA / DSA)→ block 级(MSA / CSA)→ micro-block 级(QSA)
  2. 选择器是否可训练:原生端到端训练(NSA)→ 用 KL 对齐蒸馏(MSA)→ 轻量索引器(QSA)
  3. 是否保留全局分支:几乎都保留——滑窗 + 稀疏 + 全局,三者叠加

2026 年的 CSA2 与 LSA 又开辟了第四个维度:沿层维度摊销索引与 KV——一次压缩、多层共享,一次选择、多层复用。

3.5.1 Native Sparse Attention

NSA(Native Sparse Attention,DeepSeek 与北大张铭团队合作,ACL 2025 最佳论文)要解决的是稀疏注意力最突出的现实问题:理论上的复杂度优势,历史上一直换不成实际加速——选择器本身的开销、GPU 访存的不规则性、以及“选择不可导导致没法端到端训练”,让稀疏注意力长期停留在论文里。

9.png

图 3.5 NSA 架构总览(NSA 论文 Figure 2)。三条并行分支——压缩注意力、选择注意力、滑动注意力——最后经 Gated Output 融合。

NSA 的解法是“硬件对齐 + 原生可训练”两件事一起做。

三条并行分支:

  1. 压缩注意力(Compressed):把连续的 token 分组压缩成代表 token,用低维向量近似整段历史,提供粗粒度的全局模式
  2. 选择注意力(Selected):由可训练的 indexer 对每个 query 打分,选出 top-k 个重要块,提供细粒度的精确检索
  3. 滑动注意力(Sliding):保留最近 $w$ 个 token 的原始 KV,提供局部细节

三条分支各自把原始 KV 重映射为更紧凑的表示。压缩分支把连续 $l$ 个 token 的块用可学习 MLP $\varphi$(带块内位置编码)压成一个压缩 key($d$ 为相邻块步幅):

\[\tilde{K}^{cmp}_t = f^K_{cmp}(k_{:t}) = \left\{\varphi\big(k_{id+1:id+l}\big) \;\middle|\; 0 \le i \le \left\lfloor\frac{t-l}{d}\right\rfloor\right\} \tag{3.8}\]

选择分支先由压缩分支的注意力分数导出块重要性(压缩块与选择块同尺寸时 $p^{slc}_t = p^{cmp}_t$ 直接复用),再按排名取 top-$n$ 块拼接出选中 KV:

\[p^{cmp}_t = \mathrm{Softmax}\!\left(q_t^\top \tilde{K}^{cmp}_t\right), \qquad I_t = \left\{i \;\middle|\; \mathrm{rank}\big(p^{slc}_t[i]\big) \le n\right\} \tag{3.9}\] \[\tilde{K}^{slc}_t = \mathrm{Cat}\Big[\big\{k_{il'+1:(i+1)l'} \;\big|\; i \in I_t\big\}\Big] \tag{3.10}\]

滑窗分支直接保留最近 $w$ 个 token 的原始 KV:

\[\tilde{K}^{win}_t = k_{t-w:t}, \qquad \tilde{V}^{win}_t = v_{t-w:t} \tag{3.11}\]

门控融合:三条分支各自产生 $o_c, o_s, o_w$,由 query 决定门控权重:

\[o = g_c \odot o_c + g_s \odot o_s + g_w \odot o_w \tag{3.12}\]

这让模型自己学“当前这层该侧重哪种信息”,而不是固定比例。

硬件对齐:NSA 的三个分支共享同一套 tile 布局,并配套专门的 CUDA kernel,把选择器的开销压到可忽略——这正是“硬件对齐”四个字的含义:算法设计时就考虑好了 kernel 怎么写。

效果上,在 64k 序列长度下,相比 FlashAttention:前向最高 9× 加速、反向 6× 加速,且呈“越长越优”趋势;在通用基准、长文本任务与指令推理上均持平或超越全注意力模型。

import torch
import torch.nn as nn


class NativeSparseAttention(nn.Module):
    """
    NSA 的三分支骨架(省略 tile 级 kernel 优化)。
    三条分支共享同一套 Q/K/V 投影,这是硬件对齐的关键。
    """

    def __init__(self, d_model, n_heads, n_groups=16, n_selected=16, window_size=512,
                 compressor_size=64, dtype=None):
        super().__init__()
        self.h = n_heads
        self.dh = d_model // n_heads
        self.g = n_groups            # 压缩后的代表 token 数
        self.k_sel = n_selected      # 选择分支保留的块数
        self.w = window_size
        self.dc = compressor_size    # 压缩分支维度

        self.wq = nn.Linear(d_model, d_model, bias=False, dtype=dtype)
        self.wk = nn.Linear(d_model, d_model, bias=False, dtype=dtype)
        self.wv = nn.Linear(d_model, d_model, bias=False, dtype=dtype)
        # 压缩分支:把 token 分组聚合成 g 个代表
        self.compressor = nn.Linear(d_model, self.dc, bias=False, dtype=dtype)
        # 选择分支的 indexer(可训练)
        self.indexer = nn.Linear(d_model, d_model, bias=False, dtype=dtype)
        self.gate = nn.Linear(d_model, 3 * self.h, bias=False, dtype=dtype)  # 三分支门控
        self.wo = nn.Linear(d_model, d_model, bias=False, dtype=dtype)

    def forward(self, x):
        B, L, _ = x.shape
        h, dh = self.h, self.dh
        q = self.wq(x).view(B, L, h, dh).transpose(1, 2)
        k = self.wk(x).view(B, L, h, dh).transpose(1, 2)
        v = self.wv(x).view(B, L, h, dh).transpose(1, 2)

        # ---- 分支 1:压缩注意力(粗粒度全局)----
        groups = L // self.g if L >= self.g else 1
        pooled = x.view(B, groups, -1).mean(dim=2)                 # (B, G, d)
        c = self.compressor(pooled)                                  # (B, G, dc)
        ck = c.unsqueeze(2).expand(-1, -1, h, -1).reshape(B, groups, h, self.dc).transpose(1, 2)
        cv = ck
        o_c = torch.softmax((q @ ck.transpose(-2, -1)) / self.dc**0.5, dim=-1) @ cv

        # ---- 分支 2:选择注意力(细粒度检索,indexer 打分 + top-k)----
        idx_scores = (self.indexer(q) @ self.indexer(k).transpose(-2, -1)) / self.dh**0.5
        idx_scores = idx_scores.masked_fill(torch.ones_like(idx_scores, dtype=torch.bool)
                                            .triu(1), float("-inf"))
        _, topk = torch.topk(idx_scores, k=min(self.k_sel, L), dim=-1)     # (B,h,L,k)
        sel_k = torch.gather(k, 2, topk.unsqueeze(-1).expand(-1, -1, -1, dh))
        sel_v = torch.gather(v, 2, topk.unsqueeze(-1).expand(-1, -1, -1, dh))
        o_s = torch.softmax((q @ sel_k.transpose(-2, -1)) / dh**0.5, dim=-1) @ sel_v

        # ---- 分支 3:滑动注意力(局部细节)----
        qpos = torch.arange(L, device=x.device).view(-1, 1)
        kpos = torch.arange(L, device=x.device).view(1, -1)
        win = (kpos <= qpos) & (kpos > qpos - self.w)
        sc = (q @ k.transpose(-2, -1)) / dh**0.5
        sc = sc.masked_fill(~win, float("-inf"))
        o_w = torch.softmax(sc, dim=-1) @ v

        # ---- 门控融合 ----
        g = self.gate(x).view(B, L, 3, h).permute(2, 0, 3, 1).unsqueeze(-1)  # (3,B,h,L,1)
        g = torch.sigmoid(g)
        o = g[0] * o_c + g[1] * o_s + g[2] * o_w
        o = o.transpose(1, 2).reshape(B, L, -1)
        return self.wo(o)

NSA 的真正贡献不是“稀疏”本身,而是证明了稀疏注意力可以同时做到原生可训练和硬件对齐。论文发表后已被 DeepSeek、华为、字节等多家团队实现或复现。

3.5.2 Deepseek Sparse Attention

DSA(DeepSeek Sparse Attention)是 NSA 的工程化继任者,搭载在 2025 年 9 月发布的 DeepSeek-V3.2-Exp 上——这是稀疏注意力机制第一次真正进入开源生产模型(业界原本普遍预期 V4 会采用 NSA,结果 DSA 抢先登场)。

DSA 的核心是两个新组件:

闪电索引器(Lightning Indexer):一个轻量模块,为当前 query 与每个候选历史 token 计算相关性分数。关键是它足够便宜——不进入主注意力路径,仅作一次粗略筛选。

Top-k 选择器:按分数取 top-$k$ 个 token(默认 2048)做精细注意力,其余全部跳过。

于是核心注意力复杂度从 $O(L^2)$ 降到 $O(L \cdot k)$。 两个组件的形式化定义如下。闪电索引器对每个候选历史 token $s$ 计算相关性分数:

\[I_{t,s} = \sum_{j=1}^{n_h} w^I_{t,j} \cdot \mathrm{ReLU}\!\left(q^I_{t,j} \cdot k^I_s\right) \tag{3.13}\]

其中 $q^I_{t,j}$、$k^I_s$ 是对隐状态的低秩投影,$w^I_{t,j} \in \mathbb{R}$ 为第 $j$ 个索引头的可学习权重。Top-k 稀疏注意力只对分数最高的 $k$ 个条目做核心注意力:

\[u_t = \mathrm{Attn}\!\left(h_t,\ \{c_s \mid I_{t,s} \in \mathrm{Top\text{-}k}(I_{t,:})\}\right) \tag{3.14}\]

其中 $c_s$ 是 MLA 的压缩潜变量 KV 条目,$h_t$ 为查询隐状态——选中的正是 MLA 条目,因此 DSA 与 MLA 天然可组合。

关键设计是 DSA 叠加在 MLA 之上而不是替代 MLA:索引器打分用的是各层隐状态的小投影,选中的正是 MLA 的压缩潜变量 KV 条目,再走标准 MLA 注意力路径。两者天然可组合——MLA 让每个条目变小,DSA 让触碰的条目变少。

训练采用三阶段:先用短期的“密集预热”让索引器学会模仿原模型的注意力分布,再进入“稀疏训练”阶段让整个模型适应新稀疏模式,最后沿用与前代完全相同的后训练流程(专家蒸馏 + GRPO)。这种“只改一处、其余全部对齐”的实验设计,让性能差异可以干净地归因到注意力机制本身。

DeepSeek 同时开源了 TileLang 和 CUDA 两版算子,并建议研究性实验优先用 TileLang 版本以便调试。结果上,V3.2-Exp 在与 V3.1-Terminus 严格对齐的设置下,各公开基准表现基本持平,同时把 API 价格下调了 50% 以上。

代码实现(闪电索引器 + Top-k 稀疏注意力的简化骨架):

import torch
import torch.nn as nn
import torch.nn.functional as F


class DeepSeekSparseAttention(nn.Module):
    """DSA:闪电索引器打分 + Top-k 稀疏注意力(叠加在 MLA 之上,此处以共享 KV 示意)。"""

    def __init__(self, d_model, n_heads, k=2048, idx_dim=64, n_idx_heads=4):
        super().__init__()
        self.n_heads = n_heads
        self.head_dim = d_model // n_heads
        self.k = k
        self.n_idx_heads = n_idx_heads

        self.wq = nn.Linear(d_model, d_model, bias=False)
        self.w_kv = nn.Linear(d_model, self.head_dim, bias=False)  # 压缩条目,K=V 共享
        self.wo = nn.Linear(d_model, d_model, bias=False)
        # 闪电索引器:低秩 q/k 投影 + 逐头权重
        self.w_iq = nn.Linear(d_model, idx_dim * n_idx_heads, bias=False)
        self.w_ik = nn.Linear(d_model, idx_dim, bias=False)
        self.w_w = nn.Linear(d_model, n_idx_heads, bias=False)

    def forward(self, x):
        B, L, _ = x.shape
        h = self.head_dim
        q = self.wq(x).view(B, L, self.n_heads, h).transpose(1, 2)   # (B,H,L,h)
        c = self.w_kv(x)                                             # (B,L,h)

        # ---- 闪电索引器:ReLU 打分 + 逐头加权求和 ----
        q_i = self.w_iq(x).view(B, L, self.n_idx_heads, -1)           # (B,L,J,d)
        k_i = self.w_ik(x)                                           # (B,L,d)
        s = F.relu(torch.einsum("bljd,bmd->bljm", q_i, k_i))
        w = self.w_w(x)                                              # (B,L,J)
        idx = (w.unsqueeze(2) * s).sum(-1)                           # (B,L,L)
        idx = idx.masked_fill(torch.ones(L, L, dtype=torch.bool,
                                      device=x.device).triu(1), float("-inf"))

        # ---- Top-k 稀疏注意力(选中条目同时充当 K 与 V)----
        topk = idx.topk(min(self.k, L), dim=-1).indices              # (B,L,k)
        c_sel = torch.gather(c.unsqueeze(1).expand(-1, L, -1, -1), 2,
                             topk.unsqueeze(-1).expand(-1, -1, -1, h))
        sc = torch.einsum("bhld,blmd->bhlm", q, c_sel) / h ** 0.5
        o = torch.einsum("bhlm,blmd->bhld", torch.softmax(sc, dim=-1), c_sel)
        return self.wo(o.transpose(1, 2).reshape(B, L, -1))

3.5.3 CSA、HCA 与 CSA2

2026 年 4 月的 DeepSeek-V4 把稀疏化推到了新维度:不再压缩“头”,而是压缩“序列”。MLA 是沿头维度压缩 KV,而 V4 的 Compressed Attention 沿序列维度压缩——后者才是百万上下文下 KV 显存爆炸的真正来源(增长的主导因素是序列长度,不是头数)。

10.png

图 3.6 DeepSeek-V4 整体架构(DeepSeek-V4 技术报告 Figure 2)。Transformer Block 内是 CSA / HCA 交替,残差部分换成了 mHC 的 Pre-Block / Post-Block Mixing。 11.png

图 3.7 CSA 核心架构(DeepSeek-V4 技术报告 Figure 3):Token-Level Compressor 做门控池化压缩,Lightning Indexer 打分、Top-k Selector 筛选后走 Shared-KV MQA 核心注意力。

CSA(Compressed Sparse Attention) 是细粒度的“精读”路径,流水线是“压缩 → 索引选择 → 核心注意力”:

  1. Token 级压缩器:把连续 $m$ 个 token(DeepSeek-V4 为 4)的 K/V 用 softmax 门控池化 + 可学习位置偏置合并成 1 个 KV 条目,且相邻分组重叠 $m$ 个 token,避免硬边界切断相邻信息
  2. 闪电索引器(FP4 实现):给压缩块打分时用 ReLU 而非 Softmax——它只关心“哪些块足够相关”,不需要概率分布
  3. Top-1024 选择:选出最相关的 1024 个压缩块(对应原始 4096 个 token)做完整注意力
  4. 滑动窗口:额外保留最近 $n_{win}$ 个未压缩的原始 KV,保证“刚才说过的话”依然清晰

CSA 的关键公式如下(记号沿用 V4 报告:$H \in \mathbb{R}^{n \times d}$ 为隐状态序列,$C_a, C_b$ 为两组 KV 条目,$Z_a, Z_b$ 为压缩权重,$m$ 为压缩率)。两组 KV 条目与压缩权重由低秩投影得到:

\[C_a = H \cdot W^{KV}_a,\ \ C_b = H \cdot W^{KV}_b, \qquad Z_a = H \cdot W^{Z}_a,\ \ Z_b = H \cdot W^{Z}_b \tag{3.15}\]

相邻分组重叠的门控池化压缩($B_a, B_b \in \mathbb{R}^{m \times c}$ 为可学习位置偏置,$\mathrm{Softmax}_{row}$ 沿行归一化,$\odot$ 为逐元素乘):

\[S = \mathrm{Softmax}_{row}\big([Z_a + B_a;\ Z_b + B_b]\big), \qquad C^{Comp}_i = \sum_{j} S_{a,j} \odot C_{a,j} + \sum_{j} S_{b,j} \odot C_{b,j} \tag{3.16}\]

闪电索引器用 ReLU 打分(而非 Softmax)并做 Top-k 筛选,选中条目进入核心注意力:

\[I_{t,s} = \sum_{h} w^I_{t,h} \cdot \mathrm{ReLU}\!\left(q^I_{t,h} \cdot K^{IComp}_s\right), \qquad C^{SprsComp}_t = \big\{C^{Comp}_s \mid I_{t,s} \in \mathrm{Top\text{-}k}(I_{t,:})\big\} \tag{3.17}\]

最后对选中条目做共享 KV 的 MQA核心注意力(查询由共享的压缩向量 $c^Q_t = h_t \cdot W_{DQ}$ 上投影得到,压缩条目同时充当 key 与 value):

\[o_{t,i} = \mathrm{CoreAttn}\!\left(q_{t,i},\ C^{SprsComp}_t,\ C^{SprsComp}_t\right) \tag{3.18}\]

HCA(Heavily Compressed Attention) 是粗粒度的“泛读”路径:

  1. 把连续 $m’$ 个 token(128 倍压缩)合并成 1 个 KV 条目,作为整个序列的全局摘要
  2. 不做稀疏筛选,直接对所有压缩条目做稠密注意力

HCA 不做筛选的原因在于成本结构:128 倍压缩后条目数已经少到全量注意力完全算得动(1M token → 约 8000 条),而全量注意力天然不会漏掉重要信息——这正是稀疏选择器最大的风险点。

HCA 的公式更简单——压缩率 $m’ \gg m$、不重叠、无选择器,直接对全部压缩条目做稠密注意力:

\[S = \mathrm{Softmax}_{row}(Z + B), \qquad C^{Comp}_i = \sum_{j=m\'i}^{m\'(i+1)-1} S_j \odot C_j \tag{3.19}\]

其中 $C = H \cdot W^{KV}$、$Z = H \cdot W^{Z}$,$B \in \mathbb{R}^{m’ \times c}$ 为位置偏置;核心注意力与 CSA 相同,只是对象换成了全部压缩条目:

\[o_{t,i} = \mathrm{CoreAttn}\!\left(q_{t,i},\ C^{Comp},\ C^{Comp}\right) \tag{3.20}\]

层间堆叠策略(V4-Pro 的 61 层):

表 3.5 DeepSeek-V4-Pro 的层间注意力堆叠策略

层 注意力类型 意图
0 — 1 仅 HCA 极廉价的全局摘要
2 — 60 CSA / HCA 交替 局部精细 + 全局骨架兼顾
最后一层 MTP 仅滑动窗口 最高精度输出

12.png

图 3.8 HCA 核心架构(DeepSeek-V4 技术报告 Figure 4)。重度压缩的 KV 条目 + 滑动窗口,没有 Top-k 选择器。

效果:DeepSeek-V4-Pro 的单 token 推理 FLOPs 仅为 V3.2 的 27%、KV Cache 为 10%;V4-Flash 更低至 10% / 7%。相对标准 GQA-8 基线,KV Cache 约为其 2%。

代码实现(CSA 的“压缩 → 索引 → 选择 → 注意力”四步,对应论文公式 9—19):

import torch
import torch.nn as nn
import torch.nn.functional as F


class TokenLevelCompressor(nn.Module):
    """CSA 的 token 级压缩器:两路重叠分组 + softmax 门控池化。

    关键:C^a 覆盖 [m*i, m*(i+1)),C^b 覆盖 [m*(i-1), m*i),两者重叠 m 个 token,
    使相邻压缩条目在边界处信息连续(硬边界不切断语义)。
    """

    def __init__(self, d_model, c, m):
        super().__init__()
        self.m = m
        self.c = c
        self.w_kv_a = nn.Linear(d_model, c, bias=False)
        self.w_kv_b = nn.Linear(d_model, c, bias=False)
        self.w_z_a = nn.Linear(d_model, c, bias=False)
        self.w_z_b = nn.Linear(d_model, c, bias=False)
        # 可学习位置偏置(每个组内位置一个)
        self.bias_a = nn.Parameter(torch.zeros(m, c))
        self.bias_b = nn.Parameter(torch.zeros(m, c))

    def forward(self, h):
        # h: (B, L, d) -> c: (B, n_blk, c)
        B, L, d = h.shape
        m = self.m
        n_blk = L // m

        ca = self.w_kv_a(h).view(B, n_blk, m, self.c)     # (B,n,m,c) 无重叠
        cb = self.w_kv_b(h).view(B, n_blk, m, self.c)     # (B,n,m,c) 同上
        za = self.w_z_a(h).view(B, n_blk, m, self.c) + self.bias_a
        zb = self.w_z_b(h).view(B, n_blk, m, self.c) + self.bias_b

        # 简化示意:ca 与 cb 各自做组内 softmax 门控池化(真实实现中 cb 右移 m 个 token 形成重叠)
        sa = torch.softmax(za, dim=2)                     # Softmax_row
        sb = torch.softmax(zb, dim=2)
        comp = (sa * ca).sum(2) + (sb * cb).sum(2)        # (B, n_blk, c)
        return comp


class LightningIndexer(nn.Module):
    """闪电索引器:低秩 query + ReLU 评分 + head 加权求和(公式 13–16)。"""

    def __init__(self, d_model, d_c, c_i, n_head):
        super().__init__()
        self.n_head = n_head
        self.c_i = c_i
        self.w_dq = nn.Linear(d_model, d_c, bias=False)     # 降维
        self.w_iuq = nn.Linear(d_c, n_head * c_i, bias=False)  # 升维切头
        self.w_w = nn.Linear(d_model, n_head, bias=False)    # head 权重

    def forward(self, h, k_comp):
        # h: (B, L, d) query 隐状态;k_comp: (B, n_blk, c_i) 压缩索引键
        B, L, d = h.shape
        c_q = self.w_dq(h)                                  # (B, L, d_c)
        q_i = self.w_iuq(c_q).view(B, L, self.n_head, self.c_i)  # (B,L,h,c_i)
        w_i = self.w_w(h)                                    # (B, L, h)

        # 索引分数:I = Σ_h w_h · ReLU(q_h · k_h^T),只看已完整观察的块(block-causal)
        logits = F.relu(torch.einsum("blhc,bnc->blnh", q_i, k_comp))  # (B,L,n,h)
        scores = (w_i.unsqueeze(2) * logits).sum(-1)          # (B, L, n_blk)
        return scores


class CompressedSparseAttention(nn.Module):
    """CSA 主体:压缩 → 索引 → Top-k 选择 → Shared-KV MQA → 分组输出投影。"""

    def __init__(self, d_model, c, m=4, k=1024, n_head=8, n_idx_head=4):
        super().__init__()
        self.k = k
        self.compressor = TokenLevelCompressor(d_model, c, m)
        self.indexer = LightningIndexer(d_model, d_c=c // 2, c_i=c // 2, n_head=n_idx_head)
        self.w_dq = nn.Linear(d_model, c, bias=False)
        self.w_uq = nn.Linear(c, n_head * c, bias=False)
        self.n_head = n_head
        self.norm_q = nn.RMSNorm(c)
        self.norm_kv = nn.RMSNorm(c)
        self.out_proj = nn.Linear(n_head * c, d_model, bias=False)

    def forward(self, h, sliding_kv=None):
        B, L, d = h.shape
        c_comp = self.compressor(h)                          # (B, n_blk, c) 既当 K 又当 V

        # 1) 索引打分(压缩版索引键)
        scores = self.indexer(h, c_comp)                     # (B, L, n_blk)

        # 2) Top-k 选择(block-causal:只允许选已完整观察的块)
        n_blk = c_comp.size(1)
        causal = torch.tril(torch.ones(L, n_blk, device=h.device, dtype=torch.bool),
                            diagonal=0)                       # p_b+m-1 <= i
        scores = scores.masked_fill(~causal, float("-inf"))
        topk_idx = scores.topk(min(self.k, n_blk), dim=-1).indices  # (B, L, k)

        # 3) Shared-KV MQA:压缩条目同时是 key 与 value
        c_q = self.w_dq(h)
        q = self.w_uq(c_q).view(B, L, self.n_head, -1)       # (B,L,nh,c)
        q = self.norm_q(q)
        kv = self.norm_kv(c_comp)                             # (B, n_blk, c)

        # 按 topk 索引收集(示意:真实实现用 gather + FlashAttention 变体)
        sel = torch.gather(kv, 1, topk_idx.unsqueeze(-1).expand(-1, -1, -1, kv.size(-1)))
        # 4) 核心注意力:q 与选中的压缩 KV 条目
        attn = torch.einsum("blhc,blkc->blhk", q, sel)
        o = torch.softmax(attn, dim=-1)

        # 5) 分组输出投影(n_head 很大时分组降维,节省开销)
        o = self.out_proj(o.reshape(B, L, -1))
        return o


class HeavilyCompressedAttention(nn.Module):
    """HCA:128× 重压缩 + 稠密注意力(无 Top-k),对应公式 20–26。"""

    def __init__(self, d_model, c, m_prime=128, n_head=8):
        super().__init__()
        self.m_prime = m_prime
        self.w_kv = nn.Linear(d_model, c, bias=False)
        self.w_z = nn.Linear(d_model, c, bias=False)
        self.bias = nn.Parameter(torch.zeros(m_prime, c))
        self.w_dq = nn.Linear(d_model, c, bias=False)
        self.w_uq = nn.Linear(c, n_head * c, bias=False)
        self.n_head = n_head
        self.out_proj = nn.Linear(n_head * c, d_model, bias=False)

    def forward(self, h):
        B, L, d = h.shape
        mp = self.m_prime
        n_blk = L // mp
        C = self.w_kv(h).view(B, n_blk, mp, -1)              # (B,n,mp,c)
        Z = self.w_z(h).view(B, n_blk, mp, -1) + self.bias
        S = torch.softmax(Z, dim=2)                           # Softmax_row
        comp = (S * C).sum(2)                                 # (B, n, c) 全局摘要

        q = self.w_uq(self.w_dq(h)).view(B, L, self.n_head, -1)
        # 稠密注意力:对所有压缩条目全量计算(条目少,天然不用稀疏筛选)
        attn = torch.einsum("blhc,bnc->blnh", q, comp)
        o = torch.softmax(attn, dim=-2)
        return self.out_proj(o.reshape(B, L, -1))

要点回顾:压缩器负责把 KV 条目降到 1/m,索引器负责只挑相关条目,MQA 负责共享 KV 减少显存——三者分工明确,缺一不可。HCA 则直接放弃稀疏筛选,用“足够粗的压缩 + 全量注意力”换稳定性。

CSA2(Compressed Sparse Attention 2)。V4.1-Flash(arXiv:2609.19969)把 KV 压缩推进到第三个维度——层维度。条目大小与序列长度两个维度已经被充分挖掘,而相邻层选中的 Top-K 块高度重合、主 KV 又压缩自同一份隐藏状态,每层各存一份、各算一遍的冗余由此显形。CSA2 的答案是跨层复用:多层共享同一份主 KV 与索引 K,需要时连 Top-K 选择结果一并沿用。

13.png

图 3.9 CSA2 的三种运行模式(DeepSeek-V4.1-Flash 技术报告 Figure 4)。绿色张量由本层计算,黄色张量从前序层借用。Full 模式自带主 KV、索引 K 与 Top-K 索引;Reindex 模式借用主 KV 与索引 K、用自己的索引 Q 重新选择 Top-K;Reuse 模式连 Top-K 索引也直接复用。

每个注意力层被静态指派三种模式之一,差别仅在主 KV、索引 K、Top-K 索引三项资源的来源:

\[(\mathbf{C}_l,\ \mathbf{K}^I_l,\ \mathcal{I}_l) = \begin{cases} \big(\mathrm{Compress}(H_l),\ \mathrm{Proj}(\mathbf{C}_l),\ \mathrm{TopK}(Q^I_l, \mathbf{K}^I_l)\big), & l \in \mathcal{F} \\ \big(\mathbf{C}_{l'},\ \mathbf{K}^I_{l'},\ \mathrm{TopK}(Q^I_l, \mathbf{K}^I_{l'})\big), & l \in \mathcal{R}_{\mathrm{idx}} \\ \big(\mathbf{C}_{l'},\ \mathbf{K}^I_{l'},\ \mathcal{I}_{l'}\big), & l \in \mathcal{R}_{\mathrm{use}} \end{cases} \tag{3.21}\]

其中 $l’$ 为 $l$ 之前最近的 Full 层,$\mathbf{C}$ 为主 KV 条目,$\mathbf{K}^I$ 为索引键,$\mathcal{I}$ 为选出的 Top-K 索引。三种模式下每层都保留自己的全局 Q 与 SWA KV——层与层之间共享的只是历史,不是全部。

表 3.6 CSA2 三种层模式的资源来源与本层计算量

模式 主 KV / 索引 K Top-K 索引 本层计算
Full 本层压缩生成 本层选择 压缩 + 索引 + 选择
Reindex 借用前序 Full 层 本层重新选择 索引 Q 打分 + 选择
Reuse 借用前序 Full 层 直接复用 无(仅核心注意力)

V4.1-Flash 的 40 层 CED 布局里,encoder 的 2—19 层按“1 Full + 5 Reuse”分三组,decoder 的 20—23 层为“1 Full + 3 Reuse”,24—39 层按“1 Reindex + 3 Reuse”分四组。38 个全局注意力层里只有 4 个 Full、4 个 Reindex,其余 30 个全部 Reuse。Reuse 层的轻量化是极端的:prefill 只需 15 个 kernel,decode 只需 11 个。

层级稀疏索引器(Hierarchical Sparse Indexer)。即便有了 Reuse,剩下的索引层仍要对整个可见上下文打分,成本随长度线性增长。解码器把选择拆成两级:第一个 Full 层在完成自己的 Top-512 之外,按每 8 个位置一个子块、取块内索引分数最大值做块级粗排,保留最优的 2048 个块(16384 个位置)作为共享候选池;后续 Reindex 层只在池内精排:

\[\mathcal{P}_t = \mathrm{arg\,topK}\big(\{I^{\mathrm{blk}}_{t,p}\},\ 2048\big), \qquad \mathcal{I}_t = \mathrm{arg\,topK}\big(\{I_{t,s}\}_{s \in \mathcal{P}_t},\ 512\big) \tag{3.22}\]

候选池大小固定,深层索引器的单查询成本因此与上下文长度无关。该机制在后训练阶段引入,训练与推理使用同一限制——深层索引器从一开始就在自己将要服役的候选分布上学习。

压缩器也随之简化:V4 的 CSA 用 $2m$ 个重叠源条目加绝对位置偏置构造压缩条目,CSA2 去掉重叠与位置偏置,索引 K 直接由主 KV 投影得到,可动部件更少,训练更快。压缩率上 encoder 取 $m = 2$(两个 token 合一),decoder 因逐 token 生成取 $m = 1$;选择粒度也从 V4 的 Top-1024 收紧到 Top-512。整个架构放弃了 CSA / HCA 混合,改为纯 CSA2。

SWA Bounded Replay。滑窗分支的 KV 在 V4 中必须逐层持久化,长上下文下 SSD 压力可观。V4.1-Flash 不再持久化 SWA KV:需要时只把最近 $n_{\mathrm{win}} = 128$ 个 token 重新回放一遍来近似重建,回放误差在后训练中被模拟出来,让模型学会与之共存。持久化 KV Cache 由此降到 V4-Flash 的约 1/8。

效果:CSA2 与 FP4 主 KV(E2M1 格式,每 16 个通道共享一个 E4M3 缩放因子)叠加后,全局 KV Cache 落到每 token 890 字节,约为 V4-Flash 的 1/4;配合 CED 的非对称激活(prefill 8B / decode 16B),上下文从 4K 扩到 1M(256 倍),单 token 解码 FLOPs 只增加约四分之一。

代码实现(简化压缩器 + 层级索引器 + 三种模式路由):

import torch
import torch.nn as nn
import torch.nn.functional as F


class Compressor(nn.Module):
    """CSA2 简化压缩器:无重叠分组、无位置偏置,softmax 门控池化。"""

    def __init__(self, d_model, c, m=2):
        super().__init__()
        self.m = m
        self.w_kv = nn.Linear(d_model, c, bias=False)
        self.w_z = nn.Linear(d_model, c, bias=False)

    def forward(self, h):
        B, L, d = h.shape
        n_blk = L // self.m
        C = self.w_kv(h).view(B, n_blk, self.m, -1)      # (B, n, m, c)
        Z = self.w_z(h).view(B, n_blk, self.m, -1)
        S = torch.softmax(Z, dim=2)                      # 组内门控
        return (S * C).sum(2)                            # (B, n, c) 主 KV


class HierarchicalSparseIndexer(nn.Module):
    """层级稀疏索引器:块级粗排构造候选池,池内 token 级精排 Top-512。"""

    def __init__(self, d_model, d_c, n_head=4, block=8, pool_blocks=2048, topk=512):
        super().__init__()
        self.block, self.pool_blocks, self.topk = block, pool_blocks, topk
        self.w_q = nn.Linear(d_model, n_head * d_c, bias=False)
        self.w_w = nn.Linear(d_model, n_head, bias=False)

    def forward(self, h, k_idx):
        # h: (B, L, d);k_idx: (B, n_blk, d_c) 由主 KV 投影得到的索引键
        B, L, _ = h.shape
        q = self.w_q(h).view(B, L, -1, k_idx.size(-1))
        w = self.w_w(h)
        logits = F.relu(torch.einsum("blhc,bnc->blnh", q, k_idx))
        scores = (w.unsqueeze(2) * logits).sum(-1)       # (B, L, n_blk)

        # 块级粗排:每 block 个位置取最大分,保留 pool_blocks 个候选块
        n_blk = scores.size(-1)
        nb = (n_blk + self.block - 1) // self.block
        pad = nb * self.block - n_blk
        blk_score = F.pad(scores, (0, pad), value=-1e30).view(B, L, nb, self.block).amax(-1)
        pool = blk_score.topk(min(self.pool_blocks, nb), dim=-1).indices
        mask = torch.zeros(B, L, n_blk, dtype=torch.bool, device=h.device)
        mask.scatter_(-1, pool, True)

        # 池内 token 级精排
        scores = scores.masked_fill(~mask, float("-inf"))
        return scores.topk(min(self.topk, n_blk), dim=-1).indices  # (B, L, topk)


class CSA2Layer(nn.Module):
    """CSA2 注意力层:mode ∈ {full, reindex, reuse},静态指派。

    三种模式都保留本层自己的全局 Q 与 SWA KV;共享的只是主 KV、
    索引 K 与(Reuse 时的)Top-K 索引。
    """

    def __init__(self, d_model, c, mode="full", m=2, topk=512, n_head=8):
        super().__init__()
        self.mode = mode
        self.compressor = Compressor(d_model, c, m) if mode == "full" else None
        self.proj_idx = nn.Linear(c, c, bias=False)      # 索引 K = Proj(主 KV)
        self.indexer = HierarchicalSparseIndexer(d_model, c // 2, topk=topk)
        self.w_dq = nn.Linear(d_model, c, bias=False)
        self.w_uq = nn.Linear(c, n_head * c, bias=False)
        self.w_sw = nn.Linear(d_model, c, bias=False)    # 每层自己的 SWA KV
        self.out_proj = nn.Linear(n_head * c, d_model, bias=False)

    def forward(self, h, shared=None):
        # shared: 前序 Full 层的 (main_kv, idx_k, topk_idx)
        if self.mode == "full":
            main_kv = self.compressor(h)                 # 本层压缩
            idx_k = self.proj_idx(main_kv)               # 索引 K 由主 KV 投影
            topk_idx = self.indexer(h, idx_k)            # 本层选择
            shared = (main_kv, idx_k, topk_idx)
        elif self.mode == "reindex":
            main_kv, idx_k, _ = shared                   # 借 KV,重选择
            topk_idx = self.indexer(h, idx_k)
        else:                                            # reuse:全盘复用
            main_kv, idx_k, topk_idx = shared

        B, L, _ = h.shape
        q = self.w_uq(self.w_dq(h)).view(B, L, -1, main_kv.size(-1))
        kv = torch.gather(main_kv, 1,
                          topk_idx.unsqueeze(-1).expand(-1, -1, -1, main_kv.size(-1)))
        attn = torch.softmax(torch.einsum("blhc,blkc->blhk", q, kv), dim=-1)
        return self.out_proj(attn.reshape(B, L, -1))

从 MLA 到 CSA/HCA 再到 CSA2,KV 压缩的重心依次经过三个维度:条目大小(头维度上的矩阵分解)、序列长度(有损的语义摘要)、层数(跨层复用)。前两者决定每条缓存多大、序列上存多少条,第三者决定同样的内容存几份。MLA 的分解是精确的,后两者的压缩是近似的——但只有后者能把 KV Cache 压到个位数百分比、乃至每 token 百字节量级。

3.5.4 Qwen Sparse Attention

QSA(Qwen Sparse Attention)出自 Qwen3.8-Flash-Next 的架构论文(arXiv:2608.30320),针对的是 DSA 这类 token 级稀疏注意力的一个具体痛点:上下文越长,索引器本身越成为负担——索引器的复杂度本身就成了新的瓶颈。

14.png

图 3.10 QSA 架构(Qwen3.8-Next 架构论文 Figure 3)。左侧是压缩的轻量索引器(Partial RoPE + Zero-Centered RMSNorm + AvgPool),右侧是 micro-block 粒度的稀疏注意力。

QSA 的两级压缩策略:

  1. 压缩的轻量索引器:先用 AvgPool 把序列聚合成 micro-block,再用极小的投影计算 block 级别的分数——索引开销随上下文增长被摊薄
  2. micro-block 粒度选择:Top-k 选择器选出的不是 token 而是 micro-block,注意力核在块级访问下保持连续的 KV 读取

与 DSA 的两点关键差异:

  • 粒度:DSA 是 token 级,QSA 是 micro-block 级
  • 索引器开销:QSA 的索引器自带压缩,DSA 的索引器随长度线性变贵

另外 QSA 每层独立做序列压缩,不跨层共享索引。这让它不依赖跨层注意力相似性——对于 GDN 层与全局注意力层交错的 hybrid 架构尤其重要(GDN 层没有“注意力相似性”可以共享)。

QSA 的关键公式(论文式 12—16)如下。索引器采用 MQA:每个 query 头做独立轻量投影并归一化,key 全组共享一个头:

\[\hat q^h_i = \mathrm{RMSNorm}(W^h_Q x_i), \qquad k_i = W_K x_i \tag{3.23}\]

key 按 $r$ 个 token 一块做平均池化压缩(块起始位置 $p_b = br$),压缩后再施加 partial RoPE,避免平均不同旋转相位的表示:

\[\hat k_b = \mathrm{RMSNorm}\big(\mathrm{AvgPool}(k_{p_b:p_b+r-1})\big), \qquad \bar k_b = \mathrm{PRoPE}(\hat k_b, p_b) \tag{3.24}\]

块级重要性分数由 ReLU 查询–键相似度在各索引头上求和,且只给已完整观测的块打分(block-causal):

\[I_{ib} = \begin{cases} \sum_{h=1}^{H} \mathrm{ReLU}\big(\langle q^h_i, \bar k_b \rangle\big), & p_b + r - 1 \le i \\ -\infty, & \text{otherwise} \end{cases} \tag{3.25}\]

给定 token 预算 $K$,按块预算 $K_B = \lceil K/r \rceil$ 取分数最高的块(末尾未满的块始终保留):

\[B_i = \mathrm{TopK}_{K_B}\big(\{I_{ib}\}_b\big), \qquad K_B = \left\lceil \frac{K}{r} \right\rceil \tag{3.26}\]

代码实现(压缩轻量索引器 + micro-block Top-k,对应论文公式 12—16):

import torch
import torch.nn as nn
import torch.nn.functional as F


class CompressedLightweightIndexer(nn.Module):
    """QSA 索引器:MQA 形式,key 先 AvgPool 压成 micro-block,再 RMSNorm + partial RoPE。"""

    def __init__(self, d_model, head_dim=128, n_head=4, r=32):
        super().__init__()
        self.n_head = n_head
        self.head_dim = head_dim
        self.r = r                                    # micro-block 大小(ratio)
        self.w_q = nn.Linear(d_model, n_head * head_dim, bias=False)
        self.w_k = nn.Linear(d_model, head_dim, bias=False)   # 单共享 key 头(MQA)
        self.norm_q = nn.RMSNorm(head_dim)
        self.norm_k = nn.RMSNorm(head_dim)

    def forward(self, x, rope_fn):
        B, L, d = x.shape
        r = self.r
        n_blk = L // r

        # 1) 独立轻量投影(公式 12)
        q_raw = self.w_q(x).view(B, L, self.n_head, self.head_dim)
        k = self.w_k(x)                                # (B, L, hd)

        # 2) key 走 AvgPool 压成 micro-block,再 RMSNorm(公式 13)
        #    注意顺序:先内容压缩、后位置编码,避免把不同旋转相位平均掉
        k_blk = k.view(B, n_blk, r, self.head_dim).mean(2)   # (B, n_blk, hd)
        k_blk = self.norm_k(k_blk)

        # 3) partial RoPE:只对 64/128 维施加旋转(公式 14)
        q = self.norm_q(q_raw)
        pos_q = torch.arange(L, device=x.device)
        pos_k = torch.arange(n_blk, device=x.device) * r      # 每块取起始位置 p_b = b·r
        q = rope_fn(q, pos_q)
        k_blk = rope_fn(k_blk, pos_k)

        # 4) block-causal ReLU 评分(公式 15)
        logits = F.relu(torch.einsum("blhc,bnc->blnh", q, k_blk))  # (B,L,n,h)
        scores = logits.sum(-1)                                   # (B, L, n_blk)
        # 只允许给"已完整观察"的块打分:p_b + r - 1 <= i
        causal = torch.arange(n_blk, device=x.device).view(1, 1, -1) * r + (r - 1)
        causal = causal <= torch.arange(L, device=x.device).view(1, -1, 1)
        scores = scores.masked_fill(~causal, float("-inf"))
        return scores


class QwenSparseAttention(nn.Module):
    """QSA:索引器选 micro-block,块内 token 全展开,最后不完整块恒保留。"""

    def __init__(self, d_model, n_head=8, head_dim=128, r=32, token_budget=2048):
        super().__init__()
        self.indexer = CompressedLightweightIndexer(d_model, head_dim // 2, n_head=4, r=r)
        self.r = r
        self.token_budget = token_budget
        self.n_head = n_head
        self.head_dim = head_dim
        self.w_q = nn.Linear(d_model, n_head * head_dim, bias=False)
        self.w_k = nn.Linear(d_model, n_head * head_dim, bias=False)
        self.w_v = nn.Linear(d_model, n_head * head_dim, bias=False)
        self.norm_q = nn.RMSNorm(head_dim)
        self.norm_k = nn.RMSNorm(head_dim)
        self.out_proj = nn.Linear(n_head * head_dim, d_model, bias=False)

    def forward(self, x, rope_fn):
        B, L, d = x.shape
        r = self.r
        # 1) 索引打分
        scores = self.indexer(x, rope_fn)                # (B, L, n_blk)
        n_blk = scores.size(-1)

        # 2) Top-K 选块(块预算 K_B = ⌈K/r⌉),再展开到 token 预算 K
        k_blk_budget = -(-self.token_budget // r)
        blk_idx = scores.topk(min(k_blk_budget, n_blk), dim=-1).indices   # (B,L,kb)
        # 展开成 token 索引(示意)
        tok = (blk_idx.unsqueeze(-1) * r + torch.arange(r, device=x.device))  # (B,L,kb,r)
        tok = tok.reshape(B, L, -1)[:, :, :self.token_budget]                 # 截到预算

        # 3) 最后一个不完整块恒保留(保证当前 chunk 信息不丢)
        last_blk = torch.full((B, L, 1), L - (L % r) - r, device=x.device,
                              dtype=tok.dtype)
        tok = torch.cat([last_blk.clamp(min=0), tok], dim=-1)

        # 4) 核心注意力(Q/K 同样走 RMSNorm + partial RoPE,保持稳定)
        q = self.norm_q(self.w_q(x).view(B, L, self.n_head, self.head_dim))
        k = self.norm_k(self.w_k(x).view(B, L, self.n_head, self.head_dim))
        v = self.w_v(x).view(B, L, self.n_head, self.head_dim)
        q = rope_fn(q, torch.arange(L, device=x.device))

        # 按 tok 收集 K/V(示意 gather)
        idx = tok.unsqueeze(-1).expand(-1, -1, -1, self.n_head * self.head_dim)
        k_sel = torch.gather(k.reshape(B, L, -1), 1, idx).view(
            B, L, -1, self.n_head, self.head_dim)
        v_sel = torch.gather(v.reshape(B, L, -1), 1, idx).view(
            B, L, -1, self.n_head, self.head_dim)

        attn = torch.einsum("blhc,blkhc->blhkc", q, k_sel) / self.head_dim ** 0.5
        o = torch.softmax(attn, dim=3)
        o = torch.einsum("blhkc,blkhc->blhc", o, v_sel)
        return self.out_proj(o.reshape(B, L, -1))

为什么这样设计:索引器开销是 O(L·n_blk) 而非 O(L²),随上下文增长被摊薄;micro-block 粒度让 KV 读取保持连续、对 Tensor Core 友好;每层独立压缩则让它天然适配 hybrid 架构(GDN 层与注意力层交错时,不依赖跨层相似性)。

效果:1M token 下 QSA 的注意力 kernel 在 prefill 加速 7.6×、decode 加速 4.9×;在 90% Prefix Cache 命中率的线上场景实验中,1M 上下文下 prefill 吞吐达到 Qwen3.7-Plus 的 8.6×。

3.5.5 MiniMax Sparse Attention

MSA(MiniMax Sparse Attention,arXiv:2606.13392)是目前工程完成度最高的块级稀疏注意力之一——它同时开源了推理 kernel,并已用在生产模型 MiniMax-M3 上。

15.png

图 3.11 MSA 总览(MSA 论文 Figure 1)。左半是 Index Branch(打分 → Block Max Pooling → Top-K Index),右半是 Main Branch(Top-K KV Block 上的精确稀疏注意力)。右侧热力图展示了两个 GQA 组各自选中的不同块。

MSA 的核心设计是两分支 + 每组独立选择:

Index Branch:只增加两个投影矩阵。每个 GQA 组有一个 index query 头、一个共享的 index key 头;对可见的 key token 打分后做 block max pooling 得到块级分数,再做 Top-k 选择。包含 query 自身的局部块永远被强制保留,避免选择器把“眼前的上下文”丢掉。

Main Branch:在选中的块上做精确的块稀疏 softmax 注意力。每个 query 头有自己的 query 投影,但共享所在组的块集合。

关键参数:块大小 $B_k = 128$,每组保留 $k = 16$ 个块,即每个 query 固定只看 $2048$ 个 KV token。于是单 query 成本从 $O(N)$ 变成 $O(kB_k)$——这是一个与上下文长度无关的常数。

选择发生在 GQA 组内共享、组间独立:一个 KV 头服务多个 query 头,它们共享同一组块;不同组则可以关注不同的长程区域。

两分支的公式如下。Index Branch 每个 GQA 组一个索引 query 头、全组共享一个索引 key 头:

\[Q_{idx} = X W^{idx}_q \in \mathbb{R}^{N \times H_{kv} \times d_{idx}}, \qquad K_{idx} = X W^{idx}_k \in \mathbb{R}^{N \times 1 \times d_{idx}} \tag{3.27}\]

token 级分数经 block max pooling 聚合成块分数($B_b$ 为第 $b$ 个块的下标集合,空块记 $-\infty$),再取 Top-k 块:

\[S^{idx,(r)}_{i,j} = \frac{(Q^{idx})^{(r)}_i (K^{idx})^\top_j}{\sqrt{d_{idx}}}, \qquad M^{idx,(r)}_{i,b} = \max_{j \in B_b,\ j \le i} S^{idx,(r)}_{i,j} \tag{3.28}\] \[I^{(r)}_i = \mathrm{TopK}_{b}\big(M^{idx,(r)}_{i,\cdot},\ k\big) \tag{3.29}\]

Main Branch 在选中块的可见 token 上做精确的块稀疏 softmax 注意力(组 $r$ 内所有 query 头共享 $I^{(r)}_i$):

\[O^{(h)}_i = \mathrm{softmax}\!\left(\frac{Q^{(h)}_i \big(K^{(r)}[I^{(r)}_i]\big)^\top}{\sqrt{d_h}}\right) V^{(r)}\big[I^{(r)}_i\big] \tag{3.30}\]

训练方法是 MSA 最有价值的部分。Top-k 不可导,语言建模损失无法训练索引投影。MSA 用 KL 对齐损失解决:让 Index Branch 的分布去匹配 Main Branch 在被选 token 上的(组平均的)注意力分布,充当 teacher。三个稳定化机制:

  • Gradient Detach:对 Index Branch 的输入做 stop-gradient,把 KL 损失限制在索引投影内,避免梯度打回主干引起 loss 发散
  • Indexer Warmup:前若干步两分支都跑全注意力,先让索引器学会路由
  • 强制 Local Block:保留一个名额给邻近上下文

KL 对齐损失把 Main Branch 的组平均分布作为 teacher,蒸馏进 Index Branch 的分数分布($P^{idx,(r)}_{i,\cdot}$ 为索引分支在选中 token 上的 softmax 分布):

\[\mathcal{L}_{KL} = \frac{1}{N H_{kv}} \sum_{i=1}^{N} \sum_{r=1}^{H_{kv}} D_{KL}\!\left(\mathrm{stopgrad}\big(P^{(r)}_{i,\cdot}\big) \,\big\|\, P^{idx,(r)}_{i,\cdot}\right) \tag{3.31}\]

算子协同设计:

  • exp-free Top-k:softmax 保持序关系,所以对原始分数排序即可得到相同索引,内核跳过 max / exp / sum——128K 上下文、$k=16$ 时比 torch.topk 快 5.1×,比 TileLang 的 radix-select 还快 3.7×
  • KV-outer 稀疏注意力 + query gather:遍历 KV 块比遍历 query 算术强度更高,把 $\lceil 128/G \rceil$ 个 query 位置打包进一个 $128 \times 128$ 的 score MMA

效果(109B 参数原生多模态模型,3T token 预算):与 GQA 持平的同时,1M 上下文下每 token 注意力计算量减少 28.4×;配合协同设计的 kernel,在 H800 上取得 14.2× prefill / 7.6× decode 的墙钟加速。

import torch
import torch.nn as nn


class MiniMaxSparseAttention(nn.Module):
    """MSA:Index Branch 打分选块,Main Branch 在选中块上做精确稀疏注意力。"""

    def __init__(self, d_model, n_heads, n_kv_groups, block_size=128, topk_blocks=16, dtype=None):
        super().__init__()
        self.h, self.g = n_heads, n_kv_groups
        self.dh = d_model // n_heads
        self.Bk, self.k = block_size, topk_blocks
        self.wq = nn.Linear(d_model, d_model, bias=False, dtype=dtype)
        self.wk = nn.Linear(d_model, n_kv_groups * self.dh, bias=False, dtype=dtype)
        self.wv = nn.Linear(d_model, n_kv_groups * self.dh, bias=False, dtype=dtype)
        # Index Branch 只多两个投影
        self.q_idx = nn.Linear(d_model, n_kv_groups * self.dh, bias=False, dtype=dtype)
        self.k_idx = nn.Linear(d_model, self.dh, bias=False, dtype=dtype)
        self.wo = nn.Linear(d_model, d_model, bias=False, dtype=dtype)

    def forward(self, x):
        B, L, _ = x.shape
        h, g, dh = self.h, self.g, self.dh
        q = self.wq(x).view(B, L, h, dh).transpose(1, 2)
        k = self.wk(x).view(B, L, g, dh).transpose(1, 2)
        v = self.wv(x).view(B, L, g, dh).transpose(1, 2)

        # ---- Index Branch:组级打分 -> block max pooling -> Top-k 块 ----
        qi = self.q_idx(x).view(B, L, g, dh).transpose(1, 2)      # (B,g,L,dh)
        ki = self.k_idx(x).view(B, L, dh)                          # (B,L,dh) 共享 index key
        scores = (qi @ ki.transpose(-2, -1)) / dh**0.5             # (B,g,L,L)

        n_blocks = (L + self.Bk - 1) // self.Bk
        pad = n_blocks * self.Bk - L
        s = torch.nn.functional.pad(scores, (0, pad), value=-1e30)
        s = s.view(B, g, n_blocks, self.Bk).amax(dim=-1)           # block max pooling

        # 强制保留 query 所在局部块
        blk = torch.arange(n_blocks, device=x.device)
        qblk = torch.arange(L, device=x.device) // self.Bk
        local = (blk.view(1, -1) == qblk.view(-1, 1))            # (L, n_blocks)
        s = s.masked_fill(local.view(1, 1, L, n_blocks), 1e30)
        _, sel = torch.topk(s, k=min(self.k, n_blocks), dim=-1)    # (B,g,L,k)

        # ---- Main Branch:gather 选中的块,做精确稀疏注意力 ----
        blk_idx = (sel.unsqueeze(-1) * self.Bk
                   + torch.arange(self.Bk, device=x.device).view(1, 1, 1, -1)).clamp(max=L)
        blk_idx = blk_idx.view(B, g, L, self.k * self.Bk)          # (B,g,L,k*Bk)
        sel_k = torch.gather(k, 2, blk_idx.unsqueeze(-1).expand(-1, -1, -1, dh))
        sel_v = torch.gather(v, 2, blk_idx.unsqueeze(-1).expand(-1, -1, -1, dh))

        qg = q.view(B, L, g, h // g, dh).permute(0, 2, 4, 1, 3).reshape(B, g, h // g, L, dh)
        att = torch.softmax((qg @ sel_k.transpose(-2, -1)) / dh**0.5, dim=-1)
        o = (att @ sel_v).view(B, g, h // g, L, dh).permute(0, 3, 1, 2, 4).reshape(B, L, h * dh)
        return self.wo(o)

MSA 的设计哲学是“简单与可扩展”——没有 NSA 那样的三分支,也没有 QSA 那样的层级压缩,就是干净的两分支 + 组级 Top-k,并且把选择粒度定在能吃到 Tensor Core 的块大小上。

3.5.6 LongCat Sparse Attention

LSA(LongCat Sparse Attention,arXiv:2608.01662,美团 LongCat-2.0 采用)以 DSA 为基线,关心的不是“选哪些”,而是“选完之后怎么办”。论文对 DSA 的实测画像给出两个瓶颈。其一是 Indexer Output Discontiguity:每个选中 token 独立 gather 一条 1152 字节的潜在 KV,只跨 3 条 cacheline,内存级并行度约 6%,净有效带宽只有峰值的约 4.5%,反向传播的 scatter_add 写冲突使访存进一步串行化。其二是 Indexer High Overhead:索引器延迟从 4K 上下文的 0.034 ms 涨到 1024K 的 0.930 ms(27 倍),而核心稀疏注意力恒定在 0.10 ms 左右——索引器在 1024K 时占单层解码延迟的 90%。

16.png

图 3.12 LSA 架构(LSA 论文 Figure 1)。左为 owner 层:Block Indexer 块级粗排 + Token Indexer 精排,预算对半分给连续的流式区域与动态选择的非连续区域;右为 reuse 层:直接复用 owner 层的 Top-K 索引。两层共享索引参数。

LSA 用三项互补机制分别对应两个瓶颈:SI 解决访存连续性,CLI 与 HI 解决索引开销。SI 与 CLI 参与训练,HI 是训练无关的即插即用模块。

表 3.7 LSA 三项索引机制对比

机制 针对瓶颈 做法 训练/推理 关键超参
SI(流式感知索引) 输出非连续 预算对半分给固定连续区与动态稀疏区 训练 + 推理 $K_{\mathrm{sink}}=16$、$K_{\mathrm{swa}}=1024$
CLI(跨层索引) 索引重复计算 owner 层选择、reuse 层复用,跨层蒸馏 训练 + 推理 组大小 $N=2$(MTP 为 3)
HI(层级索引) 单次打分 $O(L)$ 块级粗排候选页 + 页内精排 仅推理 $P=128$、$B=8$、$M=1024$

Streaming-Aware Indexing(SI)。稀疏预算不再全部交给动态选择,而是划出固定的一半给“可预测的连续区域”——开头的注意力汇聚(sink)与最近的滑动窗口:

\[\mathcal{S}_t = \underbrace{\{1, \dots, K_{\mathrm{sink}}\}}_{\text{sink}} \cup \underbrace{\{t - K_{\mathrm{swa}} + 1, \dots, t\}}_{\text{SWA}} \cup \underbrace{\mathrm{arg\,topK}\big(\{I_{t,s}\},\ K_{\mathrm{sparse}}\big)}_{\text{sparse}} \tag{3.32}\]

总预算 $K = K_{\mathrm{sink}} + K_{\mathrm{swa}} + K_{\mathrm{sparse}} = 2048$,其中 $K_{\mathrm{sink}} = 16$、$K_{\mathrm{swa}} = K_{\mathrm{sparse}} = 1024$。这个 50 : 50 的划分有实测依据:在全注意力模型上,流式区域(sink + 滑窗)平均捕获约 83% 的注意力质量,75% 以上的固定预算则开始损害长程检索。内核侧把核心注意力拆成稀疏与滑窗两个算子,分发到不同硬件流重叠执行,再以 online-softmax 合并;选中 KV 的跨步 chunk 重叠率从 65% 提升到 82%,单层 reload 延迟从 53.9 μs 降到 30.5 μs。

Cross-Layer Indexing(CLI)。相邻层的 Top-K 集合高度一致——实测成对重叠 57.4%,复用相邻层索引仍能覆盖目标层 93.2% 的注意力质量。CLI 把连续 $N$ 层编成一组:组内第一层(owner)执行索引器,其余层(reuse)直接沿用其选中的 token 集合,索引计算降到 $1/N$。朴素复用有质量损失,解决办法是让 owner 的索引器为整组服务——用跨层蒸馏让共享索引器去匹配组内每一层的注意力分布:

\[\mathcal{L}_{\mathrm{CLI}} = \sum_{i=0}^{N-1} \mathcal{L}_I^{(l+i)} \tag{3.33}\]

其中 $l$ 为组首层,$\mathcal{L}_I^{(l+i)}$ 是共享索引器输出与第 $l+i$ 层注意力分布之间的蒸馏损失。没有这项蒸馏,$N = 2$ 时 128K 针检索准确率降到 70%;加上蒸馏后回到 96%,甚至优于无蒸馏的 $N = 4$(82%)。实验中主模型取 $N = 2$,$N = 4$ 在长上下文上有可测损失;MTP 的 3 个预测步共享一组索引($N = 3$),draft 由主模型验证,不影响最终生成质量。

Hierarchical Indexing(HI)。HI 把单次打分拆成粗排与精排两步:先把序列切成大小 $P = 128$ 的页、每页再切成 $B = 8$ 的子块,用子块索引键均值 $\mathbf{k}^{\mathrm{mean}}_n$ 做块级显著性粗排,取 Top-$M$($M = 1024$)个候选页;token 级精排只在候选页内进行:

\[I^{\mathrm{page}}_{t,p} = \sum_{j=1}^{H^I} w^I_{t,j} \sum_{n \in \mathrm{page}_p} \mathrm{ReLU}\big(\mathbf{q}^I_{t,j} \cdot \mathbf{k}^{\mathrm{mean}}_n\big), \qquad \mathcal{P}_t = \mathrm{arg\,topK}\big(\{I^{\mathrm{page}}_{t,p}\},\ M\big) \tag{3.34}\]

两次 Top-K 的成本从 $O(L)$ 降为 $O(L/P + MP)$。HI 不参与训练,推理期直接启用:1024K 上下文的 prefill 索引选择加速 4.11 倍;128K 以下反而略慢,论文在 256K 以上才开启(约 200K 处出现收益拐点)。

效果。在 LongCat-Flash-Lite(69B-A3B)与 LongCat-Flash(560B-A27B)两个规模上,LSA 与全注意力在通用、推理、代码与长文本基准上全面持平(HELMET 59.02 对 MLA 的 58.50);注意力层训练延迟加速 1.53—1.61 倍,prefill TTFT 加速 1.42—3.60 倍,decode TPOT 加速 1.25—1.40 倍。训练效率的提升直接转化成上下文预算:LongCat-2.0(1.6T-A48B)在有限算力下把原生上下文做到 100 万 token,开源的 LongCat-Flash-Lite-Sparse 同样以 LSA 支撑 1M 上下文。

代码实现(HI 两级选择 + SI 流式预算拼接 + CLI 组调度):

import torch
import torch.nn as nn
import torch.nn.functional as F


class LSAIndexer(nn.Module):
    """LSA 索引器:HI 块级粗排 -> 页内精排,SI 流式预算拼接。

    CLI 的组调度在模型外层完成:owner 层调用 forward,
    reuse 层直接沿用返回的索引;跨层蒸馏损失 L_CLI 叠加到主损失上。
    """

    def __init__(self, d_c, n_head=16,
                 k_sink=16, k_swa=1024, k_sparse=1024,
                 page=128, sub=8, top_m=1024):
        super().__init__()
        self.k_sink, self.k_swa, self.k_sparse = k_sink, k_swa, k_sparse
        self.page, self.sub, self.top_m = page, sub, top_m
        self.n_head = n_head

    def score(self, q, k):
        """DSA 式打分:I = Σ_h w_h · ReLU(q_h · k_h^T)。"""
        return F.relu(torch.einsum("blhc,bnc->blnh", q, k))       # (B,L,n,h)

    def forward(self, q, k_idx, k_sub, seq_len, use_hi=False):
        # q: (B,L,h,d_c);k_idx: (B,n,d_c) token 级索引键;k_sub: (B,n_sub,d_c) 子块均值键
        B, L = q.shape[:2]
        n = k_idx.size(1)

        if use_hi:
            # HI 粗排:子块均值键 -> 页分 -> Top-M 页 -> 候选 token 集
            sub_s = self.score(q, k_sub).amax(-1)                  # (B,L,n_sub)
            per = self.page // self.sub                            # 每页子块数
            n_pages = (n + self.page - 1) // self.page
            page_s = F.pad(sub_s, (0, n_pages * per - sub_s.size(-1)), value=-1e30)
            page_s = page_s.view(B, L, n_pages, per).sum(-1)
            pages = page_s.topk(min(self.top_m, n_pages), -1).indices
            tok = (pages.unsqueeze(-1) * self.page
                   + torch.arange(self.page, device=q.device)).clamp(max=n - 1)
            cand = torch.zeros(B, L, n, dtype=torch.bool, device=q.device)
            cand.scatter_(-1, tok.flatten(-2), True)
        else:
            cand = torch.ones(B, L, n, dtype=torch.bool, device=q.device)

        # HI 精排 / 平铺打分:token 级索引
        tok_s = self.score(q, k_idx).amax(-1)                      # (B,L,n)
        tok_s = tok_s.masked_fill(~cand, float("-inf"))
        dyn = tok_s.topk(min(self.k_sparse, n), -1).indices        # 动态稀疏区

        # ---- SI:sink + 滑窗 + 动态稀疏,连续区占一半预算 ----
        t = torch.arange(seq_len, device=q.device).view(1, -1, 1)
        sink = torch.arange(self.k_sink, device=q.device).view(1, 1, -1)
        off = torch.arange(self.k_swa, device=q.device).view(1, 1, -1)
        swa = (t - self.k_swa + 1 + off).clamp(min=0)              # 最近 k_swa 个位置
        return torch.cat([sink.expand(B, L, -1), swa.expand(B, L, -1), dyn], dim=-1)


class CrossLayerGroup(nn.Module):
    """CLI:连续 n 层一组,owner 层执行索引,reuse 层共享 token 集合。"""

    def __init__(self, indexer, n=2):
        super().__init__()
        self.indexer = indexer
        self.n = n

    def forward(self, q, k_idx, k_sub, seq_len, use_hi=False):
        sel = self.indexer(q, k_idx, k_sub, seq_len, use_hi)       # owner 选择
        return [sel] * self.n                                      # 组内共享

LSA 与 CSA2 在 2026 年给出了同一个判断:索引与 KV 沿层维度摊销,是稀疏注意力继续降本的下一段路。两者的分工不同——CSA2 把复用做进架构与缓存格式(连 FP4 与回放重建一起算账),LSA 则把访存连续性当成一等公民,让选中的 token 在 HBM 里尽量连成片。稀疏注意力的“少算一部分”,至此同时包含了“少存几份”与“少跑几趟”。

3.6 Linear Attention

线性注意力不是“稀疏注意力”,而是改变计算顺序。

17.png

图 3.13 Softmax 注意力与线性注意力的计算方式对比(MiniMax-01 论文 Figure 5)。

Softmax 注意力必须先算出 $QK^T$($N \times N$)再 softmax:

\[O = \mathrm{softmax}\!\left(\frac{QK^T}{\sqrt{d}}\right) V \quad \Rightarrow \quad O(N^2 d) \tag{3.35}\]

线性注意力利用矩阵乘法的结合律,换一个顺序,先算 $K^TV$($d \times d$,很小):

\[O = Q\left(\phi(K)^T V\right) \quad \Rightarrow \quad O(N d^2) \tag{3.36}\]

其中 $\phi$ 是核函数(如 $\phi(x) = \mathrm{elu}(x)+1$),替代 softmax 的指数。

复杂度从 $O(N^2 d)$ 变成 $O(Nd^2)$——当 $N \gg d$ 时是巨大的胜利。而且推理时的状态是固定大小(每个 head 维护一个 $d_k \times d_v$ 的矩阵),根本没有 token 级 KV Cache,prefill 是 $O(n)$、decode 每步 $O(1)$。

线性注意力早期未能落地的原因。 2020—2023 年的尝试(Katharopoulos et al., “Transformers are RNNs” 等)用简单核函数,质量代价是困惑度上升 10—15%,在真实语言建模上从未超越 softmax,也就从未进入生产模型。

2025 年的转折点:gated delta rule。核心洞察是——问题不在“线性”本身,而在“怎么更新这个固定大小的状态”。纯加性更新 $S_t = S_{t-1} + k_t v_t^T$ 会让状态无界增长,而且无法纠正已有错误关联;而 softmax 归一化的本质是“竞争”。delta rule 引入预测误差项,把状态更新变成“擦除旧关联 + 写入新关联”;再叠加一个可学习的衰减门控制遗忘,线性注意力才第一次在质量上接近 softmax。

3.6.1 演进过程:从核替换到门控 delta rule

线性注意力十年脉络,本质是“状态怎么更新”这一行代码被反复改写:

表 3.8 线性注意力的十年演进脉络

年份 代表工作 状态更新 关键改进 遗留问题
2020 Linear Transformer(Katharopoulos et al.) $S_t = S_{t-1} + k_t v_t^T$ 核函数 $\phi(x)=\mathrm{elu}(x)+1$ 替代 softmax,首次 $O(N)$ 质量差,困惑度 +10—15%
2020 Performer(Choromanski et al.) $S_t = S_{t-1} + \phi(k_t) v_t^T$(FAVOR+ 随机特征 $\phi$) 无偏近似 softmax,理论优雅 随机特征方差大,长上下文不稳
2023 RetNet(Sun et al.) $S_t = \gamma\, S_{t-1} + k_t v_t^T$ 引入多尺度衰减 $\gamma$,chunkwise 并行起步 $\gamma$ 与输入无关,记忆寿命固定
2023 Mamba(Gu & Dao) 选择性 SSM:$x_t = A_t x_{t-1} + B_t u_t$ 数据依赖的输入门,硬件高效扫描 与注意力殊途,理论不统一
2024 Mamba2 / SSD(Dao & Gu) $S_t = \alpha_t\, S_{t-1} + k_t v_t^T$,标量 $\alpha_t = \exp(\Delta_t A)$ 数据相关 状态空间对偶性:证明 SSM ⊂ 线性注意力 仍是加性更新,无纠错能力
2024 GLA(Yang et al.) $S_t = \mathrm{diag}(\alpha_t) S_{t-1} + k_t v_t^T$ 数据依赖的门控衰减 $\alpha_t$ 加性更新仍会累积错误关联
2024 DeltaNet(Yang et al.) $S_t = (I-\beta_t k_t k_t^T) S_{t-1} + \beta_t k_t v_t^T$ delta rule:擦除旧关联 + 写入残差误差 无遗忘门,长上下文衰减不足
2024 Gated DeltaNet $S_t = \alpha_t (I-\beta_t k_t k_t^T) S_{t-1} + \beta_t k_t v_t^T$ delta rule + 门控衰减,两者兼得 标量 $\alpha_t$,chunk 内对角块算不动
2025 Kimi Linear / KDA $S_t = \mathrm{Diag}(\alpha_t)(I-\beta_t k_t k_t^T) S_{t-1} + \beta_t k_t v_t^T$,逐通道 $\alpha_t \in (0,1)^{d_k}$ 下界衰减(scaled sigmoid)→ 全部上 Tensor Core 工程成熟,进入生产
2026 Kimi K3 $S_t = \mathrm{Diag}(\alpha_t)(I-\beta_t k_t k_t^T) S_{t-1} + \beta_t k_t v_t^T$(KDA 层;全局层为 Gated MLA) hybrid 落地,1M 上下文、2.5× 扩展效率 —

三条主线一目了然:

  1. 衰减机制:从无衰减(2020)→ 标量固定衰减(RetNet)→ 标量数据相关衰减(GLA/GDN)→ 逐通道数据相关衰减(KDA)——遗忘粒度越来越细
  2. 更新规则:从纯加性(Hebbian 式累积)→ delta rule(预测误差 + 定点擦写)——从“只记新东西”到“能改错”
  3. 硬件适配:从不实用(2020—2023)→ chunkwise 并行(RetNet/GLA)→ 对角块走 Tensor Core(KDA)——线性注意力的复兴本质是工程胜利

补充:MiniMax 的 lightning attention(MiniMax-01,2025)走的也是“数据依赖门控”路线,Qwen3-Next 的 GDN hybrid(3:1)与 Kimi K3 的 KDA hybrid(3:1)不约而同选择了同样的混合比例——这不是巧合,而是“线性层管长程、全局层管精确检索”这条分工在大量实验后收敛出的稳定解。

注:线性注意力属于“用递归状态换显存”的路子,与 FlashAttention 的“用 IO 优化换带宽”是两条正交的路线,最终被合并到 hybrid 架构里。

3.6.2 Gated Delta Net

Gated DeltaNet(arXiv:2412.06464,Yang et al., 2024)把 delta rule 与 Mamba2 的门控机制结合起来,是 2025 年线性注意力复兴的起点。

18.png

图 3.14 Gated DeltaNet 的(混合)架构与 block 设计(Gated DeltaNet 论文 Figure 1)。右侧是单个 Gated DeltaNet block:短卷积 → Q/K L2 归一化 → Gated Delta Rule → 输出门。

Gated Delta Rule 的递推:设每个 head 维护状态矩阵 $S_t \in \mathbb{R}^{d_k \times d_v}$,

\[\tilde{S}_{t-1} = \alpha_t S_{t-1} \qquad \text{(先衰减)} \tag{3.37}\] \[e_t = v_t - \tilde{S}_{t-1}^\top k_t \qquad \text{(预测误差)} \tag{3.38}\] \[S_t = \tilde{S}_{t-1} + \beta_t k_t e_t^\top \qquad \text{(delta 更新)} \tag{3.39}\] \[y_t = S_t^\top q_t \qquad \text{(读出)} \tag{3.40}\]

等价地写成一式:

\[S_t = \alpha_t\left(I - \beta_t k_t k_t^\top\right) S_{t-1} + \beta_t k_t v_t^\top \tag{3.41}\]

两个门的作用是互补的:$\alpha_t \in (0,1)$ 是数据相关的衰减,控制已有状态的寿命;$\beta_t \in (0,1)$ 控制 delta 更新的写入强度。delta 项先估计“当前 $k_t$ 已经关联了什么值”,只写入残差误差——重复或相似的 key 会更新已有关联,而不是让外积无界累积。这种“定点擦除-写入”正是 GDN 与纯加性线性注意力的分水岭。

并行计算推导(Chunkwise Parallel Form)。递推式虽然优雅,但逐 token 串行在 GPU 上跑不动。GDN 的工程价值在于把它改写成块内并行、块间串行的形式——这才是它能训得动的原因。把序列切成长度 $C$ 的 chunk,记 $S[t]$ 为进入第 $t$ 个 chunk 的状态。

第一步:把递推展开成“累计转移 × 初始状态 + 局部贡献”。对 chunk 内第 $r$ 个位置,部分展开:

\[S_r[t] = S[t] \prod_{i=1}^{r}\Big(\alpha_i[t]\,\big(I - \beta_i k_i k_i^\top\big)\Big) + \sum_{i=1}^{r} \beta_i v_i k_i^\top \prod_{j=i+1}^{r}\Big(\alpha_j\,\big(I - \beta_j k_j k_j^\top\big)\Big) \tag{3.42}\]

记 $F_r[t]$ 为累积转移(含衰减与 Householder),$G_r[t]$ 为局部写入项,则 $S_r[t] = F_r[t] + G_r[t]$,输出为 $o_r = S_r[t]^\top q_r$。

第二步:Householder 连乘用 WY 表示化简。直接算 $\prod (I-\beta_i k_i k_i^\top)$ 是 $O(C^2)$ 次矩阵乘,太贵。经典 WY 表示(Bischof & Loan, 1985)把它化为低秩形式:

\[P_r[t] = I - \sum_{i=1}^{r} w_i[t]\, k_i[t]^\top, \qquad w_r[t] = \beta_r\Big(k_r - \sum_{i<r} w_i (k_i^\top k_r)\Big) \tag{3.43}\]

这样 $F_r[t] = \gamma_r[t]\, P_r[t]$($\gamma_r$ 为累计衰减),$G_r[t] = \sum_{i=1}^{r} \frac{\gamma_r}{\gamma_i}\, \tilde u_i k_i^\top$,其中 $\tilde u_r$ 同理用 WY 递推算出。

第三步:写成纯矩阵乘的形式。定义衰减掩码 $\Gamma \in \mathbb{R}^{C\times C}$,$(\Gamma)_{ij} = \gamma_i / \gamma_j$($i \ge j$,否则 0),并把向量“衰减到 chunk 首”或“衰减到 chunk 尾”:

\[\overleftarrow{q}_r = \gamma_r\, q_r \quad\text{(衰减到 chunk 起点)}, \qquad \overrightarrow{k}_r = \frac{\gamma_C}{\gamma_r}\, k_r \quad\text{(衰减到 chunk 终点)} \tag{3.44}\]

则 chunk 内输出为:

\[O[t] = \underbrace{\overleftarrow{Q}[t]\, S[t]^\top}_{\text{跨块贡献}} + \underbrace{\big(Q[t]K[t]^\top \odot \Gamma[t]\big) V[t]}_{\text{块内贡献}} \tag{3.45}\]

全部都是矩阵乘——可以吃满 Tensor Core。跨块部分只依赖上一 chunk 的状态(串行但便宜),块内部分完全并行。这就是 GDN 论文强调的 “hardware-efficient”:复杂度仍是 $O(Nd^2)$,但常数项小到可以实际训练。

推广:当 $\gamma_t$ 与输入无关时,上式退化为 RetNet 的 retention;当 $\beta_t=0$ 时退化为带衰减的纯线性注意力(GLA)。GDN 是这族“递归状态 + 门控”算法里最通用的一个特例。

block 组成:短因果卷积(注入局部上下文)→ Q/K 做 L2 归一化 → 门控 delta 递推 → sigmoid 输出门 + 零中心 RMSNorm。

为什么必须 hybrid:纯线性注意力的召回能力(大海捞针式精确检索)不如全注意力。主流做法是 3:1 混合——3 层 Gated DeltaNet + 1 层全局注意力。Qwen3-Next 首次把这个组合用在生产模型上,MiniMax-01 的 lightning attention、Kimi 的 KDA 都属于同一族。

import torch
import torch.nn as nn
import torch.nn.functional as F


class GatedDeltaNet(nn.Module):
    """Gated DeltaNet 递推的最小实现(逐步递推版,便于理解;实际训练用 chunkwise 形式)。"""

    def __init__(self, d_model, n_heads, d_state=64, dtype=None):
        super().__init__()
        self.h = n_heads
        self.dh = d_model // n_heads
        self.dv = d_model // n_heads
        self.wq = nn.Linear(d_model, n_heads * self.dh, bias=False, dtype=dtype)
        self.wk = nn.Linear(d_model, n_heads * self.dh, bias=False, dtype=dtype)
        self.wv = nn.Linear(d_model, n_heads * self.dv, bias=False, dtype=dtype)
        self.w_beta = nn.Linear(d_model, n_heads, bias=False, dtype=dtype)   # 写入门
        self.w_alpha = nn.Linear(d_model, n_heads, bias=False, dtype=dtype) # 衰减门
        self.w_gate = nn.Linear(d_model, n_heads * self.dv, bias=False, dtype=dtype)
        self.wo = nn.Linear(n_heads * self.dv, d_model, bias=False, dtype=dtype)

    def forward(self, x):
        B, L, _ = x.shape
        h, dh, dv = self.h, self.dh, self.dv
        q = F.normalize(self.wq(x).view(B, L, h, dh), dim=-1)
        k = F.normalize(self.wk(x).view(B, L, h, dh), dim=-1)
        v = self.wv(x).view(B, L, h, dv)
        beta = torch.sigmoid(self.w_beta(x)).unsqueeze(-1)      # (B,L,h,1)
        alpha = torch.sigmoid(self.w_alpha(x)).unsqueeze(-1)    # (B,L,h,1)
        gate = torch.sigmoid(self.w_gate(x).view(B, L, h, dv))

        S = torch.zeros(B, h, dh, dv, device=x.device, dtype=x.dtype)  # 固定大小状态
        outs = []
        for t in range(L):
            S = alpha[:, t] * S                                    # (1) 衰减
            kt = k[:, t].unsqueeze(-1)                            # (B,h,dh,1)
            vt = v[:, t].unsqueeze(-1)                            # (B,h,dv,1)
            e = vt - S.transpose(-1, -2) @ kt                      # (2) 预测误差 (B,h,dv,1)
            S = S + beta[:, t] * kt @ e.transpose(-1, -2)         # (3) delta 更新
            y = S.transpose(-1, -2) @ q[:, t].unsqueeze(-1)       # (4) 读出 (B,h,dv,1)
            outs.append(y.squeeze(-1))
        o = torch.stack(outs, dim=1) * gate
        return self.wo(o.reshape(B, L, h * dv))

上面是逐步递推版,便于理解递推逻辑;实际训练用 chunkwise 并行版,把“块内并行 + 块间串行”拆开后全部计算都是矩阵乘:

import torch


def gdn_chunkwise(q, k, v, alpha, beta, chunk_size=64):
    """Gated DeltaNet 的 chunkwise 并行实现。

    块内:解一次下三角系统得到"写入向量" Ē,把 delta rule 的误差修正
          吸收进伪值,随后用衰减掩码做纯矩阵乘;
    块间:串行传递固定大小的状态 S ∈ R^{d_k×d_v}。
    所有重计算都是矩阵乘,可吃满 Tensor Core。

    参数
    ----
    q, k  : (B,H,L,d_k)  已做 L2 归一化的 query / key
    v     : (B,H,L,d_v)  value
    alpha : (B,H,L,1)    衰减门 ∈ (0,1)
    beta  : (B,H,L,1)    写入门 ∈ (0,1)
    """
    B, H, L, d_k = q.shape
    d_v = v.shape[-1]
    C = chunk_size
    o = torch.zeros(B, H, L, d_v, device=q.device, dtype=q.dtype)
    S = torch.zeros(B, H, d_k, d_v, device=q.device, dtype=q.dtype)

    for s in range(0, L, C):
        e = min(s + C, L)
        c = e - s
        qc, kc, vc = q[:, :, s:e], k[:, :, s:e], v[:, :, s:e]
        ac, bc = alpha[:, :, s:e], beta[:, :, s:e]            # (B,H,c,1)

        # 1) 块内累计衰减 γ_r = ∏_{i≤r} α_i
        gamma = torch.cumprod(ac, dim=2)                       # (B,H,c,1)

        # 2) 解下三角系统求写入向量 Ē
        #    (I + diag(β)·A) Ē = diag(β)·(V̄ − K·S),A = tril(KKᵀ, −1)
        #    V̄ = V/γ 是吸收衰减后的等效写入值;Ē 同时编码 delta rule 的误差修正
        KKt = kc @ kc.transpose(-1, -2)                        # (B,H,c,c)
        A = torch.tril(KKt, diagonal=-1)                       # 严格下三角
        M = torch.eye(c, device=q.device, dtype=q.dtype) + A * bc
        v_bar = vc / gamma.clamp_min(1e-6)                     # (B,H,c,d_v)
        rhs = bc * (v_bar - kc @ S)                            # diag(β)·(V̄ − K·S)
        E_bar = torch.linalg.solve_triangular(M, rhs, upper=False)

        # 3) 块内并行输出:O = diag(γ)·[Q·S + (QKᵀ ⊙ Causal)·Ē]
        causal = torch.tril(torch.ones(c, c, device=q.device, dtype=q.dtype))
        A_intra = (qc @ kc.transpose(-1, -2)) * causal         # (B,H,c,c)
        o[:, :, s:e] = gamma * (qc @ S + A_intra @ E_bar)

        # 4) 块尾状态:S ← γ_C·(S + Kᵀ Ē)
        gamma_last = gamma[:, :, -1:, :]                       # (B,H,1,1)
        S = gamma_last * (S + kc.transpose(-1, -2) @ E_bar)

    return o

与逐步递推版的对应关系:递推版第 (1)(2)(3) 步的“衰减 → 误差 → 写入”被压缩成一次三角求解(第 2 步),第 (4) 步的逐 token 读出变成第 3 步的批量矩阵乘。串行部分只剩“块间状态传递”(第 4 步),代价是常数级的。

Gated DeltaNet 让线性注意力第一次同时具备固定大小状态(显存恒定)与接近 softmax 的质量。它没有取代注意力,而是成为 hybrid 架构里负责“压缩记忆”的那一半。

3.6.3 Kimi Delta Attention

KDA(Kimi Delta Attention)是 Kimi K3 在 Gated DeltaNet 基础上的一次针对性升级,核心是解决 delta rule 在 GPU 上跑不动的工程问题。

19.png

图 3.15 Kimi K3 架构(Kimi K3 技术报告 Figure 2)。左下角是 KDA 的内部结构(q/k 走 L2 + Conv,α/β 走独立线性),右侧是 3×KDA + 1×Gated MLA 的混合堆叠,红色细线是 Block Attention Residual。

KDA 的递推把 delta rule 与逐通道的遗忘门结合:

\[S_t = \left(I - \beta_t k_t k_t^\top\right) \mathrm{Diag}(\alpha_t) S_{t-1} + \beta_t k_t v_t^\top, \qquad \tilde{o}_t = S_t^\top q_t \tag{3.46}\]

与 GDN 的 $\alpha_t$ 是每个 head 一个标量不同,KDA 的 $\alpha_t$ 是逐通道(channel-wise)的——不同特征维度可以有各自的记忆寿命。

下界衰减(Lower-bounded decay) 是 KDA 最关键的工程创新。chunkwise 计算时,chunk 内需要除以累计衰减的倒数 $1/\Gamma$,而 $\Gamma$ 是 $(0,1)$ 上保留因子的连乘,倒数会无界增长、在 BF16 下溢出。Kimi Linear 用 negative-Softplus 映射并对角块用显式 position-pair 计算——这很慢。

Kimi K3 换了一个更聪明的参数化,用 scaled sigmoid 给 log-decay 一个下限:

\[g_t^h = g_{\min} \cdot \mathrm{Sigmoid}\left(e^{A_h} z_t^h\right), \qquad g_{\min} = -5 \tag{3.47}\]

于是每个保留因子都满足 $\alpha_t > e^{-5} \approx 6.7 \times 10^{-3}$,16-token 块的累计 log-decay 落在 $(-80, 0)$,对应的倒数缩放因子小于 $e^{80}$,始终在 BF16 的动态范围内。结果是对角块也能用 Tensor Core 的稠密矩阵乘法,彻底消除了 position-pair 这条慢路径。

KDA 还把输出门从低秩参数化改成了全秩投影:

\[y_t = W_o\left[\mathrm{Sigmoid}(W_g x_t) \odot \mathrm{RMSNorm}(\tilde{o}_t)\right] \tag{3.48}\]

input-dependent 的全秩门让每个 token 能精细调控从循环状态读出的通道,而不是所有 token 共享一个低秩方向。

并行计算推导(Chunkwise Form + Context Parallelism)。KDA 在 GDN 的 chunkwise 思路上更进一步,彻底对齐 Tensor Core。记进入 chunk 的状态为 $S[t]$,$\gamma_{i\to j}[t] = \prod_{r=i}^{j}\alpha_r[t]$(逐通道累计衰减)。

Chunkwise 并行形式:KDA 的 UT 变换产出 $U[t], W[t]$,定义伪值项 $\tilde V[t] = U[t] - W[t] S[t]$(把“状态修正项”吸收进一个伪值矩阵),则 chunk 内所有输出一次性并行算出:

\[A[t] = \mathrm{Tril}\Big[\big(Q[t]\odot \Gamma_{1\to C}[t]\big)\big(K[t] / \Gamma_{1\to C}[t]\big)^\top\Big], \qquad O[t] = \underbrace{\big(\Gamma_{1\to C}[t]\odot Q[t]\big) S[t]}_{\text{跨块贡献}} + \underbrace{A[t]\,\tilde V[t]}_{\text{块内贡献}} \tag{3.49}\]

其中 $\mathrm{Tril}(\cdot)$ 保留对角线(因为每个输出读的是当前 token 更新后的状态),$\Gamma_{1\to C}[t] \in \mathbb{R}^{C\times d_k}$ 把累计衰减逐行堆叠。关键在于 $A[t]$ 与两处贡献全部是矩阵乘——配合下界衰减(对角块不再需要 position-pair 慢路径),整条计算路径都能走 Tensor Core。

FlashKDA 内核:chunk 内并行、chunk 间串行的两阶段若朴素交替,SM 会在串行传播时空闲。Kimi 用 CUTLASS 写了 FlashKDA,把工作拆成 token-parallel 阶段与 head-parallel 递推,各自独立调度,让 chunk 内计算与跨 chunk 状态传播重叠,大幅超越 Triton 参考实现。

Context Parallelism(跨设备并行)。这是 KDA 与 vanilla 线性注意力最不同的地方。softmax 注意力做 context parallel 要交换随序列长度增长的 KV 块;线性注意力只携带固定大小的状态 $S \in \mathbb{R}^{d_k\times d_v}$,通信量是常数。但 KDA 不能像 vanilla 线性注意力那样“各 rank 算从零开始的局部状态再求和”——因为 delta rule 的更新是 $S_t = M_t S_{t-1} + \beta_t k_t v_t^\top$,其中 $M_t = (I-\beta_t k_t k_t^\top)\mathrm{Diag}(\alpha_t)$ 是 token 相关的矩阵,它会作用在传入状态上。因此需要累积转移矩阵精确还原:

\[M_{t\leftarrow 1}[i+1] := \prod_{r\leftarrow 1}^{t} M_r, \qquad S_t[i+1] = \tilde S_t[i+1] + M_{t\leftarrow 1}[i+1]\, S_T[i] \tag{3.50}\]

其中 $\tilde S_t[i+1]$ 是“从 $S=0$ 出发”的本地状态,$S_T[i]$ 是上一个 rank 传出的末状态。展开后:

\[S_t[i+1] = \tilde S_t[i+1] + M_{t\leftarrow 1}[i+1] \sum_{j=1}^{i}\Big(\prod_{l\leftarrow j+1}^{i} M_{l\leftarrow 1}[l]^\top\Big)\, \tilde S_T[j] \tag{3.51}\]

含义是:每个 rank 的本地段效果与传入状态无关,可以独立算,之后用累积转移矩阵精确合成。这让 KDA 在多卡上仍能保持线性注意力的“常数通信量”优势,同时不牺牲 delta rule 的表达力。

在 Kimi K3 中的位置:KDA 不是替代全局注意力,而是与 Gated MLA 按 3:1 混合——KDA 负责高效的长上下文 token 混合,Gated MLA 负责无约束的全局内容交互(Gated MLA 用了 NoPE,位置感知完全交给 KDA,从而为百万上下文外推免除了重调 RoPE 频率的麻烦)。

上面的推导给出 chunkwise 并行形式的骨架;与 GDN 不同,KDA 的衰减门是逐通道的,累计衰减 $\Gamma$ 因此是一个逐通道矩阵,不能像 GDN 那样用一个标量掩码概括。实现上把衰减掩码“吸收”进 $Q$、$K$ 的缩放里——$\Gamma \odot Q$ 与 $K / \Gamma$ 的点积天然携带 $\gamma_i / \gamma_j$ 衰减比:

import torch


def kda_chunkwise(q, k, v, z, beta, w_log_a, chunk_size=64, g_min=-5.0):
    """Kimi Delta Attention 的 chunkwise 并行实现。

    与 GDN 版的三点差异:
    1. 衰减门是逐通道的(channel-wise),不同特征维度有各自的记忆寿命;
    2. log-decay 用 scaled sigmoid 给出下界 g_min = −5,保证 BF16 下倒数不溢出;
    3. 用"缩放 Q/K"(Γ⊙Q 与 K/Γ)代替显式衰减掩码,把累计衰减吸进点积。

    参数
    ----
    q, k    : (B,H,L,d_k)  已 L2 归一化
    v       : (B,H,L,d_v)
    z       : (B,H,L,d_k)  衰减 logits(scaled sigmoid 的输入)
    beta    : (B,H,L,1)    写入门
    w_log_a : (H,d_k)      逐 head、逐通道的温度参数 A_h
    """
    B, H, L, d_k = q.shape
    d_v = v.shape[-1]
    C = chunk_size
    o = torch.zeros(B, H, L, d_v, device=q.device, dtype=q.dtype)
    S = torch.zeros(B, H, d_k, d_v, device=q.device, dtype=q.dtype)

    for s in range(0, L, C):
        e = min(s + C, L)
        c = e - s
        qc, kc, vc = q[:, :, s:e], k[:, :, s:e], v[:, :, s:e]
        bc = beta[:, :, s:e]                                # (B,H,c,1)
        zc = z[:, :, s:e]                                   # (B,H,c,d_k)

        # 下界衰减:g = g_min·σ(A⊙z) ∈ (g_min, 0),α = exp(g) ∈ (e^{g_min}, 1]
        g = g_min * torch.sigmoid(w_log_a.view(1, H, 1, d_k) * zc)
        alpha = torch.exp(g)                                 # (B,H,c,d_k)

        # 逐通道累计衰减 γ_r = ∏_{i≤r} α_i
        gamma = torch.cumprod(alpha, dim=2)                  # (B,H,c,d_k)

        # 把衰减掩码吸进点积:q̃ = Γ⊙Q,k̃ = K/Γ,有效 key = Γ⊙K
        q_sc = qc * gamma                                    # (B,H,c,d_k)
        k_sc = kc / gamma.clamp_min(1e-6)                    # (B,H,c,d_k)
        k_eff = kc * gamma                                   # (B,H,c,d_k) Γ⊙K

        # 解下三角系统求写入向量 Ē(编码 delta rule 的误差修正)
        # 误差方程里的内积是 ⟨Γ⊙k_r, k_i/Γ⟩,故 Gram 矩阵 = (Γ⊙K)(K/Γ)ᵀ
        A_mat = torch.tril(k_eff @ k_sc.transpose(-1, -2), diagonal=-1)
        M = torch.eye(c, device=q.device, dtype=q.dtype) + A_mat * bc
        rhs = bc * (vc - k_eff @ S)                          # diag(β)·(V − (Γ⊙K)·S)
        E_bar = torch.linalg.solve_triangular(M, rhs, upper=False)

        # 块内并行输出:O = (Γ⊙Q)·S + Tril[(Γ⊙Q)(K/Γ)ᵀ]·Ē
        A_intra = torch.tril(q_sc @ k_sc.transpose(-1, -2))  # 含对角的因果下三角
        o[:, :, s:e] = q_sc @ S + A_intra @ E_bar

        # 块尾状态:S ← Γ_C·S + [(Γ_C/Γ)⊙K]ᵀ Ē
        gamma_last = gamma[:, :, -1, :]                       # (B,H,d_k)
        S = (gamma_last.unsqueeze(-1) * S
             + (k_sc * gamma_last.unsqueeze(2)).transpose(-1, -2) @ E_bar)

    return o

与 GDN 版的结构完全对齐:第 2 步的三角求解替代逐步递推,第 3 步把输出变成纯矩阵乘,第 4 步串行传状态。差别全在“衰减怎么进点积”——GDN 的标量衰减用显式掩码 $\Gamma_{ij} = \gamma_i/\gamma_j$,KDA 的逐通道衰减则拆成 $\Gamma \odot Q$ 与 $K / \Gamma$ 两路缩放,让 Tensor Core 不必显式构造 $C \times C$ 掩码。FlashKDA 内核进一步把这两个阶段重叠调度,消除串行传播时的 SM 空闲。

从 Gated DeltaNet 到 KDA,线性注意力这十年的主线是“把 delta rule 搬上 Tensor Core”。数学没变多少,但下界衰减这一个参数化技巧,就把线性注意力从“能跑但慢”变成了“能训能推”。

3.7 Gated Attention

前面各节的改进大多落在注意力的计算路径上:共享 KV、低秩压缩、稀疏化、线性化。Gated Attention 换了一个切入角——在 softmax 注意力的输出上加一道门。改动只有十来行代码,却被 Qwen 团队在 3.5T token 的系统消融中验证为显著有效,获 NeurIPS 2025 最佳论文。

20.png

图 3.16 Gated Attention 的门控位置、性能对比与训练曲线(Qiu et al., 2025, Figure 1)。左图是 G1—G5 五个候选门控位置,G1(SDPA 输出后)效果最好;中图是 15B MoE 模型的 PPL 与 MMLU 对比;右图是 1.7B dense 模型的训练损失——门控不仅降低最终损失,还消除了 loss spike。

机制。在每个注意力头的 SDPA 输出 $H$ 上,乘一个由当前 token 隐藏状态产生的 sigmoid 门:

\[H' = H \odot \sigma(X W_\theta), \qquad O = H' W_O \tag{3.52}\]

其中 $X$ 是 pre-norm 后的隐藏状态,$W_\theta \in \mathbb{R}^{d_{model} \times d_k}$ 是每个头独立的门控投影,$\sigma$ 是 sigmoid。门是逐元素、逐头独立、乘性的。论文在位置(5 种)、粒度(elementwise / headwise)、参数共享(head-specific / head-shared)、激活函数(sigmoid / SiLU)、运算方式(乘性 / 加性)这几个维度上共训练了 30 余种变体,逐一消融后锁定了上述组合。

五个门控位置。论文对比了 G1(SDPA 输出后)、G2(Value 投影后)、G3(Key 投影后)、G4(Query 投影后)、G5(最终输出层后)五个插入点。G1 显著领先:15B MoE 上 PPL 从 6.026 降到 5.761,MMLU 从 58.79 升到 60.82。G2 也能消除隐状态离群值,但不消除 attention sink;G3、G4 保持底层映射的线性,收益几乎为零。这说明门必须插在 $W_V W_O$ 这条低秩线性映射的中间,才能引入有效的非线性。

为什么门控有效。论文归结为两个机制:

  1. 非线性:$W_V W_O$ 是低秩线性映射,表达力受秩约束。在两者之间插一个 sigmoid 门,等价于在低秩映射中间引入一次非线性变换,打破了秩瓶颈。
  2. query 依赖的稀疏门控:门分数 $\sigma(X W_\theta)$ 随 query 变化,天然呈稀疏分布——多数位置的门接近 0,只有少数相关位置被放行。这直接抑制了 attention sink:baseline 模型首 token 的注意力占比高达 46.7%,加门控后降到 4.8%(单层峰值从 83% 降到 4%),隐状态最大激活值从 1053 降到 94。

attention sink 的消除带来额外收益:长上下文外推能力显著提升——YaRN 扩展到 128k 时,64k 长度的 RULER 得分从 37.51 提到 66.60,128k 从 31.65 提到 58.82。训练稳定性也受益:baseline 在学习率 8e-3 时发散,门控模型正常训练且 PPL 更低;门控本身的开销不到 2% wall-time。

与前文的呼应。Gated Attention 把“门控”这条线索从 FFN(SwiGLU)和线性注意力($\alpha_t/\beta_t$ 门)延伸到了 softmax 注意力内部。NSA 的三分支门控输出、Switch Heads 的 sigmoid 门控、Forgetting Transformer 的遗忘门都属于同一家族,但此前的论文各自把收益归因到稀疏选择或路由上,没有单独量化门控的贡献——Gated Attention 的价值恰恰是把“门”从其他机制里剥离出来单独验证。该技术已用于 Qwen3-Next:它把 Gated Attention(3.7 节)与 Gated DeltaNet(3.6.2 节)交替堆叠,前者负责全局内容交互,后者负责长上下文压缩记忆。

import torch
import torch.nn as nn
import torch.nn.functional as F


class GatedAttention(nn.Module):
    """Gated Attention:SDPA 输出后乘一个 head-specific 的 sigmoid 门。"""

    def __init__(self, d_model, n_heads, head_dim=None):
        super().__init__()
        self.h = n_heads
        self.dh = head_dim or d_model // n_heads
        self.w_q = nn.Linear(d_model, n_heads * self.dh, bias=False)
        self.w_k = nn.Linear(d_model, n_heads * self.dh, bias=False)
        self.w_v = nn.Linear(d_model, n_heads * self.dh, bias=False)
        self.w_gate = nn.Linear(d_model, n_heads * self.dh, bias=False)  # 门控投影
        self.w_o = nn.Linear(n_heads * self.dh, d_model, bias=False)

    def forward(self, x, mask=None):
        B, L, _ = x.shape
        h, dh = self.h, self.dh
        q = self.w_q(x).view(B, L, h, dh).transpose(1, 2)   # (B,h,L,dh)
        k = self.w_k(x).view(B, L, h, dh).transpose(1, 2)
        v = self.w_v(x).view(B, L, h, dh).transpose(1, 2)

        attn = F.scaled_dot_product_attention(q, k, v, attn_mask=mask)

        # G1 门控:head-specific、elementwise、sigmoid、乘性
        gate = torch.sigmoid(
            self.w_gate(x).view(B, L, h, dh).transpose(1, 2)  # (B,h,L,dh)
        )
        attn = attn * gate                                     # Hadamard 乘

        out = attn.transpose(1, 2).reshape(B, L, h * dh)
        return self.w_o(out)

Gated Attention 的实验方法值得记一笔:在固定算力预算下逐维度消融、把门控从其他机制里隔离出来单独度量,这种“控制变量”的做法在架构研究里并不多见。它的结论也朴素——不需要新算子或新架构,只加一个逐元素乘法,PPL、长上下文、训练稳定性就同时获益。

4. 归一化的改进(Normalization)

残差连接(Add)与归一化(Norm)通常成对出现。残差连接的优点是让梯度在层与层之间可以直接回传,从而避免梯度消失,同时使网络能够更有效地学习“特征增量”,而非从头重构表示。但它也有一个缺点:残差项每经过一层就累加一次,层数一多就容易引起数值膨胀。这一缺点恰好是归一化的作用所在。归一化的核心作用是防止数值爆炸、稳定前向传播:无论 Add 之后的数值有多大,归一化都会统一地把数值除以方差(或均方根),将特征向量的尺度拉回到一个稳定的范围内(通常均值接近 0,方差接近 1),从而保证传给下一层的数据始终是温和且可控的。

2017 年的原始 Transformer 采用的是 Post-Norm 架构,即先做残差、再做归一化;而现代主流则是 Pre-Norm 架构,即先做归一化、再做残差。这一看似简单的调整,使得残差连接能够更好地发挥作用。

两种范式的公式对比:

Post-Norm(原始 Transformer):

\[x_{l+1} = \text{LN}\big(x_l + \text{Sublayer}(x_l)\big) \tag{4.1}\]

Pre-Norm(现代主流,GPT-2 之后被广泛采用):

\[x_{l+1} = x_l + \text{Sublayer}\big(\text{LN}(x_l)\big) \tag{4.2}\]

21.png

图 4.1 (a) Post-LN 与 (b) Pre-LN 的层结构对比(Xiong et al., 2020, Figure 1)。注意归一化在残差加法的哪一侧。

Pre-Norm 的好处是主干梯度有一条“无障碍通道”(恒等路径),深层网络训练更稳、可以不用 warm-up;代价是同等参数量下效果可能略逊 Post-Norm(表示能力被主干稀释),所以现代模型常配合更好的学习率调度与更深的堆叠。

以上是归一化“放在哪里”(block 级位置)的演进;至于归一化本身“怎么算”,本章按 LayerNorm(4.1 节)、BatchNorm(4.2 节)、RMSNorm(4.3 节)与 L2Norm(4.4 节)的顺序分别介绍。

4.1 LayerNorm

LayerNorm是对每个样本的所有特征做归一化,这消除了不同样本间的大小关系,但是保留了一个样本内不同特征之间的大小关系。LayerNorm 适用于 NLP 领域,这时输入尺寸为 b × l × d (批量大小x序列长度x嵌入维度)。它对每个 token 的特征向量逐个进行归一化计算。

LayerNorm 的目的是把输出的特征数据重新拉回到“均值为 0,方差为 1”的标准分布,然后再乘以一个可学习的缩放参数 $\gamma$ 和平移参数 $\beta$。这个标准公式是这样的:

\[y = \frac{x - \mu}{\sigma} \odot \gamma + \beta \tag{4.3}\]

其中最耗时的是计算这个均值 $μ$,计算它需要遍历一遍当前的所有特征,把它们加起来求一个平均数。对于动辄几千维、上万维的超长向量来说,这一步计算既费算力又拖慢硬件的显存读取速度。

4.2 BatchNorm

与 LayerNorm 的逐样本归一化不同,BatchNorm是对整个 batch 样本内的每个特征做归一化,这消除了不同特征之间的大小关系,但是保留了不同样本间的大小关系。BatchNorm 适用于 CV 领域,这时输入尺寸为 b × c × h × w (批量大小x通道x长x宽),图像的每个通道 c 看作一个特征,BN 可以把各通道特征图的数量级调整到差不多,同时保持不同图片相同通道特征图间的相对大小关系其计算式为:

\[y = \frac{x - \mu_{\mathcal{B}}}{\sqrt{\sigma_{\mathcal{B}}^2 + \epsilon}} \odot \gamma + \beta \tag{4.4}\]

其中 $\mu_{\mathcal{B}}$ 与 $\sigma_{\mathcal{B}}^2$ 是当前 mini-batch 内该特征维度的均值与方差,$\epsilon$ 为数值稳定项,$\gamma$、$\beta$ 是可学习的缩放与平移参数。

尽管 BatchNorm 在 CNN 中非常成功,它并不适合 Transformer 类架构:其一,NLP 序列长度不一,padding 位置会污染 batch 统计量;其二,小 batch 下统计量噪声大,训练不稳定;其三,自回归推理时 batch 常退化为单样本,batch 统计失真。因此 Transformer 从一开始就选择了 LayerNorm(4.1 节),并进一步演化出 RMSNorm(4.3 节)。

4.3 RMSNorm

RMSNorm(Root Mean Square Normalization,均方根归一化)是 LayerNorm 的简化且高效的替代方案,来自 2019 年的论文 Root Mean Square Layer Normalization(Zhang & Sennrich, 2019),被 T5 和当前流行的 LLaMA 模型所使用,旨在稳定训练过程、加速收敛,同时减少计算开销。其提出的动机是 LayerNorm 运算量比较大:RMSNorm 的性能与 LayerNorm 相当,但可以节省 7% 到 64% 的运算。

2019年,Biao Zhang 和 Rico Sennrich做了一系列消融实验,LayerNorm的完整操作利用了平移不变性和缩放不变性,先通过$x−μ$,强制让这一层激活值的均值变成 0,再通过除以标准差$\sigma$,强制让激活值的方差变成 1。他们发现,对于归一化而言,最有效的是缩放操作而不是平移。$μ$ 减与不减并不关键,真正起决定性作用的是“除以方差”这个缩放动作,它能有效控制前向传播时数值的膨胀,并平滑损失函数的地形。

我们再来看一下LayerNorm 中的方差 $\sigma$ 和 RMSNorm 中的 RMS(x) 的底层公式:

\[\sigma = \sqrt{\frac{1}{d} \sum_{i=1}^{d} (x_i - \mu)^2} \tag{4.5}\] \[\text{RMS}(x) = \sqrt{\frac{1}{d} \sum_{i=1}^{d} x_i^2} \tag{4.6}\]

如果数据的均值 $μ$ 本身就接近于 0,那么 $\sigma$ 和 $\text{RMS}(x)$ 在数学上是完全等价的。所以RMSNorm出现了,这个改进直接让一层的前向和反向传播速度提升 10% 到 40%。

22.png

图 4.2 RMSNorm 的定义公式(Zhang & Sennrich, 2019)

23.png

图 4.3 RNNSearch 机器翻译任务上 LayerNorm 与 RMSNorm 的验证集 BLEU 曲线(RMSNorm 论文 Figure 4)。RMSNorm 以更少计算取得更好的收敛。

表 4.1 RMSNorm 的主要特性

|特性|说明| |—|—| |核心思想|仅使用输入特征的均方根(RMS) 进行缩放归一化,省略了LayerNorm中的均值中心化(去均值)步骤。| |关键公式|RMS(x) = √( (1/n) * Σ(x_i²) ),归一化输出:y = (x / RMS(x)) * γ。| |主要优势|计算效率更高(计算量减少约30% )、数值稳定性好、在多数场景下性能与LayerNorm相当甚至更优。| |应用场景|广泛应用于Transformer及其变体模型(如LLaMA、Mamba ),替代编码器/解码器中的LayerNorm。| RMSNorm的工作流程可以清晰地分为三个步骤:

  1. 计算均方根(RMS):对于输入向量 x,首先计算其最后一个维度(通常是特征维度)上所有元素平方的均值,然后开平方根,并加上一个为了数值稳定的小常数 ϵ(如1e-8)防止除零错误。

  2. 执行归一化:将输入向量 x的每个元素除以上一步计算出的RMS值。这一步将所有特征的尺度统一到一个相对稳定的范围内,但不改变它们的中心位置(即不强制均值为0)。

  3. 应用仿射变换:引入一个可学习的缩放参数 γ(通常是与输入维度相同的向量,初始化为1),与归一化后的结果进行元素级乘法。这一步为模型提供了灵活性,允许它决定每个特征需要被恢复或调整到何种程度。RMSNorm 的原始论文和许多实现中不包含可学习的偏移参数 β,这是它与LayerNorm的另一个区别。

与常见的归一化方法进行对比:

表 4.2 常见归一化方法对比

方法 核心操作 主要特点 适用场景
RMSNorm 仅除以RMS,不减去均值。 计算高效,性能稳定,是许多现代架构的首选。 Transformer系列模型(如LLaMA )、RNN、CNN。
LayerNorm 先减去均值,再除以标准差。 经典方法,具备平移和缩放不变性,但计算量稍大。 早期的Transformer模型(如原始BERT、GPT-2)。
BatchNorm(见 4.2 节) 对一个小批次(Batch)内所有样本的同一特征维度进行归一化。 依赖批量大小,对小批量效果不佳。 卷积神经网络(CNN)用于图像分类等任务。

RMSNorm相对于LayerNorm最显著的优势在于计算效率。由于省略了计算均值以及相应的减法操作,RMSNorm的计算量更小,训练速度更快,并且在某些情况下显示出更好的训练稳定性。

代码:

class RMSNorm(nn.Module):
    """RMSNorm 层:通过均方根归一化最后一维,并可选与 Qwen3 保持 float32 计算兼容。"""
    def __init__(self, emb_dim, eps=1e-6, bias=False, qwen3_compatible=True):
        super().__init__()
        self.eps = eps
        self.qwen3_compatible = qwen3_compatible
        self.scale = nn.Parameter(torch.ones(emb_dim))
        self.shift = nn.Parameter(torch.zeros(emb_dim)) if bias else None

    def forward(self, x):
        # 保留输入的原始 dtype,最后再转换回来
        input_dtype = x.dtype

        # Qwen3 兼容模式下先转为 float32,避免精度损失
        if self.qwen3_compatible:
            x = x.to(torch.float32)

        # 计算最后一维的均方根并做归一化
        variance = x.pow(2).mean(dim=-1, keepdim=True)
        norm_x = x * torch.rsqrt(variance + self.eps)
        norm_x = norm_x * self.scale  # 乘以可训练缩放参数

        if self.shift is not None:
            norm_x = norm_x + self.shift  # 可选的偏置项

        return norm_x.to(input_dtype)  # 恢复到输入 dtype

4.4 L2Norm(QK-Norm)

最后一个改进藏在注意力算子内部:对 Query 和 Key 各做一次归一化再算点积。它不改变注意力的表达能力,只掐住 logits 的量级——是超大模型训练的稳定机制。

问题是什么。随着模型变大、训练变长,注意力 logits($QK^\top$ 的输出)会指数级增长。小模型时这个增长被其他训练动态掩盖;一旦参数上百亿、训练几十万亿 token,logits 膨胀会导致 softmax 饱和、损失尖峰、训练直接崩溃(training instability spike)。这是 ViT-22B(220 亿参数)在扩展视觉 Transformer 时踩到的坑。

解法出人意料地简单:在 $Q$、$K$ 各自的投影输出上,各加一个归一化(L2 归一化或 RMSNorm),把向量范数钉住,logits 就有了上界:

\[\hat{Q} = \frac{Q}{\|Q\|_2}\ (\text{或 } \mathrm{RMSNorm}(Q)), \qquad \hat{K} = \frac{K}{\|K\|_2}, \qquad \mathrm{Attn}(Q,K,V) = \mathrm{softmax}\!\left(\frac{\hat{Q}\hat{K}^\top}{\sqrt{d_k}}\right)V \tag{4.7}\]

用 L2 归一化时,$\hat q^\top \hat k \in [-1, 1]$(两个单位向量的点积),logits 天然有界;再乘 $\sqrt{d_k}$ 只是恢复尺度。代价:归一化把“向量模长”这一路信息抹掉了,而模长有时携带相关性强度——因此有个别工作发现 QK-Norm 会轻微削弱长上下文检索的敏感度,但换来的训练稳定性收益在大模型上压倒性地划算。

起源与普及:QK-Norm 由 ViT-22B(Dehghani et al., 2023, arXiv:2302.05442)系统性提出并验证,随后被 Llama 3、Gemma 2/3、DeepSeek-V4 等大模型采纳为默认配置。原始实现用 LayerNorm(沿 ViT 惯例),Llama 3 换成 RMSNorm 效果相当——因为目的只是限制幅度,哪种归一化都能做到。

在本文档各处的体现:QK-Norm 并非孤立的小技巧,前面见过的多个架构都在用它:

表 4.3 QK-Norm 在各架构中的归一化位置

架构 归一化位置 形式
Gated DeltaNet(3.6.2) Q/K 投影后、递推前 L2Norm($|q|=|k|=1$)
Kimi Delta Attention(3.6.3) $\mathrm{L2Norm}(\mathrm{Swish}(\mathrm{ShortConv}(W x)))$ L2Norm
CSA / HCA(3.5.3) 核心注意力前对 query 与压缩 KV 条目 RMSNorm
Qwen Sparse Attention(3.5.4) 索引器与核心注意力的 q/k RMSNorm
import torch
import torch.nn as nn
import torch.nn.functional as F


class QKNormAttention(nn.Module):
    """QK-Norm:算点积前分别归一化 Q、K,掐住 logits 量级。"""

    def __init__(self, d_model, n_head, use_rms=False):
        super().__init__()
        self.n_head = n_head
        self.head_dim = d_model // n_head
        self.use_rms = use_rms
        self.w_q = nn.Linear(d_model, d_model, bias=False)
        self.w_k = nn.Linear(d_model, d_model, bias=False)
        self.w_v = nn.Linear(d_model, d_model, bias=False)
        self.out = nn.Linear(d_model, d_model, bias=False)
        self.rms = nn.RMSNorm(self.head_dim)

    def _norm(self, x):
        if self.use_rms:
            return self.rms(x)
        # L2 归一化(沿 head_dim 维度)
        return F.normalize(x, p=2, dim=-1)

    def forward(self, x, mask=None):
        B, L, D = x.shape
        q = self.w_q(x).view(B, L, self.n_head, self.head_dim)
        k = self.w_k(x).view(B, L, self.n_head, self.head_dim)
        v = self.w_v(x).view(B, L, self.n_head, self.head_dim)

        q = self._norm(q)                                 # <-- QK-Norm 的核心
        k = self._norm(k)

        attn = torch.einsum("blhc,bshc->blhs", q, k) / self.head_dim ** 0.5
        if mask is not None:
            attn = attn.masked_fill(~mask, float("-inf"))
        o = torch.softmax(attn, dim=-1)
        o = torch.einsum("blhs,bshc->blhc", o, v)
        return self.out(o.reshape(B, L, -1))

QK-Norm 是“归一化放哪里”这条线的细化——从 block 级(Pre-Norm/Post-Norm,见本章开头)深入到 attention 内部的向量级。它与 logit soft-capping(Gemma 2 用 $\tanh$ 软截断)、z-loss(PaLM)是同一族稳定化手段:不牺牲表达,只把激活/分数的量级关进可控区间。

归一化这十年的演进路线可以概括为:BatchNorm(CV)→ LayerNorm(NLP/Transformer)→ Post-Norm → Pre-Norm → RMSNorm + QK-Norm。每一步都在做减法:去掉不必要的统计量(均值)、去掉不必要的位置(挪到残差前)、去掉不必要的参数(β)。不变的是“控制数值尺度”这一使命。

5. 位置编码的改进

Transformer 的注意力是置换等变的(permutation equivariant)——不显式注入位置信息,模型就分不清 “猫追狗” 和 “狗追猫”。十年来位置编码经历了 绝对 → 相对 → 旋转 三代演进,但目标从未变过:把「序」注入到「集」里。

绝对位置编码根据单个单词的绝对位置来定义位置编码,每个位置都会分配一个位置编码,将位置编码的表征和单词本身的表征进行融合,再输入给Self Attention,相当于在输入层就把位置信息给弥补上去。绝对位置编码从实现方式上又分为固定式和可学习式,固定式形如原生的Transformer所采用的三角sin-cos位置编码,所谓固定指的是根据一个无参的固定公式就可以推演出位置编码,而可学习式没有固定的位置编码公式,通过初始化位置向量让模型根据上下文数据自适应地学习出来,Bert和GPT采用的可学习式。

原始论文的正弦-余弦位置编码:

\[PE_{(pos, 2i)} = \sin\left(\frac{pos}{10000^{2i/d_{\text{model}}}}\right) \tag{5.1}\] \[PE_{(pos, 2i+1)} = \cos\left(\frac{pos}{10000^{2i/d_{\text{model}}}}\right) \tag{5.2}\]

不同维度 $i$ 对应不同频率的正弦波,形成从高频到低频的“尺子”。论文选择固定式而非可学习式,是因为固定式在训练长度之外有外推潜力——但实践证明 sin-cos 的外推能力有限,这为后来的 RoPE 埋下伏笔。

旋转位置编码RoPE是一种固定式的绝对位置编码策略,但是它的绝对位置编码配合Transformer的Attention内积注意力机制能达到相对位置编码的效果。RoPE的本质是对两个token形成的Query和Key向量做一个变换,使得变换后的Query和Key带有位置信息,进一步使得Attention的内积操作不需要做任何更改就能自动感知到相对位置信息。RoPE 的出发点与策略采用的是相对位置编码思想,实现方式却是绝对位置编码。

24.png

图 5.1 RoPE 的实现示意——对 Q/K 向量按二维子空间施加 $m\theta$ 的旋转(Su et al., 2021, Figure 1)。

RoPE 的数学形式:设 token 位置为 $m$,Query/Key 向量为 $x$,RoPE 对 $x$ 的每对相邻维度 $(x_{2i}, x_{2i+1})$ 施加一个角度为 $m\theta_i$ 的二维旋转:

\[f(x_m, m) = R_{\Theta, m} x_m, \qquad \theta_i = 10000^{-2i/d} \tag{5.3}\]

在二维情况下,旋转矩阵写作:

\[R_{\Theta, m} = \begin{pmatrix} \cos m\theta & -\sin m\theta \\ \sin m\theta & \cos m\theta \end{pmatrix} \tag{5.4}\]

RoPE 的巧妙之处在于:通过旋转操作编码绝对位置,同时在内积中自然体现相对位置。其核心性质是内积只依赖相对位置差:

\[\langle f(q, m), f(k, n) \rangle = g(q, k, m - n) \tag{5.5}\]

也就是说,对 Q 在位置 $m$、K 在位置 $n$ 施加旋转后,二者的点积只与 $m-n$ 有关——用绝对编码的方式实现了相对编码的效果,而且不需要修改注意力计算本身。

强大的长度外推性:外推性是指模型处理比训练时所见文本更长的文本的能力。RoPE本身具有良好的外推性质,而且研究者们还开发了像NTK-aware缩放这样的技术,可以进一步通过微调甚至不微调的方式,有效扩展模型的上下文处理窗口。例如,让一个原本只能处理2048个token的模型,能够有效处理8192个token甚至更长的文本。

NTK-aware 缩放的核心直觉:不要均匀缩放所有频率(那样高频细节会被破坏),而是把 $\theta_i = 10000^{-2i/d}$ 中的基频 10000 调大:

\[\theta_i' = \left(b \cdot s\right)^{-2i/d} \tag{5.6}\]

其中 $s$ 为缩放因子,$b$ 为基频。高频维度几乎不变(保住局部位置分辨力),低频维度被“拉长”(提供更远的位置范围),从而实现免微调或少微调的上下文扩展(如 RoPE-NTK、YaRN、Dynamic NTK)。

import torch


def precompute_rope_params(head_dim, max_len=4096, base=10000.0):
    """预计算 RoPE 的 cos/sin 表:inv_freq 形状 (head_dim//2,)"""
    inv_freq = base ** (-torch.arange(0, head_dim, 2).float() / head_dim)  # θ_i
    positions = torch.arange(max_len).float()                             # m
    angles = torch.outer(positions, inv_freq)                             # (max_len, head_dim//2)
    return torch.cos(angles), torch.sin(angles)                           # 各 (max_len, head_dim//2)


def apply_rope(x, cos, sin, offset=0):
    """
    对 Q 或 K 施加旋转位置编码。
    x:   (B, h, L, head_dim)
    cos: (max_len, head_dim//2),由 precompute_rope_params 预计算
    """
    B, h, L, d = x.shape
    cos = cos[offset:offset + L].to(x.dtype)      # (L, head_dim//2)
    sin = sin[offset:offset + L].to(x.dtype)

    x1, x2 = x[..., 0::2], x[..., 1::2]           # 偶数维 / 奇数维,各 (B,h,L,d//2)
    cos = cos[None, None, :, :]                    # 广播到 (1,1,L,d//2)
    sin = sin[None, None, :, :]
    out = torch.empty_like(x)
    out[..., 0::2] = x1 * cos - x2 * sin           # 二维旋转的显式形式
    out[..., 1::2] = x1 * sin + x2 * cos
    return out


# 示例:d_model=2048, num_heads=32 -> head_dim=64

# head_dim=64 -> 32 个二维子空间,每个子空间一个频率 θ_i

cos, sin = precompute_rope_params(head_dim=64, max_len=8192)
q = torch.randn(2, 32, 128, 64)                    # (B, h, L, head_dim)
q_pos = apply_rope(q, cos, sin)                    # 注入位置信息

位置编码十年演进:sin-cos(2017,固定绝对)→ 可学习绝对(BERT/GPT)→ 相对位置偏置(T5/DeBERTa)→ RoPE(2021,旋转绝对=相对)→ RoPE 外推扩展(NTK/YaRN/LongRoPE)。不变的是“注入序信息”;变的是“用绝对、相对还是旋转实现”以及“能不能外推到更长上下文”。

6. 前馈神经网络 (Feed Forward)

注意力机制负责在词与词之间找关系,而 FFN 负责在每个词内部进行特征的深度加工和记忆存储。现代大模型在这个部分,也发生了深刻的变化。

25.png

图 6.1 Transformer 中的 FFN 子层(Vaswani et al., 2017, Figure 1 局部)——Add & Norm 之后的两层线性变换。

原始 Transformer 的前馈网络是两层线性变换夹一个激活函数(位置逐点共享参数):

\[\text{FFN}(x) = W_2 \, \sigma(W_1 x + b_1) + b_2 \tag{6.1}\]

其中 $W_1 \in \mathbb{R}^{d_{\text{model}} \times d_{ff}}$,$W_2 \in \mathbb{R}^{d_{ff} \times d_{\text{model}}}$,原始论文取 $d_{ff} = 2048 = 4 \times d_{\text{model}}$,$\sigma$ 为 ReLU。FFN 占了 Transformer 的大部分参数(两层大矩阵),这也是为什么它是“记忆存储”的主要载体——研究发现事实性知识大量存在 FFN 的权重里。

FFN 的第一个重大变化来自 GLU(Gated Linear Units) 及其变体。Shazeer 在 2020 年《GLU Variants Improve Transformer》中把门控引入 FFN,用一个额外的门控分支控制信息流:

\[\text{GLU}(x) = (xW + b) \otimes \sigma(xV + c) \tag{6.2}\]

其中 $\otimes$ 是逐元素乘法,$\sigma$ 是 sigmoid 门。把激活函数换掉就得到现代主流的 SwiGLU 与 GeGLU:

\[\text{SwiGLU}(x) = \big(\text{Swish}_{\beta=1}(xW) \otimes xV\big) U \tag{6.3}\] \[\text{GeGLU}(x) = \big(\text{GELU}(xW) \otimes xV\big) U \tag{6.4}\]

SwiGLU 把 FFN 从“两层矩阵”变成“三块矩阵”($W, V, U$),PaLM、LLaMA、Qwen、Mistral 等几乎所有现代大模型都采用 SwiGLU。为了保持参数量不变,中间维度通常从 $4d$ 调整为 $\frac{8}{3}d$(再对齐到硬件友好的倍数)。

class FeedForward(nn.Module):
    """
    FFN 十年演进一览:
    - 原始 Transformer (2017): Linear -> ReLU -> Linear
    - BERT/GPT 时代:          Linear -> GELU -> Linear
    - 现代 LLM (LLaMA/Qwen):   SwiGLU 门控 FFN
    """
    def __init__(self, d_in, d_hidden=None, variant="swiglu", bias=False):
        super().__init__()
        if d_hidden is None:
            # SwiGLU 惯例:8/3 * d_in,再取 256 的倍数对齐
            d_hidden = int((8 / 3) * d_in)
            d_hidden = ((d_hidden + 255) // 256) * 256

        self.variant = variant
        if variant == "original":
            self.fc1 = nn.Linear(d_in, 4 * d_in, bias=bias)
            self.fc2 = nn.Linear(4 * d_in, d_in, bias=bias)
            self.act = nn.ReLU()
        elif variant == "gelu":
            self.fc1 = nn.Linear(d_in, 4 * d_in, bias=bias)
            self.fc2 = nn.Linear(4 * d_in, d_in, bias=bias)
            self.act = nn.GELU()
        elif variant == "swiglu":
            # 三个投影:门控分支 W、值分支 V、输出投影 U
            self.w_gate = nn.Linear(d_in, d_hidden, bias=bias)  # W
            self.w_up   = nn.Linear(d_in, d_hidden, bias=bias)  # V
            self.w_down = nn.Linear(d_hidden, d_in, bias=bias)  # U
            self.act = nn.SiLU()                                # Swish(x) = x*σ(x)
        else:
            raise ValueError(f"unknown variant: {variant}")

    def forward(self, x):
        if self.variant == "swiglu":
            # SwiGLU(x) = ( SiLU(xW) ⊗ xV ) U
            return self.w_down(self.act(self.w_gate(x)) * self.w_up(x))
        return self.fc2(self.act(self.fc1(x)))

FFN 的演进路线:ReLU 两层(2017)→ GELU 两层(2018-2019,BERT/GPT-2)→ SwiGLU 三块门控(2020 至今)→ 变体搜索/MoE-FFN(2022 至今)。MoE(Mixture-of-Experts)把 FFN 的“一个大网”换成“多个小专家 + 路由”,本质是把 FFN 的参数容量继续放大,同时保持激活参数量不变——这可以看作 FFN 思路的延伸。不变的是“逐位置、非线性、存知识”;变的是“要不要门控、激活函数用什么、要不要稀疏化”。

6.1 MoE

MoE(Mixture-of-Experts,混合专家)不是新东西,但它重新定义了 FFN 的扩展方式:把 FFN 从“一个大网”换成“多个小专家 + 一个路由器”,参数容量与激活计算量解耦。

26.png

图 6.2 Switch Transformer 的 encoder block(Switch Transformer 论文 Figure 2)。稠密 FFN 被替换成“Router + 多个 FFN”,每个 token 走其中一条。

Switch Transformer(Fedus et al., 2021,arXiv:2101.03961)是现代 MoE 的分水岭。它的两个关键设计:

  1. Top-1 路由:每个 token 只送到得分最高的那个专家。论文证明 top-1 在负载均衡策略下质量不输 top-2,但计算量减半
  2. 容量因子(Capacity Factor):每个专家最多接收 $\frac{c \cdot T}{E}$ 个 token($T$ 是 batch 内 token 数,$E$ 是专家数,$c$ 是容量因子)。超出容量的 token 被直接丢弃并绕过后续 FFN,同时引入辅助损失来平衡专家负载

路由公式:

\[y = \sum_{i=1}^{E} \mathrm{Router}(x)_i \cdot \mathrm{FFN}_i(x), \qquad \mathrm{Router}(x) = \mathrm{Softmax}(W_r x) \tag{6.5}\]

Mixtral(2024)改用 top-2 路由,在激活参数不变的情况下提升质量。

DeepSeekMoE(DeepSeek-V2/V3)则引入了两个更精细的机制,把 MoE 从“大而全”推向“专而精”:

  • 细粒度专家切分(Fine-grained Expert Segmentation):把 FFN 的隐藏维度切成更细的多个专家,总参数量不变但专家数量更多 → 每个专家的参数更少、专精程度更高
  • 共享专家隔离(Shared Expert Isolation):指定若干永远激活的共享专家,负责捕获通用知识,避免通用知识被路由到不同专家而重复学习

DeepSeekMoE 的输出:

\[h_t' = u_t + \sum_{i=1}^{N_s} \mathrm{FFN}_i^{(s)}(u_t) + \sum_{i=1}^{N_r} g_{i,t}\,\mathrm{FFN}_i^{(r)}(u_t) \tag{6.6}\]

其中 $g_{i,t}$ 是路由权重(对选中专家做归一化),$N_s$ 与 $N_r$ 分别是共享专家与路由专家的数量。DeepSeek-V3 进一步改用无辅助损失的负载均衡(auxiliary-loss-free),用可学习的偏置项替代辅助损失,避免辅助损失对训练目标的干扰。

近两年的规模竞赛:

表 6.1 近年 MoE 模型的规模对比

模型 总参数 激活参数 专家数 每 token 激活
Mixtral 8×7B 46.7B 12.9B 8 2
DeepSeek-V3 671B 37B 256 8
Kimi K3 2.78T 104B 896 16
Qwen3.8-Flash-Next 125B (+51B n-gram) 6B 512 10 + 1 共享

Kimi K3 的 Stable LatentMoE 还加了两个稳定化手段:一是把路由专家的作用空间压到 3584 维的潜空间再送进专家(省算力),二是在专家聚合与上投影之间插入 RMSNorm,降低路由分支对尺度变化的敏感度。

MoE 的代价:路由带来的 all-to-all 通信是分布式训练的最大瓶颈之一;专家负载不均会导致“热专家”拖慢整步;小 batch 场景下专家利用率低下。DeepSeek 的无辅助损失均衡、Kimi K3 的 Quantile Balancing(用路由分数的 $(1-k/n)$ 分位数直接反推每个专家的偏置)、Qwen 的 10+1 路由配置,都是在解决同一类问题。

import torch
import torch.nn as nn
import torch.nn.functional as F


class MoE(nn.Module):
    """Top-k 路由 MoE(Mixtral 风格),含负载均衡辅助损失。"""

    def __init__(self, d_model, d_ff, n_experts=8, top_k=2):
        super().__init__()
        self.n_experts, self.top_k = n_experts, top_k
        self.gate = nn.Linear(d_model, n_experts, bias=False)
        # 每个专家一个独立 FFN(SwiGLU)
        self.w_gate = nn.ModuleList([nn.Linear(d_model, d_ff, bias=False) for _ in range(n_experts)])
        self.w_up = nn.ModuleList([nn.Linear(d_model, d_ff, bias=False) for _ in range(n_experts)])
        self.w_down = nn.ModuleList([nn.Linear(d_ff, d_model, bias=False) for _ in range(n_experts)])

    def forward(self, x):
        B, L, D = x.shape
        logits = self.gate(x)                                  # (B,L,E)
        probs = F.softmax(logits, dim=-1)
        topk_w, topk_i = probs.topk(self.top_k, dim=-1)        # (B,L,k)
        topk_w = topk_w / topk_w.sum(dim=-1, keepdim=True)     # 归一化路由权重

        out = torch.zeros_like(x)
        for e in range(self.n_experts):
            # 找出路由到专家 e 的 (token, slot)
            mask = (topk_i == e)                                # (B,L,k)
            if not mask.any():
                continue
            tok_idx, slot_idx = mask.nonzero(as_tuple=True)
            xe = x[tok_idx]                                     # (N_e, D)
            ye = self.w_down[e](F.silu(self.w_gate[e](xe)) * self.w_up[e](xe))
            # 用累加技巧把同一 token 的多个专家输出按权重合并
            out.index_add_(0, tok_idx, ye * topk_w[tok_idx, slot_idx].unsqueeze(-1))

        # 负载均衡辅助损失:鼓励各专家被均匀选中
        density = probs.mean(dim=(0, 1))                        # (E,)
        routing = (topk_i == torch.arange(self.n_experts, device=x.device)).float().mean(dim=(0, 1))
        aux_loss = self.n_experts * (density * routing).sum()
        return out, aux_loss

MoE 把 FFN 的“参数效率”和“计算效率”解耦,是大模型规模化的主要推手之一。它十年不变的仍是每个 token 逐位置非线性变换这个本质,变的只是“这一层用一个大 FFN 还是多个小专家”。

6.2 Latent MoE

6.1 节介绍的所有 MoE 变体中,专家都工作在完整的隐藏维度 $d$ 上。Latent MoE 则另辟蹊径:让路由专家在一个低维潜空间(latent space)里工作,只把共享专家留在原维度。这一改动直接解锁了“极细粒度、超多专家”的配置。

动机:维度换专家数。传统 MoE(如 Mixtral)的每个专家都是 $d \to d_{ff} \to d$ 的完整 FFN,参数成本高,专家数只能开到 8 个、16 个量级。DeepSeekMoE 证明“细粒度专家 + 共享专家”更好,但专家仍占完整宽度。Latent MoE 的洞察是:路由专家真正需要的是“变换能力”,不是“输出宽度”。让路由专家在 $\ell \ll d$ 的潜空间里做变换,单个专家就便宜得多,于是可以把专家池开到数百个,让每个 token 激活的专家更“专”。

结构(Kimi K3 的 Stable LatentMoE,对应论文公式 11):

\[z = W^\downarrow x \in \mathbb{R}^{\ell}, \qquad u = \sum_{i \in T_k(x)} p_i\, E_i^{\mathrm{routed}}(z), \qquad y = \sum_{j=1}^{N_s} E_j^{\mathrm{shared}}(x) + W^\uparrow \mathrm{RMSNorm}(u) \tag{6.7}\]

三步走:

  1. 降维:$W^\downarrow: \mathbb{R}^d \to \mathbb{R}^\ell$ 把 token 隐状态投到潜空间($\ell \ll d$)
  2. 潜空间路由:$z$ 被 dispatch 到选中的 $k$ 个路由专家($E_i^{\mathrm{routed}}: \mathbb{R}^\ell \to \mathbb{R}^\ell$),加权聚合得 $u \in \mathbb{R}^\ell$
  3. 升维 + 共享专家:$W^\uparrow$ 把 $u$ 投回 $\mathbb{R}^d$,再与 $N_s$ 个全宽度共享专家($E_j^{\mathrm{shared}}: \mathbb{R}^d \to \mathbb{R}^d$)的输出相加

27.png

图 6.3 标准 MoE 与 LatentMoE 的架构对比(Elango et al., 2026, Figure 1)。(a) 标准 MoE 中路由专家直接工作在完整隐藏维度 $d$ 上;(b) LatentMoE 先把 token 下投影到潜空间 $\ell$ 再做专家计算,最后上投影回 $d$。下投影省下的路由参数与 all-to-all 通信开销,被用于成倍扩大专家池与 top-k。

为什么共享专家要全宽:共享专家负责“所有 token 都需要的基础变换”,它必须能直接作用在 $d$ 维隐状态上、保持表达力;而路由专家负责“特定 token 的专门变换”,潜空间里的低维表示足够。两者分工后,Kimi K3 把 $N_s = 2$ 个全宽共享专家固定在每一层。

Stable LatentMoE 的三个稳定组件。LatentMoE 开到 896 个路由专家、每 token 只激活 16 个($k=16$,稀疏度极高)时,训练会遇到两个典型失败模式:激活爆炸($W^\uparrow$ 直接作用在 scale 会剧烈波动的 $u$ 上)与路由崩塌(负载严重不均)。Kimi K3 用三招治:

  1. Normalized LatentMoE:在专家聚合 $u$ 与上投影 $W^\uparrow$ 之间插一个 $\mathrm{RMSNorm}$。这一步稳定了路由分支的 scale,让它与全宽共享分支相加时不会互相干扰——而且实测还稳定地降低了验证损失。
  2. SiTU-GLU:路由专家内部的激活用 $\beta$-softcap 的 GLU(详见 7.3),把大值封顶在 $\beta_1\beta_2 = 100$,杜绝低精度下的溢出离群值。
  3. Quantile Balancing(QB):负载均衡不用固定的辅助损失,而是按 router 分数的分位数给每个专家设 bias。做法是 Top-$(k+1)$ 路由(第 $k+1$ 名的分数即进入 Top-$k$ 的门槛 $\alpha_i$),再反推每个专家要达到目标负载 $q = mk/n$ 所需的 bias $b_j$。这样一次前向就能算出下一 step 的 bias,负载均衡从“加惩罚项”变成“精确对齐目标”。
import torch
import torch.nn as nn
import torch.nn.functional as F


class LatentMoE(nn.Module):
    """Latent MoE:路由专家在潜空间工作,共享专家保持全宽度。"""

    def __init__(self, d_model, latent_dim, n_shared=2, n_routed=896,
                 k=16, n_iter_norm=0):
        super().__init__()
        self.k = k
        self.latent_dim = latent_dim
        self.w_down = nn.Linear(d_model, latent_dim, bias=False)     # 降维
        self.w_up = nn.Linear(latent_dim, d_model, bias=False)       # 升维
        self.norm_u = nn.RMSNorm(latent_dim)                          # Normalized LatentMoE

        # 共享专家:全宽度,每个 token 都过
        self.shared = nn.ModuleList([
            nn.Sequential(
                nn.Linear(d_model, 4 * d_model, bias=False),
                nn.SiLU(),
                nn.Linear(4 * d_model, d_model, bias=False),
            ) for _ in range(n_shared)
        ])
        # 路由专家:潜空间内,参数成本低
        self.routed = nn.ModuleList([
            nn.Sequential(
                nn.Linear(latent_dim, 4 * latent_dim, bias=False),
                nn.SiLU(),
                nn.Linear(4 * latent_dim, latent_dim, bias=False),
            ) for _ in range(n_routed)
        ])
        self.router = nn.Linear(d_model, n_routed, bias=False)
        self.bias = nn.Parameter(torch.zeros(n_routed))   # Quantile Balancing 的 bias

    def forward(self, x):
        B, L, d = x.shape

        # 1) 共享专家:全宽度直接处理
        shared_out = sum(expert(x) for expert in self.shared)         # (B,L,d)

        # 2) 降维 + 路由(Top-k 带 bias)
        z = self.w_down(x)                                            # (B,L,latent)
        scores = self.router(x) + self.bias
        topk_w, topk_i = torch.topk(torch.softmax(scores, dim=-1), self.k, dim=-1)

        # 3) 潜空间专家聚合(分组计算,同 token 多专家用 index_add 合并)
        u = torch.zeros(B, L, self.latent_dim, device=x.device, dtype=x.dtype)
        for e in range(len(self.routed)):
            mask = (topk_i == e)
            if not mask.any():
                continue
            tok_idx, slot_idx = mask.nonzero(as_tuple=True)
            flat = z.reshape(-1, self.latent_dim)[tok_idx]
            ye = self.routed[e](flat) * topk_w.reshape(-1, self.k)[tok_idx, slot_idx].unsqueeze(-1)
            u.reshape(-1, self.latent_dim).index_add_(0, tok_idx, ye)

        # 4) Normalized LatentMoE:先 RMSNorm 再升维(抑制 scale 波动)
        routed_out = self.w_up(self.norm_u(u))                        # (B,L,d)

        return shared_out + routed_out

参数账本(Kimi K3 实测):896 个路由专家每 token 激活 16 个 + 2 个全宽共享专家,让“激活专家数”从传统 MoE 的 8 个扩到 18 个,同时因为路由专家在 $\ell$ 维潜空间工作,总参数量与激活参数量的比值远高于全宽度专家的方案。加上 QB 把负载打到完美均衡,专家并行训练不再被最慢的 rank 拖累。

Latent MoE 是 MoE 这条线的一个有趣的转向——不再只调“专家数/激活数”,而是改专家工作的空间。它与 MLA 压缩 KV、CSA 压缩序列是同一哲学的三次应用:在不损失表达力的前提下,把高维的东西投到低维去做选择性计算,只在必要处还原到高维。

7. 激活函数

激活函数是 Transformer 前馈网络中的非线性来源。原始 Transformer 的 FFN 由两层全连接线性层组成,中间夹着一个 ReLU 激活函数。此后,激活函数经历了几轮更替——从 ReLU 到 GELU,再到现代模型普遍采用的 SiLU/Swish,以及面向数值稳定性的 SiTU-GLU。本章逐一介绍这些变化。

28.png

图 7.1 GELU、ReLU、ELU 曲线对比(Hendrycks & Gimpel, 2016, Figure 1)。

7.1 ReLU、GELU 与 SiLU

原始 Transformer 的 FFN 用 ReLU,BERT/GPT 时代换成了 GELU,如今 LLaMA、Qwen 等现代模型则普遍采用 SiLU/Swish。本节按这一演进顺序介绍这三个逐元素激活。

表 7.1 ReLU、GELU 与 SiLU 的特性对比

特性 ReLU GELU SiLU / Swish
公式 f(x) = max(0, x) f(x) = x * Φ(x) f(x) = x * σ(x)
是否平滑 否 (在 x=0 处不可导) 是 是
是否单调 是 是 是
输出范围 [0, +∞) ≈ (-0.17, +∞) ≈ (-0.28, +∞)
计算效率 非常高 较低 (因需计算Φ(x)) 较高
主要思想 稀疏激活 基于随机正则化的平滑近似 平滑、自门控
常见应用 CNN, 早期模型 Transformer (BERT, GPT) 逐渐成为主流
  1. ReLU
  • 公式: $f(x) = max(0, x)$
  • 导数: $f’(x) = \begin{cases} 1 & x > 0 \ 0 & x < 0 \end{cases}$(在 x=0 处未定义,通常设为 0 或 1)

优点:

  • 缓解梯度消失: 对于正区间的输入,梯度恒为 1,彻底解决了饱和区梯度消失的问题(与 Sigmoid/Tanh 相比)。
  • 计算高效: 仅需比较和赋值操作,速度极快。
  • 稀疏性: 负半轴输出为 0,使得网络具有稀疏激活性,更接近生物神经元,可能带来更好的表征能力。

缺点:

  • Dying ReLU(神经元死亡): 如果一个神经元的大部分输入在训练后都落入负半轴,那么其梯度将恒为 0。这意味着该神经元将永久“死亡”,不再对任何数据做出响应,参数也无法更新。
  • 非零中心: 输出均值恒大于 0,可能影响梯度下降的动态。
  • 非平滑: 在 x=0 处存在一个尖锐的转折点,这在数学上不够优雅。
  1. GELU
  • 公式: $GELU(x) = x * \Phi(x)$

  • 其中$\Phi(x)$ 是标准高斯分布的累积分布函数。

  • 近似公式: $GELU(x) \approx 0.5 * x * (1 + \tanh\left[\sqrt{\frac{2}{\pi}} \left(x + 0.044715 * x^3\right)\right])$

  • 在实际实现(如 Hugging Face Transformers, PyTorch, TensorFlow)中通常使用这个近似公式以保证效率。

优点:

  • 基于概率的平滑激活: GELU 的设计融合了 Dropout 和 Zoneout 的思想。它不再是像 ReLU 那样根据输入的“符号”来硬性决定是否激活,而是根据输入的“幅度”来软性决定。可以理解为:x * Φ(x) 中的 Φ(x) 是“x 被保留(即不被 Dropout)的概率”。
  • 在 Transformer 中表现优异: 由于其平滑性和概率解释,GELU 在 BERT、GPT 等最先进的 Transformer 模型中成为默认选择,通常能带来比 ReLU 和 ELU 更好的性能。
  • 处处平滑: 避免了 ReLU 的尖锐转折点。
  • 在负区域有界: 与 SiLU 类似,在负值区域有平滑的曲线和非零输出。

缺点:

  • 计算最复杂: 即使使用近似公式,其计算量也显著高于 ReLU 和 SiLU。
  1. SiLU / Swish

SiLU(Sigmoid-Linear Unit),也就是 Swish 激活函数($\beta=1$ 时),由 Google 在 2017 年通过自动搜索发现,如今已成为 LLaMA、Qwen 等现代模型的默认选择(尤其在 SwiGLU FFN 中)。

29.png

图 7.2 Swish 激活函数及其一阶导数(Ramachandran et al., 2017, Figure 4 & 5)。注意负值区域的平滑“下垂”与导数的非单调性。

  • 公式: $SiLU(x) = x*\sigma(x)$
  • 其中 $\sigma(x)$是 Sigmoid 函数: $\sigma(x) = \frac{1}{1+e^{-x}}$

  • 导数: $f’(x) = \sigma(x) + x\sigma(x)(1-\sigma(x))$

优点:

  • 平滑且非单调下界: 曲线平滑,没有不可导点。在负值区域,它不会直接变为 0,而是有一个平滑的过渡,并有一个小小的负值下界(约 -0.28)。这避免了“神经元死亡”问题,同时保留了负值区域的一些信息。
  • 自门控: 公式 x * σ(x) 可以看作一个“门”,Sigmoid 的输出 σ(x) 作为一个软开关,控制有多少 x 可以通过。这种门控机制与 LSTM/GRU 中的门类似,被认为有助于捕获更复杂的信息流。
  • 性能卓越: 在 Google 的原始研究中,Swish 在多个视觉和语言任务上一致优于 ReLU。

缺点:

  • 计算成本稍高: 需要计算 Sigmoid 函数,比 ReLU 慢。

在正半轴,SiLU 继承了 ReLU 的优良性质:其导数接近于 1,正区间输入的梯度得以在深层网络中稳定回传。当 $x$ 为负时,它不像 ReLU 那样直接置零(由此导致神经元死亡),而是保留一个平滑的、微小的负值。

三种激活函数的 PyTorch 实现与对照:

import torch
import torch.nn as nn
import math


class ReLUOrig(nn.Module):
    """f(x) = max(0, x) —— 简单、稀疏,但有 dying ReLU 问题"""
    def forward(self, x):
        return torch.clamp(x, min=0.0)


class GELU(nn.Module):
    """GELU: f(x) = x * Φ(x),默认用 tanh 近似(与 HF/PyTorch 一致)"""
    def forward(self, x):
        return 0.5 * x * (1.0 + torch.tanh(
            math.sqrt(2.0 / math.pi) * (x + 0.044715 * torch.pow(x, 3.0))
        ))


class SiLU(nn.Module):
    """SiLU/Swish: f(x) = x * σ(x),平滑自门控,负值区有微小负输出"""
    def forward(self, x):
        return x * torch.sigmoid(x)


# 对照实验:三种激活的输出差异

x = torch.linspace(-3, 3, 7)
print("ReLU:", ReLUOrig()(x))
print("GELU:", GELU()(x))
print("SiLU:", SiLU()(x))

# 现代模型中 SiLU 的典型用法就是 SwiGLU 的门控:

# out = (SiLU(x @ W) * (x @ V)) @ U

7.2 Sigmoid

Sigmoid 是深度学习最早的一批激活函数,也是今天 Transformer 里几乎不作为“激活”、却处处作为“门”的那个函数。

\[f(x) = \frac{1}{1+e^{-x}}, \qquad f'(x) = f(x)\left(1 - f(x)\right) \tag{7.1}\]

30.png

图 7.3 Sigmoid 的函数形状(左)与导数(右)。左图对照零中心的 tanh——sigmoid 输出恒在 $(0,1)$,均值大于 0(非零中心);右图导数峰值仅 0.25,且两侧饱和区梯度趋近 0,正是深层网络梯度消失的数学来源。

它的历史地位:RNN 时代(1990s—2015)Sigmoid 与 Tanh 是默认激活函数,用于 LSTM/GRU 的门控。但作为逐元素激活函数,它有两个致命缺陷:

  1. 梯度消失:输入绝对值较大时 $f’(x) \to 0$,深层网络反向传播时梯度指数级衰减
  2. 非零中心:输出恒在 $(0,1)$ 且均值大于 0,会让下游层的梯度带偏

正因为如此,从 ReLU 开始,sigmoid 就从“激活函数”的位置退到了“门控函数”的位置。

Sigmoid 在现代 Transformer 中的应用位置:

表 7.2 Sigmoid 在 Transformer 中的典型应用位置

位置 形式 出处
GLU 的门 $\sigma(xW) \odot xV$ Shazeer 2020 (GLU Variants)
SiTU-GLU 的门 $\sigma(W_g x) \odot \beta_1\tanh(\cdot)$ Kimi K3
Gated DeltaNet 的衰减门 / 写入门 $\alpha_t = \sigma(W_\alpha x_t)$,$\beta_t = \sigma(W_\beta x_t)$ Yang et al. 2024
Gated Attention 的输出门 $\sigma(W_g x) \odot \mathrm{Attn}(x)$ Qwen3-Next
MoE 路由 $\mathrm{Softmax}(W_r x)$(softmax = 归一化的 sigmoid 家族) Switch Transformer
门控残差的读写门 $\sigma(\cdot)$ Qwen3.8-Flash-Next
mHC 的 Birkhoff 投影 Sinkhorn-Knopp 内部用 $\exp$ 归一化 DeepSeek mHC

Sigmoid 适合作为门控的原因:它把任意实数平滑地映射到 $(0,1)$,天然是一个“开关强度”。输出有界(不会像 SwiGLU 的线性因子那样无界增长),梯度在饱和区虽然小但有界——配合低秩投影做门,不会引入离群值。这也是 Kimi K3 的 SiTU-GLU 选择“sigmoid 门 + tanh soft-cap”而不是纯 Swish 门的原因。

import torch
import torch.nn as nn


class SigmoidGate(nn.Module):
    """GLU 形式的门控:sigmoid 门 * 线性值。"""

    def __init__(self, d_in, d_out, dtype=None):
        super().__init__()
        self.w_gate = nn.Linear(d_in, d_out, bias=False, dtype=dtype)
        self.w_up = nn.Linear(d_in, d_out, bias=False, dtype=dtype)

    def forward(self, x):
        return torch.sigmoid(self.w_gate(x)) * self.w_up(x)

Sigmoid 作为“激活函数”输给了 ReLU,作为“门”却输给了 SiLU。十年里激活函数的形态一直在变,但“用 sigmoid 控制信息通过多少”这个思想,从 LSTM 一直活到今天的 Gated DeltaNet 和 Gated Residual。

7.3 Situ-GLU

SiTU-GLU(Sigmoid Tanh Unit GLU)是 Kimi K3 提出的新激活函数,专门解决 SwiGLU 在超大 MoE 规模下的激活离群值与溢出问题。

31.png

图 7.4 GLU / SwiGLU / SiTU-GLU 的门与上分支对比及标量响应曲线(Kimi K3 技术报告 Figure 4)。SiTU-GLU 逼近上界 $\beta_1\beta_2 = 100$,而 SwiGLU 继续无界增长。

问题出在哪:SwiGLU 的两个乘性因子都是无界的——

\[\text{SwiGLU}(x) = \big(\underbrace{x \cdot \sigma(x)}_{\text{线性因子无界}} \odot \underbrace{xV}_{\text{上分支无界}}\big) \tag{7.2}\]

当两个大坐标恰好相遇,就会产生巨大的乘积。在 2.8T 参数、896 个专家的规模下,这种激活离群值会导致低精度算术溢出,或迫使共享的量化 scale 牺牲普通数值的精度。Kimi K3 的原话是:这条链路上“近似四次连续矩阵乘”,条件数很差,内部激活会爆炸。

GLU 的老问题:原始 GLU 的 sigmoid 门避免了无界门增长,但也失去了 Swish 在正半轴近似线性的优良响应。

SiTU-GLU 的解法:对 Swish 门里的线性因子、以及上分支,各自独立施加一个平滑 soft-cap:

\[\text{SiTU-GLU}(x) = \Big[\beta_1 \tanh\!\Big(\frac{W_g x}{\beta_1}\Big) \odot \sigma(W_g x)\Big] \odot \Big[\beta_2 \tanh\!\Big(\frac{W_u x}{\beta_2}\Big)\Big] \tag{7.3}\]

其中 Kimi K3 取 $\beta_1 = 4$、$\beta_2 = 25$。

三个设计要点:

  1. scaled tanh 在原点附近近似线性($\tanh(u) \approx u$),因此保留了 SwiGLU 的局部响应特性,不会像硬截断那样在小值区间损失梯度
  2. 两个因子都被有界化:门的线性因子 $ f \le \beta_1 = 4$,上分支 $ f \le \beta_2 = 25$,因此乘积的绝对值不超过 $\beta_1\beta_2 = 100$
  3. 保留 sigmoid 门:门的“软开关”语义不变,仍是 $[0,1]$ 的开合强度

对照来看:

表 7.3 SiTU-GLU 与 SwiGLU 的门控对照

  门分支 上分支 乘积上界
GLU $\sigma(x)$ $x$ 无界
SwiGLU $x\sigma(x)$ $x$ 无界
SiTU-GLU $\beta_1\tanh(x/\beta_1)\cdot\sigma(x)$ $\beta_2\tanh(x/\beta_2)$ $\beta_1\beta_2 = 100$
import torch
import torch.nn as nn


class SiTUGLU(nn.Module):
    """Kimi K3 的 SiTU-GLU:对门线性因子与上分支分别施加 scaled-tanh soft-cap。"""

    def __init__(self, d_in, d_hidden, beta1=4.0, beta2=25.0, dtype=None):
        super().__init__()
        self.beta1, self.beta2 = beta1, beta2
        self.w_gate = nn.Linear(d_in, d_hidden, bias=False, dtype=dtype)  # 门
        self.w_up = nn.Linear(d_in, d_hidden, bias=False, dtype=dtype)    # 上分支
        self.w_down = nn.Linear(d_hidden, d_in, bias=False, dtype=dtype)

    def forward(self, x):
        g = self.w_gate(x)
        u = self.w_up(x)
        # 门分支:scaled tanh soft-cap 作用在 Swish 的线性因子上
        gate = self.beta1 * torch.tanh(g / self.beta1) * torch.sigmoid(g)
        # 上分支:独立 soft-cap
        up = self.beta2 * torch.tanh(u / self.beta2)
        return self.w_down(gate * up)

SiTU-GLU 是一次面向数值稳定性的激活函数再设计。它的思路很有代表性——不是发明新的曲线形状,而是在已有曲线(Swish)上“加一个天花板”,用极小的代价(两次 tanh)换掉整条链路上的溢出风险。这也说明激活函数的竞争维度已经从“表达能力”扩展到了“数值鲁棒性”。

7.4 Softplus

Softplus 是“平滑版 ReLU”,定义为 $\mathrm{softplus}(x) = \ln(1 + e^x)$。它与前文几个激活函数的关系耐人寻味:其导数恰好是 7.2 节的 Sigmoid,曲线则逼近 ReLU——因此它既是 Sigmoid 的原函数,又是 ReLU 的光滑近似。

32.png

图 7.5 Softplus 及其导数。左图对比 Softplus 与 ReLU,注意 $x=0$ 处 Softplus 以 $\ln 2 \approx 0.693$ 平滑过渡、无折点;右图为 Softplus 的导数——正是 Sigmoid,在 $[0,1]$ 内连续。

定义与性质。Softplus 的值域为 $(0, +\infty)$,处处可导:

\[\mathrm{softplus}(x) = \ln(1+e^x), \qquad \mathrm{softplus}'(x) = \sigma(x) = \frac{1}{1+e^{-x}} \tag{7.4}\]

当 $x \to +\infty$ 时 $\mathrm{softplus}(x) \approx x$,当 $x \to -\infty$ 时 $\mathrm{softplus}(x) \to 0$,两端渐近行为与 ReLU 一致,只是在原点附近把“尖角”换成了圆滑的过渡。它由 Dugas et al. (2001) 为期权定价引入;与 ReLU 相比,其梯度连续、无“神经元死亡”问题,但代价是正向要算一个 $\exp$ 与一个 $\log$,反向还要再算一次 Sigmoid。

数值稳定形式。直接计算 $\ln(1+e^x)$ 在 $x$ 较大时会产生 $e^x$ 溢出,因此实现上改写为:

\[\mathrm{softplus}(x) = \max(x, 0) + \ln\!\big(1 + e^{-|x|}\big) \tag{7.5}\]
这个等价形式把指数项压到 $e^{- x }$ 上,任取 $x$ 都不溢出。PyTorch 的 softplus 自带 threshold 参数,当 $x$ 超过阈值时直接返回 $x$(导数近似为 1),在正半轴几乎等价于 ReLU。

在现代架构中的角色:线性注意力的衰减门。Softplus 作为逐元素激活函数并不常用于 FFN(那里被 SiLU/GELU 占据),但它在线性注意力里找到了关键位置——衰减门的参数化。回顾 3.6.2 的 Gated DeltaNet,状态更新需要每个 token 一个衰减因子 $\alpha_t \in (0,1]$。沿袭 Mamba-2 与 Gated DeltaNet 的约定,Kimi K3 报告中的 Kimi Linear 用负 Softplus 把衰减 logits 映射到 $(-\infty, 0)$,再取指数:

\[g_h^t = -e^{A_h}\,\mathrm{softplus}(z_h^t) \in (-\infty, 0)^{d_k}, \qquad \alpha_h^t = \exp(g_h^t) \in (0, 1)^{d_k} \tag{7.6}\]

之所以选 Softplus 而非别的正函数:它输出严格为正、单调、处处光滑,取负后天然落在 $(-\infty, 0)$,正好让 $\alpha$ 落入 $(0,1)$。但 Kimi K3 发现这个“无下界”的负 Softplus 会让某些位置的 $\alpha \to 0$,进而使分块 KDA 的对角块无法用 Tensor Core 高效计算(详见 3.6.3),于是改用带下界的 scaled sigmoid 替代它。这正是 Softplus 在最新一代架构中“被替换”的现场。

import torch
import torch.nn as nn


def stable_softplus(x):
    """数值稳定的 softplus:max(x,0) + log(1+exp(-|x|)),避免 exp 溢出。"""
    return torch.clamp(x, min=0) + torch.log1p(torch.exp(-torch.abs(x)))


class NegativeSoftplusDecay(nn.Module):
    """线性注意力(Mamba-2/GDN 风格)的衰减门:负 softplus -> exp 得到 (0,1] 衰减。"""

    def __init__(self, d):
        super().__init__()
        self.w_a = nn.Linear(d, d, bias=False)    # 衰减 logits z
        self.log_a = nn.Parameter(torch.zeros(d))  # 逐通道温度 A

    def forward(self, x):
        z = self.w_a(x)                            # 衰减 logits
        g = -torch.exp(self.log_a) * stable_softplus(z)  # (-inf, 0)
        alpha = torch.exp(g)                       # (0, 1],即每步的状态衰减因子
        return alpha


# 作为逐元素激活时,softplus 的典型调用:
#   y = torch.nn.functional.softplus(x, beta=1.0, threshold=20)
#   # 超过 threshold 时直接返回 x,正半轴几乎等价于 ReLU

Softplus 的定位与 SiLU、Sigmoid 不同:它几乎没有作为“FFN 主激活”登场的时刻,却凭借“导数等于 Sigmoid、值域严格为正、处处光滑”这三个性质,长期驻扎在需要平滑正输出的地方——概率模型的尺度参数、变分推断的方差项,以及线性注意力里那个把衰减压进 $(0,1]$ 的门。它提醒我们,激活函数的价值不只在“给 FFN 引入非线性”,也在于“为某个量提供合适的取值空间”。

激活函数十年演进:Tanh/Sigmoid(RNN 时代)→ ReLU(2012 深度学习复兴,进原始 Transformer)→ GELU(2016,BERT/GPT 时代)→ SiLU/Swish(2017 搜索发现,现代 LLM 默认)。退居门控的 Sigmoid、作为其原函数回归的 Softplus,以及把门控与数值稳定性一并解决的 SiTU-GLU,是这条主线之外的必要补充。激活函数的“最优解”一直在被重新发现——ReLU 便宜,GELU 优雅,SiLU 折中;现代模型几乎统一到了 SiLU 上。变的是曲线的形状,不变的是“引入非线性、控制梯度流”这一使命。

8. 残差

残差连接是 Transformer 中最稳定的组件,其基本形式 $y = F(x) + x$ 自 ResNet 提出以来几乎未曾改变。但这十年间,围绕残差流的宽度、连接方式与信息流动,仍出现了一系列新的探索——从标准残差,到流形约束超连接(mHC)、块注意力残差(Block Attention Residual)与门控残差(Gated Residual)。本章依次介绍这些演进。

8.1 Residual Add

残差连接(Residual Connection)源自 2015 年何恺明等人的 ResNet,是深度学习历史上最重要的结构创新之一。它的形式简单到只有一行:

\[y = F(x, \{W_i\}) + x \tag{8.1}\]

其中 $F(x, {W_i})$ 是子层(注意力或 FFN)的输出。原始 Transformer 论文中的 “Add & Norm” 里的 Add 就是这个残差连接。

33.png

图 8.1 ResNet 残差学习的 building block(He et al., 2015, Figure 2)——恒等映射 $x$ 与 $F(x)$ 相加。

残差的重要性首先体现在梯度流上。反向传播时:

\[\frac{\partial \mathcal{L}}{\partial x} = \frac{\partial \mathcal{L}}{\partial y} \left(1 + \frac{\partial F}{\partial x}\right) \tag{8.2}\]

括号里的 1 提供了一条“梯度高速公路”:即使 $\frac{\partial F}{\partial x}$ 很小(梯度消失)或很大(梯度爆炸),梯度都能沿着恒等路径直达浅层。这使得训练上百层甚至上千层的网络成为可能。残差的第二重意义是学习增量:子层只需要学“在输入基础上改什么”($F(x)$ 是残差增量),而不需要从零重构整个表示,这降低了学习难度。

在 Transformer 中,每个子层都包裹在残差里:

\[x_{l+1} = x_l + \text{Sublayer}(\text{Norm}(x_l)) \tag{8.3}\]

注意力子层和 FFN 子层各自一条残差路径,堆 $N$ 层后形成 $N$ 条并行的恒等通路。正因如此,前面归一化一节才会说“残差叠加导致数值膨胀”——残差的缺点(数值累加)恰好要靠 Norm 来治,这就是 Add & Norm 要绑在一起的原因。

import torch
import torch.nn as nn


class TransformerBlock(nn.Module):
    """残差 + 归一化的标准包装:Pre-Norm 形式(现代主流)"""
    def __init__(self, d_model, num_heads, d_ff, dropout=0.1):
        super().__init__()
        self.attn = MultiHeadAttention(d_model, num_heads, dropout)
        self.ffn = FeedForward(d_model, d_ff, variant="swiglu")
        self.norm1 = RMSNorm(d_model)   # 或 nn.LayerNorm(d_model)
        self.norm2 = RMSNorm(d_model)
        self.drop = nn.Dropout(dropout)

    def forward(self, x, mask=None):
        # 注意力子层:Pre-Norm + 残差  x + Attn(LN(x))
        h = self.norm1(x)
        x = x + self.drop(self.attn(h, mask))
        # FFN 子层:Pre-Norm + 残差  x + FFN(LN(x))
        h = self.norm2(x)
        x = x + self.drop(self.ffn(h))
        return x


# 残差的梯度直觉验证:恒等路径让梯度直达浅层

x = torch.randn(2, 8, 64, requires_grad=True)
block = TransformerBlock(d_model=64, num_heads=8, d_ff=172)
y = block(x)
y.sum().backward()
print("input grad norm:", x.grad.norm().item())  # 梯度不会消失为 0

残差这十年基本没有变过——这是 Transformer 里最“保守”的组件。从 ResNet 到原始 Transformer,再到 GPT-5 时代的万层模型,$y = F(x) + x$ 一行公式始终是骨干。唯一的微调是与 Norm 的相对位置(Post-Norm → Pre-Norm)以及偶尔的缩放(如 DeepNet 的 $\beta$ 缩放残差分支,用于稳定超深网络)。不变的是恒等映射,变的只是它的“伴生品”归一化放在哪。

8.2 mHC

mHC(Manifold-Constrained Hyper-Connections,流形约束超连接)是 DeepSeek 在 2025 年底发布的工作(arXiv:2512.24880),也是这十年里对残差连接最激进的一次改造。

34.png

图 8.2 三种残差连接范式的对比(mHC 论文 Figure 1)。(a) 标准残差;(b) Hyper-Connections(HC),残差流加宽并加多个映射算子;(c) mHC,把这些映射算子约束到 Birkhoff 流形上。

第一步:HC 拓宽了残差流。Hyper-Connections 把单一的残差状态 $x_l$ 扩展成 $n$ 条并行分支 $h_l^{res} \in \mathbb{R}^{n \times d}$,并用可学习的映射矩阵(Res Mapping / Pre Mapping / Post Mapping)在读写之间做重组。残差流的宽度可扩展了,模型容量随之提升。

第二步:HC 打破了恒等映射。这正是问题所在——HC 的自由形态连接矩阵从根本上削弱了残差连接固有的恒等映射特性。实测后果是:在 27B 模型中,HC 的多层残差映射在反向传播中导致信号最大放大倍数逼近 3000 倍,训练出现严重的信号爆炸与梯度异常,同时 GPU 显存占用和通信带宽显著上升。

第三步:mHC 把连接矩阵投影回受约束的流形。核心思想是——不要自由地学连接矩阵,而是把它投影到 Birkhoff 多面体(双随机矩阵构成的集合,满足行和列和均为 1)上:

\[\mathcal{P}_{\mathcal{M}}(A) = \arg\min_{P \in \mathcal{M}} \|A - P\|_F^2, \qquad \mathcal{M} = \{P \in \mathbb{R}^{n \times n} : P \ge 0,\ P\mathbf{1} = \mathbf{1},\ P^\top \mathbf{1} = \mathbf{1}\} \tag{8.4}\]

投影用 Sinkhorn-Knopp 算法实现,训练时最多迭代 20 次。

这一约束之所以有效,在于双随机矩阵的谱范数被约束在 1 以内——无论深层堆叠多少层,信号都不可能被放大。恒等映射性质在拓扑意义下被恢复了,同时保留了 HC 的连接表达能力。

效果:mHC 支持 3B 到 27B 规模的稳定训练,在 8 个下游任务上全面超越 HC 与 baseline——BBH 推理任务 +2.1%、DROP 阅读理解 +2.3%。当扩展率 $n = 4$ 时,仅带来约 6.7% 的额外训练时间开销。

已经是生产架构:DeepSeek-V4 全面用 mHC 替换了标准残差连接,残差流宽度扩展因子 $n_{hc} = 4$,投影同样用 Sinkhorn-Knopp(训练时最多 20 次迭代)。

import torch
import torch.nn as nn
import torch.nn.functional as F


def sinkhorn_knopp(A, n_iter=20, eps=1e-9):
    """把矩阵投影到 Birkhoff 多面体(双随机矩阵)。"""
    Q = A.abs().clamp_min(eps)
    Q = Q / Q.sum()
    for _ in range(n_iter):
        Q = Q / Q.sum(dim=1, keepdim=True)     # 行归一化
        Q = Q / Q.sum(dim=0, keepdim=True)     # 列归一化
    return Q


class ManifoldConstrainedHyperConnection(nn.Module):
    """mHC:残差流宽度 n,用投影到双随机矩阵的方式保证恒等映射性质。"""

    def __init__(self, d_model, n_branches=4, n_iter=20):
        super().__init__()
        self.n = n_branches
        self.n_iter = n_iter
        self.res_map = nn.Parameter(torch.eye(n_branches).unsqueeze(0).repeat(d_model, 1, 1))
        self.pre_map = nn.Parameter(torch.eye(n_branches).unsqueeze(0).repeat(d_model, 1, 1))
        self.post_map = nn.Parameter(torch.eye(n_branches).unsqueeze(0).repeat(d_model, 1, 1))
        self.norm = nn.RMSNorm(d_model) if hasattr(nn, "RMSNorm") else nn.LayerNorm(d_model)

    def forward(self, x, sublayer):
        # x: (B, L, n, d)  拓宽后的残差流
        B, L, n, d = x.shape
        res = self.res_map                                     # (d, n, n)
        pre = self.pre_map
        post = self.post_map
        # 流形约束:投影到双随机矩阵集合(谱范数 <= 1,杜绝信号放大)
        res_p = torch.stack([sinkhorn_knopp(res[i], self.n_iter) for i in range(d)])
        pre_p = torch.stack([sinkhorn_knopp(pre[i], self.n_iter) for i in range(d)])
        post_p = torch.stack([sinkhorn_knopp(post[i], self.n_iter) for i in range(d)])

        h_in = torch.einsum("blnd,dmn->blmd", x, pre_p)       # Pre Mapping
        h_out = sublayer(self.norm(h_in))                     # 子层计算
        h_out = torch.einsum("blnd,dmn->blmd", h_out, post_p)  # Post Mapping
        return x + torch.einsum("blnd,dmn->blmd", h_out, res_p)  # Res Mapping + 残差

mHC 是这十年残差故事里最具启发性的一次——标准残差十年未动,但“恒等映射”这条底线被重新论证了一遍。HC 证明了残差流可以加宽,mHC 证明了加宽的同时还能守住恒等映射这个底线。

8.3 Single-Pass mHC

DeepSeek-V4.1-Flash 对 mHC 做了一次纯工程化的改造——Single-Pass mHC:数学不变、内核融合做到极致。原始 mHC 把每个 token 的 $n$ 条残差流记为 $X_l \in \mathbb{R}^{n \times d}$($l$ 为 block 序号,$d$ 为隐藏维度),更新式为

\[X_{l+1} = B_l X_l + C_l F_l(A_l X_l), \qquad (A_l, B_l, C_l) = H(X_l) \tag{8.5}\]

其中 $H$ 是含归一化与投影的系数预测器,$A_l \in \mathbb{R}^{1 \times n}$、$C_l \in \mathbb{R}^{n \times 1}$、$B_l \in \mathbb{R}^{n \times n}$ 为逐 token 预测的系数,$F_l$ 为第 $l$ 个 block 的子层。工程实现将该式拆成三个 kernel——Residual Update、Coefficient Prediction、Input Mixing——彼此存在数据依赖:输入混合系数 $A_l$ 必须等隐藏维度上的规约完成才能拿到,三个 kernel 只能串行,Residual Stream 被反复读写,激活访存量(Activation Memory Traffic)约为理论下限的两倍。

Single-Pass mHC 的改动只有一处,却很关键:把 Input Mixing 使用的系数向前错开一个 Block——当前 Block 直接消费上一 Block 已经算好的混合系数,内部依赖就此解除:

\[X_{l+1} = B_l X_l + C_l F_l(A_{l-1} X_l), \qquad (A_l, B_l, C_l) = H(X_l) \tag{8.6}\]

即输入混合改用 $A_{l-1}$,不再依赖当前 block 刚算出的系数,$X_l$ 的每个 tile 可立即同时用于输入混合与系数累积。激活访存正好减半:

\[\text{Activation Memory Traffic}:\quad (4n + 4)\,d \;\longrightarrow\; (2n + 2)\,d \tag{8.7}\]

($n$ 为残差流宽度扩展因子,$d$ 为隐藏维度)。

35.png

图 8.3 Single-Pass mHC 结构示意(依据 DeepSeek-V4.1-Flash 技术报告 2.4.1 节整理)。(a) 原始 mHC 的三个 kernel 因数据依赖串行执行,$X_l$ 被反复读写;(b) 系数错开一个 Block 后依赖解除,Mega-mHC 内核把 Residual Update、Input Mixing 与 Coefficient Prediction 融合为一次读、一次写。

部署侧再通过 Mega-mHC Kernel 把 Residual Update、Input Mixing、Coefficient Prediction 以及部分 Norm、FP8 转换全部融合进一个 memory-bound 内核,让 Residual Stream 接近“一次读取、一次写入”。DeepSeek 报告称系数错位带来的性能损失可以忽略。

8.4 Block Attention Residual

Block Attention Residual 是 Kimi K3 提出的跨层信息流机制(AttnRes 的分块实现),它把残差连接从“顺序累加”改成了“可学习的跨层检索”。

36.png

图 8.4 三种残差连接范式的结构对比(Attention Residuals 论文 Figure 1,arXiv:2603.15031)。(a) 标准残差:所有层输出以固定单位权重顺序累加;(b) Full AttnRes:每层通过可学习 pseudo-query $w$ 与注意力权重 $\alpha$ 检索全部前层输出;(c) Block AttnRes:层先分块、块内求和压缩,跨块再做注意力——显存从 $O(Ld)$ 降到 $O(Nd)$($N$ 为块数)。

AttnRes 的核心洞察:既然 Transformer 用注意力让每个位置去选择性访问所有前序位置,那为什么不用同样的方法,让每一层去选择性访问所有前层?传统残差是“所有层的信息一股脑累加到我身上”,而深层网络里,早期的特征会被反复混合、逐步稀释。

机制:给每一层一个可学习的 pseudo-query $w_l$,对前面所有层的输出做注意力:

\[\alpha_{i \to l} = \frac{\phi(q_l, k_i)}{\sum_j \phi(q_l, k_j)}, \qquad h_l = \sum_{i<l} \alpha_{i \to l} \cdot v_i \tag{8.8}\]

其中 $\phi(q, k) = \exp\left(q^\top \mathrm{RMSNorm}(k)\right)$——RMSNorm 防止大范数的层主导权重,这是这个公式能稳定工作的关键。

Block 版本解决什么问题:全量形式的复杂度是 $O(L^2 d)$($L$ 是序列长度),层数不到 100 时算术开销尚可接受,但真正的瓶颈是 $O(Ld)$ 的显存——每一层都要保存前面所有层的输出用于检索。

Kimi K3 的 Block AttnRes 把 93 层分成 8 个 block:block 内的层输出先求和压缩成一个表示,跨 block 之间再做注意力:

\[\text{显存/通信}: O(Ld) \;\longrightarrow\; O(Nd) \tag{8.9}\]

其中 $N$ 是 block 数。同时 block 结构还能配合 online softmax 进一步减少推理时间成本。

效果:Kimi K3 相对 Kimi K2 获得约 2.5 倍的扩展效率提升,AttnRes 是四大架构创新之一(另外三个是 KDA、Gated MLA、Stable LatentMoE)。它与 mHC 是同一类思路的两种形态——mHC 把残差流“加宽并约束”,AttnRes 把残差流“变成可检索的层索引”。

残差连接的十年,从“恒等映射”(ResNet)到“可学习映射”(Hyper-Connections)到“流形约束的可学习映射”(mHC)再到“跨层注意力检索”(AttnRes),本质是同一条主线:让深层网络的信息流不再是一条只能累加的单行道。

8.5 Gated Residual

Gated Residual(GR)是 Qwen3.8-Flash-Next 采用的门控残差设计,可以看作Hyper-Connections 的加宽 + GatedNorm 的逐元素门控两者的合体。

37.png

图 8.5 Qwen3.8-Flash-Next 架构(Qwen3.8-Next 架构论文 Figure 1)。左侧是 3×GDN + 1×QSA 的层堆叠,右侧展开显示每个子层如何通过 GR Read / GR Write 与加宽后的残差流交互。

结构:原始的单一残差流被扩展成 4 条并行分支,每条分支独立归一化,再由门控加权合并读取:

\[\text{Read}: \quad h = \sum_{i=1}^{n} w_i \odot \mathrm{RMSNorm}(h_i), \qquad w = \sigma\left(\text{LowRank}\big(\mathrm{RMSNorm}(h_{1..n})\big)\right) \tag{8.10}\]

两个门:

  • 读门(Read Gate):逐元素、数据相关。在子层读取残差流之前,先用一个低秩(rank 320)投影算出逐通道的 sigmoid 门控值,决定这一层能从残差流里“读取”多少信息,而不是无差别全盘接收
  • 写门(Write Gate):每个分支一个标量门(sigmoid),子层计算完成后决定这次结果以多大强度写回对应分支

官方模型卡里的配置参数:hc_count = 4(分支数)、hc_lowrank = 320(分支间混合的瓶颈秩)、output_gate_type = sigmoid。

和 mHC 的关键差异:Qwen 去掉了 HC 原有的 Hres 混合算子,因此每条分支成为一个纯粹的历史累加器,可以精确追踪每层信息最终流向了哪里。分析发现:4 条分支里通常有一条专门负责跨越很多层传递早期信息(如第 0 层的输出被第 15、19 层大量读取),另外三条主要负责相邻层之间的短程传递。这种“一条专线负责长途、其余专线负责市内”的分工是模型自己学出来的,并非人为设定。

两个反直觉的消融结论:

  1. 数据依赖的读写机制只让训练损失多降了 0.002,但下游基准分数提升了 1.98 分——损失端和下游端的表现完全倒过来。只盯损失曲线做架构决策,会严重低估这类改动的真实价值
  2. 团队试过让每层只读取门控值最高的两条分支而非全部四条,以省下推理时的显存搬运。预训练阶段损失和基准分数几乎不受影响,看起来是无代价的优化——但微调后生成质量明显下滑,最终放弃。这是一个重要的教训:预训练阶段看起来免费的优化,可能要到更后期的训练阶段才会显现

去掉 Hres 也有工程理由:Hres 需要额外读一遍完整的残差状态,在参数量固定的推理场景下这是主导性的内存搬运开销。

Gated Residual 与 mHC 同属“残差流加宽 + 动态门控”这一脉,但 Qwen 的选择更激进也更实用——用低秩门控(rank 320,远小于隐层维度)换取细粒度控制,用去掉分支混合换取推理效率。十年残差故事的落点,从“恒等映射”变成了“恒等映射 + 数据相关的阀门”。

9. 十年变与不变

表 9.1 Transformer 十年演进的“变与不变”总览

组件 2017(原始 Transformer) 2026(现代 LLM) 变了吗? 驱动原力
骨架 堆叠子层 + 残差 + Norm 基本相同(Decoder-only) 不变 结构已被验证为局部最优
注意力 MHA,$O(n^2)$ GQA/MLA + FlashAttention 大变 KV Cache 显存与推理带宽
归一化 Post-Norm + LayerNorm Pre-Norm + RMSNorm + QK-Norm 大变 深层训练稳定性、去掉冗余计算
位置编码 sin-cos 绝对 RoPE + NTK/YaRN 外推 大变 长上下文需求
FFN 2 层 ReLU,$4d$ SwiGLU 门控,$\frac{8}{3}d$(或 MoE) 大变 参数效率与知识存储
激活函数 ReLU SiLU(Swish) 中变 平滑性、自门控、搜索
残差连接 $y = F(x)+x$,单行道累加 底座仍是 $y = F(x)+x$,但 HC/mHC、AttnRes、Gated Residual 等改造了残差流 有变 恒等映射之外,还要可控的深层信息流
训练目标 下一词预测 下一词预测(+多模态扩展) 基本不变 简单有效

回看这十年的原动力,可以归结为两个字:规模。模型变大、序列变长、部署成本变敏感——每一次结构改动,几乎都是在回答“如何让这个结构在更大的规模上还能训得动、推得快、装得下”。而那些没变的部分(堆叠、注意力+FFN 的交替、下一词预测),恰恰证明了 2017 年那次设计的先见之明:骨架已经够好,剩下的多是工程与效率的精雕细琢。就连残差连接这样看似定型的组件,也被 HC、mHC、AttnRes 与 Gated Residual 重新设计了一遍——变的是残差流的宽度与信息流动方式,不变的是恒等映射这条底线。

附录 FlashAttention

FlashAttention(Dao et al., 2022)是 Transformer 十年里影响最深远的注意力工程实现:不改数学,只改数据搬运,却同时压下了 $O(n^2)$ 注意力的显存占用与墙钟开销,如今已被各大训练与推理框架广泛采用。第 3 章把“优化 IO 与调度”列为三条主线之一,本附录展开其核心机制与 FA1 到 FA4 的版本演进。

A.1 IO 感知的 tiling 与 online softmax

FlashAttention 利用 GPU 的存储层级(SRAM 快但小,HBM 大但慢),用 tiling + online softmax 把注意力分块计算,避免把 $N \times N$ 的注意力矩阵物化到 HBM 中。

38.png

图 A.1 GPU 存储层级与 FlashAttention 的 tiling 策略(FlashAttention 论文 Figure 1)。

IO 复杂度分析:标准注意力的 HBM 访问量为 $O(N^2 + Nd^2)$(必须写出并读回 $N \times N$ 的注意力矩阵);FlashAttention 降至 $O(N^2 d^2 / M)$,其中 $M$ 为 SRAM 大小。当 $d \le M$ 时随 $N$ 仅线性增长。

online softmax 的关键恒等式(分块归一化):

\[\ell = \sum_{j} e^{q \cdot k_j - m}, \qquad m = \max_j (q \cdot k_j) \tag{A.1}\]

在分块扫描时维护 running max $m$ 与 running sum $\ell$,每读入新块就更新并对旧输出做 rescale,数学上严格等价于全局 softmax,却无需看到完整行。

# 工程实践中直接调用官方库,而非手写 CUDA

# pip install flash-attn

from flash_attn import flash_attn_func

# q, k, v: (batch, seqlen, nheads, headdim), fp16/bf16

out = flash_attn_func(q, k, v, causal=True)   # 因果掩码解码

A.2 从 FA1 到 FA4

FlashAttention 1→4 的演进本质上是软硬件协同的十年缩影:FA1 证明 IO 是瓶颈;FA2 优化 warp 调度与寄存器分配;FA3 针对 Hopper 架构(TMA、WGMMA)重写;FA4 进一步面向 Blackwell。数学从未变过,变的是怎么把同样的数学映射到下一代硬件上。

参考文献

  1. Vaswani, A., Shazeer, N., Parmar, N., et al. (2017). Attention Is All You Need. NeurIPS 2017. arXiv:1706.03762.
  2. Shazeer, N. (2019). Fast Transformer Decoding: One Write-Head is All You Need. arXiv:1911.02150.(MQA)
  3. Ainslie, J., Lee-Thorp, J., de Jong, M., et al. (2023). GQA: Training Generalized Multi-Query Transformer Models from Multi-Head Checkpoints. EMNLP 2023. arXiv:2305.13245.(GQA)
  4. Dao, T., Fu, D. Y., Ermon, S., et al. (2022). FlashAttention: Fast and Memory-Efficient Exact Attention with IO-Awareness. NeurIPS 2022. arXiv:2205.14135.
  5. Zhang, B., Sennrich, R. (2019). Root Mean Square Layer Normalization. NeurIPS 2019. arXiv:1910.07467.(RMSNorm)
  6. Xiong, R., Yang, Y., He, D., et al. (2020). On Layer Normalization in the Transformer Architecture. ICML 2020. arXiv:2002.04745.(Pre-LN / Post-LN)
  7. Su, J., Ahmed, M., Lu, Y., et al. (2021). RoFormer: Enhanced Transformer with Rotary Position Embedding. arXiv:2104.09864.(RoPE)
  8. Ba, J. L., Kiros, J. R., Hinton, G. E. (2016). Layer Normalization. arXiv:1607.06450.
  9. Shazeer, N. (2020). GLU Variants Improve Transformer. arXiv:2002.05202.(SwiGLU/GeGLU)
  10. Hendrycks, D., Gimpel, K. (2016). Gaussian Error Linear Units (GELUs). arXiv:1606.08415.
  11. Ramachandran, P., Zoph, B., Le, Q. V. (2017). Searching for Activation Functions. arXiv:1710.05941.(Swish/SiLU)
  12. He, K., Zhang, X., Ren, S., Sun, J. (2015). Deep Residual Learning for Image Recognition. CVPR 2016. arXiv:1512.03385.(ResNet/残差)
  13. Nair, V., Hinton, G. E. (2010). Rectified Linear Units Improve Restricted Boltzmann Machines. ICML 2010.(ReLU)
  14. Devlin, J., Chang, M. W., Lee, K., Toutanova, K. (2018). BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding. arXiv:1810.04805.
  15. Brown, T. B., et al. (2020). Language Models are Few-Shot Learners. NeurIPS 2020. arXiv:2005.14165.(GPT-3)
  16. Liu, A., et al. (2024). DeepSeek-V2: A Strong, Economical, and Efficient Mixture-of-Experts Language Model. arXiv:2405.04434.(MLA)
  17. DeepSeek-AI (2024). DeepSeek-V3 Technical Report. arXiv:2412.19437.(MLA / DeepSeekMoE)
  18. Yuan, J., Gao, H., Dai, D., et al. (2025). Native Sparse Attention: Hardware-Aligned and Natively Trainable Sparse Attention. ACL 2025 Best Paper. arXiv:2502.11089.(NSA)
  19. DeepSeek-AI (2025). DeepSeek-V3.2-Exp: Boosting Long-Context Efficiency with DeepSeek Sparse Attention. GitHub 技术报告(DeepSeek_V3_2.pdf).(DSA)
  20. DeepSeek-AI (2026). DeepSeek-V4 Technical Report. arXiv:2606.19348.(CSA / HCA / Compressed Attention)
  21. Lai, X., Xu, W., Yang, Y., et al. (2026). MiniMax Sparse Attention. arXiv:2606.13392.(MSA)
  22. Yang, N., et al. (2024). Gated Delta Networks: Improving Mamba2 with Delta Rule. arXiv:2412.06464.(Gated DeltaNet)
  23. Kimi Team (2026). Kimi K3: Open Frontier Intelligence. arXiv:2607.24653.(KDA / AttnRes / SiTU-GLU / Stable LatentMoE)
  24. Xie, Z., Wei, Y., Cao, H., et al. (2026). mHC: Manifold-Constrained Hyper-Connections. arXiv:2512.24880.(mHC)
  25. On the Design of Qwen3.8-Next Architecture: Evaluation, Efficiency, and Training Stability. arXiv:2608.30320.(GDN / QSA / Gated Residual)
  26. Mistral AI (2023). Mistral 7B. arXiv:2310.06825.(SWA / Rolling Buffer Cache)
  27. Fedus, W., Zoph, B., Shazeer, N. (2021). Switch Transformers: Scaling to Trillion Parameter Models with Simple and Efficient Sparsity. JMLR 2022. arXiv:2101.03961.(Top-1 路由 MoE)
  28. Jiang, A., et al. (2024). Mixtral of Experts. arXiv:2401.0408.(Top-2 路由 MoE)
  29. MiniMax-AI (2025). MiniMax-01: Scaling Foundation Models with Lightning Attention. arXiv:2501.08313.(Lightning Attention / TransNormer)
  30. Katharopoulos, A., et al. (2020). Transformers are RNNs: Fast Autoregressive Transformers with Linear Attention. ICML 2020.(线性注意力开山)
  31. Zhu, B., et al. (2023). Transformers are SSMs: Generalized Models and Efficient Algorithms Through Structured State Space Duality. ICML 2024.(Mamba-2 / gated SSM)
  32. Bai, S., et al. (2023). Gemma 2: Improving Open Language Models at 2.6B Parameters. arXiv:2408.00118.(SWA 交替)
  33. Team, G. (2025). Gemma 3 Technical Report. arXiv:2503.19786.(SWA 5:1)
  34. Kimi Team (2026). Attention Residuals: Rethinking depth-wise aggregation. arXiv:2603.15031.(AttnRes / Block AttnRes)
  35. DeepSeek-AI (2026). DeepSeek-V4.1-Flash: Pushing the Limits of KV Cache Compression. arXiv:2609.19969.(CSA2 / Single-Pass mHC / SWA Bounded Replay / FP4 KV Cache)
  36. Katharopoulos, A., Vyas, A., Pappas, N., Fleuret, F. (2020). Transformers are RNNs: Fast Autoregressive Transformers with Linear Attention. ICML 2020. arXiv:2006.16236.(Linear Transformer)
  37. Choromanski, K., et al. (2021). Rethinking Attention with Performers. ICLR 2021. arXiv:2009.14794.(Performer / FAVOR+)
  38. Sun, Y., et al. (2023). Retentive Network: A Successor to Transformer for Large Language Models. arXiv:2307.08621.(RetNet)
  39. Gu, A., Dao, T. (2023). Mamba: Linear-Time Sequence Modeling with Selective State Spaces. arXiv:2312.00752.(Mamba)
  40. Dao, T., Gu, A. (2024). Transformers are SSMs: Generalized Models and Efficient Algorithms through Structured State Space Duality. ICML 2024. arXiv:2405.21060.(Mamba2 / SSD)
  41. Yang, S., et al. (2024). Gated Linear Attention Transformers with Hardware-Efficient Training. ICML 2024. arXiv:2312.06635.(GLA)
  42. Dehghani, M., et al. (2023). Scaling Vision Transformers to 22 Billion Parameters. ICML 2023. arXiv:2302.05442.(QK-Norm / L2Norm)
  43. Dai, D., et al. (2024). DeepSeekMoE: Towards Ultimate Expert Specialization in Mixture-of-Experts Language Models. ACL 2024. arXiv:2401.06066.(细粒度专家 / 共享专家)
  44. Elango, V., Bhatia, N., Waleffe, R., et al. (2026). LatentMoE: Toward Optimal Accuracy per FLOP and Parameter in Mixture of Experts. arXiv:2601.18089.(潜空间路由专家 / LatentMoE 架构图)
  45. Dugas, C., Bengio, Y., Bélisle, F., Nadeau, C., Garcia, R. (2001). Incorporating Second-Order Functional Knowledge for Better Option Pricing. NIPS 2000.(Softplus 激活函数)
  46. Qiu, Z., Wang, Z., Zheng, B., et al. (2025). Gated Attention for Large Language Models: Non-linearity, Sparsity, and Attention-Sink-Free. NeurIPS 2025 Best Paper. arXiv:2505.06708.(SDPA 输出后 head-specific sigmoid 门)
  47. Meituan LongCat Team (2026). LongCat Sparse Attention: Taming the Lightning via Streaming-aware Hierarchical Cross-Layer Indexing. arXiv:2608.01662.(LSA / SI / CLI / HI)
  48. Ioffe, S., Szegedy, C. (2015). Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift. ICML 2015. arXiv:1502.03167.(BatchNorm)
]]>
什么是context parallelism http://zhifeiding.github.io/programming/2026/10/29/%E4%BB%80%E4%B9%88%E6%98%AFContext-Parallelism/ 2026-10-29T00:00:00+00:00 http://zhifeiding.github.io/programming/2026/10/29/什么是Context Parallelism 1. 概述

在大语言模型的上下文越来越长背景下,Context Parallelism逐渐成为一种必要的模型并行方案。同时,为了能同时适配长上下文和短上下文的需求,Dynamic CP技术也应运而生。vLLM和SGLang推理引擎针对Prefill和Decode分别实现了PCP(Prefill CP)和DCP(Decode CP),NVIDIA Megatron也实现了CP,以及Helix的Dynamic CP,Alibaba也发布了Adaptive Context Parallelism相关论文。

2. 什么是CP

传统的TP并行针对head_num进行切分,需要通过AllReduce汇聚结果,通信量大,而且受限于head_num。而且随着上下文越来越大,即使进行TP切分之后,激活和KVCache也越来越大。因此,针对超长上下文的需求,针对序列进行切分就是个迫切的需求了。

CP(Context Parallelism,上下文并行)沿序列(token)维度切分。长度为 $T$ 的序列被切成 $C$ 份,每个 CP rank 持有 $T/C$ 个 token 的激活以及对应区间的 KV Cache。除 Attention(以及线性注意力的循环状态)外,Linear、MLP、Norm 等算子都是逐 token 独立的,天然可以按 token 并行;唯一的跨 token 依赖在注意力上,需要通过 rank 间通信解决。

与 TP 的对比:

维度 Tensor Parallel Context Parallel
切分维度 head / hidden 维 序列(token)维
每层通信 AllReduce,通信量大 只传注意力相关数据(KV / Q / latent / 状态)
并行度上限 受 head 数限制(GQA/MLA 下更紧) 受序列长度限制,与 head 数无关
KV Cache 按 head 切分,head 数少时会复制 按 T 切分,显存占用随 CP 度下降
激活显存 按 head 切,长序列仍线性增长 按序列切,长序列显存近似除以 $C$

CP 与 TP / PP / DP / EP 正交,可以组合成 4D/5D 混合并行。实践中的经验顺序是先用 Megatron-SP(TP 内的序列并行,几乎零额外开销),激活显存仍然不够时再叠加 CP。

因果注意力下如果按 rank 顺序连续切分,前段 rank 历史 KV 少、计算量小,后段 rank 历史 KV 多、计算量大,约有 2 倍的不均衡,慢的 rank 决定整体速度。通用解法是 zigzag(首尾配对)切分。序列切成 $2C$ 个块,rank $r$ 拿块 $r$ 和块 $2C-1-r$,即一个轻载前段块配一个重载后段块,各 rank 计算量趋衡。

zigzag 切分:切分矩阵与因果 mask 矩阵(C=4,序列切成 2C=8 块) ① 顺序切分:rank r 拿块 2r、2r+1 —— 后段 rank 越背越重 切分矩阵 query 块 → rank B0 B1 B2 B3 B4 B5 B6 B7 ← 同时是 mask 的列(key 块) 因果 mask 矩阵 行 = query 块 着色 = 参与计算 灰 = 因果屏蔽 行着色 = 该行归属 rank B0B1B2B3 B4B5B6B7 各 rank 负载(着色格数) R0 3 R1 7 R2 11 R3 15 rank 负载 = 其拥有的 query 行在下三角中的着色格数(∝ 计算量):3 / 7 / 11 / 15,约 5 倍不均衡 ② zigzag:rank r 拿块 r 与块 2C−1−r —— 首尾配对,负载相等 切分矩阵 query 块 → rank B0 B1 B2 B3 B4 B5 B6 B7 R0←B0+B7,R1←B1+B6,R2←B2+B5,R3←B3+B4 因果 mask 矩阵 行 = query 块 着色 = 参与计算 灰 = 因果屏蔽 B0B1B2B3 B4B5B6B7 各 rank 负载(着色格数) R0 9 R1 9 R2 9 R3 9 轻载前段块(行短)配重载后段块(行长),每个 rank 的着色格数均为 9,慢 rank 不再拖累整体

▲ 切分矩阵与因果 mask 矩阵:切分矩阵给出每个 query 块归属的 rank;mask 矩阵行为 query 块、列为 key 块,下三角(着色)参与计算、上三角(灰)被因果 mask 屏蔽。顺序切分下负载 3/7/11/15,zigzag 首尾配对后各 rank 均为 9

2.1 Prefill CP

Prefill 阶段是算力受限(compute-bound)的,一个长请求有 $T$ 个新 token 要计算 Q/K/V 并做完整的因果注意力。Prefill CP 的目标是摊薄 TTFT(Time-To-First-Token),把请求切成 $N$ 个 chunk,每个 GPU 计算一个 chunk 的 Q/K/V 及该 chunk query 对应的注意力输出。按 KV 的处理方式分两种策略。

  1. Partial Query + Full KV(AllGather KV):请求长度适中、能放得下完整 KV 时,各 rank AllGather 得到完整 KV,每个 GPU 只为自己那部分 query 计算注意力输出。实现最简单,无需改注意力 kernel,是 SGLang Prefill CP、vLLM PCP、DSA-CP 的主流做法;代价是每层需要临时放得下全局 KV(可逐层聚合、用完即弃以压峰值)。现代架构(MLA / DSA / CSA)把 KV 压得很小,AllGather 的代价可接受。
  2. Partial Query + Partial KV(Ring Attention):请求太长、全局 KV 放不下时,KV 块沿环 P2P 传/收,每个 rank 用本地 Q 对收到的 KV 块做部分注意力,用在线 softmax(LSE)合并结果。通信可与计算重叠,但实现复杂度和 kernel 侵入性更高。

工程要点:

  • 头尾配对切分(如 vLLM PCP 的 DualChunkSwap、SGLang Zigzag 策略、Megatron CP):序列填充到 $2C$ 的倍数后切成 $2C$ 块,rank $i$ 拿 chunk $i$ 与 chunk $2C-1-i$,并通过 restore 索引把 AllGather 后交错的 token 顺序还原回因果顺序。
  • 对带 Indexer 的稀疏注意力(DSA、DeepSeek V4 系列),Indexer 的 top-k 选择计算也随 query 切分到各 rank,避免重复计算——这本身也是 Prefill CP 的收益之一。
  • 与 EP 组合时,CP 可以用 all-to-all 的 token dispatch/combine 替代 TP 的 AllReduce,降低通信时间。
  • 收益参考:SGLang 合入 CP 后长文本 TTFT 最多降低约 80%(PR #12065);Moreh 的 SLOPE 引擎在 100K 上下文做到 TTFT < 2s,而同配置 SGLang TP=8 约 9s。

2.2 Decode CP

Decode 阶段是访存受限(memory-bound)的。每步只有少量 query token,却要扫描大量存在 paged KV cache 里的历史 KV。Decode CP 的目标是把 KV Cache 按序列维切开,腾出显存放更多请求(更大 batch),从而提高吞吐。

先看 KV Cache 的分片路径。模型有 $H$ 个 KV head 时,TP 可以先按 head 维切 KV(就是普通的 TP 分片);但 $H$ 由模型结构决定,当 tp_size > H 时每个 GPU 上的 KV Cache 会复制 tp_size / H 份。以 MLA 模型(DeepSeek-R1 / Kimi-K2,推理时只有 1 个 KV head)为例,tp=8 就是 8 倍 KV 复制。此时就需要 DCP 再沿 T 维切 KV Cache。

  • dcp_size 取值范围 $[1, tp_size/H]$,DCP 不增加进程数,只是复用 TP/PCP 的 rank 去掉 KV 复制。dcp_size 越大复制越少,但通信开销越大。
  • 交错(interleave)分片:decode 过程中 KV Cache 持续增长,vLLM 用交错策略沿 T 维分片——token $i$ 存到 dcp_rank (i % dcp_world_size)(cp_kv_cache_interleave_size=1,token 级对齐),或按 block 级对齐(先填满前序 rank 的 block 再写下一个 rank),使未来 token 的 KV 天然落到正确的 GPU 上,各 rank 显存均匀增长。该方案由 Moonshot 的 Chao Hong 提出,Helix 论文(arXiv:2507.07120)中有详细说明。
  • 通信方式与 prefill 相反,decode 的 Q 极小、KV 极大,所以走传 Q——把 Q 广播/AllGather 到各 rank,每个 rank 对本地 KV 分片算 partial attention 输出和 softmax 统计量(LSE),再做一次很小的合并(在线 softmax trick,通信量与上下文长度无关)。vLLM DCP 提供 ag_rs(AllGather + ReduceScatter)与 a2a(交换 partial output + LSE 后用 Triton kernel 合并)两种通信后端。

案例(vLLM 官方文档):

  • DeepSeek-R1(MLA,1 个 KV head):单机 tp=8 → 8 倍 KV 复制,加 -dcp 8 可完全消除复制。
  • Kimi-K2:tp=16 → 16 倍复制,可加 -dcp 16 完全消除,或 -dcp 8 把复制降到 2 倍(通信限制在节点内,开销更小)。
  • Qwen3-235B-A22B(4 个 KV head):tp=8 → 2 倍复制,加 -dcp 2 消除。

去掉 KV 复制之后 batch size 可以显著放大,Moonshot 在 Kimi K2 分享中给出的经验是 2~3 倍吞吐提升,对 RL rollout 这类吞吐敏感场景尤其重要。

3. 不同的CP通信方案

针对CP并行,有多种不同的通信实现方案,常见的有传递Q、传递KV,或者Ulysses的All-to-All(常被混称为 AllGather)。下面分别介绍。

3.1 传 KV:Ring Attention(P2P 环)

Q 留在本地,KV 块沿环形拓扑 P2P send/recv 传一圈。第 $t$ 轮每个 rank 用手里的 KV 块算本地 Q 的部分注意力结果,同时把该块发给下一个 rank、从上一个 rank 收新块,用 FlashAttention 的 LSE 累加技巧合并,最终结果与单卡稠密注意力等价。只要单块计算时间能盖住单块传输时间,通信可以完全隐藏。这是 Megatron cp_comm_type=p2p 的默认实现(工业级 Ring Attention,即 zigzag 切分 + 双 CUDA stream 重叠 + FA2 集成)。

Ring Attention 每轮通信日程(C=4):KV 块顺时针 P2P,一轮一跳 第 0 轮(初始) GPU 0 K₀ GPU 1 K₁ GPU 2 K₂ GPU 3 K₃ 本轮算 tile:i = j(对角) 第 1 轮 GPU 0 K₃ GPU 1 K₀ GPU 2 K₁ GPU 3 K₂ 本轮算 tile:i − j = 1 第 2 轮 GPU 0 K₂ GPU 1 K₃ GPU 2 K₀ GPU 3 K₁ 本轮算 tile:i − j = 2 第 3 轮 GPU 0 K₁ GPU 1 K₂ GPU 3 K₀ GPU 3 K₃ 本轮算 tile:i − j = 3 ↑ 每轮的通信:4 对 P2P send/recv 同时进行(各 GPU 把手里的 K 块发给下家、从上家收新块),与本轮 tile 计算重叠 score tile 矩阵:颜色 = 计算发生的轮次 K₀K₁K₂K₃ Q₀Q₁ Q₂Q₃ 轮0轮1轮2轮3 轮0轮1轮2 轮0轮1 轮0 maskmaskmask maskmask mask 轮 0:每 rank 用起始 K 块算对角 tile(i=j) 轮 1:块移一跳后算次对角(i−j=1) 轮 2:算 i−j=2 轮 3:算 i−j=3,下三角全部算完 每轮每 rank:① 用手里的 K 块算 1 个 tile → partial (O, LSE) ② P2P 发块、收新块(双 CUDA stream 与 ① 重叠) 灰 = causal mask 的 tile(j>i),不算;转满 C 轮后 LSE 合并 = 精确注意力 通信量:每轮每链路 1 个 KV 块,共 C 轮 = KV 沿环走一遍 O(T);zigzag 切分只改行归属,轮转日程不变

▲ Ring Attention 每轮通信日程:上排 = 4 轮中各 GPU 持有的 K 块与每轮 P2P 跳转;下排 = score tile 矩阵(颜色 = 该 tile 被计算的轮次,灰 = 因果 mask)

  • 优点:CP 度不受 head 数限制,通信量 $O(T)$(KV 沿环走一遍)。
  • 缺点:多轮流通信 + 结果合并,kernel 需要支持 partial output/LSE 合并;小块时 kernel 效率下降。

3.2 传 KV:AllGather KV

一次性 AllGather 完整 KV,本地 Q 对全局 KV 用标准注意力 kernel 算完即可。实现最简单、不改 kernel、不受 head 数限制,但需要临时放得下整层全局 KV,且全量 KV 要在 CP 组内传一遍。由于 MLA/DSA/CSA 等架构把 KV 压缩得非常小(MLA 传 latent,CSA 传压缩条目),”先 AllGather KV 再算”反而常常是又快又好写的选择。SGLang Prefill CP、vLLM PCP、昇腾 DSA-CP 都采用该方案。

AllGather KV:通信前每 rank 只有本地 KV 分片,通信后每 rank 补齐全局 KV 通信前(虚线格 = 缺失的 KV 块) AllGather 后(颜色深浅 = 来源 rank) GPU 0 Q₀ KV₀ GPU 1 Q₁ KV₁ GPU 2 Q₂ KV₂ GPU 3 Q₃ KV₃ GPU 0 Q₀ KV₀ KV₁ KV₂ KV₃ GPU 1 Q₁ KV₀ KV₁ KV₂ KV₃ GPU 2 Q₂ KV₀ KV₁ KV₂ KV₃ GPU 3 Q₃ KV₀ KV₁ KV₂ KV₃ AllGather KV 沿 T 维聚齐 通信后:每 rank 用本地 Q 行块对全局 KV 做标准 FlashAttention,整行块一次算完,无需 LSE 合并 KV 占用 T/C·d → T·d(逐层聚合、用完即弃);通信量 = 全量 KV 在 CP 组内传一遍;不改 kernel,CP 度不受 head 数限制

▲ AllGather KV 示意图:通信前每 rank 只持有自己的 KV 分片(虚线格为缺失块),AllGather 后每 rank 补齐全局 KV 矩阵(颜色深浅区分来源 rank),Q 行块始终不动

3.3 传 Q:AllGather Q / Pass Q

KV 按 T 维分片留在本地不动,把(很小的)Q 送达各 rank,每个 rank 对本地 KV 分片算 partial output $O_j$ 和 LSE $L_j$,再合并。通信量与 KV Cache 大小无关,因此是 decode 阶段的首选。按 Q 的送达方式分为两种形态。

3.3.1 AllGather / Broadcast Q

一次性集合通信把 Q 聚齐到每个 rank。DCP 组与 TP 组重叠时各 rank 只持有自己那部分 head 的 Q 分片,需要先沿 head 维 AllGather 得到完整 Q;DCP 组独立时 Q 本来就在各 rank 复制,一次 broadcast 即可。之后各 rank 用完整 Q 对本地 KV 分片算 score 列段 $S_j$ 并得到 $(O_j, L_j)$,再做一次 LSE 感知的归并(all-reduce 或 gather,如 vLLM 的 cp_lse_ag_out_rs)。全程两个集合通信拍、延迟低,是 SGLang Decode CP(all-gather Q)、vLLM DCP、Helix 的 KV Parallelism 的共同选择。

AllGather / Broadcast Q:通信前 Q 按 head 维分片,通信后每 rank 持有完整 Q 通信前(虚线格 = 缺失的 head 分片) AllGather Q 后(颜色深浅 = 来源 rank) GPU 0 q₀ KV₀ 常驻 GPU 1 q₁ KV₁ 常驻 GPU 2 q₂ KV₂ 常驻 GPU 3 q₃ KV₃ 常驻 GPU 0 q₀ q₁ q₂ q₃ KV₀ 常驻 GPU 1 q₀ q₁ q₂ q₃ KV₁ 常驻 GPU 2 q₀ q₁ q₂ q₃ KV₂ 常驻 GPU 3 q₀ q₁ q₂ q₃ KV₃ 常驻 AllGather Q 沿 head 维聚齐 之后:各 rank 用完整 Q 对本地 KV 分片算 score 列段 S_j [q, T/C],得到 (O_j, L_j) S₀ (O₀,L₀)S₁ (O₁,L₁)S₂ (O₂,L₂)S₃ (O₃,L₃) LSE 合并 → O 通信:AllGather Q(∝ q·d)+ 一次 LSE 归并(all-reduce / gather);与 KV Cache 大小、上下文长度均无关

▲ AllGather Q 示意图:通信前每 rank 只有 1/C 的 Q head 分片(虚线格为缺失),AllGather 后每 rank 补齐完整 Q(颜色深浅区分来源 rank),KV 分片始终不动;随后各 rank 算 score 列段并 LSE 合并

3.3.2 Pass Q(环形传 Q)

Ring 传 KV 的镜像版本。KV 分片常驻不动,Q 连同运行中的 partial 累加器 $(O_{acc}, L_{acc})$ 沿环逐 hop P2P 传递,每个 rank 收到后用本地 KV 算 partial、按在线 softmax 合并进累加器再发给下家,转满 $C$ 跳回到源 rank 时累加器即精确输出,连最后的归并通信都省掉。通信量同样只正比于 $q$(与 KV 无关),但 $C$ 跳串行,延迟高于 AllGather Q。适合 KV 常驻缓存的多轮 decode / incremental prefill,即 Meta《Context Parallelism for Scalable Million-Token Inference》提出的 pass-Q 变体(动态切换见 3.5)。

Pass Q:KV 分片常驻,Q 携带 partial 累加器沿环逐 hop 传递 环形拓扑(KV 分片常驻) GPU 0KV₀ 常驻 GPU 1KV₁ 常驻 GPU 2KV₂ 常驻 GPU 3KV₃ 常驻 KV 不动 Q 包沿环逐 hop 每轮通信日程(C=4,源 = GPU 0) 轮 0 Q 在 GPU 0(源),acc = ∅ 算本地 KV₀ 的 partial acc ← (O₀, L₀) send Q+acc → GPU 1 轮 1 Q 到 GPU 1,acc = (O₀,L₀) 算本地 KV₁ 的 partial acc ⊕ (O₁, L₁)(在线合并) send → GPU 2 轮 2 Q 到 GPU 2,acc 含 KV₀~₁ 算本地 KV₂ 的 partial acc ⊕ (O₂, L₂) send → GPU 3 轮 3 Q 到 GPU 3,acc 含 KV₀~₂ 算本地 KV₃ 的 partial acc ⊕ (O₃, L₃) = 完整 O send → GPU 0(闭环) 通信前 通信后(C 跳之后) Q 在 GPU 0,acc = ∅ 尚无 KV 信息 Q 回到 GPU 0 acc = 完整 O(免归并) 传递的数据:Q [q, d] + 累加器 (O_acc [q, d],L_acc [q]);通信方式:逐 hop P2P send/recv,串行 C 跳 通信量 C·(2qd + q),与 KV Cache 无关;结果在源 rank 直接成型,省掉最后的归并通信 适合 KV 常驻的多轮 decode / incremental prefill(即 3.5 节 Meta 论文的 pass-Q);缺点:串行延迟高于 AllGather Q

▲ Pass Q 示意图:KV 分片常驻各 rank(左),Q 携带 partial 累加器沿环逐 hop 传递并就地在线合并(右,四轮日程全画),通信前 acc 为空,C 跳后回到源 rank 即为完整输出

3.4 Ulysses:All-to-All 换维

DeepSpeed-Ulysses 在注意力前后各做一次 All-to-All,把”按序列切分”转置成”按 head 切分”,通信后每个 rank 持有全序列的部分 head,直接用普通 FlashAttention,再 All-to-All 转回序列切分。不需要改 kernel、不需要 LSE 合并;通信量正比于 $T$ 但不随 CP 度增长(序列与设备等比例扩展时通信量恒定)。

Ulysses:两次 All-to-All,把 [T, H] 矩阵在「行切分」与「列切分」间转置 ① 序列切分(按行) ② head 切分(按列) ③ 转回序列切分 Q / K / V [T, H],每个 GPU 持 T/4 行 × 全部 H 列 每个 GPU 持全部 T 行 × H/4 列 输出 [T/4, H] 各回各位 GPU 0:[B, T/4, H] GPU 1:[B, T/4, H] GPU 2:[B, T/4, H] GPU 3:[B, T/4, H] H/4 H/4 H/4 H/4 GPU 0:[B, T/4, H] GPU 1:[B, T/4, H] GPU 2:[B, T/4, H] GPU 3:[B, T/4, H] 列方向示意:T 全长(↑↓)× H/4(每列色块 = 一个 GPU 的 head 子集) All-to-All 行切 → 列切 All-to-All 列切 → 行切 中间状态:普通 FlashAttention 算「全序列 × 本地 head 列块」,无需 LSE 合并 硬约束:CP 度 ≤ head 数 —— GQA/MQA/MLA 的 KV head 极少,Ulysses 很快到顶

▲ Ulysses 示意图:矩阵从「按行(序列)切分」经 All-to-All 变为「按列(head)切分」(注意:其集合通信原语是 All-to-All,不是 AllGather)

  • 硬约束:CP 度 ≤ head 数(GQA/MQA/MLA 下 KV head 极少,约束非常紧;MLA 吸收式下甚至没有 per-head KV 可交换,有效 Ulysses 度塌缩为 1)。
  • 注意:Ulysses 的集合通信原语是 All-to-All,不是 AllGather,常被混称。

3.5 混合方案:USP / a2a+p2p、pass-KV / pass-Q 动态切换

Ulysses 与 Ring 不互斥,USP(Unified Sequence Parallelism)在 2D 网格上组合两者——节点内(NVLink)用 Ulysses All-to-All,节点间(IB/RoCE)用 Ring P2P,总 CP 度可超过 head 数。Megatron-Core 的 cp_comm_type=a2a+p2p 配合 hierarchical_context_parallel_sizes(内层 a2a 尺寸、外层 p2p 尺寸)就是该思路。USP 论文还给出 4D 并行的推荐嵌套顺序(TP < SP-Ulysses < SP-Ring < ZeRO-DP < PP)。

USP / a2a+p2p:[T, H] 矩阵二维切块 —— 行向 Ring,列向 Ulysses 矩阵切分(行 = 序列块,列 = head 块) GPU 0T/2 × H/2 GPU 1T/2 × H/2 GPU 2T/2 × H/2 GPU 3T/2 × H/2 ← 列方向:a2a 组 ← 行方向:p2p 环 每块 = 一个 GPU 持有的 (序列段 × head 子集) 两层通信(示例:节点内 4 GPU × 节点间 2) 节点 0(NVLink):Ulysses a2a GPU 0 GPU 1 GPU 2 GPU 3 节点 1:GPU 4-7 同构 a2a 节点间(IB / RoCE):Ring P2P 传 KV 块 KV 块沿 p2p 环跨节点流转,与计算重叠 总 CP 度 = a2a 度 × p2p 度(例 4×2=8),可超过 head 数;对应 Megatron hierarchical_context_parallel_sizes

▲ USP 混合示意图:[T, H] 矩阵二维切块,列向走 Ulysses a2a(节点内)、行向走 Ring P2P(节点间)

另一种混合:pass-KV / pass-Q 动态切换(Meta CP,MLSys 2025)

USP 是把 3.4 的 Ulysses 与 3.1 的 Ring 在拓扑层面组合;Meta 的《Context Parallelism for Scalable Million-Token Inference》(arXiv:2411.01783,MLSys 2025)则是在算法层面把 3.1 的传 KV 与 3.3 的传 Q 做成两个无损精确的 ring attention 变体 pass-KV 与 pass-Q,运行时按负载动态切换。这是首篇公开 CP 推理系统实现细节的论文,16 节点 128 张 H100 上 Llama3-405B 的 1M 上下文 prefill 仅 77 秒(并行效率 93%、FLOPS 利用率 63%),128K prefill 3.8 秒;RDMA 与中低带宽 TCP(约 3 GB/s per rank)下均近线性扩展——跨节点通信量比 TP 小数个量级(8 节点 128K 时 TP16 已比 CP8 慢约 100%)。

  • pass-KV(= 3.1 Ring):Q 行块留本地,KV 沿环 P2P 传 $N-1$ 轮,每轮 SendRecv 与 attention 计算重叠,LSE(论文称 merge attention)合并。通信量 $\propto T\cdot N_{kv}\cdot d$,GQA 下比传 Q 小约 $N_h/N_{kv}$ 倍(405B 约 16 倍)。适合 full prefill 与 miss rate 高(新 token 占比大)的 partial prefill;decode 不用($T=1$ 时传 KV 几乎总是更贵)。
  • pass-Q(= 3.3 传 Q):KV 分片跨轮常驻(persistent KV cache,多轮会话间不动),Q 沿环传,各 rank 对本地 KV 分片算 partial $(O_j, L_j)$,经 All2All + permute 把散落在各 rank 的部分结果归位后再 LSE 合并。通信量只与新 token 数 $T$ 成正比,与 KV cache 长度无关。适合 decode($T=1$)与 miss rate 低($P \gg T$)的 partial prefill。
  • 动态切换启发式:记 miss rate $=T/(T+P)$($T$=新 token 数,$P$=已缓存 KV 长度),传 Q 消息更小当且仅当 $\frac{T}{T+P}\le 2\frac{N_{kv}}{N_h}$(405B 阈值 12.5%);实际再叠加 roofline 条件(pass-KV 的 SendRecv 能被 attention 遮蔽时优先 pass-KV)、计入 All2All 的精化式,以及经验拟合 $h(T,P)=-1.059\log T+1.145\log\frac{T}{T+P}+12.112$($h>0$ 选 pass-KV)。128K / CP4 实测切换点约在 miss rate 5% 附近。
  • 多轮 persistent KV 与负载均衡:KV cache 按 CP rank 分布存储、跨轮保持;序列切 $2N$ 块、rank $i$ 取 $(C_i,\ C_{2N-1-i})$ 首尾配对(同 zigzag 切分),同时均衡 attention 计算与 KV 显存;decode 用 round-robin 每步偏移 1 个 index,防某 rank 既背 decode 又持最长 KV 而 OOM;ring 消息按 $\max_i P_i+\lceil T/N\rceil$ 等长 padding 以满足集合通信接口。
  • 适用边界:CP 显著降低 TTFT,但 TTIT 略有退化(padding + SendRecv/All2All 开销),论文建议与 PD 分离(DistServe / Mooncake)配合部署。
pass-KV / pass-Q 动态切换(Meta CP):同一 Ring 拓扑,按 KV miss rate 决定传什么 多轮会话: P:已缓存 KV(persistent,跨轮常驻) T:新 token miss rate = T/(T+P) decode 每步 T=1 → miss rate≈0;full prefill P=0 → miss rate=100%;多轮 partial prefill 介于两者之间 切换规则:T/(T+P) ≤ 2·N_kv/N_h → pass-Q;否则 → pass-KV Llama3-405B:N_h=128、N_kv=8 → 阈值 12.5%;实测切换点 ≈ miss rate 5%(128K / CP4) pass-KV(miss rate 高 / full prefill)— 即 3.1 Ring K₀K₁K₂K₃ Q₀ Q₁ Q₂ Q₃ KV 列块沿环轮转 轮 0:算 K₀ → 发 K₀ / 收 K₃ 轮 1:算 K₃ → 发 K₃ / 收 K₂ 轮 2:算 K₂ → 发 K₂ / 收 K₁ GPU0 视角(C=4):末块 K₁ 收到后算完 N−1 轮 SendRecv 与计算重叠 → LSE 合并 传 KV:∝ T·N_kv·d,GQA 下比传 Q 小约 N_h/N_kv 倍(405B:16×) pass-Q(miss rate 低 / decode)— 即 3.3 传 Q KV₀KV₁KV₂KV₃ Q₀ Q₁ Q₂ Q₃ Q 行块沿环流转,KV 列块常驻 轮 0:Q₀@R0×KV₀ → 发 Q₀ 轮 1:Q₀@R1×KV₁ → 发 Q₀ 轮 2:Q₀@R2×KV₂ → 发 Q₀ 每轮只传小 Q(decode 每步 1 token) 4 个 partial 散在各 rank → All2All 归位 + LSE 合并 传 Q:∝ T·d·e,与 KV cache 大小无关;KV 分片跨轮常驻(persistent) 负载均衡:序列切 2N 块、首尾配对 rank i ← (C_i, C_{2N−1−i})(同 zigzag),计算与 KV 显存双均衡; decode 用 round-robin 每步偏移 1 个 index 防 OOM;ring 消息按 max_i P_i + ⌈T/N⌉ 等长 padding 传递的数据 / 通信方式:pass-KV = KV 张量 P2P 环传(N−1 轮 SendRecv 与计算重叠);pass-Q = 小 Q P2P 环传 + All2All 归位; 两者均以 LSE 合并收尾、无损精确,按 miss rate T/(T+P) 与 roofline 自动切换,覆盖 full prefill / persistent KV prefill / decode

▲ pass-KV / pass-Q 动态切换示意图:上 = 多轮会话的 miss rate 与切换规则;下左 = pass-KV 的 KV 列块每轮环传(GPU0 视角),下右 = pass-Q 的 Q 行块每轮环传 + KV 列块常驻

3.6 公共组件:LSE 合并(在线 softmax)

“传 KV 分多轮算”与”传 Q 分片算”最终都要把部分注意力结果合并成精确结果。设分片 $j$ 给出归一化输出 $O_j$ 和对数和 $L_j$。

\[m = \max_j L_j,\quad w_j = \exp(L_j - m),\quad O = \frac{\sum_j w_j O_j}{\sum_j w_j}\]

这就是 FlashAttention 的 online softmax 推广,也是 decode CP 通信量极小的原因——每步只需传输出和 LSE,无需传原始 KV。

LSE 合并:score 行分段的 (O_j, L_j) 加权合成精确输出 各分片的 partial 结果 合并(在线 softmax) O₀ L₀(logsumexp) O₁ L₁(logsumexp) O₂ L₂(logsumexp) 合并公式(在线 softmax) m = max_j L_j w_j = exp(L_j − m) O = Σ_j w_j·O_j / Σ_j w_j 输出 O 矩阵视角:score 行 S[q, T] 列向切成 j 段 → 各段独立 softmax 得 (O_j, L_j) → 加权拼回整行 段 0 段 1 段 2 段 3 ↓ 各段 (O_j, L_j) ↓ 合并后的完整 score 行 / 输出 O

▲ LSE 合并示意图:各列段 partial 结果 (O_j, L_j) 与整行 score 矩阵的对应关系

3.7 选型小结

方案 通信内容 通信量 受 head 数限制 Kernel 侵入 典型场景
Ring(传 KV) KV 块 P2P $O(T)$,可重叠 否 高(LSE 合并) 训练长序列、超长 prefill
AllGather KV 全量 KV $O(T)$,一次性 否 低 Prefill CP、KV 小的架构
传 Q Q + 输出/LSE 与 KV 无关 否 中 Decode CP
Ulysses(a2a) Q/K/V/O $O(T)$,恒定 是 低 节点内、head 多
USP(a2a+p2p) 组合 组合 部分 中 多节点混合拓扑
CP 通信方案选择路径 CP 通信方案选择 哪个阶段? Prefill Decode 全局 KV 放得下吗? Q 小、KV 大 → 放得下 放不下 AllGather KV Ring(传 KV 块) 传 Q + LSE 合并 补充判断:head 富余 + 节点内高带宽、想少改 kernel → Ulysses(a2a);CP 度需超过 head 数的多节点场景 → USP(a2a+p2p) 无论哪条路径,多轮/多分片的部分结果最终都用 LSE 合并收尾

▲ CP 通信方案选择路径

4. 不同Attention的CP

大语言模型的Attention有多种实现,比较常见的包括GQA, MLA, DSA, GDN, KDA, CSA等。而不同的Attention在CP时需要传递的数据有所不同。下面分别针对不同的Attention进行详细介绍。

4.1 GQA

GQA(Grouped Query Attention)让多个 query head 共享一组 KV head,KV Cache 省了,但代价是 KV head 数很少(Llama3-8B 8 个、Qwen3-235B 4 个)。这带来两个 CP 特征。

  • Ulysses 度被 KV head 数卡死:a2a 按 head 切分时 CP 度 ≤ KV head 数,GQA 下很快到顶;这也是 Ulysses 在现代模型上逐渐让位给 Ring/AllGather KV/传 Q 的原因。
  • 传 KV 或传 Q 都可行:prefill 常用 AllGather KV(SGLang/vLLM PCP),decode 常用传 Q(AllGather Q + 本地 KV 分片 + LSE 合并)。vLLM DCP 中 GQA 的 dcp_size 上界是 tp_size / H_kv;当 DCP 组与 TP 组重叠时各 rank 的 Q head 不同,需要先沿 head 维 AllGather Q,算完再用 LSE 感知的 all-reduce/a2a 归并(如 cp_lse_ag_out_rs)。
GQA 的 CP:传 KV 矩阵或传 Q 矩阵 GQA 结构(4 个 Q head 共享 1 个 KV head) Q h0 Q h1 Q h2 Q h3 KV head 0 KV head 数 H_kv 少 → Ulysses 度 ≤ H_kv 很快到顶 路径 A(Prefill):传 KV —— AllGather KV 矩阵 Q₀Q₁Q₂Q₃ KV₀ KV₁ KV₂ KV₃ AllGather(T 维)→ 全局 KV [T, d] × H_kv Q 行块 [T/C, H_q] 不动,对全局 KV 标准 FlashAttention 路径 B(Decode):传 Q Q [q, H_q, d](很小) broadcast / all-gather ↓ KV₀KV₁ KV₂KV₃ 各 rank 对本地 KV 分片 → (O_j, L_j) LSE 合并(很小) 传递的数据:KV 矩阵 [T, d] × H_kv(路径 A)或 Q 矩阵 [q, H_q, d] + partial (O, LSE)(路径 B) 通信方式:AllGather(KV 沿 T)/ broadcast+AllGather(Q 沿 head,DCP 与 TP 重叠时)+ LSE 感知归并

▲ GQA 的 CP:结构(左)与两条通信路径——传 KV 矩阵(中)或传 Q 矩阵(右)

4.2 MLA

传递Latent

MLA(Multi-head Latent Attention,DeepSeek-V2/V3)把每 token 的 K/V 压缩成一个共享的低秩 latent $c_{KV}$($d_{kv}=512$)外加解耦 RoPE key $k_{pe}$,比 per-head KV 小约 32 倍。CP 时传递的是 latent 而非 per-head KV。

  • AllGather latent(或 ring 传递 latent)即可让每个 rank 对全局历史做注意力;配合 absorb 形式(把 $W_{kv,b}$ 吸收进 Q,直接在 latent 上算注意力)是推理 decode 的标准做法,也是 vLLM DCP 支持 MLA 的路径(可选 --dcp-q-replicate:在 DCP 组内冗余复制 MLA 的 Q 投影,省掉每步的 query all-gather)。
  • Ulysses 对 MLA 失效:吸收式下不存在 per-head KV 可供 a2a scatter,有效 Ulysses 并行度塌缩为 1,因此 MLA 的 CP 基本走 AllGather latent 或 ring 传 latent。
  • 训练侧的坑:Megatron-Core 明确禁止在训练中使用 absorbed 形式(代码里硬 assert)——absorb 的中间激活在 $n_h \times d_{kv}$ 维,比 per-head K/V 更大,激活显存多 20~34%(DeepSeek-V3 规模最多 9.2GB)。LAGA(Latent All-Gather Attention,arXiv:2607.17644)给出训练方案:通信仍只传 latent,但本地重建 per-head K/V 再做显式注意力,通信量降 1.98 倍、显存与显式形式持平,跨节点长序列吞吐提升 1.07~1.24 倍。
  • KV Cache 复制案例:DeepSeek-R1 推理只有 1 个 KV head,tp=8 时 8 倍复制,DCP=8 消除——这是 DCP 最典型的应用场景。
MLA 的 CP:传 latent 矩阵(而非 per-head KV) 对比:per-head KV vs MLA latent K/V h0K/V h1 K/V h2K/V h3 per-head KV:大(H × T) c_KV [T, 512] k_pe MLA latent:小约 32 倍 CP 通信:AllGather latent 矩阵(或 ring 传 latent) c⁰ c¹ c² c³ latent 分片 [T/C, 512] AllGather → 全局 latent [T, 512] + 解耦 k_pe [T, 64] 一并聚合 absorb 形式: W_kv,b 吸进 Q → 直接在 latent 上算注意力 decode 标准做法 传递的数据:latent c_KV [T, 512] + k_pe [T, 64](每 token 一条,与 head 数无关) 通信方式:AllGather(prefill / decode)或 ring P2P 传 latent;vLLM DCP 可选 --dcp-q-replicate 省掉每步 query all-gather Ulysses 失效:absorb 式下无 per-head KV 可做 a2a scatter,有效 Ulysses 度塌缩为 1 训练侧:Megatron-Core 禁用 absorbed(激活回归 20~34%);LAGA 通信只传 latent、本地重建 per-head K/V

▲ MLA 的 CP:latent 矩阵的形状对比(左)与 AllGather latent 通信(右)

4.3 DSA

DSA(DeepSeek Sparse Attention,DeepSeek-V3.2-Exp)由两部分组成。Lightning Indexer(轻量 MQA 式打分器,全层合计约 0.85B 参数)为每个 query 从全序列中选出 top-k 个最相关 token(64K 时 k≈2048),再对选中 token 做细粒度稀疏注意力,复杂度从 $O(L^2)$ 降到 $O(kL)$。

CP 下需要传递的数据有两类。

  1. MLA latent KV(DSA 叠加在 MLA 之上);
  2. Indexer 的 Key(indexer key cache,避免 decode 重算)。

top-k 选择需要对全序列的 indexer key 打分,因此需要全局 key 视图——主流做法是 AllGather latent KV + indexer key,之后各 rank 只为本地 query 做 indexer 打分与稀疏注意力。在 weight-absorb 模式下 latent 与 indexer key 都很小,AllGather 的内存与通信代价完全可以接受;昇腾 TorchTitan 适配时明确对比过 RingAttention 与 AllGather KV 两条路,结论是 DSA 场景下 AllGather KV 实现简单且灵活,Ring 形式复杂且不划算。工程上还有以下配套做法。

  • 用 2C 头尾对称切片 + token 重排做负载均衡(每层 Indexer 与稀疏注意力前把 KV 还原回因果顺序),SGLang 的 interleave 策略、vLLM PCP 的 DualChunkSwap 同理;
  • SGLang Prefill CP 的 Interleave 策略对 DSA 有专门集成(按请求切分 query/indexer 元数据,按需 gather indexer key 与 MLA KV);
  • vLLM Ascend 曾实现独立的 DSA-CP(enable_dsa_cp),现已由通用的 PCP 路径承接(两者不能同时开启)。
DSA 的 CP:传 latent 矩阵 + indexer key 矩阵 需要全局视图的两份数据(top-k 打分要扫全部 indexer key) MLA latent [T, 512] Indexer key [T, d_idx] 2C 头尾配对切分(负载均衡) query/indexer 按块 r 与块 2C−1−r 配对 通信:AllGather latent + AllGather indexer key(weight-absorb 下两者都很小;Ring 形式复杂不划算) 之后各 rank 本地完成:indexer top-k 打分 → 稀疏注意力(示意 mask 矩阵) 行 = query,列 = 全序列 key;绿 = top-k 选中(k≈2048 / 64K) 每 rank 只为本地 query 做: ① indexer 打分(对全局 indexer key) ② top-k 稀疏注意力(对全局 latent)

▲ DSA 的 CP:传递 latent 与 indexer key 两份矩阵(AllGather),本地做 top-k 稀疏注意力(mask 为动态选中列)

4.4 GDN

需要传递状态S

GDN(Gated DeltaNet)是线性注意力,用固定大小的循环状态 $S \in \mathbb{R}^{d_v \times d_k}$ 替代随序列增长的 KV Cache,门控遗忘 + delta rule 定向覆盖。

\[S_t = \alpha_t S_{t-1} + \beta_t (v_t - \alpha_t S_{t-1} k_t) k_t^\top, \qquad o_t = S_t q_t\]

$\alpha_t \in (0,1]$ 控制旧状态整体遗忘,$\beta_t$ 控制当前 key 关联的写入强度。CP 下的特征:

  • 需要传递的是状态 $S$:没有 KV Cache 可传,也不能用 AllGather KV 方案;状态大小固定(例如 Qwen3-Next 每 head 128×128),跨 rank 同步状态的通信量与序列长度无关。
  • 串行依赖:$S_t$ 依赖 $S_{t-1}$,训练/预填充用 chunk-wise 并行(chunk 内矩阵并行、chunk 间串行传状态)摊开;CP 时可以沿序列在 rank 间传递状态(类似 ring 的状态链),或者各段先独立算”状态转移”再精确合成。
  • 兜底方案还有重算和 Ulysses(按 head 切),但状态链传递是通信量最优的。
  • 实际模型多为混合架构,如 Qwen3.5/Qwen3.8-Flash-Next 大部分层用 GDN、少数层保留全局注意力,CP 时两类层分别走状态传递与常规注意力通信。
GDN 的 CP:只传固定大小的状态矩阵 S 序列分段 → 各段独立求 (Φᵢ, Bᵢ) → 状态链传播(无 KV Cache 可传) 段 0(rank 0) 段 1(rank 1) 段 2(rank 2) 段 3(rank 3) 本地算 (Φ₀, B₀)本地算 (Φ₁, B₁)本地算 (Φ₂, B₂)本地算 (Φ₃, B₃) 零通信、可并行零通信、可并行零通信、可并行零通信、可并行 S₀ [d_v, d_k] S₁ = S₀Φ₀ + B₀ S₂ = S₁Φ₁ + B₁ P2P 传 S(固定大小) P2P 传 S(固定大小) → S₃ → S_out 传递的数据:状态矩阵 S ∈ ℝ^(d_v×d_k)(或等价段级摘要 (Φᵢ, Bᵢ))—— 大小固定,例如每 head 128×128 通信方式:沿序列顺序的 P2P 状态链(类 ring,但传的是 S 而非 KV);也可两两合成摘要后一次性求末状态 通信量与序列长度无关;段内输出 o_t = S_t q_t 在 S_in 到位后本地并行算

▲ GDN 的 CP:无 KV 可传,rank 间只传固定大小的状态矩阵 S

推导过程

① 从线性注意力到循环状态。 去掉 softmax 后,因果注意力可以写成 $o_t = \sum_{s \le t} (q_t^\top k_s) v_s$。定义状态 $S_t = \sum_{s \le t} v_s k_s^\top \in \mathbb{R}^{d_v \times d_k}$,立刻得到递推形式

\[S_t = S_{t-1} + v_t k_t^\top, \qquad o_t = S_t q_t\]

状态大小固定,不随序列增长。但它只能叠加,同一个 key 写入 $v_1$、$v_2$ 之后读回的是 $v_1 + v_2$,旧关联无法被覆盖。

② Delta rule(定向覆盖)。 写入前先把 $k_t$ 处的旧关联减掉,只写”修正量”。

\[S_t = S_{t-1} + \beta_t (v_t - S_{t-1} k_t)\, k_t^\top\]

展开右式 $= S_{t-1} - \beta_t S_{t-1} k_t k_t^\top + \beta_t v_t k_t^\top$,合并得等价形式

\[S_t = S_{t-1}(I - \beta_t k_t k_t^\top) + \beta_t v_t k_t^\top\]

即”先在 $k_t$ 方向擦除旧值、再写入 $v_t$”,$\beta_t \in (0,1]$ 是写入步长。同一 key 连续写 $v_1、v_2$ 后读回恰为 $v_2$(可直接验证),这就是 delta rule 的”定向覆盖”。

③ 加遗忘门(Gated DeltaNet)。 对旧状态整体乘衰减 $\alpha_t \in (0,1]$。

\[S_t = \alpha_t S_{t-1} + \beta_t (v_t - \alpha_t S_{t-1} k_t)\, k_t^\top \;\equiv\; S_{t-1} T_t + \beta_t v_t k_t^\top, \qquad T_t = \alpha_t (I - \beta_t k_t k_t^\top)\]

等价性:右式展开 $S_{t-1}\,\alpha_t(I - \beta_t k_t k_t^\top) + \beta_t v_t k_t^\top = \alpha_t S_{t-1} + \beta_t(v_t - \alpha_t S_{t-1} k_t) k_t^\top$。于是整层递推是右乘线性转移 $T_t$ 加一次秩一写入,输出 $o_t = S_t q_t$。$\alpha$ 负责”整体快速遗忘”,delta 负责”定向覆盖”,两者互补。

④ 段级分解——为什么各段可以独立求值再合成。 对长度为 $L$ 的一段递推展开。

\[S_L = S_0\, \Phi_{0 \to L} + \sum_{s=1}^{L} \beta_s v_s k_s^\top\, \Phi_{s \to L}, \qquad \Phi_{s \to L} = T_{s+1} T_{s+2} \cdots T_L \;(\Phi_{L \to L} = I)\]

即 $S_L = S_0 \Phi + B$,其中 $B = \sum_s \beta_s v_s k_s^\top \Phi_{s \to L}$ 完全由该段的 $(k, v, \alpha, \beta)$ 决定,与输入状态 $S_0$ 无关。这带来 CP 需要的两个关键性质。

  1. 各 rank 可先零通信地并行求出本段的 $(\Phi, B)$;
  2. 段间只需把固定大小的状态沿链传播,$S^{(i)}{\text{out}} = S^{(i)}{\text{in}} \Phi_i + B_i$;也可以两两合成段级摘要($\Phi_{12} = \Phi_1 \Phi_2,\ B_{12} = B_1 \Phi_2 + B_2$)后一次性得到末状态。段内各位置输出 $o_t = (S_{\text{in}} \Phi_{\text{in} \to t} + B_{\text{in} \to t}) q_t$ 在 $S_{\text{in}}$ 到位后本地并行算。

这就是”需要传递状态 $S$”的来源。CP 之间要传的只是 $d_v \times d_k$ 的状态(或等价的段级摘要),通信量与序列长度无关。

⑤ Chunk-wise 并行(GPU 上的落地)。 标量 $\alpha_t$ 可以提到连乘外面,于是

\[\Phi = \Big(\prod_t \alpha_t\Big) \prod_t (I - \beta_t k_t k_t^\top) = \Big(\prod_t \alpha_t\Big)\,(I - K C K^\top)\]

其中 $K = [k_1, \dots, k_L]$,$C$ 是由 $\beta_t$ 与两两内积 $k_i^\top k_j$ 决定的 $L \times L$ 小矩阵(WY / Woodbury 表示)。段内计算因此归结为几次 GEMM 加一个小三角求解,而不再是 $L$ 步串行递推;chunk 之间再串行传状态。FlashKDA、FLA 系 kernel 的 chunkwise 算法都基于这一结构,CP 时的”chunk 内并行、rank 间传状态”也由此而来。

4.5 KDA

需要采用KCP

KDA(Kimi Delta Attention,Kimi Linear,arXiv:2510.26692)是 Gated DeltaNet 的细粒度升级,把标量遗忘门换成逐通道遗忘门 $\alpha_t \in [0,1]^{d_k}$(对角转移矩阵),状态仍是固定大小,配 DPLR(对角加低秩)转移与 chunkwise 算法。Kimi-K3(2.8T 参数、1M 上下文)采用 3 层 KDA + 1 层 Gated MLA 的混合结构。

KDA 的 CP 必须用专门的 KCP(KDA Context Parallelism)。

  • 对标准线性注意力,每个序列分段的状态转移可以在不知道输入状态的情况下独立求值,再精确合成递归链;但 KDA 的逐通道遗忘门在数学上使这种”独立求值再合成”不能直接成立。
  • KCP 把状态转移中可独立计算的部分尽可能拆解出来,分发到不同 GPU 进程并行计算,再合成各段应得的状态;此外还支持单卡内 SM 级的序列二次切分(intra-device KCP)与跨设备 KCP 两条线并行——长序列(百万 token)时仅靠 TP 切 head 会让 prefill 的 SM 大量空转。
  • 跨设备只同步固定大小的状态,通信成本恒定,不随文本变长。
  • 配套工程:FlashKDA(基于 CUTLASS 的 chunkwise kernel,chunk 内并行与跨 chunk 状态传播重叠)、state-aware prefix caching(固定大小状态天然适合缓存复用)。
  • 生态:SGLang 针对 Kimi-K3 提供 fused KDA decode kernel、KDA-aware prefix caching、DP attention、MTP 与 PD 分离支持。
KDA 的 CP(KCP):独立量并行求值 + 固定状态合成 KDA 转移:D_t(对角)+ 秩一 D_t = Diag(α_t) 逐通道遗忘门 −β(D_tk)kᵀ 秩一修正 T_t = D_t − β_t(D_tk_t)k_tᵀ(DPLR) KCP 两级并行 独立量求值∏D_t / 低秩因子 / KV 统计 独立量求值拆到不同 GPU 进程 独立量求值(零通信、并行) 状态合成 只传 S [d_v, d_k] 跨设备 KCP:沿段链 P2P 传固定大小状态 卡内 KCP:SM 级序列二次切分,各 SM 算完再合并状态 传递的数据:状态 S(或段级转移摘要)+ 各段独立计算的中间量就地合成 通信方式:跨设备 P2P 状态链(成本恒定);与 FlashKDA chunkwise kernel、state-aware prefix caching 配套

▲ KDA 的 CP(KCP):DPLR 转移矩阵(左)与”独立量并行 + 固定状态合成”两级并行(右)

推导过程

① KDA 的更新律与 DPLR 转移。 把 GDN 的标量遗忘门换成逐通道(对角)遗忘门 $\alpha_t \in [0,1]^{d_k}$,$D_t = \mathrm{Diag}(\alpha_t)$。

\[S_t = S_{t-1}\, D_t (I - \beta_t k_t k_t^\top) + \beta_t v_t k_t^\top \;=\; S_{t-1}\, T_t + \beta_t v_t k_t^\top, \qquad o_t = S_t q_t\]

单步转移可以整理成

\[T_t = D_t - \beta_t (D_t k_t)\, k_t^\top\]

即 “对角 + 秩一”——这正是 DPLR(Diagonal-Plus-Low-Rank)转移矩阵;Kimi Linear 的 chunkwise 算法对这一特殊 DPLR 形式做了专门化(比通用 DPLR 求解省掉大量计算,同时保持与经典 delta rule 一致的语义)。逐通道门的意义:不同 key 维度可以有自己的遗忘速率,固定大小状态的记忆利用率更高。

② 段级仿射结构(形式上仍然成立)。 与 GDN 完全相同地展开:

\[S_L = S_0\, \Phi_{0 \to L} + \sum_{s=1}^{L} \beta_s v_s k_s^\top\, \Phi_{s \to L}, \qquad \Phi_{s \to L} = T_{s+1} \cdots T_L\]

“每段先算 $(\Phi, B)$、再合成”在代数上依旧成立——问题变成如何高效地表示与计算 $\Phi$。

③ 为什么 GDN 的”独立求值再合成”不能直接搬到 KDA。 GDN 的 $T_t = \alpha_t(I - \beta_t k_t k_t^\top)$ 中标量 $\alpha_t$ 与一切对易,可以提到连乘外面。

\[\Phi_{\text{GDN}} = \Big(\prod_t \alpha_t\Big)\,(I - K C K^\top)\]

紧凑结构(对角缩放 × 秩一修正的连乘)在段内 keys 张成的空间里封闭,段间合成只是小矩阵运算。KDA 中各 $D_t$ 是互不相同的对角阵,与秩一项不对易。

\[D(I - \beta k k^\top) = D - \beta (Dk) k^\top \;\neq\; (I - \beta k k^\top) D = D - \beta k (Dk)^\top\]

(除非 $k$ 落在退化方向上)。于是 $D_t$ 不能像标量那样外提,$(I - KCK^\top)$ 的 WY 紧凑形式不再封闭,连乘 $D_1R_1D_2R_2\cdots$($R_t = I - \beta_t k_t k_t^\top$)会不断混合对角与低秩结构,直接套用通用 DPLR 合成的代价随段长增长。这就是公开材料里说的”KDA 的状态转移函数具有记忆遗忘机制,在数学上导致额外困难,(标准线性注意力的)独立求值再合成并不直接可行”。

④ KCP 的拆解思路。 KCP 的做法是把状态转移中可以独立计算的部分尽可能拆解出来,再分发并行。

  1. 段内与输入状态无关的量照常并行求值:key/value 统计、累积对角衰减 $\prod_t D_t$、DPLR 转移中的低秩因子等,拆到不同 GPU 进程独立计算;
  2. 只沿段链传递固定大小的状态(或等价的段级转移摘要),把它与各段的独立计算结果合成——跨设备同步的就是 $d_v \times d_k$ 的状态,通信成本恒定,不随文本变长;
  3. 两级并行:除跨设备 KCP 外,还有单卡内 SM 级的序列二次切分(intra-device KCP)——百万 token 时只靠 TP 切 head 会让 prefill 的 SM 空转,KCP 把序列在 SM 之间再切一段,各自算完状态再合并,与跨设备 KCP 同时进行;
  4. 工程配套:FlashKDA 的 chunkwise kernel 把 chunk 内并行与跨 chunk 状态传播重叠;state-aware prefix caching 直接缓存固定大小的状态。

(KCP 完整数学细节以 Kimi K3 技术资料为准;公开材料给出的核心即上述”可独立计算部分拆解 + 固定状态合成”。)

4.6 CSA

CSA(Compressed Sparse Attention,压缩稀疏注意力,DeepSeek-V4)是 DSA 思路的进一步演化,核心是”先压缩、再稀疏选择”。

  1. KV 压缩:用带学习位置偏差的 softmax-gated pooling,把每 $m=4$ 个 token 的 KV 条目压缩成 1 条压缩条目,序列长度缩为 $1/m$;
  2. 稀疏选择:Lightning Indexer(FP4 精度)对压缩条目打分,每个 query 只取 top-k(512~1024)个压缩块做注意力;
  3. SWA 分支保留最近窗口内的精细局部依赖;与 HCA(Heavily Compressed Attention,压缩率约 128 倍后做稠密注意力)在层间交替堆叠(V4-Pro 61 层 CSA/HCA 交替),兼顾”近处细节、中程精准检索、远程全局覆盖”。1M 上下文下 KV Cache 降到 V3 的 10%、单 token FLOPs 降到 27%。

CP 下需要传递的数据与注意事项:

  • 传递的是压缩 KV 条目(以及独立缓存时的 indexer key),体量只有原始 KV 的 $1/m$,比 DSA 的 AllGather 更轻,”传 KV / AllGather KV”几乎是自然选择;
  • top-k 选择同样需要对全部压缩条目打分,需要全局(压缩后的)key 视图,做法与 DSA 相同;
  • 压缩组(每 $m$ 个 token)应作为 CP 切分的最小粒度,避免把一个压缩组切断到两个 rank;HCA 的高压缩摘要同理;
  • SWA 分支只依赖局部窗口,基本不需要跨 rank 通信(窗口跨界除外)。
  • 演化路线:NSA(2025-02,压缩/选择/滑窗三分支、块级稀疏)→ DSA(V3.2,token 级动态稀疏 + Lightning Indexer)→ CSA(V4,KV 压缩 + 稀疏选择)。SGLang 的 DeepSeek-V4 后端(multi-ratio compressed KV pools + FlashMLA + 稀疏索引)与 Prefill CP 的 “DeepSeek V4 interleave” 路径已有对应集成。
CSA 的 CP:传压缩 KV 矩阵(1/m)+ indexer key KV 压缩:每 m=4 条 → 1 条 原始 KV [T, d] 条目 0 条目 1 压缩 KV [T/4, d] CP 通信:AllGather 压缩条目 + indexer key 压缩 0 压缩 1 压缩 2 压缩 3 各 rank 本地分片 AllGather → 全局压缩 KV [T/4, d] 体量只有原始 KV 的 1/m + indexer key(FP4)一并聚合 压缩组(m 个 token)为切分 最小粒度,不可切断 之后本地:indexer 对压缩条目 top-k → 稀疏注意力 + SWA 局部分支 稀疏选择:行=query,列=压缩条目 SWA 分支:对角带状 mask,基本不通信 传递的数据:压缩 KV [T/4, d] + indexer key 通信方式:AllGather(比 DSA 更轻) SWA 窗口跨界时少量补传

▲ CSA 的 CP:KV 压缩矩阵(左)、AllGather 压缩条目(中)、稀疏选择与 SWA 两种 mask(右)

5. 推理和训练引擎的实现

5.1 vLLM

vLLM 把 PCP 与 DCP 做成了两个正交的命令行维度(讨论集中在 vLLM Slack #sig-context-parallel)。

PCP(Prefill Context Parallel):--prefill-context-parallel-size / -pcp

  • 按序列切分 prefill 计算,扩展进程 world size,但不增加 KV cache 的分片数;
  • 两种策略(官方文档):”Partial query, full key/value”(AllGather KV,摊薄 TTFT)与 “Partial query, partial key/value”(ring 式传 KV,应对放不下全局 KV 的超长请求),均在持续开发中;
  • 头尾式(DualChunkSwap)token 分区保证负载均衡,并用 restore 索引恢复原始 token 顺序(pcp_allgather_restore_idx);
  • PD 分离部署中 PCP 只开在 prefill(kv_producer)引擎上,decode 侧 prefill_context_parallel_size=1。

DCP(Decode Context Parallel):--decode-context-parallel-size / -dcp

  • 沿 T 维分片 decode KV Cache,不扩大 world size(复用 TP/PCP rank),dcp_size ∈ [1, tp_size / H_kv];
  • 交错分片由 --cp_kv_cache_interleave_size 控制(token 级 i % dcp_world_size 或 block 级对齐);KV 传输(KV pooling / PD 分离)时建议设为 KV cache 的 block_size(默认 128);
  • 通信后端 --dcp-comm-backend:ag_rs(AllGather + ReduceScatter)或 a2a(交换 partial output + LSE 后用 Triton kernel 合并,MLA 模型每层 NCCL 调用从 3 次降到 2 次);
  • --dcp-q-replicate:在 DCP 组内冗余复制 MLA 的 Q 投影,让 decode 跳过每步的 query all-gather;
  • 支持 MLA 与 GQA 模型,部分注意力后端还支持 DCP + MTP(multi-token prediction)组合加速解码。

5.2 SGLang

Prefill CP(2025-12 由百度百舸 AIAK 贡献合入主线,PR #12065):

  • 把 prompt 的 token 分布到 attention CP 组,各 rank 为本地 query 计算注意力(含 indexer),通过 all-gather KV cache 拿到所需 K/V;
  • ContextParallelStrategy 把 token 布局策略与模型执行/注意力 kernel 解耦,每进程一个策略实例,per-forward 状态放在 ForwardBatch.attn_cp_metadata;
  • 两种切分策略:
    • Zigzag:每请求新增 token 切成 $2C$ 个连续块,rank $r$ 拿块 $r$ 与块 $2C-1-r$(每请求独立重启切分;长度不整除时前段块拿余数),保留连续 query 块且因果负载均衡;
    • Interleave:rank $r$ 拿展平索引 $r, r+C, r+2C, \dots$(索引跨 batch 新增 token 连续、不按请求重置,position id 保持原值),query 分布更均匀,配合 DSA/DeepSeek V4 的稀疏注意力集成(gather indexer key 与 MLA KV);
  • 后端集成路径:Zigzag + FlashAttention(gather 并重排新 KV)、Zigzag + trtllm_mha(zigzag page table 的变长注意力)、Interleave + DSA、DeepSeek V4 interleave;
  • 收益:把 indexer(DSA/DeepSeek V4)这类原本各 rank 重复的工作切开;与 KV cache 分片(DSA cache LayerSplit)叠加省显存;与 EP 组合时用 all-to-all token dispatch/combine 替代 TP all-reduce。

Decode CP:

  • 采用传 Q 策略,即”an all_gather of q is done for decode stage”,各 rank 对本地 KV 分片算注意力后合并(在线 softmax/LSE);
  • RadixTree 的处理:逻辑树留在中心 scheduler,物理存储按 rank 分片(一条上下文序列的 KV cache 切到各 dcp rank),调度时把逻辑节点映射到物理 GPU rank;
  • PD 分离下 CP 感知的 KV 传输:SGLANG_DISAGGREGATION_ALL_CP_RANKS_TRANSFER 控制由 CP rank 0 单独传输还是所有 CP rank 各传自己那部分。

模型支持面上,SGLang 已适配混合注意力模型(GLM-5.3-Flash 的 MLA + DSA + KDA、Kimi-K3 的 fused KDA decode + KDA-aware prefix caching、Qwen3.8-Flash-Next 的 GDN + 稀疏注意力等),CP 需要按层类型分别处理 KV / latent / 状态 / indexer key。

5.3 Megatron

Megatron-LM / Megatron-Core 的 CP 是训练侧的工业级实现。

  • 入口参数:--context-parallel-size、--cp-comm-type(p2p / all_gather / a2a / a2a+p2p)、hierarchical_context_parallel_sizes(内层 a2a 尺寸 × 外层 p2p 尺寸)、max_seqlen_per_dp_cp_rank;
  • p2p(默认)即 Ring Attention,zigzag 切分成 $2 \times cp_size$ 块(rank $k$ 拿块 $k$ 与 $2cp-1-k$),双 CUDA stream 做计算-通信重叠,与 FlashAttention-2 深度集成,LSE 合并部分结果;
  • a2a 即 Ulysses;all_gather 为 AllGather KV;a2a+p2p 为层级混合(节点内 a2a、节点间 p2p),适合 CP 度超过 head 数的多节点场景。NVIDIA 文档的经验是能调 TP 使 CP ≤ num_kv_heads / TP 时优先纯 a2a;KV head 很少或要最大 CP 灵活性时用 p2p;
  • 与 Megatron-SP(TP 内对 norm/dropout 的序列并行,Reduction/AllGather 零额外开销)区分,SP 先上,激活仍不够再上 CP;
  • MLA 注意力在 Megatron-Core 中有 explicit(训练用,per-head K/V 参与 a2a)与 absorbed(推理 decode 用,只 gather latent)两种实现,训练路径被 assert 禁用 absorbed(激活显存回归 20~34%),低通信训练方案见 LAGA 论文;
  • TorchTitan 侧:torch 原生 CP(序列切分 + RingAttention 或 AllGatherKV)对普通注意力可用;DSA 这类需要同时处理 KV 与 indexer key 的模型要自定义 CP(CustomContextParallelContext,在 forward 里加 KV AllGather)。

6. 动态CP

为了适应不同的上下文,动态调整CP的并行度也逐渐成为研究的热点。两个代表性方向中,NVIDIA Helix 在执行时于不同并行布局间动态切换同一组 GPU;北大与阿里的 Vertumnus 则在调度时按负载动态调整 CP 度与 worker 组成。

6.1 Helix

NVIDIA 在 Helix 论文(Helix Parallelism: Rethinking Sharding Strategies for Interactive Multi-Million-Token LLM Decoding,arXiv:2507.07120)中提出面向交互式百万级 token 解码的混合执行策略。

要解决的问题:自回归 decode 有两大瓶颈——FFN 权重读取与长 KV Cache 读取。TP 能缓解 FFN 权重读取,但对注意力扩展很差:TP 宽度超过 KV head 数后 KV Cache 被低效复制,锁死并行度与 batch 上限;同时长 KV 的 DRAM 读取随 batch 线性增长。

核心做法:把 Attention 与 FFN 的并行映射在时间维度上解耦(对比传统 AFD 把两者空分到不同 GPU)。

  1. Attention 阶段用 KV Parallelism(KVP):KV Cache 沿 T 维分片到 CP rank,每 rank 对本地 KV 分片计算 partial attention;
  2. 轻量通信步合并/校正 partial 结果(LSE 式合并),保证注意力结果精确;
  3. FFN 阶段同一组 GPU 重配置为 TP(dense 模型)或 TP×EP(MoE)去算 FFN/MoE,充分利用 GPU;
  4. 拓扑为 $N = KVP \times TP_A$(注意力 TP 度 $TP_A$ ≤ KV head 数),QKV 投影冗余计算使每个 KV 分片可做完全本地的 FlashAttention;
  5. Helix HOP-B:用 batchwise overlap 把上述通信开销藏起来,保持低 TTL 的同时提高 GPU 效率;
  6. KV 块按 rank 轮转(交错)分配,保证 KVP 组内所有 GPU 显存均匀增长——这也是 vLLM DCP 交错分片的出处(由 Moonshot 的 Chao Hong 提出)。

动态 CP 怎么并行起来(机制拆解):Helix 的”动态”不是运行时改 CP 度,而是同一组 GPU 在每个 decoder layer 内被重配置两次,Attention 与 FFN 各用各的最优并行映射,时分复用同一块卡:

  1. 布局基座:$N$ 块 GPU 按 $N = KVP \times TP_A$ 组织;KV Cache 沿 T 维交错分片常驻各 rank(显存均匀增长),整个生成过程不动;
  2. Attention 段(KVP 布局):每 rank 对本地 KV 分片做完整 FlashAttention(QKV 投影冗余计算换本地性),得到 partial 输出 $(O_j, L_j)$;
  3. 轻量合并步:沿 KVP 组做一次 LSE 式合并/校正(只传 $O$ 与 LSE,量很小),注意力结果精确等价;
  4. FFN/MoE 段(TP/EP 布局):同一批 GPU 立刻换映射——dense 模型走 TP,MoE 走 TP×EP,权重 AllReduce / 路由 All-to-All;
  5. 回到 2,进入下一个 decoder layer——每个 layer 都重复”KVP → 合并 → TP/EP”这一拍;
  6. HOP-B(Helix Overlapped P2P-Batch):第 3 步的合并通信与第 2/4 步的计算在 batch 维交错——请求 A 通信时,请求 B 正在做 Attention/FFN 计算,通信被完全藏进计算,既保低 TTL 又保 GPU 利用率。

与传统做法相比,AFD 把 Attention 与 FFN 空分到不同 GPU(各干各的,但卡有一半时间闲),Helix 把两者时分到同一组 GPU;TP 单独用则 KV Cache 随 KV head 复制、batch 上限被锁死。Helix 等于”Attention 时是 CP、FFN 时是 TP/EP”,通信只有每层一次轻量 LSE 合并。

Helix 动态 CP:同一组 GPU 时分复用 KVP 与 TP/EP 两种映射 时间轴(每层一拍)→ Attention(KVP) LSE 合并 FFN(TP) Attention(KVP) LSE 合并 FFN layer i 的一拍:KVP 算注意力 → 轻量合并 → 换 TP/EP 映射算 FFN → 进入 layer i+1(同一批 GPU,反复重配置) HOP-B:batch 维交错,把合并通信藏进另一个 batch 的计算 A: Attn A: 合并 A: FFN A: Attn A: 合并 B: Attn B: 合并 B: FFN B: Attn B: 合并 请求 A 通信(橙)时 请求 B 正在计算 → 通信 100% 被藏起来 两种映射下的矩阵/权重分片(4 GPU 例) KVP:KV Cache 沿 T 交错分片(Attention 段) KV 块按 rank 轮转交错 → 各卡显存均匀增长 TP/EP:权重按 head / expert 切分(FFN 段) FFN/MoE:TP(dense)或 TP×EP(MoE),结果 AllReduce ⇄ 每拍在两种映射间切换(同一批 GPU 重配置,KV 分片不动) 传递的数据:partial (O, LSE) 的轻量合并(每层一次)+ FFN 权重 AllReduce / MoE 路由 通信方式:KVP 组内 LSE 合并 + TP AllReduce;HOP-B 让两者都被另一个 batch 的计算覆盖

▲ Helix 动态 CP 机制:每层一拍的 KVP→合并→TP/EP 时序、HOP-B 的 batch 交错、两种映射下的分片形态

效果(DeepSeek-R1、Blackwell NVL72、FP4、1M token 上下文的模拟):固定 batch 下 TTL 最多降低 1.5 倍;同等延迟预算下 batch 最多放大 32 倍。目标场景是 PD 分离架构的 generation 服务器、输入 >64K、低 batch、延迟敏感(高 tokens/s/user)的负载。

实现:TensorRT-LLM 已支持(context_parallel_size ≥ 2、cp_config.cp_type = "HELIX"、cp_config.tokens_per_block 与 KV cache 配置一致),目前支持 Blackwell 上的 MLA 模型(DeepSeek-V3 / V3-Lite),有 MLA Helix 单测与分离式服务精度测试。

6.2 Adaptive Context Parallelism

北大与阿里联合发表 Adaptive Context Parallelism for Production LLM Serving(系统名 Vertumnus,arXiv:2609.04774,2026-09),把 CP 度变成生产调度的一等公民。

背景——生产负载的三个特征:① 请求长度高度异构(几百 token 的问答到几十万 token 的文档分析/Agent 请求);② 负载随时间动态变化(到达率与长短请求占比数倍波动);③ 前缀复用普遍(系统提示、多轮对话、Agent 工作流),前缀缓存必不可少。CP 的收益与输入长度强相关:长请求从高 CP 度明显获益,短请求却会因通信开销反而变慢——静态 CP 配置注定顾此失彼。

现有方案的不足:① 完全不把 CP 度当调度维度;② 静态 CP 配置(部署时定死,最多按长度路由,不考虑缓存命中与 worker 负载);③ 虽能动态调 CP 度,但只针对活跃请求/批次,没有稳定的持久化 worker 集群,前缀缓存无法跨请求复用。

Vertumnus 的三层设计:

  1. 请求级路由:用 placement cost 在不同 CP 度的 worker 之间路由请求,代价函数 = 预测排队延迟 + cache-aware 的 prefill 时间 + GPU-time 成本;
  2. 集群级重组:随负载变化以秒级 split/merge 调整 worker 组成——短请求潮来了把大 CP worker 拆成多个小 worker,长请求潮来了把小 worker 合并成大 worker,无需重启进程或重新加载模型;
  3. 全局前缀缓存管理:跨 worker(无论 CP 度相同与否)协调缓存的放置与复制,worker 重组、请求改派时保持缓存局部性——缺的缓存块可以从别的 worker(甚至是不同 CP 度的 worker)按需复制过来。
Vertumnus:调度时动态调 CP 度(请求级路由 + 秒级 split/merge + 全局前缀缓存) ① 请求级路由(placement cost) 短请求 512 长请求 128K 短请求 256 超长 1M 代价函数 = 排队延迟 + cache-aware prefill 时间 + GPU·s ② 集群级 split / merge(秒级,不重启、不重载) worker CP=4(大) CP=2 CP=2 worker CP=1(小) worker CP=1(小) worker CP=2(合并后) 短请求潮 → split 大 worker 拆小 长请求潮 → merge 小 worker 合大 ③ 全局前缀缓存 system prompt 前缀块 多轮对话前缀块 Agent 工作流前缀块 跨 worker / 跨 CP 度统一管理 缺块按需复制,重组时保局部性 传递的数据:按 placement cost 选择的请求流 + 前缀缓存块的跨 worker 复制 "通信"方式:请求改派(秒级)与 worker split/merge(秒级),缓存块按需搬移而非全量重算

▲ Vertumnus 三层机制:请求级路由(左)、集群级 split/merge(中)、全局前缀缓存(右)

效果:64 GPU 集群、公开 + 生产负载,最高负载下平均 TTFT 最多降低 28.1%,token 加权 SLO 达成率最多提升 13.3 个百分点(对比最强基线)。

参考文献

  1. Jacobs, S. A. et al. DeepSpeed-Ulysses: System Optimizations for Enabling Training of Extreme Long Sequence Transformer Models. arXiv:2309.14509, 2023. https://arxiv.org/abs/2309.14509
  2. Liu, H., Zaharia, M., Abbeel, P. Ring Attention with Blockwise Transformers for Near-Infinite Context. arXiv:2310.01889, 2023. https://arxiv.org/abs/2310.01889
  3. Yang, A. et al. (Meta). Context Parallelism for Scalable Million-Token Inference(pass-KV / pass-Q ring attention). arXiv:2411.01783, 2024(MLSys 2025). https://arxiv.org/abs/2411.01783
  4. Fang, J., Zhao, S. USP: A Unified Sequence Parallelism Approach for Long Context Generative AI. arXiv:2405.07719, 2024. https://arxiv.org/abs/2405.07719
  5. Korthikanti, V. A. et al. Reducing Activation Recomputation in Large Transformer Models(Megatron-SP). arXiv:2205.05198, 2022. https://arxiv.org/abs/2205.05198
  6. Bhatia, N. et al. (NVIDIA). Helix Parallelism: Rethinking Sharding Strategies for Interactive Multi-Million-Token LLM Decoding. arXiv:2507.07120, 2025. https://arxiv.org/abs/2507.07120
  7. Guo, J., Wang, R., Huang, P., Lv, Z., Wang, Z., Liu, K., Lan, T., Qu, L., Wang, X., Yang, T. Adaptive Context Parallelism for Production LLM Serving(Vertumnus,北大 & 阿里). arXiv:2609.04774, 2026. https://arxiv.org/abs/2609.04774
  8. DeepSeek-AI. DeepSeek-V2: A Strong, Economical, and Efficient Mixture-of-Experts Language Model(MLA). arXiv:2405.04434, 2024. https://arxiv.org/abs/2405.04434
  9. Yuan, J. et al. Native Sparse Attention: Hardware-Aligned and Natively Trainable Sparse Attention(NSA). arXiv:2502.11089, 2025. https://arxiv.org/abs/2502.11089
  10. Yang, S. et al. Gated Delta Networks: Improving Mamba2 with Delta Rule. arXiv:2412.06464, 2024(ICLR 2025). https://arxiv.org/abs/2412.06464
  11. Kimi Team. Kimi Linear: An Expressive, Efficient Attention Architecture(KDA). arXiv:2510.26692, 2025. https://arxiv.org/abs/2510.26692
  12. Ma, C. A Training-Memory Regression in MLA Sequence Parallelism: Why Megatron-Core Forbids Absorption, and LAGA. arXiv:2607.17644, 2026. https://arxiv.org/abs/2607.17644
  13. DeepSeek-AI. DeepSeek-V3.2-Exp(DSA / Lightning Indexer). 2025-12. https://github.com/deepseek-ai/
  14. DeepSeek-AI. DeepSeek-V4(CSA / HCA 混合注意力). 2026-04(官方发布资料).
  15. vLLM 官方文档. Context Parallel Deployment(PCP/DCP). https://docs.vllm.ai/en/latest/serving/context_parallel_deployment.html
  16. SGLang 官方文档. Prefill Context Parallelism. https://docs.sglang.io/docs/advanced_features/prefill_cp ;CP 合入 PR(百度百舸 AIAK):https://github.com/sgl-project/sglang/pull/12065
  17. NVIDIA 文档. Hierarchical Context Parallel(Megatron-Bridge,cp_comm_type / a2a+p2p). https://docs.nvidia.com/nemo/megatron-bridge/
  18. NVIDIA 文档. Helix Parallelism(TensorRT-LLM). https://nvidia.github.io/TensorRT-LLM/features/helix.html
  19. Moonshot AI(Yuxin Wu). Introduction to Kimi K2(Decode Context Parallel 交错分片由 Chao Hong 提出;2~3 倍吞吐收益). PyTorch 2025 演讲纪要.
]]>
History Of SPARC | The Root of Throughput Computing http://zhifeiding.github.io/programming/history/2026/10/21/History-Of-Sparc/ 2026-10-21T00:00:00+00:00 http://zhifeiding.github.io/programming/history/2026/10/21/History-Of-Sparc 前言

最近几年系统性的学习并梳理了计算机体系结构的相关知识,稍有感悟。遂决定将之整理成文,一来沉淀所学,避免遗忘;二来将散落在各处的技术细节串联起来,形成相对完整的知识脉络;三则试图通过回顾历史,进而窥见未来的走向。

本系列文章命名为鉴往知远,主要关注计算与互联两个方向。本文是该系列中关于 SPARC 的一篇,主题为 Sun / Oracle 与 Fujitsu 两条脉络上 SPARC 处理器近三十年的演进。

如果说 POWER 是 IBM 在高端服务器市场上”以强致胜”的代表,那么 SPARC 走的就是另一条路:它诞生于学术界开放的 RISC 思潮,以寄存器窗口这一极具辨识度的设计闻名,在九十年代凭借超标量与多媒体扩展一度领跑,又在二十一世纪初断然转向吞吐量计算 (Throughput Computing),用”简单多核 + 超多线程 + 巨量内存带宽”重新定义了服务器处理器的形态。

0. 概述

0.1 SPARC 发展时间线

SPARC(Scalable Processor Architecture,可扩展式处理器架构)的源头,要追溯到 1980 年代初加州大学伯克利分校的 RISC 研究。这种起源于学校的开放思想背景,自然也影响了 Sun 自行研发的 RISC 指令集处理器。

下表按时间顺序梳理了 SPARC 发展过程中的关键节点:

  • 1984 年:Sun 开始进行 SPARC 指令集研究,开发顾问是大名鼎鼎的 David Patterson——两本必读计算机体系结构图书的作者、RISC 的创造者、RISC-V 的发起者。2018 年 3 月 22 日,David Patterson 因开发了 RISC 微处理器并让这一概念流行起来,获得 2017 年度 ACM 图灵奖。
  • 1986 年:Sun 发表 32 位的 SPARC V7 指令集。世界首颗实做出来的 SPARC 兼容处理器并非出自 Sun,而是 Fujitsu 的 MB86900。
  • 1987 年:Sun-4 系列工作站问世,处理器来源为日本 Fujitsu 与 Cypress(分别搭配来自 Weitek 和 TI 的浮点辅助运算器)。
  • 1990 年:SPARC V8 补完了整数乘法与除法等”复杂”指令,后于 1994 年成为 IEEE Std 1754。
  • 1992 年:SuperSPARC,Sun 首颗超标量 SPARC 实现,3 指令派发、全动态调度。
  • 1994 年:SPARC International 发布 SPARC V9 规范,将架构扩展到 64 位。
  • 1995 年:UltraSPARC I,首颗 SPARC V9 处理器,并引入 VIS(Visual Instruction Set)多媒体指令集。
  • 2001 年:UltraSPARC III(Cheetah)与 UltraSPARC III Cu,后者借助 0.13μm 铜导线工艺冲破 1GHz。
  • 2002 年:Sun 与 Fujitsu 联合提出 JPS(Joint Programming Specification)规范。
  • 2004 年:UltraSPARC IV,UltraSPARC 家族进入双核心时代。
  • 2005 年:UltraSPARC T1(Niagara)发布,确立 Throughput Computing 路线;同年 Sun 以 OpenSPARC 的形式开放 T1 的 RTL 源码。
  • 2007 年:UltraSPARC T2(Niagara-2),单芯片 8 核 64 线程,并集成密码学加速与 10Gb 以太网。 01.png
  • 2010 年:Oracle 完成对 Sun 的收购,UltraSPARC RK(Rock)被取消,SPARC Enterprise 品牌并入 SPARC T / M 系列。
  • 2011 年:SPARC T4,S3 核心转向双指令派发、非循序预测执行;Oracle 发布 OSA(Oracle SPARC Architecture)。
  • 2014 年:Fujitsu SPARC64 XIfx,SPARC 世界第一颗超级电脑系统单芯片。
  • ![[Pasted image 20230630141022.png]] 02.png
  • 2015 / 2017 年:SPARC M7 与 SPARC M8,分别达到 32 核 256 线程、5GHz 时钟,成为 SPARC 在高端服务器的绝响。
  • 2020 年:由 SPARC64 XIfx 演化而来的 A64FX(改用 ARM 架构)驱动”富岳”登上 Top500 首位。 0.png

0.2 本文组织形式

本文的组织形式如下:

  • 0.1 节按时间顺序梳理 SPARC 三十余年的发展里程碑,可作为全文的快速索引。
  • 0.3 节给出高频术语的中英对照表。Sun / Oracle 与 Fujitsu 两家文档对同一概念的译法常有出入。
  • 第 1 章介绍 SPARC 指令集架构,包括寄存器窗口、指令格式、分支与延迟槽、陷阱与特权级、存储模型,以及 V7 / V8 / V9 / VIS / JPS / OSA / HPC-ACE 的版本演进。
  • 第 2 章给出 SPARC 处理器的总体概览与跨代对比。
  • 第 3 至第 7 章介绍 Sun “传统”高性能 UltraSPARC 家族:SuperSPARC、UltraSPARC I、II / IIi / IIe、III / IIIi(Gemini)、IV / IV+。
  • 第 8 至第 9 章介绍转向吞吐量计算后的 UltraSPARC T1 / T2(Niagara / Niagara-2)及其多片一致性互连。
  • 第 10 章介绍令人惋惜的未竟之作 SPARC Rock。
  • 第 11 至第 12 章介绍 Oracle 时代的 SPARC T3 / T4 / T5 与 M5 / M6 / M7 / S7 / M8。
  • 第 13 章介绍 Fujitsu 的 SPARC64 系列,及其在超级计算机上的分支。
  • 最后是参考文献。

0.3 术语对照

本文沿用了大量原始技术文档中的英文术语。下表列出高频术语的中英对照,其中部分术语在 Sun / Oracle 与 Fujitsu 两家文档中译法不同:

英文 中文 说明
Register Window 寄存器窗口 SPARC 的标志性设计,相邻窗口的 out / in 寄存器物理重叠
Superscalar 超标量 每周期派发多条指令
Out-of-Order Execution 非循序 / 乱序执行 本文两种译法并存,均指指令执行顺序不同于程序顺序
Speculative Execution 预测执行 / 投机执行  
SMT(Simultaneous Multi-Thread) 同时多线程 Fujitsu SPARC64 系列采用
CMT(Chip Multi-Threading) 芯片多线程 Sun 对 Niagara 多线程方案的称谓
CMP(Chip Multi-Processor) 芯片多处理器 单芯片多核
Throughput Computing 吞吐量计算 Sun 在 T 系列上确立的路线
Crossbar 交叉开关 连接核心、L2 分体与 I/O 的片上互连
Strand 硬件线程 Sun / Fujitsu 文档中对硬件线程的称呼
Pick Queue 拾取队列 待发射指令队列,S3 为 40 项
Store Buffer (STB) 存储缓冲 暂存待写回的 store
Load Miss Queue (LMQ) 加载失效队列 跟踪未完成的 load miss
Gasket 垫片单元 核心与交叉开关之间的接口单元
TLB / ITLB / DTLB 转换后行缓冲 地址转换快表,分为指令 / 数据
MOESI — 缓存一致性协议的五种状态
TSO(Total Store Order) 全存储定序 SPARC V9 的默认内存模型
RAS 可靠性、可用性、可服务性 高端服务器的三大指标
MCM(Multi-Chip Module) 多芯片模块 SPARC64 早期采用的封装形式

1. SPARC 指令集架构

SPARC 深受早期 RISC「精简」思潮熏陶,希望所有运算动作都可单时钟周期搞定并高度流水线化,像整数除法之类的「复杂」指令就付之阙如,透过重复的简单运算取代之,这部分到了 1990 年的 SPARC v8 才补完。

Sun 在 1986 年发表 32 位的 SPARC v7 指令集,但 Sun 并不像其他厂商自己做芯片,而是开放出来并定义严谨版本,让其他厂商也能研制 SPARC 指令集的兼容处理器。这种”ISA 与实现分离、由独立机构维护标准”的做法,正是 SPARC 得名”可扩展”的另一层含义。

1995 年 SPARC v9 扩充到 64 位元与 SIMD 指令集 VIS(Visual Instruction Set),Sun 跟 Fujitsu 在 2002 年联合提出 JPS(Joint Programming Specification)规范并持续演进到 UA(UltraSPARC Architecture)、OSA(Oracle SPARC Architecture)和 Fujitsu 自行定义的高效能运算 HPC-ACE(High Performance Computing – Arithmetic Computational Extensions)。

1.1 寄存器

1.1.1 通用寄存器与寄存器窗口

寄存器窗口(Register Window) 是 SPARC 架构最具标志性的设计,其出发点是用硬件换掉函数调用时频繁的参数保存与恢复开销。

SPARC 具有 8 个全局寄存器(Global Registers) %g0–%g7,其中 %g0 硬连线为零——写入无效、读出恒为 0,编译器可借此合成 mov、cmp 等大量伪指令。

任意时刻软件可见 32 个整数寄存器:8 个全局寄存器,加上 24 个窗口寄存器,后者分为:

  • 8 个 in 寄存器 %i0–%i7:存放传入参数与返回地址
  • 8 个 local 寄存器 %l0–%l7:存放局部变量
  • 8 个 out 寄存器 %o0–%o7:存放传出参数

相邻窗口彼此重叠:当前窗口的 out 寄存器在物理上就是下一个窗口的 in 寄存器。函数调用时执行 save 指令使窗口上滑,返回时执行 restore 使窗口下滑,参数传递与现场保存因此大部分无需访存。

实现可提供的窗口数目由 NWINDOWS 决定(V8 为 2–32,V9 为 3–32)。当窗口耗尽时,save 会触发窗口溢出陷阱(window overflow trap),由软件把最老的窗口内容写回到内存中的寄存器栈;restore 时则对称地触发窗口下溢陷阱(window underflow trap)。

下图展示了 UltraSPARC T1 的寄存器文件实现:每个线程 8 个寄存器窗口,4 个线程共 640 个 64 位寄存器,构成一个 5.7 KB 的结构。

1.png

1.1.2 浮点与图形寄存器

SPARC V9 定义了 32 个 64 位的浮点/图形寄存器(Floating-Point/Graphics Registers),可视使用方式呈现为三种视图:

  • 32 个单精度寄存器 %f0–%f31
  • 32 个双精度寄存器 %d0–%d62(仅偶数编号)
  • 16 个四精度寄存器 %q0–%q60(仅 4 的倍数编号)

UltraSPARC I 的浮点/图形寄存器文件具备 5 读 3 写端口,供 VIS 的分块(partitioned)运算直接复用。Fujitsu 的 HPC-ACE 则进一步把浮点寄存器扩展到 256 个——由于 SPARC V9 的 32 位指令编码不足以寻址 256 个寄存器,必须引入补充寻址位的前置指令 SXAR,详见 1.6.5 节。

1.1.3 状态与控制寄存器

  • V8 定义了 PSR(Processor State Register)、WIM(Window Invalid Mask)、TBR(Trap Base Register)与 Y 寄存器(保存乘法高位结果)。
  • V9 引入 PSTATE(含 PRIV、IE、AM、CLE 等域)、TSTATE、TBA(Trap Base Address)、TL(Trap Level)、PIL(Processor Interrupt Level)、CWP(Current Window Pointer)、CCR、WSTATE,以及若干辅助状态寄存器(ASR)。
  • 条件码分为整数条件码 icc(32 位)与 xcc(64 位),以及 4 组浮点条件码 fcc0–fcc3。

1.2 指令格式与寻址

SPARC 的所有指令均为定长 32 位,编码为三种基本格式:

  • Format 1(op = 01):call,携带 30 位相对位移
  • Format 2(op = 00):sethi 与分支指令,携带 22 位立即数或 22 位分支位移
  • Format 3(op = 10/11):其余全部指令,携带两个源寄存器、一个目的寄存器,以及 13 位带符号立即数

由于立即数字段只有 13 位,加载任意 32 位常量需要 sethi + or 两条指令配合,这是 SPARC 代码中最常见的指令对之一。

1.3 分支与延迟槽

SPARC 采用延迟分支(Delayed Branch):紧随分支指令之后的延迟槽指令总是被执行,与分支是否跳转无关。分支指令编码中的 annul 位(a) 用于在分支不跳转(或跳转)时取消延迟槽指令的执行,从而让编译器能够更充分地填充延迟槽。

条件分支的条件来自 1.1.3 节所述的条件码寄存器。UltraSPARC 系列在取指单元中实现了 Next Field RAM 结构,用以保存分支目标地址与动态分支预测位;而 Niagara 系列为追求面积效率,直接取消了分支预测器,采用 predict-not-taken 策略。

1.4 异常、陷阱与特权级

  • V9 通过 PSTATE.PRIV 区分特权态与非特权态,并以 TL(Trap Level) 支持嵌套陷阱。
  • UltraSPARC IIi 的整数执行单元支持精确异常(Precise Exceptions)与 5 级嵌套陷阱。
  • V8/V9 均定义了三类陷阱:精确陷阱、延迟陷阱(deferred trap,如浮点异常)与中断陷阱。

1.5 存储模型

SPARC V9 定义了三种存储模型,由 PSTATE 中的相关位选择:

  • TSO(Total Store Order):默认模型。所有处理器观察到的 store 顺序一致,load 不会被重排到更早的 store 之前。UltraSPARC III 与后续处理器均声明符合 SPARC V9 TSO。
  • PSO(Partial Store Order):放宽不同地址 store 之间的顺序约束。
  • RMO(Relaxed Memory Order):进一步放宽 load 之间的顺序约束。

UltraSPARC T2 由核心的加载存储单元与 L2 缓存交叉开关共同维持 TSO 合规性,仅少数明确不需要 TSO 支持的指令例外。

在缓存一致性方面,UltraSPARC I 的外部缓存单元采用 5 态 MOESI 协议,并同时支持侦听式(snoopy)与目录式(directory-based)两种一致性协议。

1.6 版本演进

1.6.1 SPARC V7 / V8

SPARC V7(1986)是首个公开的 32 位版本,秉承极简主义,连整数乘法与除法都不提供。SPARC V8(1990)补上了 smul / umul / sdiv / udiv 与 mulscc 等指令,并完善了协处理器与 MMU 的定义;1994 年被批准为 IEEE Std 1754-1994。

1.6.2 SPARC V9

SPARC V9 将架构扩展到 64 位,主要变化包括:

  • 通用寄存器与地址空间扩展到 64 位
  • 窗口数目范围调整为 3–32,引入 PSTATE、TL 等新状态寄存器
  • 定义 TSO / PSO / RMO 三种存储模型
  • 引入 VIS 指令集与 32 个 64 位浮点/图形寄存器
  • 支持大端(big-endian)与小端(little-endian)两种数据字节序

1.6.3 VIS

VIS(Visual Instruction Set) 是 SPARC 的多媒体 SIMD 扩展,把 64 位寄存器分块为 8×8 位、4×16 位或 2×32 位进行操作,主要用于 2D/3D 图形、图像处理、实时压缩解压缩与视频特效。UltraSPARC IIi 的规格书记载:VIS 的块加载与存储(64 字节块)可在任意源/目的对齐下,维持 300 MByte/s 的主存读写带宽。

VIS 的版本演进为:VIS 1.0(UltraSPARC I/II)→ VIS 2.0(UltraSPARC III/IV)→ VIS 3.0(SPARC T4)→ VIS 4(SPARC M7)。

1.6.4 JPS / UA / OSA

Sun 与 Fujitsu 在 2002 年联合提出 JPS(Joint Programming Specification),把两家各自的实现差异收敛为统一规范,其后续演进依次为:

  • UA(UltraSPARC Architecture):Sun 时代的定义
  • OSA(Oracle SPARC Architecture):Oracle 收购 Sun 之后的定义
  • HPC-ACE(High Performance Computing – Arithmetic Computational Extensions):Fujitsu 自行定义的高效能运算扩展

1.6.5 HPC-ACE

HPC-ACE 是 Fujitsu 为超级计算机量身定制的扩展,最关键的一点是把浮点寄存器扩展到 256 个,并引入浮点乘积和 FMA(A × B + C = D)指令。

但是SPARC V9 指令的 32 位编码不足以寻址所有浮点寄存器——256 个寄存器需要 8 位元,而 FMA 指令会用到 4 个寄存器,将会吃光 32 位编码,连运算码都没地方放了——因此必须在运算指令前,排入「补充」寄存器定址位元数的前置指令 SXAR。

2.png

2. SPARC 处理器概述

Sun 在 1986 年发表 32 位的 SPARC v7 指令集之后,其处理器实现大致经历了三个阶段:

  1. 开放授权、借力代工:Fujitsu 的 MB86900 是世界首颗 SPARC 实现,Sun-4 工作站的处理器来自 Fujitsu 与 Cypress。
  2. 传统超标量高性能:从 SuperSPARC 到 UltraSPARC IV+,追求单线程性能与高时钟频率,与 x86 正面竞争。
  3. 吞吐量计算:从 UltraSPARC T1 到 SPARC M8,转向”简单多核心、超多线程、大量存储器频宽”,专攻 Web、数据库与云计算负载。

下表按代际对比了各主要 SPARC 处理器的关键规格。

项目 SuperSPARC UltraSPARC I UltraSPARC IIi UltraSPARC III UltraSPARC IV+ UltraSPARC T1 UltraSPARC T2 SPARC T4 SPARC M6 SPARC M7 SPARC64 VII SPARC64 VIIIfx SPARC64 X+ SPARC64 XII
年份 1992 1995 1997 2001 2005 2005 2007 2011 2013 2015 2008 2009 2013 2017
工艺 0.8μm BiCMOS 0.5μm 4LM CMOS 0.35μm 5LM CMOS 0.18μm 6LM Al 90nm 90nm 65nm 11M Cu 40nm 28nm 20nm 13ML 65nm 45nm 28nm 20nm
晶体管 — 5.2M 5.75M — — — ~500M ~855M 4.27B — 600M 760M 2,990M —
面积(mm²) — 315 150 330 — — 342 — — — ~424 ~513 600 —
频率 33–60MHz 143–167MHz 270–360MHz 600MHz 1.5–2.1GHz 1.0–1.2GHz 1.4GHz 2.85–3GHz 3.6GHz 4.13GHz 2.5GHz 2GHz 3.5GHz+ 2.5–4.35GHz
功耗 — 28W 24W@300MHz 70W@1.8V — — 84W — — — 135W 58W — —
派发宽度 3 4 4 4 4 1×8 2×8 2 2 2 4 4 4 —
核心数 1 1 1 1 2 8 8 8 12 32 4 8 16 12
线程数 1 1 1 1 2 32 64 64 96 256 8 8 32 96
L1 I$ — 16KB 2-way 16KB 2-way 32KB — 16KB 4-way 16KB 8-way 16KB 4-way 16KB 4-way 256KB(簇共享) — — — —
L1 D$ — 16KB 直接映射 16KB 直接映射 64KB 4-way — 8KB 4-way 8KB 4-way 16KB 4-way 16KB 4-way 256KB(核对共享) — — — —
L2 — 0.5–4MB(片外) 0.25–2MB(片外) 1–8MB(片外) 2MB 3MB 12-way 4MB 8-bank 128KB/核(私有) 128KB/核 256KB I + 256KB D 6MB(共享) 5MB(共享) 24MB(共享) —
L3 无 无 无 无 无 无 无 4MB 8-bank 48MB 64MB 无 无 无 —
内存 — 2.6GB/s 400MB/s–1.2GB/s 片上控制器 — 4×DDR2, >20GB/s 4×FBDIMM, >60GB/s 2×DDR3-1066 双通道 16 DDR3 通道/槽 16 DDR4 通道, 160GB/s — 64GB/s 102GB/s —
I/O — UPA 系统总线 PCI/66 JBus — — PCIe x8, 2×10GbE 2×PCIe x8 2.0, 2×10GbE 2×PCIe 3.0 x8 PCIe Gen3 >75GB/s Jupiter Bus 片上内存控制器 片上内存与 I/O 控制器 —
备注 首个超标量 SPARC 首颗 SPARC V9, VIS 1.0 集成 PCI 控制器 片上内存控制器 传统 UltraSPARC 绝响 Niagara, 8 核 32 线程 Niagara-2, 8 核 64 线程 S3 核心, 双发射乱序 S3 核心, 48MB L3 S4 核心, 查询卸载引擎 4 核 2 路 SMT HPC-ACE, 水冷 SWoC, HPC-ACE 12 核 96 线程

可以看大,UltraSPARC I 到 IV+ 一路堆时钟与单线程性能,T1 之后则把晶体管预算全部砸进线程数与内存带宽。

3.png

4.png

3. SuperSPARC

1992 年的 SuperSPARC采用0.8μm 制程,时钟 33–60MHz,那时英特尔 Pentium 尚未上市。

5.png

3.1 微架构

SuperSPARC(代号 Viking)是 Sun 首颗超标量 SPARC 实现。Sun 的应用分析表明每周期三条指令性价比最高,超标量引擎便围绕「三指令派发」来设计,指令的分组与调度完全由硬件动态完成,不需要编译器插手:

  • 3 instruction/cycle issue & execute, fully dynamic scheduling
  • 同时执行(Simultaneous execution)可能受以下因素限制:
    • data dependencies(数据相关)
    • control dependencies(控制相关)
    • internal resources(内部资源)
  • SuperSPARC 资源包括:
    • Single 54-bit LD/ST
    • Two 32-bit ALUs
    • Single FP Issue
    • Single Br/Jmp/Call/Return
    • Two 32-bit integer results

6.png

3.2 流水线与取指

指令在流水线中以组为单位移动。取指单元每周期取 4 条指令,前方有 8 项指令预取队列与 4 项分支目标队列;整数单元为 4 级流水线,内部是两个级联的 32 位 ALU,同组的第二条整数指令可以直接使用前一条的结果,因此单周期能完成两条整数运算。load/store 为单周期、64 位宽,没有 load-use 惩罚。

分支方面,SuperSPARC 靠预取队列与分支目标队列消化绝大多数已跳转与未跳转分支,不造成流水线停顿,再配合 SPARC 的延迟槽,分支代价在当时属于最低的一档。

3.3 浮点单元

浮点单元与整数流水线紧密耦合:浮点控制器从整数流水线末级接过浮点指令,按类型分派给两条独立的浮点流水线——加法器(FADDER,负责加减与逻辑运算)与乘法器(MULTIPLIER),各自流水化,完整支持 IEEE 单双精度。

3.4 缓存与 MMU

  • 片上 20KB 指令缓存:128 位宽访问,5 路组相联,物理寻址,64 字节行、32 字节子块,支持 burst fill 预取——是当时 RISC 处理器中最大的片上缓存
  • 片上 16KB 数据缓存:64 位宽访问,4 路组相联,物理寻址,32 字节子块;无外部缓存时写回,挂外部缓存时写穿
  • SPARC Reference MMU(SRMMU):64 项全相联 TLB,硬件自动重载,支持 36 位物理地址

3.5 MBus 与多处理器

SuperSPARC 在片上集成了完整的 MBus Level II 接口:全同步的片间多处理器总线,64 位数据通路,带一致性协议、侦听逻辑与 store/copyback 缓冲。配套的 SuperSPARC MultiCache Controller(MCC) 负责外部缓存的标签与控制——MBus 模式 1MB,XBus 模式 512KB/1MB/2MB——并内建多处理器缓存一致性的支持。

SuperSPARC 主要用在 SPARCstation 10/20 与 SPARCserver 上;SPARCcenter 2000 最多装 20 颗,Cray CS6400 最多 64 颗,Thinking Machines 的 CM-5 超算则用掉了上百颗。 7.png

3.6 工艺与封装

3.6.1 Technology

  • SuperSPARC CPU 与 Cache Controller 采用 TI 的 EPIC IIB 工艺制造
    • 0.8 micron 最小特征尺寸
    • BiCMOS——融合 Bipolar 与 CMOS 电路之长
      • Bipolar:用于 I/O 与互连
      • CMOS:用于密度与简洁性
    • 三层金属(Triple Level Metal)
    • 310 万个晶体管,die 15.98mm × 15.98mm,5V 供电,典型功耗 8W
  • 带散热片的高密度陶瓷针栅阵列封装(CPGA)
    • SuperSPARC CPU:293 pins,70 mil 引脚间距
    • SuperSPARC CC:369 pins,70 mil 引脚间距

3.7 SuperSPARC II

1994 年的 SuperSPARC II采用0.8μm 制程,时钟 75–90MHz,那年已经出现 100MHz 的英特尔 Pentium。

微架构改动不大,浮点单元增加了第三条流水线——独立的浮点除法/平方根单元(FDS),整数寄存器堆也重新设计了一番,以便冲击更高的时钟。

4. UltraSPARC I

1995 年的 UltraSPARC I 采用0.47μm 制程,时钟 143–167MHz, 同时英特尔推出 x86 历史首次正面挑战服务器市场的 Pentium Pro,时钟直扑 200MHz,并具备原生四处理器架构与非循序预测指令执行。

8.png

4.1 处理器概述

UltraSPARC I 是首颗实现 SPARC V9 的处理器,也是 VIS 指令集的首次落地。

  • Architecture
    • 64-bit SPARC-V9 with multimedia instruction extensions
  • Pipeline
    • 4-issue superscalar, 9 stage pipeline
  • Clock Freq
    • 167MHz
  • Performance @ 167MHz, 2MB E$
    • 240 SPECint92, 350 SPECfp92
  • Power Supply
    • 3.3v
  • Power @ 3.3V, 167MHz
    • 28W
    • 20mW sleep mode
  • Die Size
    • 17.7 mm × 17.8 mm = 315 mm²
  • Transistors
    • 5.2 million(其中 3.4 million 为逻辑)
  • Technology
    • CMOS 4-layer metal, 0.5 μm drawn

9.png

4.2 流水线

UltraSPARC I 为 9 级流水线,前两级的取指与译码之后,整数流水线刻意再加三级,与浮点流水线做成等长对称。这么设计是为了简化流水线同步与异常处理,顺带取消了独立的浮点队列。指令按程序序派发到多个功能部件,并行执行、可以乱序完成;为了进一步提高每周期执行的指令数,来自不同基本块(例如条件分支两侧)的指令允许排在同一个派发组里。 10.png 全芯片共 9 个执行单元,整数侧 2 个 ALU 加乘除法器,浮点 3 个子单元,图形单元 2 条独立流水线。

4.3 整数执行单元

  • 两个 ALU 构成整数运算主体
  • 带「early out」的多周期整数乘法器与多周期整数除法器
  • 8 个寄存器窗口,外加 4 组全局寄存器(normal / alternate / MMU / interrupt)
  • 陷阱寄存器在整数单元内,支持 5 级嵌套陷阱
  • 精确异常模型靠浮点流水线与整数流水线同步、对长延迟操作做陷阱预测来维持

4.4 取指与派发单元

  • 16kB ICache,伪 2 路组相联(pseudo-two-way),32B line size w/pre-decoded bits;set 由 next field 预测,寻址只需索引位
  • Next Field RAM,内含 1k 个分支目标地址与 2k 个动态分支预测项
    • 动态分支预测基于每条分支的 2 位历史,预测位随指令一起存进 ICache(每指令 2 个额外位)
    • next field 指向下一行要取的 ICache 行,已跳转分支可以单周期跟上,指令带宽与顺序代码基本持平
  • 4-entry Return Address Stack
  • 分支预测准确率:SPECint92 88%,SPECfp92 94%
  • 64-entry 全相联 ITLB,背后还有 1-entry µTLB;页面大小 8K / 64K / 512K / 4MB,44 位虚拟地址映射 41 位物理地址
  • 12-entry Instruction Buffer,由 ICache 或二级缓存供给
  • 单周期派发逻辑,每周期考察最前的 4 条指令

11.png

4.5 浮点/图形单元

  • 浮点/图形寄存器文件具备 5 read ports / 3 write ports
  • 32 个单精度 / 32 个双精度寄存器
  • 5 个功能部件,除除法/平方根单元外全部流水化
  • 高带宽:每周期 2 次 FGops
  • 短延迟:
    • FP compares:1 cycle
    • FP add/subtract/convert:3 cycles
    • FP multiplies:3 cycles
    • FP divide/square root (sp/dp):12 / 22 cycles
    • Partitioned add/subtract, align, merge, expand, logical:1 cycle
    • Partitioned multiply, pixel compare, pack, pixel distance:3 cycles

12.png

4.6 加载存储单元

  • 16 kB DCache(D$),direct-mapped,32B line size w/16B sub-blocks
  • 64-entry 全相联 DTLB,支持多种页大小
  • D$ 非阻塞(non-blocking),由 9-entry Load Buffer 支持
  • D$ 标签双端口化,允许流水线的标签检查与 line fill / snoop 并行发生
  • 从二级缓存(E$)持续每周期 1 次 load 的吞吐
  • 通过解耦的数据 RAM 与标签 RAM,实现对 D$ 与 E$ 的单周期 store
  • Store compression 大幅降低 E$ 总线占用

13.png

4.7 外部缓存与系统单元

  • E$ 容量 0.5 MB 至 4MB
  • E$ 直接映射、物理索引与物理标签,64B line size
  • 5 个缓存一致性状态:MOESI
  • 使用带 1-entry 内部写缓冲的同步 SRAM
  • 16B(+parity)E$ 接口,支持 2.6 GB/s 持续带宽
  • UDB 在电气上把 CPU/E$ 与系统数据总线隔离
  • 基于数据包的 16B 系统互连,地址总线与数据总线分离
  • 带低延迟 parking mode 的分布式仲裁
  • 系统总线频率为 CPU 时钟的 1/2 或 1/3
  • 系统地址总线支持 4 个 CPU 模块与系统控制器
  • 同时支持侦听式与目录式缓存一致性协议

14.png

5. UltraSPARC II / IIi / IIe

5.1 UltraSPARC II

1997 年的 UltraSPARC II采用0.35μm 制程,时钟 250MHz;同时英特尔推出 300MHz 的 Pentium II,而 UltraSPARC II 的微架构基本沿用 UltraSPARC I——主要是工艺提升、时钟提升,以及兼容更新的外部缓存 SRAM 模式。

15.png

5.2 UltraSPARC IIi

1997 年的 UltraSPARC IIi采用0.35μm 制程,时钟 270–360MHz;整合 PCI 控制器的微幅改进版,从 256kB 激增到 2MB 的 L2 快取存储器是最大的亮点。

  • TI Epic4 GS2,5-layer CMOS,2.6v core,3.3v I/O
  • 12mm × 12.5mm die,5.75 million transistors
  • 24 watts @ 300MHz,集成 I/O、内存、图形与中断接口
  • Sun Visual Instruction Set - VIS(TM)
    • 2-D、3-D 图形、图像处理、实时压缩/解压缩、视频特效
    • 块加载与存储(64 字节块)可在任意源/目的对齐下,维持 300 MByte/s 的主存读写带宽
  • 四路超标量指令派发
    • 6 条流水线:2 条整数,2 条浮点/图形,1 条 load/store,1 条分支
  • 高带宽 / 低延迟接口
    • 同步、片外 L2 缓存,0.25MB – 2MB
    • 到 L2 缓存的 8 字节(+parity)数据总线(持续 1.2 GByte/s)
    • 到 DRAM XCVR 的 8 字节(+ECC)数据总线(持续 400 MByte/s),或 UPA64S(持续 800 MByte/s)
    • 4 字节 PCI/66 接口(持续 190 MByte/s)
  • 指令预取
    • 缓存内动态分支预测
    • 每周期 1 个分支
    • 2-way、16kB 指令缓存
  • 非阻塞数据缓存
    • 8-entry load buffer:仅在需要 load 数据时流水停顿
    • 8-entry(每 entry 8 字节)store buffer,用于写穿至 L2 缓存
    • D-cache 命中时单周期 load-use 惩罚
    • 到 L2 缓存每 2 周期 1 次 load/store 的吞吐
  • 到 L2 缓存的非阻塞软件预取
    • 预取完成不受内存模型约束
    • 编译器用它进一步隐藏 L2 miss 的延迟
  • 同时支持小端与大端数据
  • 作为 i-Series 的首款高度集成处理器,片上集成了 DRAM 内存控制器、UPA 控制器(最高 6.4 Gbps)与 32 位 66MHz PCI 接口;未完成的访存请求从 UltraSPARC I 的 1 个增加到 3 个 load 加 2 个 store

16.png

5.2.1 Instruction Pipelines

17.png

5.2.2 Prefetch 和Dispatch Unit

  • 16kB ICache,2-way set associative,32B line size w/pre-decoded bits
  • 2kB Next Field RAM,内含 1k 个分支目标地址与 2k 个动态分支预测项
  • 用于取指预测的 4-entry Return Address Stack
  • 分支预测(软件 / 硬件)
  • 64-entry 全相联 ITLB,背后还有 1-entry µTLB
  • 12-entry Instruction Buffer,由 ICache 或二级缓存供给
  • 单周期派发逻辑,考察「最前」4 条指令
  • 控制整数操作数的旁路(bypassing)

18.png

5.2.3 Integer Execution Unit

  • 整数寄存器文件具备 7 读 / 3 写端口
  • 8 个窗口,外加 4 组各 8 个全局寄存器
  • 双整数流水线
    • ALU0 执行移位指令
    • ALU1 执行基于寄存器的 CTI、整数乘除,以及设置条件码的指令
  • 整数乘法使用带「early out」的 2-bit Booth 编码
    • 典型操作数的延迟为 5 周期
  • 整数除法使用 1-bit 不恢复余数减法算法
  • 完成单元(Completion Unit)在指令提交前缓冲 ALU / load 结果,提供集中式操作数旁路
  • 精确异常,支持 5 级嵌套陷阱

19.png

5.2.4 Floating-Point/Graphics Unit

  • 浮点/图形寄存器文件具备 5 读 / 3 写端口
  • 32 个单精度 / 32 个双精度寄存器
  • 5 个功能部件,除除法/平方根单元外全部流水化
  • 高带宽:每周期 2 次 FGops
  • 完成单元在提交前缓冲结果,支持丰富的操作数旁路
  • 短延迟
    • Floating-point compares:1 cycle
    • Floating-point add/subtract/convert:3 cycles
    • Floating-point multiplies:3 cycles
    • Floating-point divide/square root (sp):12 cycles
    • Floating-point divide/square root (dp):22 cycles
    • Partitioned add/subtract, align, merge, expand, logical:1 cycle
    • Partitioned multiply, pixel compare, pack, pixel distance:3 cycles

20.png

5.2.5 Load/Store Unit

  • 16 kB DCache(D$),direct-mapped,32B line size w/16B sub-blocks
  • 64-entry 全相联 DTLB,支持多种页大小
  • D$ 非阻塞,由 9-entry Load Buffer 支持
  • D$ 标签双端口化,允许流水线的标签检查与 line fill / snoop 并行发生
  • 从二级缓存(E$)持续每 2 周期 1 次 load 的吞吐
  • 通过解耦的数据 RAM 与标签 RAM,实现对 D$ 与 E$ 的流水化 store
  • Store compression 降低 E$ 总线占用

21.png

5.2.6 External Cache Control

  • E$ 容量 0.25 MB 至 2MB
  • E$ 直接映射、物理索引与物理标签,64B line size
  • 使用带延迟写的同步 SRAM
  • 8B(+parity)E$ 接口,支持 1.2 GB/s 持续带宽
  • 支持两种 SRAM 模式,兼容 UltraSPARC I 或 II 的 SRAM
  • ad 与 dt 各占 2 个周期;在 22 模式 SRAM 下不存在 ac 阶段
  • PCI 活动完全缓存一致
  • 到 L1 数据缓存的 16 字节填充,所需的 8 字节优先

22.png

5.2.7 UltraSPARC-IIi System Example

23.png

5.3 UltraSPARC IIi(0.25μm)/ IIe / IIe+

1998 年的 UltraSPARC IIi采用0.25μm 制程,时钟 333–480MHz);当年 6 月时钟 400MHz 的首款英特尔 Xeon 问世,x86 世界总算有了服务器处理器专用的品牌。

24.png

2000 年的 UltraSPARC IIe采用0.25μm 制程,时钟 400–500MHz;同时AMD 抢先在英特尔之前登顶 1GHz 大关。

25.png

2002 年的 UltraSPARC IIe+ 采用0.18μm 制程,时钟 550–650MHz;与此同时,0.13μm 制程的英特尔 Pentium 4 / Xeon 已达 3GHz。

6. UltraSPARC III / IIIi

2001 年的 UltraSPARC III采用0.18μm 制程,时钟 600MHz;0.18μm 制程的英特尔 Pentium 4 / Xeon 时钟抵达 2GHz。同年发表的 UltraSPARC III Cu,终于靠着 0.13μm 制程和铜导线,冲破了 1GHz,真是可喜可贺。

26.png

取代 Sun Enterprise 的 Sun Fire 服务器产品线,也一起和 UltraSPARC III 登场。

6.1 处理器概述

  • 0.18 μm,TI C07a CMOS 工艺,六层铝互连
  • 330 mm²
  • 2900 万晶体管
  • 600 MHz
  • 70W @ 1.8V(CPU DB 记载 1.6V 下 TDP 53W)
  • 35 SPECint95,60 SPECfp95
  • 代码排程与系统软件兼容 UltraSPARC I、II

随后的 UltraSPARC III Cu(Cheetah+)改用 TI 0.13 μm、七层铜互连 CMOS 工艺,时钟提升至 1002–1200 MHz,die 面积缩小到 232 mm²。

6.2 流水线

UltraSPARC III 换成了一条 14 级的流水线:

级 功能
A 地址生成 / 预译码
P I$ 访问第 1 拍 / 分支预测第 1 拍
F I$ 访问第 2 拍 / 预测第 2 拍 / 地址转换
B 分支目标计算
I 译码第 1 拍
J 译码第 2 拍 + 指令 steer
R 读寄存器堆 / 相关检查
E 整数执行 / D$ 第 1 拍
C D$ 第 2 拍 / 浮点第 1 拍
M load 对齐 / 浮点第 2 拍
W 写寄存器堆 / 浮点第 3 拍
X 流水线延长 / 浮点第 4 拍
T trap
D 写架构寄存器堆

27.png

6.3 分支预测与取指派发

改进的 Gshare 预测器采用16K 项 2 位饱和计数器,因为容量太大,访问本身分两级流水;误预测代价,taken 罚 3 拍(靠 4 项 miss queue 补救),not-taken 罚 7 拍,加权平均约 4.5 拍

  • 每拍最多取指 4 条;20 项指令队列把取指侧与执行侧解耦
  • 译码后每拍最多 6 条进入 dispatch,受执行管线资源与分组规则限制,实际 sustained 为 4-issue

6.4 整数执行单元

  • 4 integer issue
  • Arithmetic/Special Unit (ASU)
  • 两条整数管线(A0/A1,算术/逻辑/移位),其中一条只能做简单整数与 load;branch 有独立管线;整数乘除走 ASU,除法不流水
  • 流水化的判定执行(predicated execution)
  • 7R3W 端口寄存器文件

28.png

6.5 浮点/图形执行单元

  • 2 指令派发,来自
    • FP/graphics add
    • FP/graphics mul/div/sqrt
  • 完全流水化的 FP/graphics add/mul
  • 并发的 FP div/sqrt 单元
  • 5 读、4 写端口寄存器文件

29.png

6.6 存储子系统

  • L1 data cache:64KB,4-way associative
  • L1 instruction cache:32KB,4-way associative,32B 行,wave-pipelined 两拍访问
  • L1 prefetch cache:2KB,4-way associative
  • L1 write cache:2KB,4-way associative
  • L2 cache:1–8 MB,direct-mapped,片外;专用 256 位总线 @200MHz,峰值 6.4 GB/s,90KB 的标签放在片上
  • 8 entry RAW forwarding store queue
  • 片上内存控制器

30.png

6.6.2 Prefetch Cache

  • 并发填充与双读
  • 8 个未完成的软件预取
  • 自主的硬件跨步预取(stride prefetch)
  • 完全一致的缓存

31.png

6.7 内存控制器与 JBus

6.7.1 System Scaling

  • 片上内存控制器
    • 专用 128 位总线 @150MHz 直连本地 SDRAM,最多 4 个 bank、4GB
    • 带宽随处理器增加而扩展
    • 低延迟
  • 片上侦听标签(snoop tags)
  • 「No snoop」页
  • 15 个未完成事务
  • 带标签的事务(Tagged transactions)
  • 符合 SPARC V9 TSO
  • JBus 系统接口:128 位数据 + 43 位地址,150MHz;SIU 支持 15 个未完成事务与乱序数据返回

6.8 Sun Fire:UltraSPARC 在高端服务器的巅峰

之后 Sun Fire 和 Fujitsu 的 PrimePower,再一同被 Sun / Fujitsu 携手的 SPARC Enterprise 品牌取代,2010 年后,再统一更名成 SPARC M / T(与后来追加的 S)系列。

32.png

Sun Fire 也是 UltraSPARC 处理器在高端服务器的巅峰,Sun Fire 15K 最多支持 106 颗 UltraSPARC III Cu,而 Sun Fire E25K 更是 72 颗 UltraSPARC IV(144 核心)。15K 的核心是 150MHz 的 Sun Fireplane 冗余 18×18 交叉开关,整机聚合带宽 172.8 GB/s——这在 2001 年是个相当夸张的数字。

6.9 RAS

  • L1 缓存与标签支持奇偶校验(单比特纠错)
  • L2 与内存总线支持ECC
  • JBus支持奇偶校验
  • 侦听式一致性

6.10 UltraSPARC IIIi 与 Gemini

2003 年的 UltraSPARC IIIi采用0.13μm 制程,时钟 1GHz–1.6GHz;这年 AMD K8 让 x86 的世界深入 64 位元疆界,Opteron 处理器品牌也从此改变了 AMD 与 Sun 的命运。

6.10.1 Gemini 概述

Gemini 是双核版本的 UltraSPARC IIIi:

  • 双 4-issue superscalar 核心
    • 基于 64-bit SPARC V9 处理器架构
    • 每周期 2 个整数操作、2 个 FPU/graphics(VIS 1.0)操作
  • 双集成 0.5MB L2 缓存(每核一个)
    • 4-way set associative
    • 每核支持 way-locking
  • 与 UltraSPARC IIIi 处理器引脚兼容
    • JBus 系统总线接口
  • 集成到标准 DRAM 的内存接口
    • 共享 128-bit DDR1 控制器
    • 使用 2–4 条 DIMM,总容量最高 16 GB
  • 电源管理模式(E*Star)
    • 可切换到 Fcpu/2 或 Fcpu/32
  • 强健的 RAS 支持
    • L1 缓存、JBus 上的奇偶校验
    • L2 缓存、DRAM 上的 ECC
  • CMT 特性
    • 软件控制的核心禁用(core disable)
    • 动态的、缓存感知的核心停放(cache-aware core parking)
    • 遵循 UltraSPARC CMT 架构规范
  • Technology
    • 晶体管数:80 million
    • Die size:206 mm²
    • 封装:959 pin ceramic uPGA
    • 最大功耗:32 Watts @ 1.2 GHz,1.3V
    • TI 先进的 130 nm CMOS 工艺,300 mm 晶圆,53 nm Leff,7LM Cu,low-k 介质(OSG),dual Vt

33.png

6.10.2 L2 Caches

  • 每核统一 512KB L2 缓存,4-way set associative,64B line size,伪随机替换
  • 2-2 模式访问,4 周期延迟
  • 64 位数据配 8 位 ECC,带物理位扰乱(physical bit scrambling),标签受奇偶保护

34.png

35.png

6.10.3 Memory Controller

  • 两核共享的控制器
  • DDR1 兼容
  • 最多 4 条 DIMM(单面 / 双面)
  • 总内存 256MB 至 16GB
  • SSTL 输入 / 输出
  • 内存接口:128-bit 数据 + 9 位 ECC 校验位
  • E*Star 支持(Fcpu 的 1/2 或 1/32)
  • PTB 追踪 16 个打开的页
  • 峰值带宽 4.2 GB/s
  • 实测本地访问延迟:96 ns

36.png

6.10.4 Memory Subsystem Details

37.png

7. UltraSPARC IV / IV+

2004 年的 UltraSPARC IV 采用0.13μm 制程,时钟 1GHz–1.3GHz;UltraSPARC 处理器跻身双核心之列(等于 2 颗改良版 UltraSPARC III),那年刚好是 IBM Power5 在高端服务器市场的效能战争横扫千军的高峰。

38.png

2004 年,Sun 宣布腰斩 UltraSPARC V「Millennium」 与双核心 「Gemini」(即 UltraSPARC IIIi 的双核衍生版本,详见 6.10 节)之后的高性能核心规划。

39.png

7.1 UltraSPARC IV

UltraSPARC IV 的做法很实在,把两颗 UltraSPARC III 核心装进同一块 die,每核自带独立的 L1 缓存与 16 项指令队列,共享经切分的 16MB 片外 L2,对外仍是同一套 JBus / Fireplane 接口——封装、功耗与散热规格都与 UltraSPARC III 保持一致,可以直接替换进既有系统,Sun 用最低的成本把整机性能翻了一倍。

  • TI 130nm CMOS,6600 万晶体管
  • 时钟 1.2–1.35GHz,最大功耗 102W

7.2 UltraSPARC IV+

2005 年的 UltraSPARC IV+ 采用0.09μm 制程,时钟 1.5GHz–2.1GHz,传统UltraSPARC 处理器的绝响。这时双核心 64 位英特尔 Xeon 和 AMD Opteron 已在服务器市场杀声震天,再次确立 x86 处理器主导服务器市场和云端数据中心的大势。

IV+ 不只是工艺升级——90nm、9 层铜互连、2.95 亿晶体管、336mm²、90W——缓存层次做了大改,从「L1 + 片外 L2」变成三级:

  • L1:I$ 加倍到 64KB,D$ 64KB,2KB prefetch cache 保留,2KB write cache 改为全相联
  • L2:片上 2MB,4 路组相联,半频运行,写回
  • L3:片外 32MB,4 路组相联,victim / exclusive 策略,标签放在片上

零零碎碎的改进还有prefetch buffer 从 1 行扩到 8 行,I-TLB 加到 512 项,新增 32 项 BTB,D-TLB 支持 32MB / 256MB 大页,硬件 POPC 指令,FPU 的 IEEE 754 异常处理增强;I$ / D$ / prefetch cache 与 I/D-TLB 全部加上了硬件错误检测。

8. UltraSPARC T1(Niagara)

8.1 处理器概述

Sun 在 2004 年逐步重整服务器产品布局,除了短暂推出英特尔 Xeon 处理器的 Sun Fire V60 系列,兵分三路,成果可简述成以下数点:

  • 通用型服务器:Sun 成为率先压宝 AMD Opteron 的一线服务器大厂,并推出「Galaxy」系列服务器,8 颗 Opteron 的 Sun Fire M4600 为象征。这件事对 AMD 也意义深远,不仅提升 AMD 验证产品的能力,更强化企业用户对「AMD 服务器」的信心。
  • 高效能服务器:与 Fujitsu 携手 APL(Advanced Product Line),沿用 SPARC64 系列,标榜大型主机等级的可靠性。
  • 网站与数据库:Sun 购并 Afara Websystems 后,以追求「像瀑布般的巨大数据吞吐量」的 Throughput Computing 为名,集中资源打造针对网站服务器特化的 UltraSPARC T 系列(Niagara) 与数据库导向的 UltraSPARC RK(Rock)。

其中最值得大书特书的就是奠定 Sun SPARC 处理器发展方向的 Throughput Computing:

简单多核心、超多线程、大量存储器频宽。

40.png

Niagara 提升商业服务器应用吞吐量的方法,是在处理器上支持数量急剧增加的线程,以及为更高带宽而扩展的存储子系统。Niagara 在硬件上支持 32 个执行线程:架构把四个线程组织成一个线程组(thread group),线程组共享一条处理流水线,称为 Sparc pipe。Niagara 使用 8 个这样的线程组,因此 CPU 上共有 32 个线程。每个 SPARC pipe 都带有指令与数据的一级缓存。硬件通过把组内其他线程调度到 SPARC pipe 上来隐藏给定线程的内存与流水线停顿,线程切换代价为零周期。

32 个线程共享一个 3 MB 的二级缓存。该缓存为4 路分体(banked)并流水化以提升带宽,并采用12 路组相联以减轻多线程带来的冲突失效。商业服务器代码存在数据共享,可能导致很高的一致性失效(coherence miss)率。在采用分立处理器与一致性互连的传统 SMP 系统中,一致性失效要经过低频的片外总线或链路,延迟可能很高。Niagara 通过共享的片上缓存消除了这些失效,代之以低延迟的共享缓存通信。

交叉开关(crossbar) 提供 SPARC pipe、L2 缓存分体与片上其他共享资源之间的通信链路,提供超过 200 GB/s 的带宽。每个源—目的对都有 2 项的队列,交叉开关每个方向最多可排队 96 个事务。交叉开关还提供一个用于 I/O 子系统通信的端口。目的端口的仲裁采用简单的基于年龄(age-based)的优先级方案,保证所有请求者之间的公平调度。交叉开关也是整机的内存定序点(point of memory ordering)。

内存接口为 4 通道 DDR2 DRAM,支持超过 20 GB/s 的峰值带宽与最高 128 GB 的容量。

41.png

8.2 SPARC Pipe

每个线程都有一套独有的寄存器、指令缓冲与存储缓冲。线程组共享 L1 缓存、TLB、执行单元和大部分流水线寄存器。Niagara 实现了一条单发射、六级的流水线:取指(fetch)、线程选择(thread select)、译码(decode)、执行(execute)、访存(memory)和写回(write back)。

  • 取指阶段:访问指令缓存与指令 TLB(ITLB)。下一阶段通过选择 way 完成缓存访问。关键路径由 64 项全相联 ITLB 的访问决定。线程选择多路器决定四个线程程序计数器(PC)中的哪一个来执行访问。流水线每周期取两条指令。缓存中的预译码位标识长延迟指令。
  • 线程选择阶段:线程选择多路器从可用线程池中选择一个线程,把一条指令发射到下游阶段。该阶段同时维护指令缓冲。若下游阶段不可用,取指阶段从缓存取出的指令可以插入相应线程的指令缓冲。前两个阶段的流水线寄存器为每个线程各复制一份。
  • 译码阶段:来自被选中线程的指令进入译码阶段,执行指令译码与寄存器文件访问。支持的执行单元包括 ALU、移位器、乘法器与除法器。旁路单元处理那些必须在寄存器文件更新前传递给相关指令的结果。ALU 与移位指令延迟为单周期,结果在执行阶段产生。乘法与除法为长延迟操作,会引发线程切换。
  • 加载存储单元:包含数据 TLB(DTLB)、数据缓存与存储缓冲。DTLB 与数据缓存的访问发生在访存阶段。与取指阶段类似,关键路径由 64 项全相联 DTLB 的访问决定。加载存储单元内含四个 8 项存储缓冲,每线程一个。检查存储缓冲中的物理标签可以识别 load 与 store 之间的 RAW 相关,存储缓冲支持把数据旁路给 load 以解决 RAW 相关。存储缓冲的标签检查发生在写回阶段早期的 TLB 访问之后。load 数据在写回阶段后期可供旁路给相关指令。像 ADD 这样的单周期指令将在写回阶段更新寄存器文件。

42.png

8.3 寄存器文件

整数寄存器文件具备 3 读 2 写端口。读端口对应单发射机器的需求;第三个读端口用于处理 store 以及少数三源指令。两个写端口分别处理单周期操作与长延迟操作。组内不同线程的长延迟操作(load、乘法、除法)可能同时产生结果,这些指令将仲裁寄存器文件的长延迟写回端口。

SPARC V9 架构规定了如下图所示的寄存器窗口实现。单个窗口由 8 个 in、8 个 local、8 个 out 寄存器组成,在给定时刻对某个线程全部可见。一个窗口的 out 寄存器在寻址上等价于下一个顺序窗口的 in 寄存器,但它们是同一组物理寄存器。每个线程有 8 个寄存器窗口,四组这样的寄存器集支持四个线程,线程之间不共享寄存器空间。寄存器文件总共由 640 个 64 位寄存器组成,是一个 5.7 KByte 的结构。

支持大量的多端口寄存器,无论从面积还是访问时间考量都是重大挑战。Niagara 选择了一种创新的实现,以维持紧凑的面积和单周期访问时间:

  • 过程调用可以请求一个新窗口,此时可见窗口上滑,旧的 out 变成新的 in;调用返回时窗口下滑。
  • 线程可见的寄存器集合是工作集(working set),用快速的寄存器文件单元实现。
  • 全部寄存器的集合是架构集(architectural set),用紧凑的六晶体管 SRAM 单元实现。
  • 一组传送端口(transfer port)连接两个寄存器集;窗口变更事件触发线程取消选择与新旧窗口之间的数据传送。

8.3.1 Thread selection policy

线程选择策略是每周期在可用线程之间切换,优先选择最近最少使用(LRU)的线程。线程会因为长延迟指令(如 load、分支、乘法和除法)而变为不可用,也会因为流水线停顿(如缓存失效、陷阱和资源冲突)而变为不可用。

线程调度器假定 load 会命中缓存,因此可以投机地发射同一线程的相关指令。但是,与能够发射非投机指令的线程相比,这种投机线程在指令发射时被赋予更低的优先级。

8.4 存储子系统

  • L1 指令缓存 16 KB,4-way set-associative,块大小 32 bytes,采用随机替换方案以节省面积,且不带来显著性能损失。指令缓存每周期取两条指令;如果第二条指令有用,指令缓存会有一个空闲槽,流水线可用来处理行填充而不停顿。
  • L1 数据缓存 8 KB,4-way set-associative,行大小 16 bytes,采用写穿(write-through)策略。
  • 尽管 L1 缓存很小,它们仍显著降低了每线程的平均访存时间,失效率在 10% 量级。由于商业服务器应用的工作集往往很大,要让失效率显著下降,L1 缓存必须大得多,从面积角度看这种取舍并不划算。然而,线程组内四个线程有效隐藏了 L1 与 L2 失效的延迟,因此这些缓存容量是失效率、面积与”其他线程隐藏延迟的能力”之间一个良好的折中。

Niagara 使用简单的缓存一致性协议:L1 缓存写穿,load 时分配、store 时不分配;L1 行只有 valid 与 invalid 两种状态。L2 缓存维护一个影子化 L1 标签的目录,并把数据以 64 字节粒度交错分布在各分体上。L1 失效的 load 会连同其在 L1 中的替换 way 一起送到 L2 的源分体;在那里把该 load 失效地址写入目录对应的 L1 标签位置,访问 L2 取得缺失行,然后把数据返回 L1。目录因此以 L1 行粒度维护一个共享者列表。随后来自不同或相同 L1 的 store 会查找目录,并把失效请求排队发给持有该行的 L1 缓存。store 在更新 L2 之前不会更新本地缓存,在此期间 store 可以把数据传给同一线程但不能传给其他线程,因此 store 在 L2 缓存中获得全局可见性。

交叉开关在同一与不同 L2 分体的事务之间建立内存定序,并保证以相同的顺序把事务投递到 L1 缓存。L2 缓存采用写回(copy-back)策略:淘汰脏行时写回,淘汰干净行时直接丢弃。来自 I/O 设备的 DMA 通过 L2 缓存定序。四通道 DDR2 DRAM 提供超过 20 GB/s 的内存带宽。

9. UltraSPARC T2(Niagara-2)

UltraSPARC T2 是 Sun Microsystems 第二代多核多线程 SPARC 片上系统(SoC)。它在几乎相同的功耗包络内,提供了第一代 UltraSPARC T1 两倍的吞吐性能。

UltraSPARC T2 通过 8 个 SPARC 核心、每核心 8 个硬件线程,支持 64 个线程并发执行。各个核心通过高带宽交叉开关通信,共享一个 4MB、8 分体的 L2 缓存。每个 SPARC 核心包含两个整数执行单元和一个专用的浮点与图形单元,在 1.4 GHz 下提供 11.2 GFLOPS/sec 的峰值浮点吞吐;每个核心还带一个密码学单元。

在 I/O 方面,UltraSPARC T2 集成了一条 x8 PCI-Express 通道与两个带 XAUI 接口的 10G 以太网端口。内存通过 4 个片上控制器访问,每个控制器管理 2 个 FBDIMM 通道,峰值内存带宽超过 60 GB/sec。UltraSPARC T2 采用 11 层金属、1.1V、三阈值 CMOS 工艺制造,芯片在 342 mm² 的 die 上集成约 5 亿个晶体管,1.4 GHz 下功耗 84 W。

高度的系统集成,加上高吞吐量、浮点与密码学性能,UltraSPARC T2 的目标负载是 Web 服务器、数据库与应用服务器、HPC、安全网络、园区骨干网与文件服务器。

43.png

L2 缓存的每个分体为 16 路组相联,行大小 64 字节。8 个 L2 分体支持 8 个并发访问,以满足 UltraSPARC T2 的高带宽需求。SPARC 处理器核心通过高带宽、非阻塞、流水化的交叉开关与 L2 缓存通信;该交叉开关把 8 个 SPARC 核心连接到 8 个 L2 分体与 I/O 端口,总写带宽 90 GB/sec、总读带宽 180 GB/sec。

L2 缓存连接到 4 个片上 DRAM 控制器,后者直接对接一对全缓冲 DIMM(FBDIMM)通道,峰值链路速率 4.8 Gb/sec。选择 FBDIMM 是因为它以不到 DDR2 一半的引脚数提供了约 2 倍的带宽。UltraSPARC T2 的峰值内存带宽为读 50 GBytes/sec、写 26 GBytes/sec。

44.png

9.1 SPARC Core

UltraSPARC T2 上的每个 SPARC 处理器核心都完整地在硬件上支持 8 个独立线程的执行。该核心由取指单元(IFU)、两个整数执行单元(EXU0 与 EXU1)、一个浮点与图形单元(FGU)、一个加载存储单元(LSU)、一个流处理密码学单元(SPU)、陷阱逻辑单元(TLU)、内存管理单元(MMU) 与gasket unit 组成。

45.png

8 个线程被静态划分为两个各含 4 个线程的线程组 TG0 与 TG1;线程组内的 4 个线程共享一个 EXU。FGU 与 SPU 中的执行引擎由全部 8 个线程共享。

  • IFU 内含 16KB、8 路组相联、行大小 32 字节的一级指令缓存,以及 64 项指令 TLB,均由 8 个线程共享。
  • LSU 内含 4 路组相联、行大小 16 字节的一级数据缓存,以及 128 项数据 TLB,均由 8 个线程共享。
  • MMU 支持 8KB、64KB、4MB 与 256MB 页,并实现一个硬件页表遍历引擎(hardware table walk engine)用于快速重载 TLB,该引擎由 8 个线程共享。
  • TLU 处理异常与中断,并负责维护每个线程的机器状态。
  • Gasket 把来自处理器核心的内存与 I/O 请求送往交叉开关,接收来自 L2 与 I/O 端口的响应,并把响应送给相应的 SPARC 核心。

UltraSPARC T2 实现细粒度多线程 ,在静态划分的线程组 TG0 与 TG1 内,以「最近最少发射」的优先级方案,在可用线程之间逐周期切换线程。当某线程遇到长延迟事件(如缓存失效)时,它被标记为不可用,直到该事件完成前不会从该线程发射指令。每周期最多发射两条指令,每个线程组各一条,可以是两个整数操作、一个整数加一个访存操作、一个整数加一个浮点操作,或一个浮点加一个访存操作。这种细粒度多线程方案把分支误预测与缓存失效带来的吞吐损失降到最低。

9.1.1 Core Pipeline

UltraSPARC T2 对整数操作实现八级流水线,load-use 惩罚为 3 个周期。浮点流水线对相关浮点操作的延迟为 6 个周期。

46.png

9.1.2 IFU

  • 面向 8 个线程的指令缓存与 fetch/pick/decode 逻辑
  • 每周期从 I$ 取最多 4 条指令
    • 线程处于 ready 或 wait 状态
    • wait 状态:TLB miss、cache miss、指令缓冲满
    • 在 ready 线程中取「最近最少取指」者
    • 每线程一个指令缓冲
  • 无分支预测
    • predict not-taken,5 周期惩罚
  • 有限的 I$ miss 预取
  • 取指与 Pick 解耦
  • 线程分为 2 组,每组 4 个线程
  • 每周期从每个线程组各拾取(pick)一条指令
    • 在线程组内 ready 线程中取「最近最少拾取」者
    • wait 状态:相关、D$ miss、DTLB miss、divide/sqrt……
    • 优先非投机线程(例如非 load hit)
  • 译码消解冲突
    • 每个线程组独立拾取
    • 两个线程组都可拾取 load/store 或 FGU 指令

47.png

9.1.3 EXU

  • 执行整数操作
    • 部分图形操作
  • 每个 EXU 保存 4 个线程的状态
    • 整数寄存器文件(IRF)每线程 8 个寄存器窗口
    • 窗口管理逻辑(RML)
    • 加法器、移位器
  • 为 load/store 操作生成地址

48.png

9.1.4 LSU

  • 每周期一次 load 或 store
    • D$ 为 store-through
    • D$ 填充与 store 并行进行
  • Load Miss Queue (LMQ)
    • 每线程一个未完成的 load miss
    • D$ 在 load miss 时分配,在 store hit 时更新
  • Store buffer (STB) 每线程 8 个 store
    • 到同一 L2 缓存行的 store 流水化送往 L2
  • 在 load miss 与 store 之间为交叉开关仲裁
    • 公平性算法

49.png

9.1.5 MMU

  • 最多 4 个页表的硬件 tablewalk
  • 每个页表支持一种页大小
  • 三种搜索模式:
    • Sequential——按顺序搜索页表
    • Burst——并行搜索页表
    • Prediction——基于 VA 预测要搜索的页表
    • 用于前两次页表搜索排序的两位预测器
  • 最多 8 个未完成的 miss
    • 每线程一个 ITLB 或 DTLB miss

9.1.6 FGU

  • 除 divide/sqrt 外完全流水化
    • divide/sqrt 与其他线程的加法或乘法操作并行执行
  • FGU 执行整数乘法、除法、popcount
  • 面向模运算的乘法器增强
    • 内置累加器
    • XOR multiply

50.png

9.1.7 SPU

密码学流处理单元(SPU) 用于加速 SSL、TLS 与 IPsec 等安全协议中频繁使用的通用加密与认证操作。密码组(CG) 子单元支持批量加密与认证操作,模运算(MA) 子单元支持公钥密码学,两者彼此独立运行。

CG 子单元的设计目标是在两个 10Gb 以太网端口上支持线速的批量密码与哈希操作,相当于 5 GB/sec 的全双工数据带宽。CG 子单元内含 RC4、DES/3DES 以及全部 AES 变体(128、192、256 位密钥长度)的专用硬件引擎,支持 AES 的 ECB / CBC / CTR 模式与 DES 的 ECB / CBC / CFB 模式,并在硬件上支持 MD5、SHA-1 与 SHA-256 认证函数(含密钥哈希 HMAC)。UltraSPARC T2 上的 8 个 SPU 各自提供 1/8 的处理能力,在 1.4 GHz 下聚合达到 5 GB/sec 的数据速率。

MA 子单元加速 IPsec 与 SSL/TLS 等安全协议的密钥交换、密钥生成与认证功能,通过模幂运算直接以硬件支持最高 2048 位密钥的 RSA、DSA 与 Diffie-Hellman 操作,此外还支持使用椭圆曲线密码学(ECC) 的密钥交换与签名生成/验证,具体说就是加速各种标准化曲线、密钥长度超过 600 位的二元域与整数域上的点乘运算。

51.png

9.2 Victoria Falls 芯片多处理器

Victoria Falls 是 UltraSPARC T2 的双芯片版本:

  • 8 核 CMP,每核 8 Strands @ 1.4GHz
  • Niagara2 SPARCv9 核心
    • 2 × 8-stage 整数单元(每流水线 4 线程),每流水线单发射
    • 12-stage 流水化 FGU(除 divide/sqrt)
    • 集成密码学加速器
    • 16KB 8-way SA L1-I$,32B 行,写穿
    • 8KB 4-way SA L1-D$,16B 行,写穿
    • 64 项全相联 I-TLB
    • 128 项全相联 D-TLB
  • 4 MB 共享 L2$
  • 2 个双通道 FBDIMM 内存控制器
  • 集成 PCI Express I/O 桥
  • 多芯片一致性链路

9.2.1 High Level Block Diagram

  • SPC Memory Model
    • TSO 合规性由核心 Load/Store 单元与 L2$ 交叉开关共同维持
    • 例外是那些明确不需要支持 TSO 的指令
  • 非阻塞的核心—L2$ 交叉开关
    • 在来自相同与不同 L2$ 分体的事务之间建立内存定序
    • 保证以相同顺序把事务投递到 L2$ 分体
    • 180 GB/sec 命中带宽
  • 并发度
    • 每线程 8 项深的 store buffer
    • 每处理器最多 384 个事务(L2$ 上限)
  • 4MB 共享 L2$
    • 8 个分体,各自独立的 L2$ 流水线
    • 64B 缓存行,写回,Modified-LRU 替换
    • 维护 L1$ 标签的目录
  • 最小化 L2$ 冲突
    • 有限的线程投机
    • 高组相联度(每分体 16 路)
    • 组索引哈希(Set Index Hashing)
  • 微并行化挑战
    • 把串行代码段划分给并发的工作者线程
    • 无锁同步原语
    • 通过为核间通信优化的公共 L2$ 降低同步开销
    • 由于热锁、真实或伪数据共享,L1-D$ 无需侦听
  • 基于侦听的一致性互连
    • 物理地址划分到 4 个一致性平面(Coherence Planes)
    • 用点到点链路取代多条总线
    • 支持芯片直连或 Hub/Star 物理拓扑
    • 多分体 L2$ 提供充裕的侦听带宽

52.png

65nm CMOS,11 Metal Layer,709 Signal I/O,1831 Total I/O

53.png

9.2.2 Multi-chip Interconnect

  • 分布式的全局定序点
    • 各一致性平面彼此独立运行
    • 物理地址冲突由一致性 Hub 串行化
    • 缓存状态:MOESI,由 L2$ 跟踪
    • 对 M、O、E 与 S(MCU 节点)状态的侦听命中引发 C2C 传输
  • 3 个虚拟通道之间独立的包处理
  • 乱序(OOO)侦听响应
    • 公平性算法
    • 超越停滞侦听继续推进
  • 物理层
    • 每通道 14 条发送与 14 条接收 lane
    • 4.8 GT/sec,每方向 8.4 GB/sec 原始链路带宽
    • 时钟恢复、位/符号对齐、初始化与训练
  • 可靠的点到点数据链路层
    • 定长帧:144 bit 载荷 + 24 bit CRC
    • 7.2 GB/sec 载荷带宽,每链路 6.4 GB/sec 峰值数据带宽,全双工
    • 硬件帧重传、链路重训练与 lane 失效切换,并维持完整的 CRC 连续性
  • 虚拟通道:Request / Request-ack / Response / Data+Critical Data
  • 事务层:3/7/18 字节包,可跨越帧边界;出向虚拟通道队列的加权轮询仲裁

54.png

10. SPARC Rock(UltraSPARC RK)

2010 年取消的 UltraSPARC RK,就是让人感到极度惋惜的未竟之憾了:

  • 16 个 4 指令派发、非循序预测执行的核心(Sun 的历史创举)
  • 每个核心 2 条同时多线程(SMT)
  • 总计切成 4 个丛集(Cluster),每个丛集共享 1 个 32kB 指令缓存、2 个 32kB 数据缓存、2 个浮点运算器
  • 耗电量高达 250W
  • 具备更精细的多线程存储器数据同步 Transactional Memory(近似英特尔 TSX)

55.png

10.1 芯片组织

65nm 工艺,396mm²,时钟 2.3GHz。16 个核心切成 4 个簇,每簇 4 核:

  • 每簇共享一个取指单元(IFU),round-robin 服务簇内 8 个 strand,每周期取一整条 16 指令的缓存行
  • 每簇共享 32KB、4 路组相联的 L1 I$(64 项 4 路 ITLB)
  • 每对核共享 32KB、4 路、双端口的 L1 D$(各自私有 tag 阵列,load-use 3 周期;32 项 2 路 skewed-associative、带 way 预测的 micro-DTLB)
  • 每核 32 项 store buffer:2 路分体、支持 store merging;写命中同时更新 D$ 与 L2,写 miss 只更新 L2、不在 D$ 分配
  • L2 共 2MB:4 个 512KB、8 路的分体,经交叉开关连接 4 个簇、MMU 与系统接口

56.png

57.png

10.1.1 Floorplan

58.png

10.2 流水线与执行

59.png

  • 译码单元带 helper ROM,负责把复杂指令拆成微操作
  • 每周期最多 4 条指令分配到 5 个各 3 项深的 issue FIFO:A0(简单整数)、A1(复杂整数,如前导零检测)、BR、FP、MEM(每周期 1 条 load/store);FIFO 之间可以乱序推进
  • 暂时无法完成的指令进入 deferred queue(DQ)——一块单端口 SRAM
  • 非投机状态下每周期最多退休 4 条;投机成功时经 join 可以不限量批量提交

10.3 分支预测

  • 60-Kbit gshare:30K 个 2 位饱和计数器,并利用 SPARC V9 的软件预测位
  • 每周期预测 2 条 PC-relative 分支
  • 间接分支用 128 项 BTB 加 128 项返回地址预测器

10.4 寄存器堆

采用两级结构,第一级是含 1 个寄存器窗口的多端口快速堆,第二级 SRAM 保存全部 8 个窗口。每个架构寄存器都有 main 与 speculative 两份副本,用一个 1 位指针区分——投机提交(join)时只需翻转指针,不用搬数据。

10.5 内存与互连

  • 内存控制器在片外 ASIC 上,每个含 8MB L3,全系统的这些 L3 构成全局共享缓存
  • 每个内存控制器 4+1 路 FB-DIMM 通道,第 5 路为冗余
  • 处理器与内存控制器之间的串行链路对数据包做硬件压缩
  • 一致性采用目录式 MESI(不支持 O 态)

10.6 投机执行

Rock 在投机执行上做了大量激进的尝试,主要包括以下几项:

  • Execute-Ahead(EA):load miss、micro-DTLB miss、除法这类长延迟操作触发 checkpoint;其目的寄存器在 scoreboard 上标记 NA,依赖它的指令进 DQ,无关指令继续投机退休、写入 speculative 副本。长延迟操作完成后进入 replay 阶段从 DQ 重放,join 时翻转 1 位指针即可。D$ 每行每 strand 设 1 个 s-bit,用来发现投机 load 的数据被无效化或驱逐。一次 EA 可以让上千条指令在飞。
  • Simultaneous Speculative Threading (SST):单线程独占核内两个 strand(双 checkpoint),一条 strand 在 replay 的同时另一条开启新的 EA 阶段,不需要额外状态。
  • Scout threading:store buffer 或 DQ 溢出导致 EA 走不下去时,该执行流转为 hardware scout 继续向前跑,为 load 与 store 预取(Memory Level Parallelism,MLP)并顺路预热分支预测器;scout 对软件不可见,等长延迟指令完成后从该指令处恢复正常执行。
  • Parallelization of programs

10.7 Transactional Memory

  • 新增 chkpt(带 fail-pc 参数)与 commit 指令和 cps 状态寄存器,复用 EA 的 checkpoint 机制
  • 事务内的 load 置 s-bit;store 按序进 store queue,地址送 L2 做冲突侦测
  • commit 时 L2 锁定全部被写行,store queue 排空时逐行解锁(bulk commit);事务规模受 store queue 容量限制
  • TLB miss、中断、除法等都可导致 abort——best-effort,不保证事务成功,思路与后来英特尔的 TSX 近似

10.8 取消

2009 年 6 月,《纽约时报》披露 Rock 被砍,主要是性能未达预期、研发耗资巨大,再赶上 Sun 被 Oracle 收购,这个 Sun 历史上最激进的核心就此止步,只留下若干篇 IEEE 论文供后人凭吊。

11. SPARC T3 / T4 / T5

  • 2010 年的 SPARC T3:40 纳米制程,时钟 1.65GHz,16 核 128 线程,可视为 UltraSPARC T2 的加倍版,然后 SPARC Enterprise 服务器品牌也取消,统一称为 SPARC T 系列。
  • 2011 年的 SPARC T4:40 纳米制程,时钟 2.85~3GHz,8 核 64 线程。

11.1 SPARC T3

SPARC T3 相当于把两颗 T2 级别的核心阵列合进一块 die,核心本身(S2)相对 T2 改动不大,还是顺序双发射、每核 8 线程。

  • 16 核 128 线程,1.65GHz,TSMC 40nm,377mm²
  • 每核 L1:16KB I$ + 8KB D$
  • 6MB 共享 L2,带宽 461 GB/s
  • 4 条 DDR3 通道
  • 2 条 PCIe 2.0 x8,2 个 1G/10G 以太网口
  • 每核一个安全协处理器(AES/DES/SHA/RSA/ECC),带硬件随机数
  • 2 个一致性控制器、6 条一致性链路(每条 14 条单向 lane),免胶合扩展至 4 路(512 线程)
  • 6 个时钟域、4 个电压域

11.2 SPARC T4

2011 年的 SPARC T4,就是 8 个双指令派发、非循序预测执行的 S3 核心(代表第三代 SPARC 核心,并追加 VIS 3 指令集),每个核心 8 条同时多线程的产物,一路「核心堆堆乐」到 12 核 96 线程的 SPARC M6。

60.png

其主要规格为:

  • 8 个 SPARC S3 核心,每核 8 线程
  • 共享 4 MB L3,8 分体,16 路组相联
  • 2 个双通道 DDR3-1066 内存控制器
  • 2 个 PCI-Express x8 2.0 端口
  • 2 个 10G 以太网端口
  • TSMC 40 nm,约 8.55 亿晶体管

61.png

11.2.1 S3 Core

  • Out-of-order
  • Dual-issue
  • 动态线程化(Dynamically threaded)
  • 均衡的流水线设计
  • 单线程性能:估计为 S2 的 5 倍 SPECint2006、约 7 倍 SPECfp2006
  • 吞吐性能:约 S2 每线程吞吐性能的 2 倍
  • 高频、深流水:16 级整数流水线,3+ GHz
  • 广泛的分支预测:感知机方向预测器(Perceptron direction predictor)、用于预测返回地址的 Return Stack、远跳转与间接目标预测器、降低已跳转分支惩罚的 BTC
  • 面向 Oracle 应用的硬件 / 软件优化:用户态密码学指令、PAUSE 指令、融合的比较—分支指令
  • 硬件预取器:指令缓存顺序行预取器、数据缓存跨步预取器
  • 16 KB、4 路组相联 L1 指令与数据缓存
  • 128 KB、8 路组相联统一私有 L2 缓存

62.png

11.2.1.1 S3 Core Pipeline

63.png

整数流水线 16 级,Fetch1–3 → Select → Decode1/2 → Rename1/2/3 → Pick → Issue1–4 → Execute → Writeback;LSU 20 级,FGU 27 级。

发射配对规则:

  • Select 每周期按 LRU 选一个线程,最多取 2 条组成 decode group(rename 阶段不打散该组);
  • store 在译码后拆成地址与 store-data 两个微操作。
  • 每周期最多发射 2 条常规指令加 1 条 store-data:slot0 去 LSU/EXU0,slot1 去 EXU1/BRU/FGU/SPU,slot2 专走 store-data。
  • 40 项 pick queue 是核内最关键的共享资源,带 thread-hog 缓解机制;
  • 128 项 ROB 允许被单个线程独占。

Threaded S3 Core Pipeline View:

  • Pick 之前:每级流水线只有 1 个线程
  • Pick 到 Commit:每级流水线可有多个线程
  • Commit:每级流水线只有 1 个线程

64.png

11.2.2 Dynamic Threading

  • S3 上许多资源在线程间共享:load-buffer、store-buffer、pick-queue、working-register-file、reorder-buffer 等
  • 线程共享资源的方式
    • 静态资源分配:对异构负载并非最优
    • 动态资源分配:更适合异构负载,改善整体应用扩展性;资源每周期在线程之间动态配置,无需同步

11.3 SPARC T5

  • 2013 年的 SPARC T5:28 纳米制程,时钟 3.6GHz,16 核 128 线程。

T5 把 S3 核心移植到 28nm 并做了改进:ROB 扩到 128 项,四条功能管线共享前 14 级流水线以节省面积。

  • 每核 L1:16KB I$(4 路)+ 16KB D$(4 路,写穿);128KB、8 路、inclusive 的私有 L2
  • 8MB 共享 L3:8 分体、16 路、MOESI,带 reverse directory
  • 8×9 交叉开关连接核心与 L3 分体,对分带宽 1 TB/s,是 T4 的两倍
  • 内存:8 个 DDR3 BL8 调度器,80 GB/s;经 4 条 12-lane、12.8Gb/s 的私有链路接片外 BoB 缓冲芯片,最多 8 条 DDR3 通道、16 条 DIMM
  • I/O:2 条 PCIe Gen3 x8
  • 7 条一致性链路(每方向 12 lane,153.6Gb/s),免胶合一跳扩展到 8 路,精确目录一致性
  • RAS:L3 支持 DMA buffer 直接分配、竞争锁加速、缓存行刷新退役以避免持久性错误;DVFS 功耗管理;16 种加密算法硬件加速

12. SPARC M 系列

SPARC M7 升级成具改良后的快取存储器阶层和 VIS 4 指令集的 S4 核心,演进到 2017 年的 32 核心 256 线程的 SPARC M8。

65.png

12.1 SPARC M5 与 Bixby

2013 年的 SPARC M5采用28 纳米制程,时钟 3.6GHz,6 核 48 线程。

12.1.1 Oracle’s M5-32 System

  • 32 个 M5 SPARC 处理器
  • 12 个 Bixby
  • 4 个物理(硬件)域
  • 3.1 TB/s 有效载荷一致性带宽
  • 1.5 TB/s 有效载荷扩展性带宽

12.1.1.1 M5-32 System Coherence Interconnect

  • Coherence Links (CL)——每方向 12 lanes
  • Scalability Links (SL)——每方向 4 lanes
  • 每 lane 12 Gbps
  • 每处理器 7 条 CL + 6 条 SL
  • 每个 Bixby 16 条 SL

66.png

12.1.2 Bixby

  • 96 Tx + 96 Rx,16 Gb/s 长距离 AC 耦合 SerDes
  • 封装:45mm × 45mm,1677-pin FPBGA(约 500 个信号 IO)
  • 工艺:28nm,10 层金属,0.85V ASIC
  • 约 160 Mbits SRAM(约 20MB 标签)
  • 约 70M 门(nand2 等效)

67.png

12.1.2.1 Floorplan

  • 所有主要数据路径上的 SEC-DED
  • 控制信号上的奇偶校验
  • 顶部两层布线层上的定制顶层走线
    • 关键网络由 route 上的 buffer 实现,ps/mm 更快
  • PVT 无关的时钟分布

68.png

  • 每个管理一条 x4 Scalability Link
  • 为多个虚拟通道提供排队支持
  • 每个 LQU 属于单一物理域
  • 链路 FIFO(基于 RAM)上的 SEC-DED

12.1.2.3 Cross Bar Units (XBU)

  • 单独的数据路径转发流量,其规模考虑了任何队头阻塞(Head-of-Line blocking)的低效
    • FXU:24 入 × 24 出(2 周期包)
    • AXI:24 入 × 8 出
    • AXO:16 入 × 24 出
    • 交换结构实现为定制布局的硬宏(hard macros)
  • Bixby 在满线速下完全维持混合的请求与数据流量
  • FXU 属单域,AXI/AXO 为多域逻辑
  • 流水的 SEC-DED,路由控制上带奇偶校验

12.1.2.4 Address Serialization Unit (ASU)

  • 划分为 8 个并行单元
  • 每个目录单元每周期可比较并处理最多 22,656 位(全芯片每周期共 181,248 位)
  • 每周期 0.5 次请求查找(全芯片共 4 次)
  • 对入向包与标签目录内容做流水校错
  • 目录标签暂存触发器上的重试(Retry)
  • 支持最多 12 个带错误导向的硬件域
  • 每域内建自初始化(BISI)
  • 标签 RAM 擦洗器(scrubber)

12.2 SPARC M6

2013 年的 SPARC M6采用28 纳米制程,时钟 3.6GHz,12 核 96 线程。

12.2.1 SPARC S3 Core

  • Dual-issue、out-of-order
  • 集成的加密加速指令
  • 用于加速 Oracle 软件栈的增强指令集
  • 1–8 个 strand,动态线程化流水线

69.png

12.2.2 SPARC M6 Caches

  • 每核 L1 与 L2 缓存
    • L1 I-Cache:16KB,4 路,32 字节行
    • L1 D-Cache:16KB,4 路,16 字节行,写穿
    • L2 统一缓存:128KB,8 路,32 字节行,写回,inclusive
  • 每芯片共享 L3 缓存
    • 48MB,4 分体,12 路,64 字节行,inclusive,MOESI
    • 基于 PCIe TLP Processing Hints 的 DMA 分配
    • 请求打包(Request bundling),改善数据库中高访问共享区的性能

12.2.3 SPARC M6 Memory Subsystem

  • 为内存数据库与应用调优
  • 4 个高效率 DDR3 调度器
    • 每插槽 16 个 DDR 通道
    • 每 rank 调度
    • 动态调节写与读的优先级
    • DIMM 省电模式
  • 丰富的地址交织设置,以优化性能、可服务性与功耗的平衡

70.png

12.2.4 SPARC M6 IO Subsystem

  • 双 x8 PCIe Gen3
  • 原子操作:fetch-add、swap 等
  • TLP hints 把 DMA 写直接导向 L3
  • PCIe 电源管理
  • 虚拟 IO 的加速功能
  • PCIe 架构化错误
  • 通过 PCIe 消息的错误信令
  • 支持每个 PCIe root complex 及其所连结构的独立复位

71.png

12.2.5 SPARC M6 Processor

72.png

12.2.5.1 SPARC M6 Processor

  • 12 个 SPARC S3 核心,96 线程
  • 48MB 共享 L3 缓存
  • 4 个 DDR3 调度器,每插槽最大 1TB 内存
  • 2 个 PCIe 3.0 x8 lane
  • 无胶合(glue-less) 最多扩展到 8 插槽
  • 有胶合最多扩展到 96 插槽
  • 4.1 Tbps 总链路带宽
  • 42.7 亿晶体管

73.png

12.2.6 SPARC M6 Scaling

12.2.6.1 Tightly Coupled SMP

  • 最多 8 个处理器用 C-Links 直接互连
  • 可构建处理器间多链路的 2 路与 4 路配置
  • 可在插槽被去配置(de-configured)的情况下运行
  • 内存与目录地址在处理器之间切片分布
  • 内存 home 与目录 home 采用不同的地址哈希

74.png

12.2.6.2 Beyond an SMP

  • 系统目录位于 Bixby 中,通过 S-Links 连接到处理器
  • 一个 SMP 的 48 条 S-Links 代表不同的地址平面
  • 请求必须先用 C-Links 到达正确的处理器,再用 S-Links 到达目录
  • 响应沿请求的路径回溯
  • 数据在所有可用平面上动态路由

75.png

12.2.6.3 Larger Systems

76.png

77.png

12.2.7 SPARC M6 Coherence Simple Transaction

  • 请求被送往目录,SpecRead 被送往内存 home
  • 目录检查行状态,并返回带预期信息的 ReqAck
  • 若该行不在任何缓存中,目录指示内存 home 提供数据
  • 若该行在某些缓存中,目录指示某个缓存提供数据,并在必要时指示其他缓存使其副本失效
  • 请求者收集所有持有者的 InvAck,然后通知目录解锁该行

78.png

12.3 SPARC M7

SPARC M7 的要点:

  • 32 个 SPARC 核心
    • 第四代 CMT 核心(S4)
    • 动态线程化,每核 1 至 8 线程
  • 新的缓存组织
    • 共享的二级数据与指令缓存
    • 64MB 共享且可分区的三级缓存
  • DDR4 DRAM
    • 每处理器最高 2TB 物理内存
    • 内存带宽为上一代的 2–3 倍
  • PCIe Gen3 支持
  • 应用加速
    • 实时应用数据完整性(Real-time Application Data Integrity)
    • 并发内存迁移与 VA Masking
    • 数据库查询卸载引擎(DB Query Offload Engines)
  • 从 1 到 32 处理器的 SMP 扩展性
  • 一致性内存簇(Coherent Memory Clusters)
  • 工艺采用 20nm,13ML

79.png

12.3.1 M7 Core - S4

  • 动态线程化,1 至 8 线程
  • 双发射、乱序执行核心
    • 2 个 ALU、1 个 LSU、1 个 FGU、1 个 BRU、1 个 SPU
    • 40 项 Pick Queue
    • 64 项全相联 I-TLB,128 项全相联 D-TLB
    • 密码学性能改进
    • 54 bit VA,50 bit RA/PA,寻址能力提升 4 倍
  • 16 级整数流水线(与 S3 同深度,频率更高),全核最多 128 条指令在飞(128 项 ROB)
  • 发射槽位:slot0 = EXU0 + LSU,slot1 = EXU1 + BRU + FGU/SPU,另有独立的 store-data 通道,峰值 2 IPC
  • load-use 延迟:L1 5 周期、L2 19 周期、L3 41 周期
  • fetch 按「最近最少取指」选线程、decode 按「最近最少译码」,槽位内 oldest-ready-first 发射;乱序完成、strand 内按序提交

80.png

12.3.2 M7 Core Cluster

  • 每簇 4 个 SPARC 核心
  • 新的 L2$ 架构
    • 容量比上一代大 1.5 倍,而延迟周期数相同
    • 每核带宽 2 倍;每核心簇 >1 TB/s,全芯片 >8 TB/s
  • 256KB L2-I$
    • 由核心簇内所有核心共享
    • 4 路组相联,64B 行,>500 GB/s 吞吐
    • 4 个独立核心接口,每个 >128 GB/s
  • 256KB 写回 L2-D$
    • 每个 L2-D$ 由 2 个核心(core-pair)共享
    • 8 路组相联,64B 行,每个 L2-D$ >500 GB/s 吞吐
    • 每个 L2-D$ 2 个独立核心接口,每个 >128 GB/s
  • Core-pair 动态性能优化
    • 在上一代线程数下使核心执行带宽翻倍,最大化每线程性能
    • 利用每核 8 线程最大化核心吞吐

81.png

12.3.3 M7 Level 3 Cache 和On-Chip Network

  • 64MB 共享且可分区的 L3$
    • 8MB 本地区(Local Partition),各 8 路组相联
    • 本地 L3$ 周期延迟比上一代(M6)降低 >25%
    • 每芯片 >1.6 TB/s L3$ 带宽,为上一代的 2.5 倍(T5)到 5 倍(M6)
    • 缓存行可在 L3$ 分区之间复制、迁移或牺牲(victimize)
    • 硬件加速器可直接分配进目标 L3$ 分区
  • 片上网络(OCN)
    • 由 Request(环)、Response(点到点)与 Data(Mesh)三类互连构成
    • 0.5 TB/s 对分数据带宽

82.png

12.3.4 M7 Memory 和 I/O

  • 4 个 DDR4 内存控制器
    • 16 个 DDR4-2133/2400/2667 通道
    • 极大的内存容量,每处理器最高 2TB
    • 实测 160 GB/s 内存带宽(DDR4-2133),为上一代 T5 与 M6 的 2–3 倍
    • DIMM 退役(Retirement)无需停机
  • 到缓冲芯片的内存链路
    • 12.8 / 14.4 / 16 Gbps 链路速率
    • 带完整 CRC 保护的 lane 失效切换
  • 投机内存读
    • 在本地 L3$ 分区失效时通过预取降低本地内存延迟
    • 基于历史(数据、指令)与阈值设置的每请求动态决策
  • PCIe Gen3
    • 4 条内部链路,支持 >75 GB/s
    • 为上一代 T5 与 M6 的 2 倍以上

83.png

12.3.5 M7 Query Accelerator Engine

  • 8 个硅片内卸载引擎
  • 核心/线程与卸载引擎同步或异步运行
  • 通过共享内存的用户态同步

84.png

12.3.6 M7 SMP Scalability

  • 全连接的 8 处理器 SMP
    • 最多 256 核、2K 线程、16TB 内存
    • 1 TB/s 对分有效载荷带宽

  • 利用链路聚合(Link Trunking) 的全连接 2/4 处理器 SMP
  • 基于目录的一致性
  • 16 Gbps 至 18 Gbps 链路速率
  • 链路级动态拥塞避免
    • 备用路径数据路由
    • 基于目的队列占用率
  • 链路级 RAS
    • 自动帧重传
    • 自动链路重训练
    • 单 lane 失效切换

85.png

  • SMP 扩展到 32 处理器
    • 最多 1K 核、8K 线程
    • 最多 64TB 内存
  • 64 端口交换 ASIC
    • 分为 2 个交换组,每组 6 个交换 ASIC
    • 5.2 TB/s 有效载荷带宽
    • 对分有效载荷带宽为 M6 的 4 倍
  • 每 4 个处理器一个物理域
    • 全连接的本地拓扑
    • 动态组合处理器域
  • 全连接交换拓扑
    • 每个处理器与交换机之间 2 条链路
    • 一致性目录分布在各交换机中
    • 相对 M6 代际的延迟降低
  • SMP 互连 RAS
    • 链路自动重传与重训练、单 lane 失效切换
    • 链路级多路径
    • 每组 6 个 ASIC 中有 5 个即可运行

86.png

12.4 SPARC S7 与 M8

  • 2016 年的 SPARC S7 采用20 纳米制程,时钟 4.27GHz,8 核 64 线程,SPARC M7 的低价微缩版。

87.png

S7(Sonoma)用与 M7 相同的 S4 核心,8 核 2 簇,相当于四分之一颗 M7,但把 M7 需要片外 BoB 芯片承担的内存控制器、PCIe 与 InfiniBand 全部集成上片:

  • 每核 16KB + 16KB L1;每簇共享 256KB L2 I$,每对核共享 256KB 写回 L2 D$(各自 500GB/s)
  • L3 16MB 全共享、按簇分区、8 路 inclusive
  • 2 个 DDR4-2133/2400 控制器(各 2 通道),单插槽最大 1TB、77GB/s
  • 1 条 PCIe 3.0;16 个 DAX 引擎(4 单元 × 4 流水线);4 条一致性链路共 128GB/s

  • 2017 年的 SPARC M8 采用20 纳米制程,时钟 5GHz,32 核 256 线程,一颗怪物级的大芯片。

88.png

M8 换上第五代 S5 核心,发射宽度从 2 加宽到 4:

  • 最多 192 条指令在飞,每周期最多提交 4 条
  • 发射结构(据 GCC 调度器源码):两个双发射队列 PQLS 与 PQEX——slot0 为 load 与 store 地址生成,slot1 为 store-data 与分支,slot2 为 EXU2 + SPU2(3 周期加密),slot3 为 EXU3 + SPU3 + FGU + 长延迟加密单元 + Oracle Numbers Unit
  • 延迟:整数乘 10 周期、整数除 30 周期、load 3 周期、非对齐 load 11 周期
  • L1 I$ 加倍到 32KB(4 路);L2 D$ 改为每核独享 128KB(8 路),不再由核分享
  • 不变的部分:32 核 256 线程、64MB 16 路 inclusive L3(2 个 32MB 分区,热点行就近迁移)、每 4 核共享 256KB L2 I$、16KB L1 D$、DDR4-2400(185GB/s)、16GB 大页、最多 8 插槽

同是 20nm,时钟却从 M7 的 4.13GHz 提到 5.0GHz(+22.5%),靠的是核心内部重构与 DVFS——这也是 SPARC 最后一次冲击频率纪录。

13. Fujitsu SPARC64

SPARC64 之名继承自 HAL,而在 2001 年取消 HAL 原案、基于 Fujitsu GS8900 大型主机而重新开发的 SPARC64 V,则是 Fujitsu 在高端 SPARC 处理器最重要的里程碑:四指令派发、非循序指令预测执行、大型主机等级的高可靠度,效能也明显优于 Sun UltraSPARC III。

89.png

13.1 SPARC64

SPARC64 Processor Module 是一个陶瓷 MCM(多芯片模块),包含:

  • 1 个 CPU
  • 4 个 CACHE 芯片
  • 1 个 MMU
  • 1 个 CLOCK 芯片

90.png

  • 通过数据流执行(Dataflow execution)跟踪最多 64 条活动指令
  • 采用寄存器重命名(Register Renaming) 与动态分支预测(Dynamic Branch Prediction)
  • 顺序发射(ISSUE in-order),乱序执行与完成(EXECUTE/COMPLETE out-of-order),顺序提交(COMMIT in-order)
  • 内含 2 个 Load/Store 单元、2 个定点单元、2 个地址生成单元、1 个浮点乘加单元,以及 1 个自定时的浮点除法单元
  • CACHE
    • 非阻塞,采用虚拟索引与虚拟标签
    • 每个芯片 64kB,可服务来自 CPU 的 2 个独立请求
    • 4 路组相联
    • 服务投机/乱序的 CPU 请求
    • 2 个指令缓存芯片,2 个数据缓存芯片
  • CLOCK
    • 在诊断处理器控制下,为 MCM 上所有其他芯片与内存子系统提供 SYS-CLK / SCAN-CLK
    • 频率:15~280MHz,抖动 80~160ps,带可编程延迟线以控制偏移
  • PROCESS
    • Fujitsu 的 twin-well 0.4 μm、4 层金属 CS55 CMOS 工艺
    • 3 层金属用于信号布线
    • 第 4 层金属用于键合焊盘、全局电源与时钟布线
    • MCM 芯片互连采用焊料凸点键合(Solder Bump Bonding)

91.png

13.1.1 MMU

  • 与虚拟缓存、内存系统、I/O 以及诊断处理器接口
  • 通过两级地址转换与三级存储层次(虚拟地址、逻辑地址与物理地址)支持线性 64 位地址空间

92.png

  • 3 个「lookaside buffer」降低转换时间
    • 128 项全相联 VLB(VA → LA)
    • 1024 项、4 路组相联 TLB(LA → PA)
    • 8 项、3 端口、全相联 microTLB(VA → PA)
  • 内含一个 1024 项的数据缓存实地址表(Data Cache Real Address Table)
    • 保存所有数据缓存行的实地址标签
    • 若条目匹配,则是一次实命中但虚拟缓存失效,无需访存,只需重新给缓存行打标签

93.png

13.1.2 Pipeline

  • 超标量、4 发射、真正的 64 位 V9-SPARC 实现
  • 定点指令流水线 4 级
  • Load 指令流水线 6 级

94.png

95.png

13.2 SPARC64 V / V+ / VI

13.2.1 SPARC64 V

SPARC64 V 继承了 GS8900 大型主机的核心设计:4 指令派发、非循序预测执行,媒体综述常称其为 15 级流水线。JPS1 手册按功能段描述为:取指段 IA/PS/TT/IM/BB/IR(6 项指令缓冲,每项 32 字节即 8 条指令)→ 发射段 E/D → 执行段 P/B/X → 提交段 U/W,最多 64 条指令在飞。

  • 分支预测:BRHIS(branch history)16K 项、4 路组相联,外加返回地址栈(RAS);误预测惩罚约 11 周期
  • 执行单元:2 个整数 ALU(EXA/EXB,乘除仅在 EXA)、2 个地址生成器(AGA/AGB)、2 个 FPU(FLA/FLB,各含加法器与乘法器,FLA 附带 VIS 图形单元);6 个 reservation station(每站 8 项),支持投机派发;整数与浮点 update buffer 各 32 项
    • 此时的「乘加」还是乘、加两步、两次舍入,单条 FMA 指令要到 VI 才有
  • 缓存:L1 128KB 指令 + 128KB 数据,均为 non-blocking,虚拟索引、虚拟标签;片上 L2 1–2MB
  • 系统接口:128 位系统总线 @260MHz,SDR/DDR 模式峰值 4.16/8.32 GB/s;PRIMEPOWER 2500 用冗余双交叉开关互连,540MHz source-synchronous,聚合 133 GB/s,每系统板 8.3 GB/s
  • RAS——大型主机血统最直观的体现:
    • 寄存器、ALU 与内部数据通路奇偶校验
    • L1 数据 ECC、L1 标签在 L2 中备份可恢复;L2 数据与标签 ECC;内部总线 ECC
    • 硬件指令重试(instruction retry)
    • 缓存/TLB 故障时动态降级(limp-along),可预测故障可离线处理

13.2.2 SPARC64 V+

V+ 是 V 的工艺升级版:90nm CMOS、10 层铜互连,约 4 亿晶体管,294.25mm²,时钟 1.82–2.16GHz,L2 增至 3MB/4MB,核心微架构没有大改。

13.2.3 SPARC64 VI

  • 2007 年的 SPARC64 VI:90 纳米 SOI、10 层铜,5.4 亿晶体管,421.25mm²,时钟 1.5~2.4GHz,双核心(每核为改进版 V+),导入粗质多线程(Fujitsu 称为 VMT,Vertical Multi-Threading),也是首款实现单条浮点乘积和(FMA)指令的 SPARC 处理器——此前的乘加要分两步、两次舍入。

  • VMT 为 Switch-On-Event 式粗粒度多线程:L2 miss、硬件定时器、中断或显式控制指令触发线程切换;PC 与控制/整数/浮点寄存器每线程复制一份,同核线程共享 fTLB/sTLB
  • 缓存:每核 L1 128KB;双核共享 6MB 片上统一 L2,12 路组相联,256 字节行;L2 访问总线为 256 位单向读 + 128 位单向写
  • 引入 Jupiter Bus 系统互连

96.png

13.3 SPARC64 VII

13.3.1 SPARC64 VII Chip

  • 架构特性
    • 4 核 × 2 线程(SMT)
    • 内嵌 6MB L2$
    • 2.5GHz
    • Jupiter Bus
  • 工艺:Fujitsu 65nm CMOS,20.31mm × 20.86mm,6 亿晶体管,456 个信号引脚
  • 功耗:135 W(max),每核功耗相对 SPARC64 VI 降低 44%
  • 微架构
    • 约 16 级流水线,fetch/issue/commit 段由两线程共享,部分寄存器每线程复制
    • 每核执行单元:2 个整数(EXA/EXB,乘除仅在 EXA)+ 2 个地址生成(EAG)+ 2 个 FPU(FLA/FLB,支持 FMA,除法/开方非流水),reservation station 各 10 项;每核峰值 10.08 GFLOPS
    • TLB:32 项 µTLB + 1024 项主 TLB(I/D 分离)
    • 缓存:每核 L1 64KB I + 64KB D,2 路;4 核共享 6MB 统一 L2,12 路组相联,写穿;无 L3
    • RAS:整数寄存器文件升级为 ECC 保护,片上 error checker 增至约 3400 个

97.png

13.3.2 Pipeline Overview

98.png

13.3.3 Simultaneous Multi-Threading

  • 细粒度多线程
    • 取指/发射/提交阶段:每周期在两线程之间交替选择其一
    • 执行阶段:独立于线程,基于「最老的就绪者(oldest-ready)」选择指令

99.png

  • 分离的硬件队列
    • 避免线程之间相互干扰
    • 若另一线程空闲,则自动合并

100.png

13.3.4 Integrated Multi-core Parallel Architecture

  • L2 缓存
    • 由 4 个核心共享,避免伪共享(false-sharing)
    • L2 缓存与 L1 缓存之间的带宽(为 SPARC64 VI 的 2 倍)
      • L2$ → L1$:32 byte/cycle/2core × 4core
      • L2$ ← L1$:16 byte/cycle/core × 4core
  • 硬件栅栏(Hardware Barrier)
    • 芯片内核心之间的高速同步机制
    • 降低并行执行的开销

13.3.4.1 Hardware Barrier

  • 栅栏资源
    • BST:Barrier STatus
    • BST_mask:选择 BST 中的位
    • LBSY:Last Barrier synchronization status
    • 当 BST_mask 选中的所有 BST 位被置为同一值时,同步即建立
  • 用法
    • 每个核心更新 BST
    • 等待 LBSY 翻转

101.png

13.4 SPARC64 VIIIfx

  • 架构特性
    • 8 核
    • 共享 5 MB L2$
    • 内嵌内存控制器
    • 2 GHz
  • 工艺:Fujitsu 45nm CMOS,22.7mm × 22.6mm,7.6 亿晶体管,1271 个信号引脚
  • 性能(峰值)
    • 128 GFLOPS
    • 64 GB/s 内存吞吐
  • 功耗
    • 58W(TYP,30℃)
    • 水冷——低漏电功耗与高可靠性
  • 微架构改动(相对 VII)
    • 前端去掉多线程;L1 I$ 减半至 32KB;BTAC 从 8192 项 / 8 路减至 1024 项 / 2 路
    • 译码前插入一级流水线,专门用于合并 SXAR 前缀指令(见 1.6.5 节)
    • 分支 / 整数 reservation station 减至 6 / 10 项,CSE 由 64 减至 48,load queue 由 16 增至 20
    • 浮点执行管线前加一级,用来访问扩大的寄存器堆
  • 执行与寄存器
    • 2 整数 + 2 地址生成 + 4 个 FMA(FLA/B/C/D,SIMD 一条指令含 2 个 FMA),每核每周期 8 个双精度 FLOP
    • 192 个整数寄存器、256 个浮点寄存器(SPARC V9 的 8 倍,经 SXAR 前缀扩展寻址)
    • HPC-ACE 另含 sector cache(软件可控)、sin/cos 加速、除法/开方近似指令
  • 缓存:每核 L1 32KB I + 32KB D(2 路),D$ 双端口,支持 2×16B SIMD load 或 1×16B SIMD store;8 核共享 5MB L2(12 路、128 字节行、sectored)
  • 内存与互连:片上 4 个内存控制器、8 通道 DDR3,容量 64GB、峰值 64 GB/s;经外置 ICC(InterConnect Controller)芯片接 Tofu 6 维 mesh/torus 互连,每链路双向各 5 GB/s、10 个方向

102.png

13.4.1 SPARC64 VIIIfx Core spec

103.png

104.png

13.4.2 SPARC64 VIIIfx Pipeline

105.png

13.5 SPARC64 X / X+

  • 2010 年的 SPARC64 VII+ 采用65 纳米制程,时钟提升到 3GHz,L2 快取加倍到 12MB。
  • 2011 年的 SPARC64 IXfx 采用40 纳米制程,时钟 2.85GHz,16 核心,配合 PRIMEHPC FX10 超级电脑而同步发表。总之,只要看到名称内有那个小写的 fx 就知道这是超级电脑特规版了。

106.png

  • 2012 年的 SPARC64 X 采用28 纳米制程,时钟 3GHz,16 核心 32 线程,耗电量冲上 270W,存储器控制器也「搬家」到处理器内部了。

107.png

13.5.1 SPARC64 X+

  • 架构特性
    • 16 核 × 2 SMT 线程
    • 共享 24 MB L2$
    • 内存与 I/O 控制器
    • HPC-ACE
    • SWoC(Software on Chip)
  • 微架构改动(相对 VII+)
    • 流水线加深;分支预测改为 4K 项 BRHIS + 16K 项 pattern history table(BTA 4K 项)
    • 队列全面加大:RSA 24、RSE 24、RSF 20、RSBR 16、CSE 96、fetch port 32、store port 24
    • 寄存器堆:GPR 156×2 + GUB 64,FPR 128×2 + FUB 64
    • 执行单元:2EX + 2EX/EAG(每周期可执行 4 条整数指令);FMA ×4(支持 2 路 SIMD)+ FDIV ×2
  • 28nm CMOS
    • 24.0mm × 25.0mm
    • 2,990M 晶体管
    • 1,500 个信号引脚
    • 3.5GHz+
  • 性能(峰值)
    • 448 GFLOPS+
    • 102 GB/s 内存吞吐
  • RAS:SRAM ECC + 硬件指令重试(HIR),flip-flop 广泛奇偶校验;ECC 组列距由 4 扩至 8 以抗多比特翻转;关键 latch 采用 SEILA(soft-error-immune latch,抗单粒子翻转能力提升 100 倍,经大阪大学中子束实验验证)

108.png

13.5.1.1 Software on Chip (SWoC)

  • Cipher——AES / DES / SHA / RSA
  • Decimal(IEEE754 DPD、NUMBER)——加速若干 NUMBER 库
  • Database processing
    • 在硬件中加速特定的软件功能
    • 实现在浮点单元中的 SWoC 引擎可使用 128 个浮点寄存器与软件流水
    • 面积/门数 < 核心的 3%、< 芯片的 1%

13.5.1.2 SPARC64 X / X+ System Architecture

  • 从 1 扩展到 64 个 CPU 插槽(2048 线程)
    • 基于目录的缓存一致性
    • 高速互连,SPARC64 X+ 每 lane 最高 25 Gbps(SPARC64 X 最高 14.5 Gbps)
  • 系统配置
    • 基本构建块(BB)为 4 个 CPU + 2 个 XB
    • 最多 4 个 BB 可由 XB 互连
    • 16 个 BB 可通过 XB-Box 互连

109.png

110.png

13.6 SPARC64 XIfx 与 A64FX

2014 年的 SPARC64 XIfx 采用台积电 20 纳米 HKMG 制程,37.5 亿晶体管,时钟 2GHz,34 核心(32 个运算核心加 2 个辅助核心),新增 HPC-ACE2 指令集。高度整合存储器控制器与汇流排界面的 SPARC64 XIfx 堪称 SPARC 世界第一颗超级电脑系统单芯片。

  • 芯片分 2 个 CMG(Core Memory Group),每组 16+1 核共享 12MB L2(共 24MB),CMG 之间为 ccNUMA;整片 1.1 TFLOPS,L1 总带宽 4.4 TB/s
  • 核心基于 X+:每核 2 整数 + 2 地址/整数单元 + 8 个 FMA;HPC-ACE2 把 SIMD 拓宽到 256 位(一条指令 4 个双精度 FMA),每核每周期 16 个双精度 FLOP,128 个浮点寄存器;新增 stride/indirect load-store 与 compress/permutation 跨 lane 操作
  • 每核 L1 64KB I + 64KB D(4 路);GPR 188 项
  • 内存:4 个 HMC(Hybrid Memory Cube) 接口,容量 32GB,每接口 16 lane × 15Gbps,理论带宽 480 GB/s(读 240 + 写 240)——SPARC64 系列第一次用上 3D 堆叠内存
  • 互连:片上集成 Tofu2 控制器(10 端口,每链路双向各 12.5 GB/s,25Gbps SerDes 共 40 lane),6 维 mesh/torus,支持 cache injection 与原子操作;机柜间以光缆连接,直接水冷
  • 辅助核心 2 个:运行 OS daemon、I/O 与 MPI 异步通信,运算核心不被这些杂务打断

111.png

近期夺下 Top500 首位的日本理化学研究所超级电脑「富岳」,其心脏 Fujitsu A64FX,实际上就是将指令集架构,从 SPARC v9 和 HPC-ACE2,替换成 ARM v8.2A 加 SVE 的进化版。

112.png

13.7 SPARC64 XII

2017 年的 SPARC64 XII 采用台积电 20 纳米制程,时钟 2.5~4.35GHz,12 核心 96 线程(一个核心 8 线程,和 IBM Power9 一样)。

113.png

114.png

  • 每个核心内含两条独立的指令流水线,各自每周期取指 8 条、译码 4 条、执行 6 条,每条流水线 4 路 SMT,合计每核 8 线程;两条流水线之间仅共享 TLB、L1 I$ 与 L2——与其说是 8 线程单核,不如说更像「核内双芯」
  • 54.5 亿晶体管,795mm²,时钟 4.25GHz(High-Speed Mode 4.35GHz)
  • 缓存:L1 D$ 每流水线 32KB(4 路,每核合计 64KB),L1 I$ 每核 64KB(4 路)共享,L2 每核 512KB(16 路);系列首次引入 L3:32MB,16 路
  • 内存:8 通道 DDR4-2400,峰值 153 GB/s,单 CPU 最大 1.5TB;4 条 PCIe Gen3 x8
  • 系统:2 个 CPU 组成一个 Building Block,可扩展至 32 CPU / 48TB
  • RAS:端到端 ECC 与数据通路完整性保证、instruction retry 自动恢复、L1/L2/L3 缓存 way 动态降级、内存 ECC/扩展 ECC、内存镜像、周期 patrol scrubbing 与 predictive self-healing

13.8 结语

回头看 SPARC 这三十余年:出身伯克利开放的 RISC 思潮,靠寄存器窗口立身,九十年代凭超标量与 VIS 站上巅峰;x86 碾压过来之后,又果断放弃单线程军备竞赛,转进 Throughput Computing,用 Niagara 重新定义了服务器处理器该有的样子——这套思路在今天的众核与吞吐型加速器上仍然清晰可见。

另一端的 Fujitsu SPARC64 自始至终抱着大型主机级的 RAS 与浮点性能,从 GS8900 的血统一路走到 SPARC64 XII,最后把 HPC 的家底搬到 ARM 架构上,成就了「富岳」与 A64FX。

参考文献

  1. Leon, A.S., Shin, J.L., Tam, K.W., Bryg, W., Schumacher, F., Kongetira, P., Weisner, D., Strong, A., Mircrosystems, S., 2006. 5.1 A Power-Efficient High-Throughput 32-Thread SPARC Processor.
  2. Kongetira, P., Aingaran, K., Olukotun, K., 2005. Niagara: A 32-Way Multithreaded Sparc Processor. IEEE Micro 25, 21–29. https://doi.org/10.1109/MM.2005.35
  3. P. Kongetira, “A 32-Way Multithreaded SPARC Processor,” 16th Hot Chips Symp., Aug., 2004.
  4. G. Grohoski, “Niagara-2: A highly threaded server-on-a-chip,” 2006 IEEE Hot Chips 18 Symposium (HCS), Stanford, CA, USA, 2006, pp. 1-22, doi: 10.1109/HOTCHIPS.2006.7477874.
  5. Baldwin, T., n.d. UltraSPARC T2: A Highly-Threaded Power-Efficient, SPARC SOC.
  6. Hughes, J., Morton, G., Pechanec, J., Schuba, C., Spracklen, L., Yenduri, B., 2009. Transparent multi-core cryptographic support on Niagara CMT Processors, in: 2009 ICSE Workshop on Multicore Software Engineering. Presented at the 2009 ICSE Workshop on Multicore Software Engineering (IWMSE), IEEE, Vancouver, BC, Canada, pp. 81–88. https://doi.org/10.1109/IWMSE.2009.5071387
  7. G. K. Konstadinidis et al., “SPARC M7: A 20 nm 32-Core 64 MB L3 Cache Processor,” in IEEE Journal of Solid-State Circuits, vol. 51, no. 1, pp. 79-91, Jan. 2016, doi: 10.1109/JSSC.2015.2456902.
  8. P. Li et al., “4.2 A 20nm 32-Core 64MB L3 cache SPARC M7 processor,” 2015 IEEE International Solid-State Circuits Conference - (ISSCC) Digest of Technical Papers, San Francisco, CA, USA, 2015, pp. 1-3, doi: 10.1109/ISSCC.2015.7062931.
  9. Kathirgamar Aingaran, Sumti Jairath, Georgios Konstadinidis, Serena Leung, Paul Loewenstein, Curtis McAllister, Stephen Phillips, Zoran Radovic, Ram Sivaramakrishnan, David Smentek, and Thomas Wicki. 2015. M7: Oracle’s Next-Generation Sparc Processor. IEEE Micro 35, 2 (Mar.-Apr. 2015), 36–45. https://doi.org/10.1109/MM.2015.35
  10. R. Sivaramakrishnan and S. Jairath, “Next generation SPARC processor cache hierarchy,” 2014 IEEE Hot Chips 26 Symposium (HCS), Cupertino, CA, USA, 2014, pp. 1-28, doi: 10.1109/HOTCHIPS.2014.7478828.
  11. SPARC International, Inc., The SPARC Architecture Manual, Version 8, 1992. (后成为 IEEE Std 1754-1994)
  12. SPARC International, Inc., The SPARC Architecture Manual, Version 9, Prentice Hall, 1994.
  13. Weaver, D. L. and Germond, T. (Eds.), The SPARC Architecture Manual, Version 9, SPARC International, 1994.
  14. Sun Microsystems, OpenSPARC T1 Microarchitecture Specification, 2006.
  15. Stanford VLSI Group, CPU DB — UltraSPARC III (Cheetah), TI C07a / 0.18 μm / 6LM Al. http://cpudb.stanford.edu/processors/566.html
  16. cpu-collection.de, Sun Microsystems UltraSPARC III / III Cu / IIIi. https://cpu-collection.de/?l0=co&l1=Sun+Microsystems&l2=UltraSPARC++III
  17. Konstadinidis, G. K. et al., “Implementation of a Third-Generation 1.1-GHz 64-bit Microprocessor,” IEEE Journal of Solid-State Circuits, vol. 37, no. 11, 2002. https://doi.org/10.1109/JSSC.2002.803951
  18. Sun Microsystems, UltraSPARC-I Data Sheet (STP1030A), 1996. https://datasheets.chipdb.org/Sun/stp1030a.pdf
  19. Texas Instruments / Sun Microsystems, SuperSPARC Microprocessor Fact Sheet (TMS390Z50), 1992.
  20. Chaudhry, S. et al., “Rock: A SPARC CMT Processor,” IEEE Micro, vol. 29, no. 2, pp. 6–16, 2009. https://doi.org/10.1109/MM.2009.34
  21. Turullols, S. et al., “SPARC T5: A 28nm 16-Core 64-Thread Server Processor,” Hot Chips 24, 2012.
  22. Maruyama, T. et al., “SPARC64 VIIIfx: A New-Generation Octocore Processor for Petascale Computing,” IEEE Micro, vol. 30, no. 2, 2010. https://doi.org/10.1109/MM.2010.41
  23. Fujitsu, SPARC64 X / X+ Processor, Hot Chips 24, 2012.
  24. Fujitsu, SPARC64 XII (M12-2) Datasheet / Fujitsu Scientific and Technical Journal, 2017.
]]>
Maia Architecture | SDLA dataflow http://zhifeiding.github.io/programming/history/2026/10/14/Maia-Architecture-SDLA-dataflow/ 2026-10-14T00:00:00+00:00 http://zhifeiding.github.io/programming/history/2026/10/14/Maia-Architecture-SDLA-dataflow Maia | 软件定义数据流的推理引擎

前言

“问渠那得清如许?为有源头活水来。”

推理已经成为AI计算的主战场,而推理的经济性——每瓦特产出多少有效token,每美元产出多少智能——正在决定AI能否真正普惠。在这场竞赛中,GPU长期扮演万能瑞士军刀的角色。但万能往往意味着低效:线程中心的编程模型、隐式的缓存行为、把控制与数据混为一谈的调度机制,在面对LLM推理这种数据搬移远比计算吃紧的负载时,暴露出越来越多的结构性问题。

微软选择了一条不同的路。从第一代Maia开始,就坚持一个朴素而激进的哲学:把数据搬移而非线程调度,作为架构设计的核心原点。算力如渠,数据如水:计算单元能跑出多高的利用率,取决于数据能否像活水一样源源不断地流到手边;Maia芯片从根基上重新定义加速器的编程范式。三代Maia——100、200、300——便是这一哲学的三个递进式里程碑。

概述

微软Maia是一个多代际AI加速器项目,为云规模大模型推理提供从软件到芯片、从机架到数据中心的端到端协同设计。其核心创新是软件定义局部访问数据流架构SDLA(Software Defined Locally Accessed Dataflow Architecture):把控制路径编程与数据路径执行显式分离,让软件而非硬件主导数据的放置、搬移、转换与同步。

  • Maia 100于2023年11月发布,Hot Chips 2024首次公开细节,是微软第一代自研AI加速器,也是首个把”软件管理暂存内存+硬件信号量+双编程模型”工程化落地的产品。
  • Maia 200于2026年1月发布,是SDLA的开山之作,第一次把前代散落的设计直觉收敛为完整的理论分类法与指令集形式化,并工程化为Tile→Cluster→SoC→6,144芯片集群的完整体系。
  • Maia 300于2026年10月发布,是SDLA哲学的全面进化:单体裸片转向多芯粒,内存组织从UMA演进为NUMA,芯片互联从6,144增至12,288颗加速器,引入增强MXFP4与3比特存储格式;相比Maia 200,机架级token吞吐提升7倍,每瓦性能提升2.5倍以上。

下图展示了微软官方给出的两代规格对比与Maia 300封装剖面:

0.webp

表1汇总了三代Maia的芯片级峰值规格。

  • Maia 300单芯片FP4算力(13.8 POPS)相比Maia 200(10.1 POPS)仅提升约36%,HBM带宽却提升2.5倍,容量提升33%——性能重心显然不在”算得更快”,而在”喂得更足”。

表1 三代Maia芯片级峰值规格对比

峰值规格 Maia 100 Maia 200 Maia 300
制程技术 单体裸片N5 单体裸片N3P 多芯粒N2P计算
芯片面积 ~820mm²(光罩尺寸裸片) 近光罩尺寸单体裸片~26×33mm 多芯粒封装
封装 TSMC CoWoS-S CoWoS-S(~75×75mm封装) 多芯粒+8×HBM4堆栈
FP4算力 未公开 10.1 POPS 13.8 POPS
FP8算力 未公开 5.1 POPS 6.9 POPS
6比特算力 3 POPS — —
9比特算力 1.5 POPS — —
BF16算力 0.8 POPS — —
SoC TDP 700W设计/500W部署 750W 950W
HBM 4×HBM2E 6×HBM3E 8×HBM4
HBM带宽 1.8 TB/s 7.1 TB/s 18 TB/s
HBM容量 64 GB 216 GB 288 GB
片上SRAM ~500 MB(L1/L2) 272 MB(CSRAM 35MB/Cluster + TSRAM 3MB/Tile) 未公开
Tile/Cluster组织 4 Tile/Cluster×16 Cluster 10 Tile/Cluster×4 Cluster 12 Tile/Cluster×4 Cluster
后端网络 以太网600GB/s(12×400GbE) 两层以太网+AI Transport 两层以太网+AI Transport
Scale-up带宽 4800Gbps all-gather/1200Gbps any-to-any 2.8 TB/s(双向) 3.2 TB/s(双向)
主机接口 PCIe Gen5 x8(32GB/s) — PCIe Gen6 x16(128GB/s)
Pod规模 — 48 Maia/机架 96 Maia/机架
集群规模(scale-up域) — ~6k加速器 ~12k加速器

把视角从单芯片转移到机架,Maia 200到Maia 300的进步更加直观。表2是官方给出的机架级对比:HBM带宽提升5.1倍、容量提升2.7倍、FP4算力提升2.74倍,而机架功耗仅增加2.6倍——这正是”每瓦token产出”提升的物理来源。

表2 Maia 200与Maia 300机架级规格对比

机架规格 Maia 200 Maia 300 相对
Pod规模 48 96 2×
HBM带宽 341 TB/s 1,728 TB/s 5.1×
HBM带宽/瓦 5.3 GB/s 10.6 GB/s 1.95×
HBM容量 10.4 TB 27.7 TB 2.7×
FP4算力 485 POPS 1,324 POPS 2.74×
机架功耗 64 kW 167 kW 2.6×

三代Maia的架构哲学一以贯之,实现层面却有清晰的演进脉络。

  • Maia 100是思想源头,已经写下SDLA的全部关键词——把数据搬移当作推理公认瓶颈、用软件管理的大容量暂存内存取代硬件缓存、用硬件信号量驱动异步编程、用控制处理器向队列发命令、用gather-based GEMM把网络纳入数据流;但没有给这套做法命名,也没有形式化理论基础* Maia 200正式确立SDLA架构哲学,定义为可推理、可教学、可编程的体系——以数据管理为中心的分类法、DISA数据流指令集、SoC/Cluster/Tile三级控制、scratchpad取代cache的严谨论证,并用BF16峰值利用率99.69%、30% TCO节省的数字证明软件定义数据流并非纸上谈兵。
  • Maia 300是SDLA的性能递增,不推翻任何基础,把每一层推到新极限——多芯粒、NUMA、HBM4、12,288加速器superpod、动态带宽分配;7倍token吞吐与2.5倍性能/瓦的提升不靠堆功耗与面积,而靠围绕数据搬移、局部性、精度与内存效率的架构创新。

回顾三代,可以梳理出一条设计哲学的脉络:

  • 数据搬移优先 从”把数据搬移当作公认瓶颈”到SDLA分类法与DISA指令集,从scratchpad取代cache到NUMA跨芯粒局部访问,每一步都在减少数据搬移的距离、功耗与字节数。
  • 精度压缩 Maia 100首度量产MX格式并支持4/6/9比特;Maia 200给出FP4=2×FP8=8×BF16的吞吐比;Maia 300引入增强MXFP4与亚3比特存储格式。更窄的表示持续压缩每token的字节开销。
  • 网络即执行引擎 Maia 100让网络读写直接由SRAM服务;Maia 200把6,144加速器接成两层拓扑并让ATL协议进入SDLA微架构;Maia 300成为12,288加速器superpod的统一fabric。
  • 系统协同设计 从芯片到机架到数据中心,功耗、散热、网络、软件协同优化。Maia 300的50V母线、液冷80%热移除、动态功耗分配算法都是这一哲学的体现,而Maia 100早已把液冷使能列为第一代的设计约束。
  • 确定性编程 消除隐式缓存行为,让性能可预测、可规划、可复现。

Maia的故事,本质上是一个关于数据主权的故事:谁拥有数据的放置权、搬移权、转换权,谁就拥有加速器的效率上限。GPU把这些权力交给硬件缓存控制器,Maia把它们交给软件。当然,代价是增加了编程复杂度,但在编译器与运行时足够成熟时——正如Maia SDK的分层抽象所承诺的——软件拥有的主权越多,架构能榨取的效率越高。

Maia 100

发展背景

2023年11月,微软在Ignite大会上首次公开自研AI加速器Maia 100。Maia 100是面向大规模AI工作负载,专为在Azure上运行生产环境的OpenAI模型而设计。设计目标可以归为三类:

  • 垂直整合以优化性能与成本,包括软硬件协同设计、定制服务器板与量身定制的机架;
  • 第一代就要广泛部署,先把软件栈建起来、先把液冷支持打通;
  • 把成本作为一等目标——从HBM2E的选择上看得最清楚,微软刻意避开与NVIDIA、AMD争抢最紧俏的HBM产能,转而使用供应链成熟、成本可控的方案。

Maia 100由Sherry Xu(Partner Lead SoC Architect)与Chandru Ramakrishnan(Partner SW Eng Manager)在Hot Chips 2024上首次详细披露。一位SoC架构师、一位软件工程经理,这个组合本身说明Maia从第一代起就是芯片与软件并重的项目。

芯片规格

Maia 100采用台积电N5工艺,面积约820mm²,采用CoWoS-S 2.5D硅中介层封装;4个HBM2E共64GB容量、1.8TB/s带宽;L1/L2合计约500MB片上SRAM;峰值密集张量算力6bit 3 POPS、9bit 1.5 POPS、BF16 0.8 POPS;后端网络带宽600GB/s,由12条400GbE链路构成;主机接口PCIe Gen5 x8、32GB/s;设计TDP 700W,生产部署500W。

下图展示了Maia 100官方规格与承载它的液冷机架:

1.jpg

SoC架构

Maia 100采用Tile→Cluster→SoC三级组织,共16个Cluster、每Cluster 4个Tile,合计64个Tile。

下图展示了Maia 100 SoC架构,左侧为SoC全景,右侧为单个Tile的内部组成:

2.jpg

  • Tile 是最小自主计算单元,集成TTU张量单元(Tensor Unit)、TVP向量引擎(Vector engine)、TDMA数据搬移引擎(Tile Data Movement Engine)、TCP控制处理器(Tile Control Processor)、L1 SRAM与读写拷贝引擎。
  • Cluster 包含4个Tile,提供CCP集群控制处理器、CDMA集群数据搬移引擎、CSRAM(L2 SRAM)与Cluster NoC。
  • SoC 顶层包含High BW Data Mesh NOC连接全部Cluster、HBM2E PHY(4096 IO)、PAM4 112G SerDes PHY(对外12×400 PAM4)、PCIe PHY x16 Gen4(对外PCIe x8)、Chip Management与Security Management两个管理域,以及一个Image Decoder图像解码模块。

Fabric被显式拆成Data/MSG/CFG三类通道——”控制路径与数据路径分离”。

计算引擎

张量单元TTU支持宽数据类型,重点是MX格式,支持6比特与9比特计算。”9比特”对应MX家族中MXINT8加缩放因子等变体,”6比特”则是更激进的窄格式,两者的组合让Maia 100在精度—吞吐曲线上有了比同时代FP16/FP8更细的刻度。TTU构造为16×R×16单元:外层16×16是固定的输入输出粒度,中间的收缩维度R是可配置参数。中间维度R可配置是粒度损失与峰值性能之间的权衡:R越大,矩阵乘的算术强度越高、越接近峰值,但对小矩阵形状的适配性越差;R越小则相反。

向量处理器TVP是松耦合超标量引擎,自定义ISA,原生支持FP32与BF16;松耦合意味着它可以相对独立地推进自己的指令流,与TTU并行工作——TTU出结果的同时,TVP已经在做归一化或激活。

下图展示了Maia 100的ML专用架构要点:

3.jpg

TDMA支持不同的张量分片方案,分片策略由软件而非硬件决定;硬件信号量使能异步编程,后者是整个编程模型的基石,编程模型一节再展开。

存储结构

数据搬移是推理公认的瓶颈。围绕这个判断,Maia 100采取三项对策。

  • 一是数据压缩引擎加窄数据类型,支持4/6/9比特存储,用更少的比特承载同样的信息量,直接减少需要搬移的字节数。
  • 二是大容量L1/L2暂存内存且由软件管理,”Software-managed scratch pad, improve data utilization & power efficiency”。
  • 三是Mesh NoC拓扑为ML负载优化,16个Cluster以网格方式互联,路由节点分布在网格交叉点上,为规整的矩阵乘数据流提供高带宽、可预测的片上通路。

下图展示了Maia 100的数据搬移策略与Mesh NoC拓扑:

4.jpg

网络互连

Maia 100的互联网络选择以太网,不用InfiniBand,也不用自研私有fabric。高带宽以太网链路,提供4800Gbps all-gather/scatter-reduce性能,any-to-any达1200Gbps;定制RoCE-like协议,增强可靠性与负载均衡,支持AES-GCM加密——后端网络上的数据在传输层就是加密的,整机因此适合机密计算场景;统一网络同时承担scale-out与scale-up,并同时支持直连与交换两种连接方式;内置加密引擎保护用户数据。

下图展示了Maia 100的互连架构,右侧每个node内4颗加速器以直连链路两两全连接,node间经三层交换机互联:

5.jpg

这一步与微软2023年主导发起Ultra Ethernet Consortium(UEC)是同一套动作的两面:一边在标准组织里定义”AI时代的以太网”,一边在自家芯片上先把AI以太网做出来。

编程模型

Maia 100的软件栈定义了三代Maia共用的全部编程语义——后来的NEPAL、DISA、AI辅助内核优化,都是在这个框架上做加法。这一节也因此是理解Maia系列的关键。

SDK分层。Maia SDK的目标是让模型快速部署到Azure OpenAI Services。自上而下分为:

  • 框架集成 PyTorch for Maia,支持eager模式、graph模式与内核派发。
  • 开发者工具 Debugger、Profiler、Visualizer、模型量化与验证工具。
  • 编译器 Triton Compiler与API Compiler,两套编程模型各配一个。
  • Maia Kernel Lib cuBLAS类,核心ML计算内核的高度优化实现。
  • Maia Collectives NCCL类,通信内核(如AllReduce)的高度优化实现。
  • User Kernels 编译后的用户自研内核。
  • Maia Host/Device Runtime 负责内核调用与设备管理。
  • Maia Runtime Library 即HAL,暴露给软件的硬件原语集合。

下图展示了Maia SDK的分层架构:

6.jpg

“cuBLAS类+NCCL类”的并列说明微软对标的不是一个编译器,而是CUDA生态的库深度。

异步编程。Maia编程模型最核心的机制可以概括为一句话:控制处理器把异步命令发到队列,硬件线程在数据路径上执行这些命令,命令之间的依赖由硬件信号量强制保证。一条命令的结构是Command({in semaphores}, arguments, {out semaphores}),语义有四条:命令执行前等待in信号量,等输入数据就绪、输出缓冲可用;执行后发出out信号量,通知输出数据就绪、输入缓冲可复用;控制处理器在控制路径上发出命令;执行命令的线程在数据路径上直接同步。系统因此有两条执行流——控制处理器与硬件线程——二者通过信号量同步,效果是计算可以与内存搬移、网络传输重叠进行。

下图展示了Maia 100的异步编程模型,上为Cluster级控制流,下为Tile级控制流:

7.jpg

第三条与第四条的分离是设计精髓:控制处理器跑在控制路径上只管编排,真正干活的是数据路径上的硬件引擎(拷贝引擎、张量单元、向量处理器),它们各自盯住自己的in/out信号量,彼此之间不需要软件协调——依赖关系由硬件信号量直接握手,而不是靠CPU反复轮询。层级上,Cluster级控制处理器编排跨Tile的数据搬移,Tile级控制处理器编排Tile内部的计算与局部搬移;每一级都有自己的引擎、自己的SRAM、自己的控制处理器。这个结构与Maia 200的”SoC/Cluster/Tile三级控制”完全同构。

两种编程模型。Maia 100提供两层编程抽象,取舍极其清晰。Triton是开源DSL,硬件无关、可移植,编程粒度是”编程Maia Cluster、其余全自动”,信号量、内存、双缓冲都由编译器处理;Maia API是Maia专有的低层接口,需要为Cluster、Tile、Vector Processor分别写程序,一切显式。不同的编程方式实现Flash-Attention的代价:Triton约50行,Maia API约1000行——20倍的代码量差,换来的是显式信号量管理、显式内存放置、显式双缓冲,也就是对数据搬移的完全控制权。

下图展示了两种内核编程模型的对比:

8.jpg

这正是SDLA哲学在编程接口上的直接投影:架构把数据管理的权力交给软件,软件就必须付出显式管理的代价。

Gather-based GEMM。处理大规模分布式矩阵乘时,Maia 100选择用gather-based方法,而不是all-reduce based方法。收益有三:把post-GEMM的逐元素激活函数(如GELU)直接融合在SRAM里执行,提升处理速度与效率;计算与网络通信重叠,减少空闲;网络上传输量化数据(MX格式),减少传输量、降低延迟。支撑这一切的是具体的片上数据流设计:计算在Cluster内部完成,激活在片内累加而不把部分和写回HBM;权重从本地HBM加载;片上激活经NOC内环(Inner-Ring)gather,跨节点激活经9条节点内链路gather;L2 SRAM(CSRAM)缓冲高精度激活与中间结果,同时减少网络流量对HBM的访问;网络控制器ANC直接对CSRAM读写。

以下两图展示了gather-based矩阵乘的分区调度与片上数据流:

9.jpg

10.jpg

ANC直接读写CSRAM是Maia全系列网络设计的原点:网络收发的落地目标不是HBM,也不是软件缓冲区,而是集群级的CSRAM,于是”网络传输”与”片内计算”共享同一个存储层级,可以自然重叠、可以省掉一次HBM往返、可以让激活函数在数据落地的同一块SRAM上被融合执行。图9右侧的甘特图表述的就是TTU计算、TVP后处理、ANC数据搬移在时间轴上交错排布、彼此填充空隙,这正是双执行流加信号量同步想要达成的效果。

PyTorch与集合通信。微软对Maia 100”可替代性”的定义极其具体:用一行代码的改动,就能把PyTorch模型跑在Maia上——import torch.maia,再把device = "cuda"改成device = "maia"。PyTorch for Maia后端负责资源管理(HBM/SRAM/Stream/Event);eager模式用高性能内核库覆盖主流LLM/SLM使用的算子集合,提供最佳开发者体验;graph模式对接torch.compile生态,提供最佳性能。

下图展示了PyTorch集成示例:

11.jpg

集合通信方面,MCCL(Microsoft Collective Communication Library)作为Maia的TorchDistributed后端对标NCCL,覆盖send/recv/broadcast/all_reduce/reduce/all_gather/gather/scatter/reduce_scatter/all_to_all/barrier全部算子——包括Gloo缺失、对张量并行与专家并行至关重要的reduce_scatter与all_to_all。单卡性能决定模型能不能跑,集合通信库的完备性决定能不能上大集群。

下图展示了MCCL与Gloo、NCCL的算子覆盖对比:

12.jpg

开发者工具。工具链刻意与主流生态同构:maia-smi对应nvidia-smi负责设备管理;maia-gdb提供命令行调试,另有VS Code插件与Crash Dump分析;maia-prof做性能分析,maia-mon做资源使用监控;另有竞态条件分析、执行图分析、日志与性能分析库。最值得说的是虚拟平台maia-sim——Maia设备仿真器,让开发者在没有物理硬件的情况下本地开发内核。

下图展示了Maia SDK的开发者工具:

13.jpg

系统扩展

Maia 100的系统侧同样是垂直整合的产物:

  • 定制服务器板加量身定制的机架,而不是把PCIe卡插进标准服务器;
  • 液冷作为第一代的设计约束——500W部署功耗下就已经选择了液冷路线,为Maia 300的167kW液冷机架铺好了技术路径;
  • 内置加密引擎、Security Management域与AES-GCM加密的后端网络,使整机适合机密计算场景。

“设计700W、部署500W”的组合透出第一代产品的谨慎:硬件留出余量,生产功耗指标压低,换取更稳的能效与散热。

Maia 200

发展背景

如果说Maia 100是”先做出一块能用的自研芯片”,那么Maia 200就是”把这块芯片背后的方法论讲清楚,并把它推到规模化”。Maia的设计哲学可以浓缩为一句话:模型、应用框架、强化学习环境、内核、通信库、系统软件与定制芯片的协同优化,以实现per token、per watt、per dollar的最优解。Maia 200是这一哲学的第一个完整系统实现,由Saurabh Dighe(CVP, System & Architecture)和Artour Levin(VP, AI Silicon Engineering)主导设计。

SDLA架构哲学

数据管理分类法。Maia论文最具理论贡献的部分,是以1972年Flynn的指令-数据分类法为蓝本,提出一个以数据管理为核心的新分类体系。Flynn的原始分类以指令为中心,区分SISD/SIMD/MISD/MIMD;Maia的分类法关注的是数据如何被访问、如何被搬移,形成两个正交维度:数据访问范围(Local局部 vs Global全局)与数据搬移方式(隐式Load/Store vs 软件定义数据流)。两个维度交叉形成四个象限:

  • LSGA 加载/存储+全局访问,代表是标准多核CPU的共享内存抽象。
  • LSLA 加载/存储+局部访问,代表是GPU与Cerebras WSE的分布式暂存内存。
  • SDGA 软件定义+全局访问,代表是Intel I/OAT、AMD DirectPath I/O、ARM AMBA DMA。
  • SDLA 软件定义+局部访问,代表是Maia 200、AMD Xilinx XDNA、SambaNova RDU。

下图展示了这套分类法与Flynn分类法的对应关系:

14.png

GPU处于LSLA象限:它使用局部暂存内存(如共享内存),但数据搬移仍由Load/Store指令在计算流中隐式完成。Maia 200选择第四象限:数据搬移由独立的控制指令流显式编排,计算单元只能访问本地专用内存。这一选择意味着架构师不再需要为隐式缓存行为、替换策略和预取猜测买单,而是把数据管理的全部主权交给软件。

SDLA的三大编程原则。

  • 其一,暴露并行低层控制:向程序员、编译器和软件栈暴露数据搬移、转换、同步引擎的控制,实现最高性能与能效;这并非要求所有程序员都成为”ninja programmers”,而是提供选择权——编译器可生成高层抽象代码,追求极致性能的开发者可直接编排数据流。
  • 其二,管理分布式专用内存系统:允许程序员管理片上及pod内的分布式专用内存,架构师可据此优化内存并用专用引擎加速数据搬移,而非用通用缓存逻辑统一管理。
  • 其三,提供基本确定性的性能:消除隐式缓存带来的不确定性,使多次运行之间性能一致,优化调度可预先规划;这对Transformer注意力等内核尤为重要,数据流在编译期可完全规划。

与GPU编程模型的差异。经典GPU只提供单一编程层级(如CUDA),调度与细粒度DMA控制由硬件/固件实现,普通程序员无法触及。NVIDIA Hopper引入的Tensor Memory Accelerator(TMA)可被视为一种走向SDLA的尝试——提供专用内存与数据搬移引擎供程序员编排——但TMA与GPU的SIMT/warp系统交互复杂,导致Hopper的WGMMA在Blackwell中被推翻重设计为UMMA,破坏了向后兼容性。SDLA从第一天起就把显式、局部化内存编程作为基础原则,而非后补的补丁。Maia提供两层编程抽象:底层汇编(类似SASS)与高层类CUDA代码(通过LLVM类PTX抽象),控制路径用C/C++编程,可编程数据路径用数据流指令集架构DISA(Dataflow ISA)执行。

DISA。DISA定义了一条数据流指令的标准结构:同步前置条件(pre-conditions)→宏指令(macro-instruction)→同步后置条件(post-conditions),前置与后置条件通过硬件信号量实现。每条数据流指令可配置等待最多2个信号量,执行完成后可信号最多2个信号量;所有等待/信号条件有可编程的递减/递增值,命令可链式连接实现复杂同步。这一设计使数据搬移与计算之间的细粒度同步完全由硬件信号量驱动,无需软件轮询。这套ISA的语义与Maia 100的命令结构同源,Maia 200的贡献在于把信号量数量、递减/递增幅度、链式连接等细节明确下来,并把控制层级从两级扩展到三级。

下图展示了DISA的抽象机器模型:

15.png

SoC架构

Maia 200的SoC采用三级层级结构,每一级都有独立的控制处理器、内存和DMA引擎,体现了SDLA中”控制与数据路径分离并并发运行”的原则。

下图展示了Maia 200 SoC架构:

16.png

Tile 是最小自主计算单元,集成TTU张量引擎负责高吞吐矩阵乘与卷积、TVP向量处理器负责可编程SIMD运算、TSRAM作为Tile级暂存内存直接附加于两者、Tile DMA子系统在不阻塞计算流水线的情况下搬运数据(内置Reshaper引擎支持线速格式转换)、TCP控制处理器运行底层控制代码编排TTU与DMA工作,以及硬件信号量引擎实现数据搬移与计算之间的细粒度同步。这个组成与Maia 100的Tile在结构上一一对应,区别在于规模、频率与精度支持的扩展。

Cluster 每颗SoC由4个构成,每个含10个Tile。Cluster级提供CSRAM集群级共享存储跨Cluster内所有Tile可访问、Cluster DMA在CSRAM与共封装HBM之间搬运数据、CCP集群控制处理器协调Cluster内多Tile执行的控制与同步。对比Maia 100的16 Cluster×4 Tile,Maia 200改为4 Cluster×10 Tile:Cluster数量减少、单Cluster内Tile数量增加,意味着Cluster内的局部性被放大——CSRAM要服务的Tile更多了,Cluster间的NoC跳数压力下降了。这是”把数据留在更近的地方”这一原则在拓扑层面的体现。

SoC 顶层包含Device Control块(含DCP设备控制处理器、SRAM、专用DMA与Sync引擎),以及三层互连:Tile 1D mesh NoC连接Cluster内的Tile;GNoC全局NoC连接所有Cluster、HBM控制器、PCIe主机系统与ANC;Utility fabric全连接所有同步块以实现最低延迟的相互通知。NoC在物理上分割为逻辑高带宽数据平面与专用控制平面,数据流量与控制/遥测流量物理隔离——这正是Maia 100的Fabric (Data, MSG, CFG)三分通道的延续与细化。数据NoC支持QoS,可优先处理不同流量;NoC还支持多播,例如从HBM取一次张量后扇出到多个CSRAM。Maia 200对Tile和SRAM均采用冗余设计以提高良率。

计算引擎

TTU张量引擎是Maia 200的核心计算单元,针对矩阵乘与卷积优化,原生支持BF16、FP8、FP6、FP4四种精度,并支持混合精度模式(如FP8激活×FP4权重)。其关键吞吐比十分醒目:FP4吞吐为FP8的2倍、BF16的8倍;FP6与FP8吞吐相同,但通过gating实现显著能耗节省。TTU以blocked格式读取32×K×32矩阵,输出FP32或BF16累加结果,并支持组合为符合OCP标准的MXFP数据类型(E8M0缩放因子、组大小32)。把TTU的粒度从Maia 100的16×R×16提升到32×K×32,外层粒度翻倍意味着单次矩阵乘的数据块更大、算术强度更高、对大规模GEMM更友好;Maia 100当年选16×R×16的理由正是”粒度损失与峰值性能之间的权衡”——第一代保守地保留了更多灵活性,第二代则明确押注大规模GEMM。

TVP向量处理器处理TSMEM数据,可对TTU的矩阵输出做后处理;TTU也可从TSMEM读一个输入操作数以支持高效操作链,形成TTU→TVP的直接数据通路。TVP支持从INT8到FP32的丰富数据类型,处理softmax、低精度数据转换及其他非矩阵运算。

Maia 200还区分存储数据类型与计算数据类型:窄精度格式(如FP4)作为存储格式以减少传输与存储位数,通过Reshaper单元在加载时线速上采样为计算格式。Reshaper支持随机舍入或最近舍入,并可全带宽加载8:16或4:16稀疏张量到/从密集张量。”存储格式窄、计算格式宽”的分离,正是Maia 100时代”数据压缩引擎+窄数据类型”的正式化版本——只是把压缩的粒度从”引擎”细化到了”格式”,并与线速转换绑定在一起。

存储结构

SDLA架构最反直觉的决策之一,是用软件管理的暂存内存取代硬件管理的缓存。这并非草率之举,论文给出了严谨的理论论证。对于数据无关算法(data oblivious algorithms)——所有内存访问与数据流可基于编译期可得的少量参数计算——AI负载几乎天然满足这一条件:多数AI计算图是固定内核(矩阵乘、SoftMax、非线性等)的静态组合,整体数据流可预先规划;少数例外(连续动态批处理、MoE路由、提前终止)可用参数化方式在粗粒度控制层处理。

理论上,LRU缓存对于单位缓存行、大小为scratchpad两倍的缓存,其缺失率最多为最优离线管理方案的2倍。实践数据显示,手动从cache移植到scratchpad的HPC负载平均几何加速为13%。更关键的是硬件开销:cache需要tag内存阵列与地址重映射逻辑,额外面积与能耗开销约30-35%,访问延迟增加10-15%,实际的面积-时间与动态能耗总开销高达43%。

Maia 200通过显式编程的SDLA内存架构,使内存占用芯片空间低于20%——远低于许多CPU的缓存面积占比。所有SRAM与HBM均有ECC保护,可按需擦除防位翻转;编排数据移动的专用DMA引擎仅占可忽略的裸片面积;分层内存由TSRAM(Tile级暂存)、CSRAM(Cluster级共享)与共封装HBM构成。官方给出的总量是272MB片上SRAM,按多层级划分为Cluster级CSRAM与Tile级TSRAM;从SoC架构图可读出具体容量——CSRAM 35MB/Cluster、TSRAM 3MB/Tile,折算全芯片为4×35MB加4×10×3MB,即140MB CSRAM加120MB TSRAM,合计260MB,与272MB的差额在设备控制块存储与冗余阵列上。Maia 100的约500MB L1/L2 SRAM到Maia 200的272MB片上SRAM,总量是下降的,而HBM带宽从1.8TB/s提升到7.1TB/s近4倍。这组数据清楚表达了SDLA的内存选择,片上存储不必追求容量最大化,够用即可;关键是用更快的HBM与更聪明的软件放置策略,把”必须搬移的数据”减到最少。

数据搬移与控制

数据搬移架构围绕分层DMA引擎与分层NoC构建。系统包含数十个不同DMA引擎,各司其职:

  • Tile DMA负责TSRAM↔CSRAM细粒度传输,内置Reshaper引擎支持1D/2D/3D/4D张量strided搬移、scatter/gather、线速数据类型转换与稀疏支持;
  • Cluster DMA负责CSRAM↔HBM或跨Cluster搬运;
  • Network DMA管理片外链路的收发路径,与ANC紧密耦合。

NoC的创新功能包括HBM→Cluster高效广播(张量从HBM取一次后经multicast扇出到多个CSRAM)、局部高带宽Cluster流量(簇内完成层内归约、暂存交换、小集合通信)、Tile-to-Tile SRAM访问(Tile DMA与向量单元可直接读写对等Tile的SRAM)、QoS(优先处理同步信号等低延迟关键消息)与fail-safe管理平面(隔离控制/遥测流量与数据路径)。内存大小按Little定律确定,以最大化数据复用、最小化数据移动与寻址开销。

控制路径是完整SDLA微架构的最后一块拼图。Maia 200结合SoC、Cluster、Tile三级控制,各级含多个控制处理器,运行用户C/C++程序,通过DISA编排数据路径。控制处理器通常略超前于数据流运行,提前配置引擎——较大的暂存内存支持这种异步性,允许控制处理器预存足够工作使单元永不等待。层次化架构优势的典型示例是MoE路由:路由函数决定激活发送目的地时,TVP在TSRAM中评估TTU路由矩阵乘法的输出,在少数周期内将结果交给TCP,启动向各目的地的预配置数据移动操作;此数据依赖流可通过信号量与DMA操作提前建立,DMA从TVP获取scatter-gather操作——全程无需软件轮询介入。这个”控制处理器略超前、靠大暂存内存吸收异步性”的模式,在Maia 100里被描述为”控制处理器发出异步命令、硬件线程在数据路径上直接同步”;从两级控制到三级控制,从”控制处理器+队列”到”DISA+链式信号量”,Maia 200把Maia 100的工程机制变成了一套可推理的编程契约。

网络互连

当模型跨越多个加速器时,网络成为执行引擎的一部分。Maia 200采用全以太网方案,将片上SDLA的数据流编排理念延伸到跨芯片通信。它集成28个400G以太网AI网络控制器ANC,提供1.4TB/s全双工带宽,完全集成进SDLA微架构——消除外部NIC的功耗与成本开销,网络占系统成本低于20%。对比Maia 100的12×400GbE,端口数翻倍有余。

ATL协议。ANC运行于无损以太网(PFC)L2网络上的微软自研ATLv2协议,其设计后来影响了UEC的AI Base Transport Profile标准化。关键特性包括标准L2以太网帧与L3 IP路由头、全端到端AES-GCM-256加密(延续Maia 100的加密后端网络)、基于窗口的拥塞控制、ECMP+逐包负载均衡(packet spraying)、选择性重传(克服Go-Back-N的已知限制),以及网络中及GNoC到HBM的QoS。

下图展示了ATL的接收端驱动消息方案:

17.png

两层拓扑。Maia 200的网络拓扑结合固定直连链路与交换连接,是2×2 1D Hamming Mesh的特例:FCQ(Fully Connected Quad)由4个加速器通过无交换机直连链路组成最小全连接组——这正是Maia 100拓扑图中每个node内4颗加速器两两全连接的结构,Maia 200将其保留并正式命名;交换层则将连接扩展至最多6,144个加速器。28个ANC中,20个用于固定链路,8个连接交换网络;交换网络由4个相同平面组成。

下图展示了Maia 200的两层网络拓扑:

18.png

MCCL实现了两种算法:Direct connect直接将数据分片发给目标端点,消息深度为1,延迟最低,适合小传输;Ring建立逻辑1D环流水线发送,适合大传输。8芯片Allgather实测达到延迟界限的78%、带宽界限的94%。带宽平衡能力也值得注意:托盘设计看似非对称(南北向300GB/s,东西向与对角350GB/s),但交换部分可灵活用50GB/s加强南北向,实现四方向全平衡350GB/s,总平衡网络带宽1.4TB/s。

编程模型

Maia 200没有推翻Maia 100的编程模型,而是把它明确化、命名化并推向规模,新增内容主要有两点。

  • 一,低层路径获得正式名称:Maia 100时代的”Maia API”在Maia 200里被称为NEPAL(Nested Parallel Language),一个基于C++的低层语言,用于显式控制数据搬移、SRAM放置与并行执行以逼近峰值利用率;语义未变,但有了可被文档化的名字。
  • 二,单芯片Tile规模从64颗收敛到40颗,控制层级从两级变为三级,每级均有多个控制处理器运行用户C/C++程序。

从Command到DISA。把Maia 100的命令结构与Maia 200的DISA并排看,语义完全同源:in semaphores对应同步前置条件,arguments对应宏指令,out semaphores对应同步后置条件,同步原语都是硬件信号量。Maia 200新增的是细节与规模:前置条件最多等2个信号量、后置最多发2个,递减与递增幅度可配置,命令可链式连接实现复杂同步;控制粒度从Cluster/Tile两级扩展到SoC/Cluster/Tile三级。可以说Maia 100是数据流执行的工程验证,Maia 200是它的理论形式化与规模化——前者证明这套机制能跑通生产负载,后者把它写成ISA,让编译器与程序员都有了明确的目标。

性能验证

Roofline矩阵乘法。测试6,143个代表生产推理负载的矩阵乘法shape,基准系统每Cluster启用9个Tile,运行于2GHz。结果令人印象深刻:BF16计算受限区效率最高达峰值99.69%、内存受限区达峰值带宽51.4%;FP8计算受限区达峰值96%、内存受限区达56%。

以下两图展示了6,143个矩阵尺寸的BF16与FP8矩阵乘法性能:

19.png

20.png

端到端生成式推理。以公开模型Qwen 2.5 7B(28层,内维3,584,FFN中间维18,944,GQA 4个KV头)为测试对象,聚焦内存受限的token生成阶段:16,384个前序token自回归生成第16,385个token,KV cache大小939.52MiB。由PyTorch调用标准内核(无额外融合)的直接实现达2,434 tokens/s,超过估计最大性能的70%。这表明SDLA不仅在单矩阵乘法或集合操作接近峰值,对复杂端到端实际推理负载也表现优异。

下图展示了8颗Maia 200(两个托盘)的Allgather基准结果:

21.png

在系统级,相同机架功耗预算下运行MAI-Thinking-1模型,Maia 200相比微软机群中最新一代GPU实现超过40%更高的token生成吞吐,以及约30%更好的性能/美元;相比机队其他AI加速器节省30% TCO与15%能耗。

系统扩展

Maia 200采用共封装HBM,硬件层与Azure第三方GPU系统协同设计,遵循标准化机架、供电、机械架构,可与异构加速器在同一数据中心空间共存。散热设计模块化:支持风冷与液冷部署,包括第二代液冷sidecar。每个托盘含4颗SoC,深度集成Azure原生控制平面,继承生命周期、可用性、可靠性保证,支持自动化节点上线、安全原地升级、多机架协调维护。

下图展示了Maia 200托盘与风冷部署机架:

22.png

与Maia 100相比,Maia 200在系统侧最重要的变化是从”定制整机架”走向”标准化机架”:第一代要求量身定制的机架与服务器板,第二代则回到标准机架与供电架构、允许与异构加速器共存。这是规模化部署的必然选择——自研硅要跑量,就不能要求数据中心为它单独改造。

Maia 300

发展背景

如果说Maia 200证明了SDLA架构的可行性与效率优势,那么Maia 300则将这一架构推向云规模智能体推理(cloud-scale agentic inference)的极限。智能体负载——多步执行、长上下文解码、工具调用——正在改变推理的计算特征与算术强度,对局部性、内存容量/带宽、通信扩展性提出了全新要求。Maia 300由Rani Borkar和Saurabh Dighe主导设计,于2026年10月发布,是Maia项目的第三代产品。它的核心理念可以用一个词概括:系统级优化——软件、芯片、物理机架基础设施、网络与数据中心协同设计为一个统一平台。

综合来看,三代Maia的软件栈是一条不断把”显式控制”制度化的线索:Maia 100让软件能管理暂存内存与信号量;Maia 200用ISA与分类法把它变成契约;Maia 300引入AI辅助优化,试图降低遵守这份契约的人力成本。

多芯粒架构

Maia 300最大的架构跃迁是从Maia 200的单体裸片转向多芯粒设计。这一决策的逻辑清晰而务实:计算芯粒放在TSMC N2P上,先进工艺对计算的性能/瓦收益最大;内存控制器与I/O芯粒分离到针对带宽、连通性与成本优化的专用工艺上。一颗Maia 300封装内包含一颗主计算芯粒、两颗I/O芯粒、四颗内存芯粒与八个HBM4堆栈。

下图展示了Maia 300芯片封装实物:

23.webp

这一策略的效果是三重的:增加计算面积占比,把更多硅片面积用于计算而非被I/O和内存控制器占据;各功能使用最优技术,计算用最先进工艺、I/O用成熟高良率工艺;SDLA管理跨封装局部性,软件定义的数据流编排无缝跨越芯粒边界。相比Maia 200,Maia 300的系统计算FLOPs提升36%、HBM带宽提升2.5倍、scale-up带宽提升14%。

从Maia 100(N5单体820mm²)到Maia 200(N3P单体近光罩尺寸)再到Maia 300(N2P计算芯粒+专用I/O/内存芯粒),三代芯片的形态演进给出一个清晰的规律,当计算、I/O、内存控制器各自的最优工艺开始分化,多芯粒就成了必然。

下图展示了Maia 300 SoC架构:

24.webp

Maia 300单芯片的tile组织更加规整——每Cluster 12个Tile、共4个Cluster,且封装内直接集成32条400Gbps以太网链路(Maia 100为12条、Maia 200为28条),网络进一步深度嵌入SDLA微架构。

存储结构

Maia 200的SoC采用统一内存访问UMA组织,所有Cluster共享相同的HBM访问延迟与带宽。Maia 300将其演进为非统一内存访问NUMA组织,跨芯粒分布:每个计算芯粒区域有就近的HBM,SDLA的显式控制将数据放在使用它的计算旁,数据旅行距离更短,从而减少搬移能耗;软件可感知NUMA拓扑,将热数据放在本地HBM、冷数据放在远端。

内存层级也从Maia 200的6个HBM3E堆栈升级为8个HBM4堆栈,单芯片带宽从7.1TB/s提升至18TB/s、容量从216GB提升至288GB。TSRAM与CSRAM的两级结构延续下来,。更关键的是低精度格式的进化:Maia 300引入增强MXFP4与亚3比特存储格式,更窄的表示减少内存占用与数据搬移量,使系统能在相同功耗包络内支持更大模型、更长上下文、更多并发会话。此外,Maia 300的ISA向量引擎还针对softmax、低精度数据转换等关键推理内核增加了优化。

网络互连

Maia 300将网络作为SDLA架构的原生扩展。AI网络控制器实现了基于以太网的定制AI传输,两层网络构建高性能superpod,支持最多12,288颗加速器;软件看到的是统一通信模型——向superpod内任意加速器直接搬移数据的语义完全一致。

下图展示了Maia 300的两层scale-up网络:

25.webp

半交换机内机架拓扑与光学跨机架。每颗加速器将scale-up连接在直连链路(连接同一托盘内的两颗加速器)和交换路径之间分配。机架内全部96颗加速器经交换fabric形成一个pod,全部使用电气链路以降低功耗与成本;第二层则用光学链路连接最多128个机架组成superpod,光学提供跨pod扩展scale-up网络所需的距离。这个”托盘内4颗直连”的结构从Maia 100起就存在,Maia 200命名为FCQ并保留,Maia 300沿用并扩到96颗pod。

统一fabric与动态带宽。Maia 300最具架构意义的设计是单一fabric而非分离的scale-up与scale-out域:集成网络控制器与专用AI传输在pod内与跨superpod使用相同通信语义,加速器可直接向superpod内任意加速器发起通信,无需主机或独立scale-out NIC介入。三个延迟域清晰分层:直连加速器对(最低延迟)→pod内加速器(一跳)→跨pod加速器(三跳)。机架内与跨机架带宽分配不再是固定设计参数,而是基于作业打包的运行时可调参数;当只有部分加速器跨机架时,单颗加速器可向superpod内任意对端驱动最高750GB/s。”同一张网同时承担scale-up与scale-out”这个想法最早出现在Maia 100的”Unified Network”里,三代之后,它从一句设计宣言变成了12,288颗芯片的统一fabric。

灵活作业放置、分离式推理与跨机架弹性。张量并行保持在96加速器机架内,更宽的专家并行与流水线并行跨机架;作业可跨机架边界使用容量,减少负载尺寸与物理机架配置不匹配时的闲置加速器。prefill与decode worker可独立尺寸化与放置,prefill计算完成后,KV状态直接通过同一scale-up fabric搬移到选定的decode worker,而非通过主机中介的scale-out路径离开域。加速器不可用时,调度器可透明使用另一机架的热备容量,负载留在scale-up fabric上,以额外路径延迟换取可用性与资源利用率。

以下两图展示了跨机架作业放置与跨机架热备:

26.webp

27.webp

系统扩展

在数据中心规模,效率取决于在固定功耗与散热包络内能部署和运营多少有用基础设施。Maia 300的机架协同设计服务器布局与密度、网络拓扑与交换、供电与散热、以及高级遥测,实现相比Maia 200 2.5倍以上的性能/瓦。其系统层级从硅片到集成托盘再到机架与散热,逐级协同。

下图展示了Maia 300的系统三层级:

28.webp

Maia 300在167kW机架内集成96颗加速器,分布在24个四加速器托盘中,加速器密度翻倍。每个托盘包含四颗加速器及主机处理器、内存、存储与散热,取消独立主机托盘释放了机架空间,允许功耗与热资源作为统一系统管理。6个高基数交换ASIC集成到3个液冷交换托盘中,提供全部机架本地连接;50V母线架构与双路供电、集成机架自动转换开关(ATS)保证高可用;液冷移除超过80%的机架热负载(加速器、稳压器、交换ASIC),风冷支持内存、存储等辅助组件,可直接连接设施水或通过热交换器在风冷环境中散热。

下图展示了Maia 300机架架构与两代机架级规格对比:

29.webp

机架与行级功耗遥测与数据中心运营连接,使工作点可根据可用功耗与散热容量管理;数据驱动算法动态分配功耗并优化负载放置与部署密度,提升利用率与每瓦token产出。可靠性、可用性、可服务性(RAS)、可观测性、可调试性、故障处理与冗余均基于部署经验强化。对比Maia 100的”设计700W/部署500W”与”液冷使能”,Maia 300的167kW液冷机架在三年内完成了从”预留能力”到”依赖液冷”的跨越——第一代的技术选项,第三代成了刚性前提。

性能总结

Maia 300相比Maia 200实现最多7倍的机架级token吞吐提升,以及2.5倍以上的性能/瓦。它带来更高的每瓦内存带宽与每机架翻倍的加速器数,同时基于SDLA软件定义数据流架构与两层scale-up网络,支持从内核到数据中心规模的单一编程模型。

需要强调的是,Maia 300并未通过简单堆砌功耗与面积换取性能——单芯片FP4算力仅提升36%、TDP从750W增至950W,而HBM带宽、容量、每瓦效率与网络规模的提升更为显著。性能提升的真正来源,是围绕数据搬移、局部性、精度与内存效率的架构创新。

参考文献

  1. S. Xu, C. Ramakrishnan, “Inside Maia 100,” Hot Chips 36 Symposium (HCS), 2024. 幻灯片
  2. S. Xu, C. Ramakrishnan, “Inside Maia 100: Revolutionizing AI Workloads with Microsoft’s Custom AI Accelerator,” Microsoft Azure Infrastructure Blog, Aug. 2024. Link
  3. Microsoft, “Azure Maia for the era of AI,” Microsoft Azure Blog, 2024. Link
  4. S. Xu, M. Heddes, J. Peng, et al., “Maia 200: A Software Defined Dataflow System for Large-scale AI Acceleration,” arXiv:2608.24664, Aug. 2026. arXiv
  5. S. Dighe, A. Levin, “Deep dive into the Maia 200 architecture,” Microsoft Azure Infrastructure Blog, Jan. 2026. Link
  6. S. Xu, P. Ranjan, T. Hoefler, “Maia 200: Software-defined dataflow and all-Ethernet networking for efficient inference on Azure,” Microsoft Azure Infrastructure Blog, Aug. 2026. Link
  7. Microsoft, “Maia 200: The AI accelerator built for inference,” Microsoft Blog, Jan. 2026. Link
  8. R. Borkar, S. Dighe, “Deep dive into Azure Maia 300 architecture,” Microsoft Azure Blog, Oct. 2026. Link
  9. Microsoft, “Azure Maia 300 product announcement,” Oct. 2026. Link
  10. Ultra Ethernet Consortium, “Ultra Ethernet Specification.” Link
  11. M. J. Flynn, “Some Computer Organizations and Their Effectiveness,” IEEE Transactions on Computers, vol. C-21, no. 9, pp. 948-960, Sep. 1972.
]]>
Cerebras | The Largest Chip http://zhifeiding.github.io/programming/chip/2026/09/13/Cerebras-The-Largest-Chip/ 2026-09-13T00:00:00+00:00 http://zhifeiding.github.io/programming/chip/2026/09/13/Cerebras-The-Largest-Chip

“海到无边天作岸,山登绝顶我为峰。”

前言

别人把晶圆切成几百颗小芯片再想办法连起来,Cerebras反着来:一整片晶圆就是一颗芯片。山不必登,它自己就是峰。

Cerebras使用整个晶圆制造一个巨大的芯片WSE - Wafer Scale Engine,可以从下图中直观的感受到WSE和其他芯片在晶体管数量上的差异:

0.png

Cerebras的WSE通过将wafer 作为一个芯片,获得了巨大的片上访存带宽,但同时也导致系统供电,芯片良率等复杂问题,本文针对这些问题,从Cerebras第一代WSE1的系统结构,WSE2的计算架构和系统设计,WSE3系统设计到第四代系统CS-4来讲解分析。具体章节安排如下:

  • 第一章简单介绍采用Wafer Scale的一些背景
  • 第二章介绍WSE1的系统设计和编译流程
  • 第三章通过WSE2来介绍芯片内部计算架构和CS2
  • 第四章介绍WSE3芯片及系统CS3
  • 第五章介绍CS4
  • 第六章结合公开的CSL示例代码,讲Cerebras的编程模型

概述

一般而言,每一代芯片的性能提升会被归类于架构上的优化,但实际上,工艺进步,时钟频率,芯片面积等都会对芯片性能产生影响,过去五十年来对芯片性能提升的相对贡献如图所示: 1.png 可以看到工艺进步对性能影响其实是远远超过其他因素。芯片面积增加,为所需功能的晶体管数量提供了更多的硅面积。在当前技术下,存在一个最大尺寸限制。下图显示了一些比较有影响力的芯片面积增加的趋势曲线。 2.png 芯片尺寸增长放缓的原因包括:

  • 随着工艺进步,增加光罩尺寸(即晶圆的最大面积)变得更加困难,因为减少激光波长的同时维护激光聚焦在更大的区域越来越困难。这是芯片尺寸增长放缓的主要原因。
  • 随着芯片尺寸的增加,每个芯片的良率会降低。虽然有几种不同的缺陷分布模型——例如,墨菲模型、泊松模型、二项式模型、摩尔模型和 Seeds 模型 - 所有模型的共同点是,随着芯片尺寸的增加,良率呈指数级下降。虽然将芯片尺寸扩展到整个晶圆可以最大限度地提高计算和带宽,但是良率模型基本上是零良率,因为无缺陷晶圆是极其罕见的事件。晶圆级设计是一个非常具有挑战的任务。

业界在此之前有过多次尝试:

  • 1980 年,Trilogy Systems 尝试使用晶圆级集成(WSI)构建与 IBM 兼容的大型机,生产 2.5 英寸 (in) 的单芯片。这家公司由大型机时代的传奇工程师Gene Amdahl创立,拿到当时创纪录的2.3亿美元投资,但Trilogy Systems遇到了几个障碍,最终导致了这个早期晶圆级集成的努力失败,五年后公司解散。
  • 2015 年,Cerebras Systems 开始设计晶圆级芯片,WSE于2019年首次推出,成功克服了光罩尺寸限制。

WSE-1

在2019年,Cerebras推出了其第一代晶圆级芯片Wafer Scale Engine,采用台积电16nm工艺,包含1.2T晶体管,面积46225平方毫米。与此同时,当时最大的GPU则是NVIDIA的V100,包含21.1B晶体管,面积815平方毫米,具体对比如下所示: 3.png WSE-1一共有400,000 个AI 核心,芯片有 18GB 片上内存、9PB/s 内存带宽和超过 100Pb/s 的互连带宽。

这40万个核组织成84个tile(7×12排布),每个tile约4,800个核。核的官方名称是SLA core(Sparse Linear Algebra core):为深度学习的稀疏线性代数设计,但仍是可编程的通用处理器。按Cerebras在白皮书中给出的口径,深度学习训练数据里50%到98%是零值,SLA核的数据流机制让零值不触发计算,直接在发送端被过滤掉。

Cerebras CS-1 Wafer-Scale AI System

CS-1是由WSE-1组成的AI系统,前视图如下图所示,左上角是 I/O 模块,下方是 12 个电源。右边是泵模块,下面是风扇模块。 4.png 在俯视图中,可以看到 12 个 100GbE 链路朝向 15U 机箱的前部。在左侧有两个泵模块,帮助芯片保持冷却。 5.png 以下是泵模块的机械结构: 6.png

每个 Cerebras CS-1 的设计功率高达 20kW,其中约15kW给芯片本身,4kW给冷却子系统,1kW是电源转换损耗。在 15U 机箱中,每 U 仅为 1.3kW/,大约和 2U 4 节点服务器或 GPU 计算服务器的功率相当,整机只占标准机架的三分之一。该系统利用 12 个 100GbE 连接提供 1.2Tbps 的外部带宽。

Cerebras 的软件堆栈支持从 Tensorflow 和 PyTorch 等高级框架到将模型编译到晶圆级芯片上并运行。 7.png

WSE-2

第二代 WSE-2 于 2021 年发布,采用台积电7nm工艺,面积 46,225 mm^2(215mm × 215mm),包含 2.6 万亿晶体管。一个晶圆级片上网络可连接 850,000 个内核,片上存储40GB,内存带宽20PB/s,片上结构(fabric)带宽220Pb/s,比当时最大的GPU大56倍。峰值算力FP16 dense为7.5 PFLOPS,FP16 sparse为75 PFLOPS(按10倍稀疏加速折算)。85万核 × 4个FP16 FMAC × 每周期2次运算 × 1.1GHz,正好是7.5 PFLOPS。集群架构利用 CS-2扩展能力 ,以提供简单的数据并行扩展到更大的模型和更大的集群。 8.png

WSE-2的构建方式很有意思:先设计一个传统意义上可切割的die,每个die含 10,156 个核(66 × 154 排布),尺寸 17mm × 30mm,即 510 平方毫米;之后不进行切割,而是在直径300 毫米圆形晶圆内切割出最大的正方形,最终以 12 × 7 的排布容纳 84 个芯粒,合计 850,000 个核。 9.png 不同于之前的 Trilogy 使用的冗余门设计,CereBras的冗余单元是PE;WSE-2中约1%的PE作为冗余备份,以便“修复”有缺陷的 PE。 10.png

Cerebras 核是一个小核心设计, 核心物理尺寸为228μm × 170μm(约38,760μm^2),如图所示。在TSMC 7nm工艺下,一半的面积是 48 kB 的SRAM,另一半是逻辑。整个内核以 1.1GHz 时钟频率运行,峰值功耗为 30mW。 11.png 每个小核都有48 kB的本地SRAM,分成8个单端口bank,每个bank都是32位宽,每个周期可以支持 256 位访问;小核每个周期支持两个 64 位读和一个64 位写, 一共 192 位访问。所有的内存都是核内单独寻址,没有传统意义上的共享内存。所有核之间的共享是通过互联网络完成。除了本地SRAM,还有一个256B 软件管理的缓存,用于经常访问的数据结构。 12.png 张量操作在 64 位数据路径上执行,由四个 16 位融合乘法累加组成(FMAC),支持FP16 和BF16,可选支持FP32。为了优化性能和灵活性,指令集架构将张量作为一等的操作数,类似通用寄存器或内存。下图显示了一个示例在 3D 和 2D 张量上操作的 FMAC 指令操作数。 13.png 小核使用数据结构寄存器 (DSR) 作为指令的操作数,有 44 个DSR,每个 DSR 都包含一个描述符,其中包含指向张量和其他关键信息的指针,如张量的长度、形状和大小。利用DSR,硬件架构可以原生支持最多4D 张量。

小核同时支持八个张量操作,称为微线程,硬件上可以逐个周期切换。调度器持续监控正在处理的张量的输入和输出的可用性,选择一个符合条件且资源可用的微线程。每个微线程也分配了优先级,因此,如果多个微线程有资格运行,调度器将选择优先级更高的微线程。每个微线程都可以完全访问所有资源,包括核中的寄存器和内存,没有任何静态分区。

互联使用 2-D 网络拓扑,如下图所示。其中每个核有一个Fabric Router。Fabric Router采用的五端口设计,每个都是32 位双向接口;使用低缓冲的无损流量控制,实现核间的单时钟周期延迟。基本数据包是单个 16 位数据元素(FP16 或 BF16)。除了这些 16 位数据外,还有 16 位数据的控制信息,构成 32 位细粒度包。为了进一步优化结构设计,它使用完全静态的布线,效率高且低开销。每个内核有 24 个本地静态可以配置的路由,称为Color。所有Color之间是非阻塞的,通过时间多路复用到相同的物理链路上。在整个网络范围内,这些本地独立Color被组合在一起支持整个核心阵列大量路由。此外,还使用每种Color对于各种不同的数据和控制通信,因此固定的 24 种Color使更多不同消息或任务总数。每个交换矩阵路由器中设计有本机广播和组播功能 14.png

不同于传统的封装之后安装到PCB,向晶圆提供小于 1 V且接近 20,000 A 的峰值电流,同时保持良好的稳压是艰巨的挑战。Cerebras采用分布在晶圆表面的 300 多个单独的电压调节模块 (VRM),每个WSE-2使用多个 VRM 确保电源分配冗余,并可单独控制每个电源域。 15.png

16.png Cerebras 开发了 MemoryX 技术来解决 CS-2 中超出 WSE-2 的内存容量问题。Cerebras CS-2 集群支持 1.5TB 和 12TB MemoryX 单元:

  • 支持4TB-2.4PB容量
  • 支持2000 亿 - 120 万亿个权重,带优化器状态
  • 支持DRAM 和闪存混合存储 MemryX用于权重更新/优化器的内部计算,处理智能流水线以掩盖延迟,可扩展到极端模型大小,独立于计算的容量扩展 17.png

模型权重存储在MemoryX内部的DRAM 和SSD中,权重流式传输到CS-2 系统,一次一层,并以1.2 Tb/s的带宽流式传输到 CS-2 中;CS-2 依赖数据流机制进行计算,权重永远不会存储在 CS-2 系统上。 18.png 使用数据流调度,每个单独的权重将触发对片上存储的激活的AXPY 操作。训练时一旦每个权重计算完成,就会被丢弃,而不会存储在芯片上,并移动到下一个权重。为了降低从MemoryX流式传输每个权重的成本,每个权重在 CS-2 上复用多次。在向后传递时,梯度以相反的方向流出并返回 MemoryX 单元,并更新权重。 19.png

GEMM在晶圆上的映射方式以Transformer层为例,激活张量的三个维度(Batch、Sequence、Hidden)中,Hidden沿fabric的x方向切分,Batch和Sequence沿y方向切分。权重按行广播到各列核上,每个非零权重触发本地一列激活的FMAC;部分和的归约走PSUM/FSUM两条命令:所有列收到PSUM命令后在行方向上做环形归约,其中一列收到FSUM命令负责存出最终结果。归约通过微线程与下一行权重的FMAC计算重叠,不占额外时间。这套映射下单芯片可以不切分地跑完100,000 × 100,000的MatMul——对当时任何模型都够用,这也是”不需要模型并行”的底气所在。 如此之多流式传输和计算资源,延迟可能是一个挑战,尤其是在更新期间。Cerebras 解决延迟问题

  • 通过消除对延迟敏感的通信实现高效的性能扩展
  • 粗粒度流水线
    • 前向/增量/梯度是完全流水线的
    • 迭代中的所有流都没有层间依赖关系
  • 细粒度流水线
    • 权重更新和前向传递的重叠涵盖了迭代间依赖关系 20.png

SwarmX Interconnect 是可横向扩展到多个系统的高速结构。 SwarmX 上的光学器件几乎不可插拔并且有 12 个端口。 21.png 通过专门设计的称为 SwarmX的互连来实现数据并行横向扩展,如下图所示。SwarmX 位于存储权重的MemoryX 单元和用于计算的 CS-2 系统之间;SwarmX 将权重广播到所有 CS-2 系统,并对所有 CS-2 的梯度进行reduction。在内部,SwarmX 使用树形拓扑来支持模块化和低成本扩展。按2021年发布时的口径,SwarmX最多可把192台CS-2连成一台逻辑设备,合计1.63亿个核,训练同一个网络。 22.png

CS-2整机的部署规格是15U机箱,445mm宽、1005mm深,300kg,最大功耗23kW,供电为6+6冗余的4kW电源模块,12个100GbE数据口合计1.2Tb/s I/O。冷却为内部闭环、直达芯片的液冷,泵模块1+1冗余热插拔;机房没有水路也可以选液冷转风冷的部署方式。这些数字与CS-1一脉相承,差别主要在芯片从16nm换成7nm后,同样功耗下性能翻了一倍还多。

WSE-2的数据流协议天然带稀疏加速。发送方的核会过滤掉零值数据,接收方因此直接跳过这部分计算,而不是照做一遍乘加。相比GPU照算不误,ReLU、Max Pool这类天然产生稀疏的算子在WSE-2上可以白捡一部分性能和功耗。CS-2集群的规模上限是192个系统。

WSE-3

WSE-3 于2024年3月发布,采用台积电5nm工艺,比最大的GPU 大 57 倍,包含 4 万亿个晶体管、900,000 个 AI 优化计算核; 44 GB片上SRAM,可以提供21PB/s内存带宽,片上结构带宽214Pb/s(折合26.7PB/s),峰值AI算力125 PFLOPS。晶圆仍由84个die组成,每个die约10,700个核。WSE-3在与WSE-2相同的功耗和价格下做到了两倍性能。

对于计算核,16位数据(FP/BF16)的SIMD从4-wide翻倍到8-wide,8位定点(INT8)到16-wide,另加了一组快速非线性函数指令;调度与稀疏机制不变,仍是细粒度数据流调度加原生非结构化稀疏加速。存储侧,每核48kB SRAM不变,但本地缓存从256B扩到512B,DSR从44个增加到48个。 23.png

每个die边界上是480条24Gb/s的并行链路,合计2,880GB/s;整片晶圆跨越划片线的链路共40,320条,带宽242TB/s,而所有这些互连的总功耗只有97W(单个die边界1.1W,折合0.05pJ/bit)。对照DGX-H100那种靠SerDes、线缆和交换芯片堆出来的互联(5.0pJ/bit),同带宽下功耗差出近百倍;WSE-3的die间I/O带宽是DGX-H100的33倍,功耗却只有六分之一。

Cerebras CS-3 Wafer Scale System

Cerebras CS-3 是第三代晶圆扩展系统。顶部有 MTP/MPO 光纤连接,以及用于冷却的电源、风扇和冗余泵。 24.png Cerebras 需要为巨型芯片提供电力、数据和冷却,同时还需要管理相对较大的区域的热膨胀等。在内部,芯片是液冷的,热量可以通过风扇或水带走。 25.png 26.png 与具有八个 NVIDIA H100 GPU 和内部 NVSwitch 和 PCIe 交换机的 NVIDIA DGX H100 系统相比,CS-3是一个更大的构建块。 27.png

28.png

这一代的一大特点是更大的集群,多达 2048 个 CS-3,用于多达 256 exaFLOP 的 AI 计算。CS-3的系统I/O仍是12个100GbE端口(1.2Tb/s),与CS-2一致;对外部署的最大案例是与G42合作的Condor Galaxy网络:CG-1(圣克拉拉)与CG-2(斯托克顿)各4 exaFLOPS,CG-3到CG-5(达拉斯)合计20 exaFLOPS,规划中的CG-6到CG-9(明尼阿波利斯)再贡献32 exaFLOPS。 29.png 这个 12PB 的内存数字是一个高端超大规模 SKU,专为快速训练 GPT-5 大小的模型而设计。Cerebras 还可以缩小到类似单个 CS-2 ,并支持服务器和网络。借助 CS-3,大幅增加了 MemoryX 选项,包括面向企业客户的 24TB 和 36TB SKU,以及面向超大规模客户的 120TB 和 1,200 TB 选项。 30.png 部分内存不仅是片上内存(44GB),还包括支持服务器中的内存。

推理:权重从”流过”变成”住下”

2024年8月,Cerebras发布推理服务。推理的负载特征与训练完全不同:生成1,000个token就要把模型完整读1,000遍,瓶颈在内存带宽而不在计算。

Cerebras的应对是把执行模型整个反过来。训练时权重从MemoryX逐层流过晶圆、用完即弃;推理时权重一次性装进片上SRAM,住下不走。Llama3.1-8B是8B参数、FP16占16GB,44GB的片上SRAM直接装下整模型;Llama3.1-70B是140GB,摊到4片晶圆共176GB上,晶圆之间只传激活,走CS-3的低延迟RDMA over Ethernet。

计算组织也随之改变,模型的各层被映射到晶圆上的不同区域,区域大小按该层的存储和计算需求划分,权重与KV cache都存在本区域的SRAM里。每个区域一个token,局部batch为1;相邻区域物理上紧挨着,流水线级间几乎没有延迟;最后一个区域的输出绕回第一个区域,生成下一个token。GPU是多张卡合力跑一层,Cerebras是晶圆的一角跑一层,方向正好相反。单用户只占用一小部分内存带宽,富余的带宽用来并行多个用户;prompt已知全部内容,还可以把多个prompt token同时塞进流水线的不同级。官方的性能数据是Llama3.1-8B单用户1,800 tokens/s,70B单用户450 tokens/s

CS-4

2026年8月18日,Cerebras发布第四代系统CS-4,首批出货在同年第三季度。按CS-1到CS-3的命名惯例,CS-4本该对应一块新的晶圆,也就是WSE-4。但并没有。CS-4装的是三颗WSE-3 Turbo,配套的是一套重新设计的机架,叫Nexus Platform Architecture。Cerebras第一次在没有换代硅片的情况下推出了换代系统。

31.png

WSE-3 Turbo

WSE-3 Turbo和已经在出货的WSE-3是同一块硅片:4万亿晶体管、46,225mm²、台积电5nm、900,000个AI核、44GB片上SRAM,全部没变。变的是时钟,从约1.4GHz提到约2.8GHz。所谓Turbo就是超频。主频翻倍,单晶圆的指标也跟着翻倍:AI算力125 PFLOPS提到250 PFLOPS,内存带宽21.6PB/s提到43.2PB/s,片上结构带宽26.7PB/s提到53.5PB/s,片外I/O带宽1.2Tbit/s提到2.4Tbit/s。乘以机架内的三颗晶圆,就是CS-4的系统级数字。

频率能提上去,靠的不是架构,是供电。Nexus把AC/DC转换放到距晶圆约0.5mm的位置,常规GPU板上这个距离大约50mm,近了约100倍;同时用铜母线(busbar)取代PCB走线供电。板级功率损耗被压到很小,送到WSE-3T的电力翻倍,频率才上得去。 32.png

规格

官方datasheet给出的CS-3与CS-4对比如下。

规格 CS-3(单晶圆) CS-4(三晶圆)
架构 单晶圆机箱 有源供电机架,三个模块化计算背包
晶圆 1 × WSE-3 3 × WSE-3 Turbo
AI算力(sparse FP16) 125 PFLOPS 750 PFLOPS
内存带宽 21.6 PB/s 129.6 PB/s
片上结构带宽 26.7 PB/s 160.5 PB/s
系统I/O带宽 1.2 Tbit/s 7.2 Tbit/s
I/O延迟 5 微秒 2 微秒
片上SRAM 44 GB 132 GB
支持模型规模 最高24万亿参数(配MemoryX) 超过50万亿参数(厂商口径)

单颗WSE-3 Turbo的规格,4万亿晶体管、46,225mm²、TSMC 5nm、900,000核、44GB SRAM、250 PFLOPS(sparse FP16)、43.2PB/s内存带宽、53.5PB/s片上结构带宽、2.4Tbit/s I/O带宽。

Nexus机架平台

Nexus不是芯片,是可复用的机架框架,电源从前面进,计算从后面插。Cerebras说同一个机架会依次承载CS-4、CS-5、CS-6,换晶圆不必换机架。也就是说,CS-4是平台的第一代,不是硅片的第一代。Nexus围绕计算、供电、I/O三件事重新组织,各自做成自包含模块。

Wafer-Scale Backpack是计算模块。它后置安装,垂直挂接到电源阵列上,把电源转换、直接液冷、高速I/O和控制电子折叠进一个围绕晶圆的紧凑三维封装里。计算与供电解耦之后,制造简化,部署时间从数天缩到数小时;相比上一代,组件减少50%,自动化制造比例提高60%。

模块化I/O方面,CS-4引入了新的可编程I/O子系统,带宽翻倍、延迟减半。新的Wafer I/O Module从晶圆边缘引出模块化的可编程fabric,支持两种连接模式:标准RoCE v2 RDMA over Ethernet,用于接入现有基础设施;Direct Wafer Links,无交换机直连,支持机架内和跨机架,晶圆间延迟低至2微秒。

33.png

34.png

存储结构

CS-4的存储思路没有变,全部是片上SRAM,没有任何一级要出片取权重。单晶圆44GB、43.2PB/s,机架132GB、129.6PB/s。这个带宽GPU方案靠HBM达不到,也是Cerebras强调”每用户token速度”的原因,解码时的瓶颈通常不在浮点,而在权重从内存搬到计算单元的速度。

但容量是另一回事。44GB每晶圆、132GB每机架,对前沿模型来说仍然很小,大模型必须摊到很多晶圆上。晶圆面积也已经没有余量,晶圆面积已被逻辑和SRAM 100%用满,多加SRAM就要少放核。按路线图,CS-6(大约再两代)会把DRAM以3D方式堆叠到计算与SRAM晶圆之上,用高带宽的垂直互连连接,容量增长而权重不必走远路。CS-5定在2027年,会用新的WSE硅片。

编程模型与部署范式

CS-4这一代软件侧最大的变化不在编译器,而在部署范式,原生支持分离式推理(disaggregated inference)。推理被拆成两段,prefill处理输入prompt并准备好模型状态,decode逐token生成。CS-4几乎只为decode而生,2微秒的晶圆间延迟就是为这个场景准备的。至于prefill,Cerebras直接把客户指向AMD Helios和AWS Trainium。

35.png

这在Cerebras的叙事里是一次后退,也是一次收缩。早年的说法是一块晶圆可以替代整个GPU机架,包办训练和推理;现在收缩到”只做低延迟token生成”这一段。在这段上巨大晶圆有物理优势,关键路径上没有芯片间跳变;而在计算密集的prefill上,不再硬碰NVIDIA的装机量和定价。这个定位比”通吃”更站得住。

执行模型沿用WSE-3那代确立的推理形态,权重一次性装进片上SRAM住下来,模型各层映射到晶圆区域,逐token流水线生成。这与训练时的权重流式(weight streaming)是两套模式——训练权重从MemoryX流过、用完即弃,推理权重驻留、激活流动。CS-4机架132GB的SRAM容量就是按”装下decode阶段的权重加KV cache”来定的一档规格,gpt-oss-120b这类模型FP8量化后正好放得下。

系统扩展

机架内有三颗晶圆,各带一个backpack;跨机架扩展靠Direct Wafer Links,无交换机,机架内与跨机架同一语义。

编程模型

整套软件栈分三层。CSL(Cerebras Software Language)是设备侧语言,语法基于Zig但并非100%兼容,编译器也不共用Zig的代码。选Zig是为了它的编译期计算能力,让PE程序既能写成可维护的模板,又能编译出接近手写的代码。SdkRuntime是主机侧Python运行时,负责加载程序、启动内核、搬运张量。再往上是cstorch,把PyTorch模型编译到晶圆上。

编程模型的硬件对象很朴素:一个PE由处理器、路由器和本地内存三样东西组成。处理器官方叫compute engine(CE),本地内存放这个PE的全部数据和代码,两者都不能被别的PE直接访问;PE之间唯一的通信设备是路由器。路由器拉出五条双向链路,四个通往邻居PE(NORTH/SOUTH/EAST/WEST),第五条连自己的CE,叫RAMP。整片晶圆就是这样一个二维mesh,每个PE有自己的内存、自己的程序计数器、自己的代码,靠32位的wavelet与邻居在单周期内互通。主机与设备的分界也在这里:CS叫device,主机CPU集群叫host,中间是并行的百兆以太网host I/O。

CSL没有线程调度器,没有缓存一致性,没有动态路由。每个PE跑一份自己的程序,占用哪块区域、走哪条通道、数据从哪个方向来,全部在编译期定死。程序员拿到的是整块晶圆的静态地图,不是一个可以”申请资源”的运行时。

一个PE,一个完整程序

最小的完整程序由两个文件构成。layout.csl是编译期求值的顶层文件,它决定程序占几个PE、每个PE绑哪段代码:

const memcpy = @import_module("<memcpy/get_params>", .{ .width = 1, .height = 1 });

layout {
  @set_rectangle(1, 1);
  @set_tile_code(0, 0, "pe_program.csl", .{ .memcpy_params = memcpy.get_params(0) });
  @export_name("y", [*]f32, false);            // 主机只读
  @export_name("init_and_compute", fn()void);  // 主机可调用
}

PE上的程序就是普通函数加显式内存:var A: [M*N]f32这48kB是这颗核的全部本地内存,gemv()照常写双重循环,最后导出符号:

fn init_and_compute() void {
  initialize();
  gemv();
  sys_mod.unblock_cmd_stream();   // 每个主机可调用函数必须以它结尾
}
comptime {
  @export_symbol(y_ptr, "y");
  @export_symbol(init_and_compute);
}

unblock_cmd_stream()这个约定背后是一套简单的规则:memcpy基础设施一次只推进一条命令,设备函数结束必须显式解锁命令流,主机侧的memcpy_d2h才能继续。主机与设备的串行化就这样由程序员写在代码末尾,而不是藏在驱动里。

--fabric-dims这个编译参数还给出了晶圆的实际规模:WSE-2编译到硬件要写757,996,WSE-3写762,1172,后者约89万个可编址PE。

Color、路由与Wavelet:数据到达即执行

CSL里最基础的概念是wavelet。它是晶圆上的基本传输单位,一个wavelet携带32位数据;数据加上控制信息构成32位细粒度包,在二维网格上以单周期延迟逐跳转发。程序的基本执行单位是数据触发任务(Wavelet-Triggered Task):wavelet到达路由器,路由器把它转给计算单元,绑定的任务随即被激活,wavelet的payload直接作为任务参数传入:

task main_task(wavelet_data: i16) void {   // 数据到达即被激活
  global = wavelet_data;
  @mov16(out_dsd, global);                 // DSD描述"往哪儿搬"
}
comptime {
  @bind_data_task(main_task, main_task_id);
}

color是编译期命名的逻辑通道,每个PE有24个,互不阻塞、时分复用到同一物理链路;路由由@set_color_config静态指定,rx说明从哪个方向收,tx说明往哪些方向转发。也就是说,通信拓扑是程序的一部分,不是运行时协商出来的。

任务分三类。data task由数据到达触发,local task由@activate(task_id)显式唤醒,control task同样由wavelet触发但用于内部控制;data task与control task合称wavelet-triggered task(WTT)。task ID是0到63的句柄,但只有一段是routable的:WSE-2上0到23号对应24个color,WSE-3上0到7号对应8个输入队列;local task的可激活区间跟着变,WSE-2是0到30,WSE-3是8到30。同一份示例代码用@is_arch("wse2")/@is_arch("wse3")分支同时兼容两代硬件,微线程与队列解耦也是WSE-3那一代核内改动的一部分。

一个task必须同时处于activated与unblocked两态才会被task picker选中,默认所有ID都既未激活也不阻塞。于是有了@block/@unblock这一对,先把某个ID阻塞住,等前置任务做完再解锁;跨PE归约的收尾就靠@activate加@unblock串起来。这是CSL版本的依赖管理,比信号量更轻。

DSD:把循环压成一条指令

CSL里表达数据搬移的核心构造是DSD(Data Structure Descriptor,数据结构描述符)。它描述一段可以不连续的内存,或一串来去的wavelet;配合DSD操作,一条硬件指令就能完成原本要写循环的重复动作。所有DSD共享一个属性extent,即这条描述符代表多少次重复操作。

DSD用@get_dsd(dsd_type, properties)构造,dsd_type取五种:mem1d_dsd、mem4d_dsd、circbuf_dsd、fabin_dsd、fabout_dsd。FIFO另有一套分配方式。

mem1d_dsd描述一维内存向量,字段是base_address(指针)、extent(u16)、stride(i8,默认1)、offset(i16,默认0)。更顺手的写法是tensor_access张量访问表达式:

const oddElements = @get_dsd(mem1d_dsd, .{
  .tensor_access = |i|{5} -> array[2 * i + 1]
});

|i|{5} -> array[2*i+1]读作:以i为归纳变量迭代5次,每次访问array[2*i+1]。它等价于.base_address=&array, .offset=1, .stride=2, .extent=5。箭头右侧必须是仿射表达式(线性加常数),好让硬件地址生成器直接展开。底层张量不必是一维的,|i|{20} -> array[i,i]取的就是20×20矩阵的对角线。tensor_access只能写编译期已知的量;stride或extent要到运行时才知道的话,就得逐字段写,比如@get_dsd(mem1d_dsd, .{.base_address = ptr, .stride = @as(i8, stride), .extent = len})。

mem4d_dsd把维度升到最多四维,归纳变量与长度一一对应:

const subset = @get_dsd(mem4d_dsd, .{
  .tensor_access = |i,j|{2,2} -> array[i, j]
});

多维DSD的stride有个读法上的陷阱:stride从左到右读,访问表达式的维度却从右到左读,内维变化最快。|i,j|{5,5} -> A[2*i+j]折算出的stride是{1, -2},j每加1地址加1,i每加1时j从4归零、净变化是2减4等于-2。四维stride最多要三个步长寄存器来存,最快那维由硬件隐含。

circbuf_dsd是环形缓冲区,base_address兼作头指针,wraparound指定回绕位置,extent可以超过buffer大小,同一个buffer于是被反复迭代。它不能直接当DSD操作的操作数,必须先经@load_to_dsr_xdsr装进DSR。

fabin_dsd与fabout_dsd描述fabric上的收发。fabin_dsd配input_queue与extent,意思是”从这个队列收这么多wavelet”,WSE-2上也可以直接写fabric_color;fabout_dsd多出两个字段,control标记控制wavelet,wavelet_index_offset让发出的wavelet带上索引。还有一类用法:color到达的wavelet可以不触发task,而由fabric DSD直接接走,做纯数据搬运。

FIFO用@allocate_fifo(buffer)在内存上划一块先进先出区,.empty_action/.full_action规定读空写满时挂起还是终止,.activate_pop/.activate_push规定进出时唤醒哪个local task。它占用硬件资源,应当省着用。

DSD属性运行期可以改,四个builtins都返回新值、不动原值:@set_dsd_base_addr换基址,@increment_dsd_offset(dsd, n, T)把访问推进n个T元素,@set_dsd_length改长度,@set_dsd_stride改步长。GEMV里每算完一列就把A_dsd推进一列,用的是第二个。

还有几个字段藏得比较深,但在数据流程序里出镜率不低。wavelet_index_offset = true让DSD操作带上index参数:对内存DSD,index是加到基址的字偏移;对fabout_dsd,index被写进每个发出wavelet的高16位,跨PE流式GEMV里”wavelet自带索引、直接选中A的哪一列”靠的就是这个。simd_mode(simd_32/simd_64)让一个wavelet装两个16位值。.zero = .{.first_source = true}在发送完成后把源操作数清零,归约累加省一次显式清零。advance_switch = true则在最后一个wavelet发出后自动推进switch位置。

DSD操作一旦涉及fabric操作数就可以异步(.{.async = true}),起一个微线程(microthread)与当前任务并发跑。完成时可选.activate唤醒一个local task、.unblock解锁一个任务,或.on_control由控制wavelet触发终止,三者取其一。异步操作消耗两类硬件资源,队列和微线程,两个并发操作不能共用:WSE-2上微线程ID由队列ID隐式决定,WSE-3上可以显式指定。优先级三档high/medium/low,WSE-2默认low,WSE-3默认high。

DSR:DSD的物理形态

DSD只是描述,硬件真正执行的是DSR(Data Structure Register,数据结构寄存器)。每个DSR属于三个寄存器文件之一:dest只作目的,src0可源可目的,src1只作源。所有DSD操作实际都在DSR上进行,操作数必须先装进DSR;这一步通常由编译器代办,也可以显式写出来。

寄存器类型五种:dsr_dest、dsr_src0、dsr_src1,加dsr_fifo_dest、dsr_fifo_src1两个FIFO变体。FIFO、环形缓冲、多维向量还要额外的扩展DSR(XDSR),多维向量再配最多三个步长寄存器(SR)存stride。WSE-2每核44个DSR、WSE-3增加到48个,指的就是这套寄存器。

@get_dsr(dsr_type, dsr_id)取寄存器,@load_to_dsr(dsr, dsd)装填;环形缓冲走@load_to_dsr_xdsr(dsr, xdsr, circbuf),4D向量走@load_to_dsr_xdsr_sr(dsr, xdsr, .{sr1, sr2, sr3}, dsd)。装填在comptime做则程序开始前就位,在任务里做则现装现用。

两个配置值得单说。save_address = true让每次DSD操作结束后把基址推进到本次访问范围之后,DSR变成一段自动前进的游标:

comptime {
  @load_to_dsr(chunks_in_dsr, chunks_in_dsd, .{ .save_address = true });
}
task main() void {
  for (@range(i16, N_CHUNKS)) |i| {
    @mov16(chunks_out_dsr, chunks_in_dsr);   // 每次拷一块,基址自己往前走
  }
}

single_step = true是给@map专用的:@map(math_lib.sqrt_f16, inDSR, outDSR)逐元素套一个数学函数,DSD在背后单步推进;拿它配别的DSD builtins是未定义行为。@set_dsr_base_addr(dsr, tensor)可以在不动长度和stride的前提下把DSR重新指向另一块张量,save_address是它的自动化版本。DSR上的异步配置(async/activate/unblock/on_control)必须在@load_to_dsr时装好,后续DSD操作照常写即可。

一个完整的GEMV

GEMV教程系列以y = Ax + b为贯穿例子,从单PE一路长到跨PE。把它的形态摊开,前面几节的概念正好各就各位。

单PE版本的pe_program.csl只有一处要动脑筋:A按行主序存,A_dsd要按列访问,于是用stride把连续内存折成一列:

const M: i16 = 4;
const N: i16 = 6;

var A: [M*N]f32;                       // 行主序
var x = @constants([N]f32, 1.0);
var b = @constants([M]f32, 2.0);
var y = @zeros([M]f32);

var A_dsd = @get_dsd(mem1d_dsd, .{ .tensor_access = |i|{M} -> A[i*N] });  // 第0列
var b_dsd = @get_dsd(mem1d_dsd, .{ .tensor_access = |i|{M} -> b[i] });
var y_dsd = @get_dsd(mem1d_dsd, .{ .base_address = &y, .extent = M });

fn gemv() void {
  for (@range(u16, N)) |i| {
    @fmacs(y_dsd, y_dsd, A_dsd, x[i]);              // y += 第i列 * x[i]
    A_dsd = @increment_dsd_offset(A_dsd, 1, f32);   // 推进到下一列
  }
  @fadds(y_dsd, y_dsd, b_dsd);                      // y += b
}

双重循环没了。@fmacs一条指令做完整列的向量-标量乘加,@increment_dsd_offset把A的访问从第i列挪到第i+1列,@constants与@zeros在声明时就把初值写好。主机侧三步走,拷进去、启动、拷回来:

runner.memcpy_h2d(A_symbol, A.ravel(), 0, 0, 1, 1, M*N, streaming=False,
  order=MemcpyOrder.ROW_MAJOR, data_type=MemcpyDataType.MEMCPY_32BIT, nonblock=False)
runner.memcpy_h2d(x_symbol, x, 0, 0, 1, 1, N, streaming=False,
  order=MemcpyOrder.ROW_MAJOR, data_type=MemcpyDataType.MEMCPY_32BIT, nonblock=False)
runner.launch("init_and_compute", nonblock=False)
runner.memcpy_d2h(y_result, y_symbol, 0, 0, 1, 1, M, streaming=False,
  order=MemcpyOrder.ROW_MAJOR, data_type=MemcpyDataType.MEMCPY_32BIT, nonblock=False)

两个PE的版本把A和x按列对半分,左PE算前一半、右PE算后一半,左PE再把部分和发给右PE做最终累加。布局文件里多出的是color配置:

const send_color: color = @get_color(0);
layout {
  @set_rectangle(2, 1);
  @set_tile_code(0, 0, "pe_program.csl", .{ .pe_id = 0, .send_color = send_color, ... });
  @set_tile_code(1, 0, "pe_program.csl", .{ .pe_id = 1, .send_color = send_color, ... });

  @set_color_config(0, 0, send_color, .{.routes = .{ .rx = .{RAMP}, .tx = .{EAST} }});
  @set_color_config(1, 0, send_color, .{.routes = .{ .rx = .{WEST}, .tx = .{RAMP} }});
}

左PE从RAMP收、往EAST发,右PE从WEST收、往RAMP发。路由器的五个方向在这两行配置里写完,运行期不再有路由决策。

设备侧的收发各自一条异步DSD操作:

fn send_right() void {
  const out_dsd = @get_dsd(fabout_dsd, .{ .extent = M, .output_queue = send_color_oq });
  @fmovs(out_dsd, y_dsd, .{ .async = true, .activate = exit_task_id });
}
fn recv_left() void {
  const in_dsd = @get_dsd(fabin_dsd, .{ .extent = M, .input_queue = send_color_iq });
  @fadds(y_dsd, y_dsd, in_dsd, .{ .async = true, .activate = exit_task_id });
}
task exit_task() void { sys_mod.unblock_cmd_stream(); }

.async = true起微线程,.activate在搬完后唤醒exit_task,由它调unblock_cmd_stream()把控制权交还主机。两个PE都走到这一步,主机才能取回y。整个程序里没有一次轮询,也没有一次全局屏障,左PE发完即走、右PE收到即算,进度完全由wavelet的到达驱动。涉及fabric的DSD操作必须异步写,官方文档的警告是同步写”可能导致性能低下或死锁”。

这套教程从gemv-00的语法讲起,逐节叠加DSD、memcpy、多PE、fabric,是官方给的入门路径。它教的是增量式写法;真正体现晶圆数据流味道的是下一节那个GEMV,同一个数学问题换一种组织方式,通信与计算完全融为一体。

跨PE的流式GEMV

gemv-checkerboard-pattern做y = Ax + b,4×4的PE阵列,M=32、N=16:A按行切四份、按列切四份,每个PE拿8×4的一块常驻内存;x从北侧流入,沿列向南广播;b从西侧进入;部分积沿行向东做链式归约,最终从最右列流出。

36.png

PE上的代码只有两个任务。收到一个x元素,就对A的一列做一次乘加;收满整列元素,唤醒归约:

task fmac_task(wlet_data: f16, idx: u16) void {
  @fmach(dsd_mul_temp, dsd_mul_temp, dsd_A, wlet_data, .{ .index = idx });
  num_recv_x += 1;
  if (num_recv_x >= X_local_dim_x) { @activate(REDUCE); }        // 收满即唤醒归约
}

task reduce_task() void {
  const c = if (last_column) @get_color(@bitcast(u16, sys_mod.MEMCPYD2H_1)) else send;
  const dsd_out = sys_mod.get_streaming_fabout_dsd(c, B_local_dim_x, send_oq);
  @faddh(dsd_out, dsd_in, dsd_mul_temp, .{ .async = true });     // y = A*x + y_west
}

这里没有任何全局屏障,也没有__syncthreads式的同步原语。整个矩阵向量乘的推进完全靠数据到达的先后关系:x到了就算,算完就往下传。DSD(Data Structure Descriptor)承担搬运描述的角色:mem1d_dsd描述本地内存的访问模式,fabin_dsd/fabout_dsd描述fabric收发;.wavelet_index_offset = true让wavelet自带的索引直接选中A的哪一列,FMA不必自己算地址。更流式的版本(gemv-09-streaming)用@increment_dsd_offset在每收到一个x之后把描述符推进一列,效果相同。

路由配置用的是棋盘式双色:

const RX_R_TX_RS = .{ .rx = .{RAMP},  .tx = .{RAMP, SOUTH} };  // 收下、转南
const RX_N_TX_RS = .{ .rx = .{NORTH}, .tx = .{RAMP, SOUTH} };  // 收北、转南
const RX_R_TX_E  = .{ .rx = .{RAMP},  .tx = .{EAST} };         // 收下、转东

偶数列用颜色1发、颜色2收,奇数列反过来。因为fabric的双向链路在归约时会被东西两侧同时占用,不做棋盘分配就会自撞。这类冲突在GPU上由硬件路由解决,在CSL里要自己安排。

copy与streaming:权重流式在API里的样子

主机侧的接口只有三个函数:memcpy_h2d()、memcpy_d2h()、launch()。memcpy_h2d有一个布尔参数streaming,把Cerebras的两套数据供给语义分开了:

runner.memcpy_h2d(A_symbol, A_prepared, 0, 0, 4, 4, M_per_PE*N_per_PE,
                  streaming=False, ...)                      # copy模式:整块驻留
runner.memcpy_h2d(MEMCPYH2D_DATA_1, x, 0, 0, 4, 1, N_per_PE,
                  streaming=True, ...)                       # streaming模式:逐wavelet注入

streaming=False是copy模式,张量整块搬进PE内存,A这样的常量走这条路;streaming=True是streaming模式,张量被拆成一串wavelet持续注入,由数据任务边收边算,激活值走这条路。前面WSE-2那一章讲的权重流,落到代码上就是这个布尔参数。权重不驻留,走streaming;每一层的激活也不需要整块落地,同样走streaming。”权重用完即弃”这个说法在API层面是直接可见的。

因为一个wavelet只有32位,FP16张量在主机侧要先做16位到32位的零扩展,回传时再把高16位剥掉。这是细粒度包设计的必然结果。

PyTorch这一层

绝大多数用户不会写CSL。PyTorch侧只需要改几行:

model = cstorch.compile(Model(), "CSX")        # 类似torch.compile,只做准备
optimizer = cstorch.optim.SGD(model.parameters(), lr=0.01)
sparsity = cstorch.sparse.RigLSparsityOptimizer(model.named_parameters(),
                                                sparsity=0.9, schedule=1000)

@cstorch.trace                                  # 圈出"跑在晶圆上"的部分
def training_step(inputs, targets):
    optimizer.zero_grad(); sparsity.apply_sparsity()
    loss = loss_fn(model(inputs), targets)
    loss.backward(); optimizer.step(); sparsity.step()
    return loss

@cstorch.step_closure                           # 圈出"留在主机"的回调
def print_loss(loss): print(f"Loss: {loss.item()}")

三个装饰器和接口划分了主机与设备的边界:compile准备计算图,trace圈出上晶圆的部分,step_closure圈出留在主机的回调。RigLSparsityOptimizer对应”原生硬件加速动态稀疏”:训练过程中动态剪枝与重连,在GPU上要靠掩码和稀疏库绕一大圈,在WSE上由数据流硬件直接处理。

参考文献

  1. S. Lie, “Inside the Cerebras Wafer-Scale Cluster: Cerebras Systems,” 2023 IEEE Hot Chips 35 Symposium (HCS), Palo Alto, CA, USA, 2023, pp. 1-41, doi: 10.1109/HCS59251.2023.10254700.
  2. G. Lauterbach, “The Path to Successful Wafer-Scale Integration: The Cerebras Story,” in IEEE Micro, vol. 41, no. 6, pp. 52-57, 1 Nov.-Dec. 2021, doi: 10.1109/MM.2021.3112025.
  3. S. Lie, “Cerebras Architecture Deep Dive: First Look Inside the Hardware/Software Co-Design for Deep Learning,” in IEEE Micro, vol. 43, no. 3, pp. 18-30, May-June 2023, doi: 10.1109/MM.2023.3256384.
  4. Wang, J., 2024. Cerebras CS-3: the world’s fastest and most scalable AI accelerator. Cerebras. URL https://www.cerebras.net/blog/cerebras-cs3/
  5. “Wafer-Scale Deep Learning,” 2019 IEEE Hot Chips 31 Symposium (HCS), Cupertino, CA, USA, 2019, pp. 1-31, doi: 10.1109/HOTCHIPS.2019.8875628.
  6. “Software Co-design for the First Wafer-Scale Processor (and Beyond),” 2020 IEEE Hot Chips 32 Symposium (HCS), Palo Alto, CA, USA, 2020, pp. 1-26, doi: 10.1109/HCS49909.2020.9220504.
  7. A. Yeung, E. Gardner, “Introducing Cerebras CS-4: The Fastest AI Just Got Faster,” Cerebras Blog, Aug. 18, 2026. URL https://www.cerebras.ai/blog/introducing-cerebras-cs-4
  8. “Cerebras Unveils CS-4: Up to 30 Times Faster than GPU-based Solutions,” Cerebras Press Release, Aug. 2026. URL https://www.cerebras.ai/press-release/cerebras-unveils-cs-4-up-to-30-times-faster-than-gpu-based-solutions-3347356
  9. “CS-4 Datasheet,” Cerebras Systems, 2026. URL https://www.cerebras.ai/cs4-datasheet
  10. “Wafer-Scale Engine: The Largest Chip Ever Built (WSE-2 Datasheet),” Cerebras Systems, 2021.
  11. “An AI Chip With Unprecedented Performance To Do the Unimaginable,” Cerebras Blog, 2021. URL https://cerebras.ai/blog/an-ai-chip-with-unprecedented-performance-to-do-the-unimaginable/
  12. J. P. Fricker, S. Lie, Cerebras CS-4 system architecture session, 2026 IEEE Hot Chips 38 Symposium (HCS), 2026.
  13. Cerebras Systems, “Cerebras SDK Documentation.” URL https://sdk.cerebras.ai
  14. Cerebras Systems, “SDK Examples” (Apache-2.0), GitHub. URL https://github.com/Cerebras/sdk-examples
  15. Cerebras Systems, “Host Runtime and Tensor Streaming,” Cerebras SDK Documentation. URL https://sdk.cerebras.ai/tensor-streaming
  16. Cerebras Systems, “PyTorch on Cerebras Wafer-Scale Cluster,” PyTorch Conference 2023. URL https://static.sched.com/hosted_files/pytorch2023/bb/PyTorchOnCerebrasWaferScaleCluster-CerebrasSystems.pdf
  17. S. Lie, “Wafer-Scale AI: GPU Impossible Performance,” 2024 IEEE Hot Chips 36 Symposium (HCS), 2024. URL http://hc2024.hotchips.org/assets/program/conference/day2/72_HC2024.Cerebras.Sean.v03.final.pdf
  18. “Cerebras CS-2 Data Sheet,” Cerebras Systems, 2021.
  19. “Wafer-Scale Engine 3: The Largest Chip Ever Built (WSE-3 Datasheet),” Cerebras Systems, 2024.
  20. “Cerebras Systems Announces World’s First Brain-Scale Artificial Intelligence Solution,” Cerebras Press Release, Aug. 24, 2021.
  21. “Cerebras Systems Unveils World’s Fastest AI Chip with Whopping 4 Trillion Transistors,” Cerebras Press Release, Mar. 13, 2024.
  22. S. K. Moore, “4 Things to Know About the Biggest Chip Ever Built,” IEEE Spectrum, Aug. 2019.
  23. Cerebras Systems, “Wafer-Scale Engine Architecture (Computing with Cerebras),” Cerebras SDK Documentation. URL https://sdk.cerebras.ai/computing-with-cerebras
  24. Cerebras Systems, “Data Structure Descriptors,” Cerebras SDK Documentation. URL https://sdk.cerebras.ai/csl/language/dsds
  25. Cerebras Systems, “Data Structure Registers,” Cerebras SDK Documentation. URL https://sdk.cerebras.ai/csl/language/dsrs
  26. Cerebras Systems, “Build a GEMV Kernel (Tutorial Series),” Cerebras SDK Documentation. URL https://sdk.cerebras.ai/csl/tutorials
]]>
Huawei's τ Chip Was Supposed to Melt http://zhifeiding.github.io/programming/chip/2026/09/04/Huawei-Chip-Was-Supposed-to-Melt/ 2026-09-04T00:00:00+00:00 http://zhifeiding.github.io/programming/chip/2026/09/04/Huawei-Chip-Was-Supposed-to-Melt 本文根据Huawei’s τ Chip Was Supposed to Melt整理,主要是反驳了业界关于“3D堆叠芯片必然导致过热”的普遍质疑。华为通过实测即将出货的 Kirin 2026 芯片证明,采用 LogicFolding(逻辑折叠) 技术的 τ 芯片不仅没有过热,反而比前代产品温度更低。尽管晶体管密度提升了55%,但在关键任务上实现了高达66%的功耗降低。主要是因为在芯片中,能量主要消耗在信号的“传输”而非“计算”本身,缩短传输距离即可大幅降温省电。

背景介绍

首先,回顾一下什么是 τ Scaling Law (韬定律)?由于无法获取EUV光刻机,华为无法继续依赖传统的几何微缩(Moore’s Law)。继而转向从芯片到系统层面进行优化: τ (Tau/Tao) 代表信号在芯片关键路径上的特征延迟。τ Scaling Law 旨在通过每一代技术持续压缩这一时间延迟,从“缩放空间”转向“缩放时间”。它不是单纯的器件技巧,而是涵盖器件、电路、芯片到系统的电子系统定律。

什么是 LogicFolding (逻辑折叠)?从芯片角度看韬定律,就是逻辑折叠:将原本平铺在二维平面上的电路折叠成垂直堆叠的层级,并通过微小的垂直链路连接。在不缩小晶体管尺寸的前提下,通过缩短信号传输距离来获得更快的速度和更低的功耗。简单类比,就像通勤一样,大部分能量消耗在路上(传输),而不是在办公桌前工作(计算)。折叠就是缩短通勤距离。

LogicFolding 的实现依赖于先进的3D混合键合工艺,这更像是一种“焊接”而非传统封装:工艺上是两片晶圆对准、加压并退火,形成共价键和铜-铜融合,实现单一体积内的垂直互连。垂直连接的密度决定了折叠是否有效。若间距过大,信号需绕行,反而增加RC延迟。

  • 设计指标:
    • 典型逻辑顶层金属间距:720 nm
    • 当前有效折叠间距:1.5 μm (良好), 1 μm (更佳)
    • 理想目标:720 nm (直接对接顶层金属)
    • 未来展望:480 nm (连接更低金属层,提升设计自由度)

实测数据 (Kirin 2026)

针对“堆叠即过热”的质疑,真实硅测试给出了相反结论:

指标 表现 备注
热性能 优于前代 推翻了3D集成必然过热的假设
晶体管密度 +55% / mm² 单位面积集成度显著提升
功耗 -66% 部分关键任务功耗大幅降低

针对NPU,GPU, CPU, DSP的详细数据比较如下: 0.png

1.png

2.png

3.png

4.png

同性能模式 (Iso-Performance)

在同性能模式 (Iso-Performance)下,Kirin 2026 各模块被限制在与前代 Kirin9030 Pro 相同的输出水平,以验证纯粹的架构能效增益。

模块 性能基准 关键参数变化 功耗降幅 功率密度变化 备注
NPU 29 TOPS 频率 63%
电压 0.85V → 0.55V
** 66%** ** 73%** 收益最大。最密集/最热模块反而降温最多,直接驳斥“堆叠必过热”论。
GPU 61 FPS 电压 200 mV ** 58%** (显著下降) 收益仅次于 NPU,验证高数据吞吐模块受益明显。
CPU HNX 基准分持平 频率 9%
电压 200 mV
** 41%** ( modest saving ) 性能核心获益显著,但功率密度节省是热点模块中最温和的。
DSP 工作量相同 面积 40% 25% 24% 唯一例外。面积收缩速度快于功耗下降速度,导致单位面积热流密度上升。

LogicFolding 的功耗收益并非均匀分布,而是与模块的数据并行度和互连密度强相关。折叠技术赋予了系统前所未有的“控制旋钮”——同一硬件既可在同性能下极致省电,也可在满血模式下刷新性能纪录。模块的数据并行度越高、对带宽需求越大(即“通勤”距离越长),LogicFolding 通过缩短垂直互连带来的回报就越高。NPU/GPU 属于此类,故收益巨大。 LogicFolding 将电路分为三类,采取不同优化策略:

| 电路类型 | 特征 | 代表模块 | 折叠收益机制 | | :———————— | :————- | :—————- | :—————————— | | 真正串行 (Truly Serial) | 需峰值频率/电压,无法并行化 | CPU 大核 (Big Core) | 仅获线性收益 (C↓)。需精细设计,难度大。 | | 可并行化 (Parallelizable) | 宽位宽、数据密集、多副本运行 | NPU, GPU, DSP | 获线性+二次方收益 (C↓ + V²↓)。收益最大区。 | | 混合型 (In-between) | 部分串行部分并行 | 各类控制器 | 介于两者之间。 | | | | | |

满血模式 (Turbo / Full Throttle) 性能

而当解除功耗限制全力运行时,折叠芯片展现了平面芯片无法企及的性能上限:

模块 满血性能表现 相对前代提升 功率密度状态
NPU 70 TOPS ** 141%** 高于平面版
GPU 帧率大幅提升 42% 高于平面版
CPU HNX 分数提升 18% 高于平面版

DSP模块

DSP 模块揭示了一个重要的物理权衡,

  • 第一代折叠 (Kirin 2026) 的功耗降低 25%,但功率密度上升 24%。主要原因是折叠使 DSP 面积缩小了 40%。分母(面积)缩小的速度快于分子(功耗)下降的速度。而对于散热而言,功率密度(W/mm²) 比绝对功耗更关键。
  • 第二代折叠 (Kirin 2027) 的解决方案实测数据显示功率密度低于平面原版,且功耗下降 47%。说明 “Density, in other words, is a design output, not a fate.” (密度是设计出来的结果,而不是注定的命运)。工程师可以通过调整折叠策略来平衡面积与热流密度。

系统的“控制旋钮” (The Control Dial)

这是 LogicFolding 带来的范式转变:

  • 平面芯片:性能与功耗强耦合,选择有限。
  • 折叠芯片:同一硬件模块拥有两种极端形态。
    • Miser (吝啬鬼模式):在同性能下极度省电、低温。
    • Record-setter (破纪录模式):在可接受的热预算内榨取极限性能。 系统调度器可以根据场景(如待机 vs 游戏)动态切换,这是平面架构从未拥有的灵活性。

设计消除热点

对于“3D 堆叠导致热量集中”的质疑,重新定义了热管理的核心指标。

首先纠正错误的度量衡,一般普遍采用总发热量 (Watts)这个错误指标。而实际应该使用热密度 (Heat Density) 和 结温 (Junction Temperature)。 - 芯片失效不是因为消耗了多少瓦,而是因为局部晶体管结温超过了 operating limit。 - 所有值得讨论的热问题,归根结底都是结温问题。

控制结温的两大机制

控制结温主要有两种机制:

  • 机制一:源头降低热密度
    • Iso-performance 下总功耗 genuine drop。
    • 功耗分布在多个有源硅层,而非单一平面 $\rightarrow$ 降低 Watts-per-cubic-millimeter (体积热密度)。
    • 体积热密度直接决定结温上限。
  • 机制二:剩余热量的主动管理
    • 水平扩散:折叠缩小了 NPU 等热点模块 footprint $\rightarrow$ 留出空间让热量在水平方向 spread out $\rightarrow$ 消除局部 hotspot $\rightarrow$ 降低结温。
    • 垂直梯度预算:底层比顶层高几度是 design parameter。将最热模块放置在 heat escape 最容易的位置。
    • 交错布局 (Interleaving):EDA 工具主动将 hot logic 和 cool logic 在不同 tier 间交错放置 $\rightarrow$ 防止热点直接垂直堆叠 (stacking directly atop one another)。

Kirin 2026 采用了保守策略

  • 选择性折叠:仅对 critical paths 应用折叠,而非盲目堆叠一切。
  • 热感知布局 (Thermal-aware placement):结合上述机制,确保结温始终保持在 safe window 内。
]]>
AI时代需要什么互联? http://zhifeiding.github.io/programming/history/2026/06/03/AI%E6%97%B6%E4%BB%A3%E9%9C%80%E8%A6%81%E4%BB%80%E4%B9%88%E4%BA%92%E8%81%94/ 2026-06-03T00:00:00+00:00 http://zhifeiding.github.io/programming/history/2026/06/03/AI时代需要什么互联 概述

当前大模型发展如火如荼,在Scaling Law的加持下模型的参数越来越大,最新的deepseek V4参数量高达1.6万亿。可是,单卡的存储容量还在200GB左右,显然,这么大的模型不可能在单卡上存放,必然会催生多卡分布式的训练和推理的需求。目前业界对多卡互联的讨论也是异常激烈,主要是分成这么几种技术流派:

  • 自研派:以NVIDIA、华为等具备雄厚实力和明确需求的公司为代表,采用自研的互联协议与设备。例如NVIDIA的NVLink/NVSwitch,以及华为的UB(Universal Bus)。这种方案的优点在于可以自主定义链路层与传输层协议,从物理层到软件栈进行垂直优化,从而最大程度优化有效带宽、降低链路延迟,并实现灵活的拓扑结构(如NVL72的扁平化全交换结构),但生态封闭、成本高昂。

  • 联盟派:以AMD、Intel、博通、思科、微软、Meta等厂商为代表,他们联合制定了UALINK、UEC、GLink等互联协议,试图通过产业联盟的方式打破垄断,构建开放生态。其技术路线通常基于成熟的以太网物理层(SerDes)进行增强,在链路层和传输层进行定制,以在性能和开放性之间寻求平衡。

  • 通用派:即大量既无能力参与标准制定,也无力自研协议与设备的中小型厂商或追求极致TCO(总拥有成本)的用户。他们基本采用标准的以太网(Ethernet,特别是RDMA over Converged Ethernet, RoCE)或InfiniBand互联方案,以充分利用现有庞大、成熟的网络设备生态和运维经验。此外,也有部分厂商探索采用PCIe Switch或CXL技术,主要用于内存池化和紧耦合系统内部连接,但如结论所述,在AI计算互联领域存在局限性。

本文将从上述第三类厂商的视角出发,探讨AI时代真正需要什么样的互联技术。下文将首先回顾CPU时代的互联技术,进而阐述AI时代对互联提出的新要求。

结论

按照惯例,我们先给出核心结论。

  • AI的多卡互联需要硬件一致性支持吗?

    不需要​ AI计算与CPU计算的关键区别在于,前者主要涉及大量数据的显式、一次性搬运(如All-Reduce、All-Gather等集合通信操作),通信模式相对规整且由软件框架(如PyTorch, TensorFlow)明确调度。因此,无需额外增加硬件复杂度来支持缓存一致性(Cache Coherence)。只要通过软件屏障(Barrier)、集合通信库(如NCCL)做好数据搬运过程中的同步即可。因此,类似CXL这种以内存一致性为核心、旨在构建统一内存视图的技术,在多卡AI训练推理互联领域前景有限,但其在内存扩展、池化方面对AI推理或内存密集型场景或有价值。

  • AI的多卡互联可以采用Mesh/Torus这类无需交换机的拓扑来降低成本吗?

    不能​ 在MoE(混合专家)模型尚未流行时,Mesh/Torus这类无中心交换机的拓扑或许在特定规整通信模式(如卷积神经网络中相邻层的通信)下还有其市场。但在MoE成为主流的今天,其动态、稀疏、All-to-All的通信模式对网络拓扑的任意两点间通信性能提出了极高要求。Mesh/Torus的网络直径(任意两节点间最大跳数)较长,且跨节点通信(如对角线上芯片间的通信)会引发严重的性能不稳定,这都是不可接受的。更遑论其在系统布线复杂性、故障诊断和可靠性方面的挑战。事实上,Google的TPU采用的2D/3D Torus架构在面临MoE等模型时已显露出不足,最新的TPU v8i已转向非torus拓扑。

  • AI的多卡互联更倾向于平坦网络拓扑还是分层互联拓扑?

    必然是平坦的网络拓扑​ 各种分层网络拓扑天然要求在多卡通信时,必须根据互联拓扑和能力在软件层面(模型并行、流水线并行策略)进行精细的数据切分和任务映射,这给程序员带来了额外的复杂性,且难以实现最优性能。最理想的方案是一个平坦的互联拓扑(例如NVIDIA的NVL72,通过多层NVSwitch使所有72个GPU处于一个交换网内),使得所有卡都能以近似相同的高带宽、低延迟相互通信。这样,开发者就无需为不同的互联层级而额外考虑复杂的数据切分与通信策略,可以更专注于算法本身。

  • 互联协议(如UALINK、UEC、GLink)中,谁将最终胜出?

    目前,全球最大的互联网络——因特网——是基于以太网构建的,市场上也因此存在大量成熟、廉价、可大规模部署的以太网交换设备。而UALINK、UEC、GLink等新兴协议,本质都是基于以太网物理层(112G SerDes及以上)进行的链路层/传输层增强,旨在在性能上对标甚至超越NVLink,在开放性上优于私有协议。然而,这些新协议目前尚缺乏大规模商用的交换设备芯片和生态系统,而AI对互联的需求又极为迫切。参考CPU和HPC(高性能计算)领域的发展路径(从专有总线到开放标准),未来主流很可能仍将是以太网的某种增强版本(可能是上述联盟标准之一,也可能是标准以太网自身的演进),但会形成一个多层次市场:高端封闭系统用私有协议,主流开放集群用增强以太网,成本敏感场景用标准以太网。

  • 常说的Load/Store语义与RDMA语义有何区别?谁会胜出?

    主要区别在于:Load/Store语义(或称“内存语义”)可以更轻松地通过CPU/GPU的访存指令(如LD/ST)直接发起对远端内存的读写,对程序员更友好,类似于访问本地内存。而RDMA属于消息语义,需要预先在通信两端构建发送/接收队列(SQ/RQ/CQ),并通过“工作请求(WR)”的方式异步触发数据传输。两者的核心区别并非是否使用DMA,因为Load/Store操作同样可由DMA引擎完成。区别在于编程模型和适用场景。在紧耦合的互联系统(如同一节点内的多卡,通过NVLink或CXL互联)中,倾向于使用Load/Store语义,以实现极低延迟和简化的编程。在松耦合的互联系统(如跨节点、跨机柜,通过以太网/InfiniBand互联)中,则更倾向于使用RDMA消息语义,因其更适应网络传输的不可靠、带内缓冲等特性。两者将长期共存,分别适用于不同层级的互联。

CPU时代的互联

实际上,多卡互联并非AI时代的新生事物,其历史可以追溯到更早。当前AI时代的互联需求(高带宽、低延迟、可扩展),在早期单芯片CPU性能不足、需要构建大规模SMP(对称多处理器)服务器时同样存在,特别是HPC和超算领域。如今,我们不过是将CPU领域的历史在GPU/AI芯片上以更大的规模和更高的要求重演一遍。下面简要回顾早期SUN和IBM小型机中的互联技术,它们展现了构建大规模紧耦合系统的不同设计哲学;以及超算里互联的一些发展趋势。

SUN的小型机互联

在2012年,SUN推出的M5 CPU有7个CL(Coherence Link)和6个SL(Scalability Link), 每个CL有12个lane,每个lane 12Gbps, 因此每个CL的带宽是144Gbps; 每个SL有4个lane,每个lane也是12Gbps, 因此每个SL的带宽是47Gbps。M5可以通过交换芯片Bixby组成32路SMP系统。 0.png 每个Bixby有16个端口,每个端口是4x16Gbps的互联能力

1.png

这是2012年,这个和现在NVIDIA采用的NVLINK+NVSWITCH形式上是比较像的,有自己的互联协议和互联设备。

到2013年,SPARC M6可以通过互联扩展成更大的系统。 下面通过交换机可以组成48,64或96路SMP系统。 2.png 3.png

2017年的M7 继续扩展SMP系统,可以是现在比较流行的8卡full mesh, 有超过1TB/s的双分带宽。 4.png 或者通过64口的交换机组成32路的SMP系统,可以提供64TB的存储 5.png

IBM的小型机

PoOWER 5是2004年推出的产品,可以通过fabric bus controller (FBC) 控制片间的通信,互联拓扑称为分布式交换,在不同配置下保持一样的行为。高端POWER5系统采用MCM封装,基本单元是8个POWER5 芯片和8个L3芯片封装成两个MCM。MCMs, 内存, 和连接I/O drawers的桥接芯片组成一个book。下图展示了16路POWER5系统:Pasted image 20230914090918.png

POWER5 在MCM内部是通过两个环状总线互联,两个环方向相反。每个总线是8B位宽,运行在处理器频率。一个Book内两个MCM通过4对8B位宽,运行在1/2处理器频率的单向总线互联。一个POWER5 book是16路SMP,对于SMT2,就是32路SMP。4个books可以互联组成64路SMP系统。每个Book中的POWER5芯片和边上芯片通过运行在1/2处理器频率的8B位宽总线互联。下图展示了64路SMP互联示意图:Pasted image 20230914091104.png

而到了2007年的POWER 6,互联就和当前流行的全连接很很像了。4个POWER6芯片组成一个基本互联单元Node,每个POWER6有5个运行在1/2处理器频率的8B位宽的SMP接口,三个专门用于Node内部互联。如下图所示:Pasted image 20230914163617.png 依赖于一致性协议上的创新,一致性总线和数据时分复用同一个物理链路,可以67%带宽分配给数据,33%分配给一致性,或者50%分配给数据,50%分配给一致性。 在一个Node内,4个POWER6组成全连接网络。

不同于之前的环拓扑,POWER6可以使用8个Node组成32路SMP的全连接拓扑,如下图所示:Pasted image 20230914163644.png

到了2017年,POWER 9继续保持了全连接的基本单元。下图展示了基于POWER9的Power E980系统逻辑架构示意图:Pasted image 20230907174711.png 4个POWER 9组成全互联的SMP系统。而不同数量的Node则可以继续互联组成不同规模的Drawer。下图分别展示了由2-, 3-, 和4-drawer组成的SMP系统的示意图:Pasted image 20230907175136.pngPasted image 20230907175155.png

HPC和超算的互联

根据A Survey of High-Performance Interconnection Networks in High-Performance Computer Systems里的统计调研,从Top100到Top500的超算系统中,包括Infiniband,Omnipath, Cray等各种专有互联的比例是在逐渐降低,而Ethernet则是比例稳步提高的。 6.png

关于互联拓扑的选择上,论文也统计了Top10的超算钟采用的拓扑结构,数据显示,除了排名第一的“富岳”(Fugaku,采用6D Torus)和排名第五的“珀尔马特”(Perlmutter,采用Dragonfly)外,可以看到绝大部分都是Fat-tree这样的简单互联拓扑结构。 7.png

AI 互联时代

前文回顾了“古典时代” CPU-SMP 互联,下文简单解析当前 AI 芯片的互联。为了应对万亿参数模型训练与 MoE(混合专家)推理的通信洪峰,各大厂商在物理层、拓扑层和协议层展开了激烈的架构竞赛。本节以 NVIDIA、华为、AMD 和 Google 的代表性互联方案为例说明。

厂商 核心协议 典型拓扑 规模上限 核心优势 生态定位
NVIDIA​ NVLink (私有) NVL72 (全交换) 72卡/域 带宽极高,生态最成熟,网内计算 封闭霸主
华为​ UB (自研) CloudMatrix (Clos+Mesh) 384卡/超级节点 异构资源池化,协议归一,TCO优化 自主可控
AMD​ IF + UALink 8卡 Mesh (当前) 8卡/节点 (当前) 开放联盟路线,CPU 生态优势 开放挑战者
Google​ 定制 ICI 3D Torus / 定制交换 数千卡/Pod 软硬垂直整合,规模极大 内部自用

NVIDIA

NVIDIA 凭借其自研的 NVLink 协议和 NVSwitch 交换芯片,构建了目前业界最成熟的“平坦化”互联生态,形成了“全交换”霸权。其核心设计哲学是消除层级,构建巨型统一算力域。

华为

华为的昇腾(Ascend)体系选择了“统一总线(UB)”路线,其核心思想是协议归一与资源池化,旨在打破 NPU、CPU 和存储之间的传统壁垒。UB是华为自研的异构互联协议,物理层可基于 SerDes 。它不仅连接 NPU-NPU,还直接连接 NPU-CPU,实现了无主从的平等协同通信。CloudMatrix384将 384 颗昇腾 910C NPU 和 192 颗鲲鹏 CPU 通过 UB 网络整合成一个“超级节点”。其内部采用两层 Clos 交换架构,实现了跨节点的近线速通信。

AMD

AMD 的互联策略经历了从“依赖 PCIe PHY”到“拥抱开放联盟”的转变,其当前方案是芯片内互联(IF)与芯片间互联(UALink) 的结合。Infinity Fabric (IF) 是AMD 的底层基础互联协议,用于 MI300X 内部连接 XCD(计算裸片)和 I/O Die,也用于节点内 8 卡全互联。单链路带宽约 50-64 GB/s(双向)。UALink(Ultra Accelerator Link) 则是由 AMD、Google、微软、博通等发起的开放联盟标准。其本质是基于 200G/400G 以太网物理层(SerDes)增强链路层协议,旨在对标 NVLink。当前(MI300X) 在单节点(如 8 卡服务器)内,利用 7 条 Infinity Fabric 链路构建 8 卡全互联 Mesh。这是典型的无交换机直连拓扑,依赖宿主 CPU 的 PCIe 进行 Scale-Out。未来(MI400+) 规划引入 UALink Switch 芯片,目标实现类似 NVL72 的 72 卡级全互联,摆脱对 PCIe 扩展的依赖。

Google

Google 作为超大规模云厂商,其 TPU 互联完全自研,其设计哲学是软硬协同的极致优化。TPU 使用 Google 自研的互联协议,物理层带宽极高(TPU v4 单链路达 200G+),且深度集成于其 TensorFlow/JAX 软件栈。TPU 的互联通道被称为 ICI(Inter-Chip Interconnect),每个 TPU 芯片有多个 ICI 端口。TPU v2/v3 主要采用 2D/3D Torus​ 拓扑,通过近邻连接构建大规模阵列。这种拓扑成本低,但网络直径长,对非规整通信(如 MoE)不友好。最新的 TPU v5 及后续架构已转向基于定制光交换芯片的拓扑(类似 Fat-Tree 或 Clos),以应对动态路由和 All-to-All 通信需求。Google 通过其“Pathways”调度系统,将整个 Pod(数千颗 TPU)虚拟化为一个巨型加速器。而最新的TPUv8i则直接放弃3D Torus,采用交换机加全互联的拓扑结构。

参考

]]>
Huawei Ascend Architecture from 910 to 950 http://zhifeiding.github.io/programming/history/2026/03/05/Huawei-Ascend-Architecture-From-910-to-950/ 2026-03-05T00:00:00+00:00 http://zhifeiding.github.io/programming/history/2026/03/05/Huawei-Ascend-Architecture-From-910-to-950 概述

随着昇腾AI处理器架构的持续演进和不同产品的快速迭代,存在多种不同的架构,例如在最新的下一代架构950在硬件设计上增加了SIMT计算单元,用于提升离散操作场景及复杂分支场景的编程易用性;对华为昇腾AI处理器架构的演进历史进行一次回顾可以帮助了解不同架构的局限和应用需求,因此,本文从Ascend 910里原始DaVinci Core架构开始,最后以950结束,分析不同架构之间的异同。本文所有信息均来自公开渠道,如有错误,还请帮忙指正。

Ascend 910 DaVinci Core

Ascend 910是第一代架构,主要由标量控制单元,向量计算单元和张量积算单元组成:

  • 标量计算单元主要负责地址等标量计算
  • 向量计算单元可以进行归一化,激活等计算;
    • 向量单元还负责数据精度转换,例如 int32、fp16 和 int8 之间的量化和解量化操作;向量单元还可以实现 fp32 操作
  • 张量计算单元主要是矩阵计算,包括卷积,全连接,矩阵乘等;张量计算中矩阵的典型尺寸为 16 x 16 x 16。因此,张量计算单元配备了 4096 个乘法器和 4096 个累加器。

DaVinci核包括多个缓冲区,分成不同层次。

  • L0 缓冲区专用于张量计算单元,分成三个单独的 L0 缓冲区,分别是缓冲区 A L0、缓冲区 B L0 和缓冲区 C L0。分别用于保存输入特征数据、权重和输出特征数据。
  • 缓冲区 A L0 和缓冲区 B L0 中的数据从 L1 缓冲区加载。
  • L0 缓冲区和 L1 缓冲区之间的通信由内存传输引擎MTE(Memory Transfer Engine) 管理。

0.png

Ascend 910X(200) 计算架构

相比之前的架构,该架构主要变化如下;

  • 增加L0C Buffer和Unified Buffer数据通路
  • 增加Unified Buffer和Global Memory数据通路

前一代架构广为人诟病的一点就是矩阵计算单元和向量计算单元不能直接共享数据,必须通过L1;这样会导致算子融合性能收益有限,所以在该架构主要针对这个缺陷做了优化。 1.png 本架构中,Cube计算单元和Vector计算单元同核部署,共享同一个Scalar计算单元。

  • Vector计算单元的数据来源来自于Unified Buffer,要求32字节对齐;
    • 数据从L0C Buffer传输至Unified Buffer需要以Vector计算单元作为中转。
  • Cube计算单元可以访问的存储单元有L0A Buffer、L0B Buffer、L0C Buffer,其中
    • L0A Buffer存储左矩阵,L0B Buffer存储右矩阵,L0C Buffer存储矩阵乘的结果和中间结果。

在该架构中支持核内同步,而不支持核间同步(硬件)。由于AI Core内部的执行单元(如MTE2搬运单元、Vector计算单元等)以异步并行的方式运行,在读写Local Memory(如Unified Buffer)时可能存在数据依赖关系。为确保数据一致性及计算正确性,需通过同步控制协调操作时序。对于软件,主要以SetFlag IDx来调用同步机制,数据从GM通过MTE搬运到UB, 之后Vector计算单元才能开始计算,计算完成的结构之后通过MTE搬运到GM,如下所示:

2.png

Ascend 910X(220) 计算架构

该架构相比之前的设计,主要修改如下:

  • 向量计算单元和矩阵计算单元分离
    • 该改动应该主要是为了更灵活的针对不同产品需求配置Vector和Cube的比例
  • 增加Fixpipe(硬件加速)
    • 增加了FP Buffer和对应的硬件加速器,一方面弥补了Vector的性能,另一方面也可以更好的做一些相关的算子融合
  • 增加BT Buffer ( Bias Table Buffer)
    • 这个改动和上一个也类似,主要是为了弥补Vector的性能,通过专用的硬件在Cube单元里进行带Bias运算可以极大的减轻Vector的压力

该架构相比原始910设计,对于一些算子会有较大提升,但也继承了缺陷,Vector和Cube计算单元缺少直接通信方式,需要通过GM。 3.png

AI Core分为AIC和AIV两个独立的核,分别用于矩阵计算和向量计算。每个核都有自己的Scalar单元,能独立加载自己的代码段。AIV与AIC之间通过Global Memory进行数据传递。

  • Vector计算单元 :Vector计算单元的数据来自于Unified Buffer,要求32字节对齐。
  • Cube计算单元 :Cube计算单元可以访问的存储单元有L0A Buffer、L0B Buffer、L0C Buffer,其中L0A Buffer存储左矩阵,L0B Buffer存储右矩阵,L0C Buffer存储矩阵乘的结果和中间结果。
  • 支持Fixpipe硬件化加速:Fixpipe是NPU将典型操作进行硬化的加速模块,位于AIC内部,配合Cube计算单元完成随路计算,主要功能如下:
    • 量化反量化:包括S322FP16、S322S32、S322S4、S322S8、S322S16、FP322FP16、FP322BF16、FP322S8、FP322S4、FP322FP32。
    • Relu功能,包括ReLu、PReLu和Leaky ReLu等典型的激活函数。
    • 数据格式转换

4.png 该架构支持核间同步。当不同核之间操作同一块全局内存时,可能存在读后写、写后读以及写后写等数据依赖问题,需要进行核间同步控制。核间同步控制分为以下几种模式:

  • 模式0:AI Core核间的同步控制。
    • 对于AIC场景,同步所有的AIC核,直到所有的AIC核都执行到CrossCoreSetFlag时,CrossCoreWaitFlag后续的指令才会执行;
    • 对于AIV场景,同步所有的AIV核,直到所有的AIV核都执行到CrossCoreSetFlag时,CrossCoreWaitFlag后续的指令才会执行。
  • 模式1:AI Core内部,AIV核之间的同步控制。如果两个AIV核都运行了CrossCoreSetFlag,CrossCoreWaitFlag后续的指令才会执行。
  • 模式2:AI Core内部,AIC与AIV之间的同步控制。在AIC核执行CrossCoreSetFlag之后, 两个AIV上CrossCoreWaitFlag后续的指令才会继续执行;两个AIV都执行CrossCoreSetFlag后,AIC上CrossCoreWaitFlag后续的指令才能执行。 5.png

算子中进行带bias的矩阵乘计算时,可将bias数据搬运至C2(Bias Table Buffer)上,调用一次Mmad接口实现矩阵乘加bias的计算,或者直接调用Matmul高阶API完成功能。相比于先将矩阵乘的结果从CO1(L0C)搬运到GM上,再搬运到UB上进行加bias的过程,减少了数据搬运的次数,可提升内存使用效率。下图分别是不带BT Buffer和带BT Buffer运算的差别: 6.png

7.png

算子实现中对矩阵乘结果进行量化计算时,可将量化参数搬运到C2PIPE2GM(Fixpipe Buffer)上,调用一次Fixpipe接口实现矩阵乘结果的量化计算。相比于将矩阵乘的结果从CO1(L0C)搬运到GM,再从GM搬运到UB,在UB进行量化计算的过程,数据搬运的次数更少,内存使用效率更高。下图分别是不带FP Buffer和带FP Buffer运算的差别: 8.png

9.png

Ascend 910X(300) 计算架构 - 偏端侧

该架构应该是偏端侧应用,所以Cube和Vector计算单元还是延续早期设计,在前端耦合; 同时引入之前的BT Buffer和FP Buffer来加速,降低Vector的需求。Cube计算单元和Vector计算单元同核部署,共享同一个Scalar计算单元。

  • Vector计算单元:数据来源来自于Unified Buffer,要求32字节对齐。
  • Cube计算单元:可以访问的存储单元有L0A Buffer、L0B Buffer、L0C Buffer,其中L0A Buffer存储左矩阵,L0B Buffer存储右矩阵,L0C Buffer存储矩阵乘的结果和中间结果。

该硬件架构不支持核间同步。 10.png

Ascend 950 计算架构

最新的架构则是Ascend 950,分为950PR(Prefill & Recommendation) 和950DT(Decode & Training)。在上一代基础上增加了SIMT模式的支持;同时结合了之前几代架构的修改,将Cube和Vector解耦,但同时增加了之间通信的数据通路。一个AI Core包含AIV和AIC的组合。 对于AIV:

  • 可支持两种工作模式:SIMT和SIMD,以VF(Vector Fuction) 为粒度进行切换;
  • 同一时刻不能同时支持SIMD和SIMT;
  • SIMT可以支持
    • 支持直接读写GM或UB (UB当做Shared Memory)
    • 一个SIMT Vector Function 运行一个 thread block,同一时刻一个AIV只能执行一个SIMT Vector Function
    • 每个AIV有4个Warp Scheduler, scheduler id = warp id % 4
    • 每个线程可使用的寄存器受线程个数限制
    • 最大支持128KB Data Cache,Data Cache直接复用UB作为Cacheline
  • SIMD优化包括:
    • 从A2/A3Memorybased架构切换到Regbased架构,增加SIMD Register File存储层次,可以直接对芯片的Vector寄存器Register进行操作,实现更大的灵活性和更好的性能。
    • 数据从Unified Buffer搬运到Register进行计算,产生的中间结果可以不用传回Unified Buffer,直接在寄存器计算;

对于AIC:

  • 新增MX FP4/MX FP8类型;
  • 增强单核算力,增大核数;
  • 不再支持s4类型;不支持4:2稀疏矩阵; 11.png 除了SIMT功能的增加,额外的优化包括新的数据通路:

  • 增加L0C Buffer -> Unified Buffer、Unified Buffer <-> L1 Buffer的数据通路;
  • 删除Global Memory -> L0A Buffer、Global Memory -> L0B Buffer的数据通路。删除L1 Buffer->Global Memory的数据通路;
  • MTE2 数据搬运能力增强:新增NDDMA搬运、新增Compact模式、增强搬运维度;
  • SSBuffer,用于AIC和AIV的核间通信;

对于SIMT API,基本可以做到和业界SIMT编程接口一一对应,如下所示: 12.png

两者实现相同Reduce算子的代码,不能说一模一样,基本上也没太多差别。 13.png

吸取之前架构的优点,AIV 和 AIC 支持独立运行机制。AIC和AIV独立运行机制,又称双主模式。在分离架构下,区别于MIX模式(包含矩阵计算和矢量计算)通过消息机制驱动AIC运行,双主模式为AIC和AIV独立运行代码,不依赖消息驱动,使能双主模式能够提高Matmul计算性能。默认情况下,双主模式不使能,需要通过MatmulConfig中的enableMixDualMaster参数开启。

]]>