目录

LLM Serving System的计算与通信建模

符号表示与基础参数

基础参数

参数 符号 示例值 含义 对性能的影响
批量大小 $B$ 16 同时处理多少个请求 $B$↑ → 硬件利用率↑, KV占用↑
序列长度 $S$ 4096 输入文本的token数 $S$↑ → 计算↑(Prefill), 内存↑(Decode)
隐藏维度 $d$ 7168 Transformer内部表示维度 $d$↑ → 计算∝$d^2$, 内存∝$d$
层数 $L$ 61 Transformer块的数量 $L$↑ → 总参数∝$L$, 推理时间∝$L$
注意力头数 $H$ 128 多头注意力的头数 $H$↑ → TP上限↑

并行策略参数

参数 符号 示例值 含义 关键关系
张量并行度 $TP$ 8 将单个tensor切分 $H \% TP = 0$,每个GPU处理$H/TP$个头
流水线并行度 $PP$ 4 将层切分到多个GPU 每个stage处理$L/PP$层,存在bubble开销
数据并行度 $DP$ 2 将batch切分 每个GPU处理$B/DP$个请求,各副本独立推理
专家并行度 $EP$ 64 将专家切分到多个GPU 每个GPU存储$E/EP$个专家,需All-to-All通信
总GPU数 $N_{GPU}$ 512 集群中GPU总数 $N_{GPU} = TP \times PP \times DP \times EP$
通信组大小 $TP_{comm}$ 8 单次通信涉及的GPU数 TP需要All-Reduce,EP需要All-to-All
每GPU参数量 $P_{GPU}$ ~50GB 单GPU存储的参数量 $P_{GPU} \approx P_{total}/(TP \times PP \times EP)$,DP不影响参数量

Continuous Batching参数(实际Serving场景)

参数 符号 含义 与简化模型的关系
Prefill请求数 $B_p$ 正在做Prefill的请求数 动态变化
Decode请求数 $B_d$ 正在做Decode的请求数 动态变化
总请求数 $B = B_p + B_d$ 当前batch中的总请求数 简化模型中$B$为固定值
Prefill token总数 $T_p = \sum_{i=1}^{B_p} S_i$ 所有Prefill请求的token总数 各请求序列长度$S_i$不同
Decode token数 $T_d = B_d$ 所有Decode请求生成的token数 每个请求生成1个token
总token数 $T_{total} = T_p + T_d$ 实际处理的token总数 通信量∝$T_{total}$,而非$B \times S$

并行策略详解(简化模型)

:以下分析为简化模型,假设batch内所有请求处于同一阶段(纯Prefill或纯Decode)。实际serving场景会使用continuous batching,详见后续章节。

TP对Attention的影响

TP的All-Reduce通信量推导

完整计算(无TP)

$$Y = X \times W$$
  • $X: (N, d_{in})$ - 输入
  • $W: (d_{in}, d_{out})$ - 权重矩阵
  • $Y: (N, d_{out})$ - 输出

TP切分(权重按行切分)

  1. 输入切分:每个GPU持有输入的一部分 $(N, d_{in}/TP)$

  2. 权重按行切分

    1
    2
    3
    4
    
    完整权重: W = (d_in, d_out)
    
    切分为TP份:
    GPU i: W_i = (d_in/TP, d_out)  <- 第 i×(d_in/TP) 到 (i+1)×(d_in/TP)-1 行
    
  3. 局部计算: 每个GPU计算:$Y_i = X_i \times W_i = (N, d_{in}/TP) \times (d_{in}/TP, d_{out}) \to (N, d_{out})$

    注意:每个GPU都产生$(N, d_{out})$的输出,但这些是部分结果

  4. 为什么是部分结果?

    完整矩阵乘法应该是:

    $$Y = \begin{bmatrix} X_0 & X_1 & \cdots & X_{TP-1} \end{bmatrix} \times \begin{bmatrix} W_0 \\ W_1 \\ \vdots \\ W_{TP-1} \end{bmatrix} = \sum_{i=0}^{TP-1} X_i W_i$$

    每个GPU只计算一项,需要All-Reduce求和得到最终结果。

  5. All-Reduce通信量

    • 需要同步的数据大小:$(N \times d_{out})$个元素
    • 实际通信量(Ring All-Reduce算法,每GPU): $$\text{发送} + \text{接收} = 2 \times \frac{TP-1}{TP} \times N \times d_{out} \text{ 个元素}$$
    • 当$TP=8$时:实际传输 $1.75 \times N \times d_{out}$ 个元素

通用规律

所有使用TP按行切分权重的层(输出投影、FFN第二层等),All-Reduce通信量取决于输出形状

$$\text{需要同步} = N \times d_{out}, \quad \text{实际传输(每GPU)} = 2 \times \frac{TP-1}{TP} \times N \times d_{out}$$

简化约定

为简洁,后文"通信量"指需要同步的数据大小(即输出大小),实际传输量需乘以系数 $2(TP-1)/TP$。

Prefill vs Decode的本质区别

虽然两者都是矩阵-矩阵乘法(当batch size $B > 1$时),但性质完全不同:

维度 Prefill Decode
输入形状 $(B, S, d)$,$S$大(如2048) $(B, 1, d)$,序列长度为1
矩阵乘法 $(B, S, d) \times (d, d)$,大矩阵 $(B, d) \times (d, d)$,小矩阵
计算量 $2BSd^2$ $2Bd^2$
算术强度 $\frac{2BSd^2}{2d^2} \approx BS$(高) $\frac{2Bd^2}{2d^2} \approx B$(低)
瓶颈 Compute-bound Memory-bound
GPU利用率 高(80-90%) 低(10-30%)
优化方向 提高并行度(TP)、计算优化 减少访存(量化、KV Cache压缩)
通信影响 可被计算掩盖 延迟直接暴露

标准MHA(Multi-Head Attention)

Prefill阶段

步骤1:QKV投影

计算:

  • 完整计算:$Q, K, V = X \times W_Q, X \times W_K, X \times W_V$

    • 输入 $X: (B, S, d)$
    • 权重 $W_Q, W_K, W_V: (d, d)$
    • 输出 $Q, K, V: (B, S, d)$ → reshape成$(B, S, H, d_h)$
  • TP切分:每个GPU处理$H/TP$个头,权重矩阵按列切分

  • 每个GPU上的shape

    • 输入 $X$: $(B, S, d)$(广播到所有GPU)
    • 权重 $W_Q, W_K, W_V$: $(d, \frac{d}{TP})$(列切分)
    • 输出 $Q, K, V$: $(B, S, \frac{d}{TP})$ → reshape成$(B, S, \frac{H}{TP}, d_h)$
  • 每个GPU计算量:$3 \times \frac{2BS \times d \times d}{TP} = \frac{6BSd^2}{TP}$(Q、K、V三个投影)

  • KV Cache占用

    • 每GPU每层:$2 \times B \times S \times \frac{d}{TP}$($\frac{H}{TP}$个头,每头K和V各$B \times S \times d_h$)
    • 所有GPU总计:$2 \times B \times S \times d$

通信:无(TP按列切分,各GPU独立计算)

步骤2:注意力计算

计算:Scaled Dot-Product Attention

  • 每个GPU上的shape

    • 输入Q, K, V:$(B, S, \frac{H}{TP}, d_h)$(每个GPU处理$\frac{H}{TP}$个头)
    • 将batch和序列维度合并:$(B \times S, \frac{H}{TP}, d_h)$ 或看作每个头处理$(B, S, d_h)$
  • 计算流程(每个头独立):

    1. $\text{Score} = \frac{Q \times K^T}{\sqrt{d_h}}$: $(B, S, d_h) \times (B, d_h, S) \to (B, S, S)$(attention score矩阵)
    2. $\text{AttentionWeights} = \text{Softmax}(\text{Score})$: $(B, S, S)$(对每行做softmax)
    3. $\text{Output} = \text{AttentionWeights} \times V$: $(B, S, S) \times (B, S, d_h) \to (B, S, d_h)$
  • 输出合并

    • 每个GPU输出:$(B, S, \frac{H}{TP}, d_h)$
    • Concat heads后:$(B, S, \frac{d}{TP})$
      • 将 $\frac{H}{TP}$ 个头的维度 $d_h$ 拼接:$\frac{H}{TP} \times d_h = \frac{d}{TP}$
  • 每GPU计算量:$\frac{2BS^2 \times d}{TP}$($Q \times K^T$和$\text{AttentionWeights} \times V$各占一半)

通信:无(各GPU独立计算各自的头)

步骤3:输出投影

计算:

  • 完整计算:$Y = \text{Concat}(Q, K, V) \times W_O$

    • 输入(concat后的attention输出): $(B, S, d)$
    • 权重 $W_O$: $(d, d)$
    • 输出 $Y$: $(B, S, d)$
  • TP切分:权重矩阵按行切分

  • 每个GPU上的shape

    • 输入: $(B, S, \frac{d}{TP})$(每个GPU的attention输出)
    • 权重 $W_O$: $(\frac{d}{TP}, d)$(行切分)
    • 局部输出: $(B, S, \frac{d}{TP}) \times (\frac{d}{TP}, d) \to (B, S, d)$(部分结果)
  • 每GPU计算量:$\frac{2BS \times d \times d}{TP}$

通信:

  • All-Reduce:合并所有GPU的部分结果$(B, S, d)$
  • 通信量:$B \times S \times d$ 个元素(每层)
  • $S$很大时通信压力大,但可被计算掩盖
  • 通信/计算比:$\frac{BSd}{BSd^2/TP} = \frac{TP}{d} \approx 0.1\%$($d=7168, TP=8$时)
Decode阶段

步骤1:QKV投影

计算:

  • 完整计算:$Q, K, V = X_{new} \times W_Q, X_{new} \times W_K, X_{new} \times W_V$

    • 输入 $X_{new}$(新token): $(B, d)$
    • 权重 $W_Q, W_K, W_V$: $(d, d)$
    • 输出 $Q, K, V$: $(B, d)$ → reshape成$(B, H, d_h)$
    • K和V追加到KV Cache
  • TP切分:权重矩阵按列切分

  • 每个GPU上的shape

    • 输入 $X_{new}$: $(B, d)$(广播到所有GPU)
    • 权重 $W_Q, W_K, W_V$: $(d, \frac{d}{TP})$(列切分)
    • 输出 $Q, K, V$: $(B, \frac{d}{TP})$ → reshape成$(B, \frac{H}{TP}, d_h)$
  • 计算量:$3 \times \frac{2B \times d^2}{TP} = \frac{6Bd^2}{TP}$(Q、K、V三个投影)

  • KV Cache占用($S$逐渐增长):

    • 每GPU每层:$2 \times B \times S \times \frac{d}{TP}$
    • 所有GPU总计:$2 \times B \times S \times d$

通信:无

步骤2:注意力计算

计算:新token的Q与所有历史KV做注意力

  • 每个GPU上的shape

    • $Q$(新token): $(B, \frac{H}{TP}, d_h)$(序列长度为1)
    • $K$(历史,从KV Cache读取): $(B, S, \frac{H}{TP}, d_h)$($S$是历史长度,包含新token)
    • $V$(历史,从KV Cache读取): $(B, S, \frac{H}{TP}, d_h)$
  • 计算流程(每个头独立,每个GPU处理$\frac{H}{TP}$个头):

    1. $\text{Score} = \frac{Q \times K^T}{\sqrt{d_h}}$: $(B, 1, d_h) \times (B, d_h, S) \to (B, 1, S)$
    2. $\text{Weights} = \text{Softmax}(\text{Score})$: $(B, 1, S)$
    3. $\text{Output} = \text{Weights} \times V$: $(B, 1, S) \times (B, S, d_h) \to (B, 1, d_h)$
  • 输出合并

    • 每个GPU输出:$(B, \frac{H}{TP}, d_h)$
    • Concat heads维度:$(B, \frac{d}{TP})$
  • 每GPU计算量:$\frac{2B \times S \times d}{TP}$($S$是历史长度)

访存(主要瓶颈):需要读取所有KV Cache

  • 每GPU访存量:$2 \times B \times S \times L \times \frac{d}{TP}$(L层的K和V)
  • 所有GPU总计:$2BSLd$

通信:无

步骤3:输出投影

计算:

  • 完整计算:$Y = \text{Concat}(\text{Output heads}) \times W_O$

    • 输入(concat后的attention输出): $(B, d)$
    • 权重 $W_O$: $(d, d)$
    • 输出 $Y$: $(B, d)$
  • TP切分:权重矩阵按行切分

  • 每个GPU上的shape

    • 输入: $(B, \frac{d}{TP})$(每个GPU的attention输出)
    • 权重 $W_O$: $(\frac{d}{TP}, d)$(行切分)
    • 局部输出: $(B, \frac{d}{TP}) \times (\frac{d}{TP}, d) \to (B, d)$(部分结果)
  • 每GPU计算量:$\frac{2B \times d \times d}{TP}$

通信:

  • All-Reduce:合并所有GPU的部分结果
  • 通信量:$B \times d$ 个元素(相比Prefill少$S$倍)
  • 关键问题:通信延迟(latency)成为瓶颈
    • Prefill:通信量大但计算更大,通信可被掩盖
    • Decode:计算快(访存受限),通信延迟暴露
  • 通信/计算比和Prefill一致:$\frac{Bd}{Bd^2/TP} = \frac{TP}{d} \approx 0.1\%$

MLA(Multi-head Latent Attention,DeepSeek V3)

核心思想

通过低秩联合压缩将 K、V 压缩到低维潜在空间存储,同时对 Q 也进行压缩以减少训练激活内存。推理时采用矩阵吸收优化,避免显式解压巨大矩阵,实现极致的 KV Cache 和内存节省。在本分析中,我们简化了这些细节,具体细节可以参照另一篇博客。

DeepSeek V3 关键参数

  • $d = 7168$ (Model Hidden Size)
  • $H = 128$ (Attention Heads 数,真实多头结构中)
  • $d_v^h = 128$ (Per-head V Dimension,V的单头维度)
  • $d_{qk}^h = 192$ (Per-head Q/K Dimension,Q/K的单头维度)
    • 其中 $d_{qk}^h = d_C^h + d_R^h = 128 + 64$
  • $d_C^h = 128$ (Q, K Non-RoPE head dimension)
  • $d_R^h = 64$ (Q, KRoPE head dimension,Decoupled)
  • $d^c_{kv} = 512$ (KV 压缩维度,潜在空间中)
  • $d^c_q = 1536$ (Query 压缩维度,潜在空间中)

Latent Vector

  • 位置:压缩维度中 ($d^c_{kv} = 512$, $d^c_q = 1536$)
  • 形式:一维向量,没有显式的多头张量形状
  • 存储:存在 KV Cache 中,但由于被压缩成不是一个个头的形式,所以TP的每个GPU都要有一份完整的拷贝。
  • 例子:$c_{KV}: (B, S, 512)$ 看不出有128个头的信息

Attention Head的划分与并行

  • 位置:Up-Projection时,注意力头会重新出现,此时可以通过TP来切分矩阵。

Prefill 阶段

步骤 1: Q 的 Down-Projection(查询压缩,无头概念)

计算:输入投影到低维潜在空间

$$c^Q_t = h_t \times W^{DQ}$$
  • 输入 $h_t$: $(B, S, d) = (B, S, 7168)$
  • 权重 $W^{DQ}$: $(d, d^c_q) = (7168, 1536)$
  • 输出 $c^Q_t$: $(B, S, d^c_q) = (B, S, 1536)$

关键特征

  • 这一步没有任何"头"的概念
  • $c^Q_t$ 是一维向量,信息压缩在1536维中,每个GPU都有这个latent vector的副本。

每个GPU计算量: $2BS \times d \times d^c_q$

步骤 2: Q 的 Up-Projection 和多头展开

第一步:线性投影展开维度

$$q_t^C = c^Q_t \times W^{UQ}$$
  • 输入 $c^Q_t$: $(B, S, 1536)$
  • 权重 $W^{UQ}$: $(d^c_q, H \times d_{C}^h) = (1536, 128 \times 128) = (1536, 24576)$
  • 输出 $q_t^C$: $(B, S, 24576)$

第二步:Reshape 成多头结构

$$q_t^C = \text{reshape}(q_t^C, (B, S, H, d_C^h)) = (B, S, 128, 128)$$
  • 现在有 $H=128$ 个头的显式结构
  • 每个头的 Non-RoPE 维度:$d_C^h = 128$

第三步:RoPE 部分

$$q^R_t = \text{RoPE}(c^Q_t \times W^{QR})$$
  • 输入 $c^Q_t$: $(B, S, 1536)$
  • 权重 $W^{QR}$: $(d^c_q, H \times d_R^h) = (1536, 128 \times 64) = (1536, 8192)$
  • 输出 $q^R_t$: $(B, S, 8192)$ → reshape 为 $(B, S, 128, 64)$
  • 应用 RoPE 位置编码

第四步:拼接成完整Q

$$q_{t} = [q_t^C; q_t^R] = (B, S, 128, 192)$$

TP 切分(按列切分):

  • 输入 $c^Q_t$: $(B, S, d^c_q)$
  • 权重 $W^{UQ}$: $(d^c_q, \frac{H}{TP} \times d_C^h)$,$W^{QR}$: $(d^c_q, \frac{H}{TP} \times d_R^h)$
  • 输出 $q_t$: $(B, S, \frac{H \times d_{qk}^h}{TP})$ → reshape 为 $(B, S, \frac{H}{TP}, d_{qk}^h)$

每个GPU计算量 : $\frac{2BS \times d^c_q \times H \times d_{qk}^h}{TP}$

步骤 3: KV 的 Down-Projection(K、V 联合压缩,无头概念)

计算

$$c^{KV}_t = h_t \times W^{DKV}$$
  • 输入 $h_t$: $(B, S, d) = (B, S, 7168)$
  • 权重 $W^{DKV}$: $(d, d^c_{kv}) = (7168, 512)$
  • 输出 $c^{KV}_t$: $(B, S, d^c_{kv}) = (B, S, 512)$

关键特征

  • 这一步也没有任何"头"的概念
  • 128个头的K、V信息全部压缩成512维向量

KV Cache 存储

  • 每个TP rank都要重复存储 $c^{KV}_t$: $(B, S, 512)$
  • 由于没有头结构,不再可以按头划分到TP

每GPU相比标准MHA

  • MHA KV Cache:$\frac{2 \times B \times S \times d }{TP} = \frac{2 \times B \times S \times 7168}{TP}$
  • MLA KV Cache:$B \times S \times d^c_{kv} = B \times S \times 512$
  • TP规模越大,KV Cache的重复越多,MLA的优势越低

每GPU计算量:$2 \times B \times S \times d \times d^c_{kv}$

步骤 4: K 的 Up-Projection

第一步:线性投影展开维度

$$k_t^C = c^{KV}_t \times W^{UK}$$
  • 输入 $c^{KV}_t$: $(B, S, 512)$
  • 权重 $W^{UK}$: $(d^c_{kv}, H \times d_C^h) = (512, 128 \times 128) = (512, 16384)$
  • 输出 $k_t^C$: $(B, S, 16384)$

第二步:Reshape 成多头结构

$$k_t^C = \text{reshape}(k_t^C, (B, S, H, d_C^h)) = (B, S, 128, 128)$$

第三步:RoPE 部分

$$k^R_t = \text{RoPE}(h_t \times W^{KR})$$
  • 输入 $h_t$: $(B, S, d)= (B, S, 7168)$
  • 权重 $W^{KR}$: $(d, d_R^h) = (7168, 64)$
  • 输出 $k^R_t$: $(B, S, 64)$ (注意只有一个头)

第四步:拼接成完整K

$$k_t = [k_t^C; k^R_t] = (B, S, 128, 192)$$

TP 切分不切分第三步

  • 输入 $c^{KV}_t$: $(B, S, d^c_{kv})$
  • 权重 $W^{UK}$: $(d^c_{kv}, \frac{H}{TP} \times d_C^h)$
  • 输出 $k_t^C$: $(B, S, \frac{H \times d_C^h}{TP})$

每GPU的计算量: $\frac{2BS \times d^c_{kv} \times H \times d_C^h}{TP} + {2BS \times d \times d_R^h}$

步骤 5: V 的 Up-Projection

$$v_{t} = c^{KV}_t \times W^{UV}$$

每个GPU计算

  • 输入 $c^{KV}_t$: $(B, S, 512)$
  • 权重 $W^{UV}$: $(d^c_{kv}, \frac{H}{TP} \times d_v^h) = (512, \frac{128}{TP} \times 128)$
  • 输出 $v_{t}$: $(B, S, \frac{128}{TP}, 128)$

计算量: $\frac{2BS \times d^c_{kv} \times H \times d_v^h}{TP}$

步骤 6: Attention 计算(128个多头做)

每个 GPU 上的 shape

  • $Q$: $(B, S, \frac{H}{TP}, d_{qk}^h) = (B, S, \frac{128}{TP}, 192)$
  • $K$: $(B, S, \frac{H}{TP}, d_{qk}^h) = (B, S, \frac{128}{TP}, 192)$
  • $V$: $(B, S, \frac{H}{TP}, d_{v}^h) = (B, S, \frac{128}{TP}, 128)$

计算流程(128个头独立计算):

  1. Score 计算

    $$\text{Score} = \frac{Q \times K^T}{\sqrt{d_{qk}^h}}$$
    • 形状: $(B, \frac{H}{TP}, S, S)$
  2. Attention 权重

    $$\text{Weights} = \text{Softmax}(\text{Score})$$
    • 形状: $(B, \frac{H}{TP}, S, S)$
  3. 加权求和

    $$\text{Output} = \text{Weights} \times V$$
    • 输出: $(B, S, \frac{H \times d_{v}^h}{TP})$

计算量

  • Score: $\frac{H}{TP}\times2BS^2 \times d_{qk}^h$
  • Attention×V: $\frac{H}{TP}\times2BS^2 \times d_{v}^h$
  • 总计: $\frac{H}{TP} \times 2BS^2 \times (d_{qk}^h + d_{v}^h)$

步骤 6: 输出投影(降维回隐藏维度)

计算

$$y_t = \text{output} \times W_O$$
  • 输入(Attention 输出): $(B, S, H \times d_{v}^h) = (B, S, 16384)$
  • 权重 $W_O$: $(H \times d_{v}^h, d) = (16384, 7168)$
  • 输出 $y_t$: $(B, S, d) = (B, S, 7168)$

TP 切分(按行切分):

  • 输入: $(B, S, \frac{H \times d_{v}^h}{TP})$
  • 权重 $W_O$: $(\frac{H \times d_{v}^h}{TP}, d)$
  • 输出: $(B, S, d)$(部分结果)

计算量: $\frac{2BS \times H \times d_{v}^h \times d}{TP}$

通信

  • All-Reduce: $BS \times d = 7168 \times BS$ 元素(与 MHA 相同)

Prefill 阶段总结

步骤 涉及的头 计算量 (每 GPU) 说明
Q Down ❌ 无 $2BS \times d \times d^c_q$ Query 压缩,无头概念
Q Up ✅ 128 $\frac{2BS \times d^c_q \times H \times (d_C^h + d_R^h)}{TP}$ Query 解压,多头展开 + RoPE
KV Down ❌ 无 $2BS \times d \times d^c_{kv}$ KV 联合压缩,无头概念,KV Cache每个TP都有副本
K Up ✅ 128 $\frac{2BS \times d^c_{kv} \times H \times d_C^h}{TP} + {2BS \times d \times d_R^h}$ K 解压,多头展开 + RoPE
V Up ✅ 128 $\frac{2BS \times d^c_{kv} \times H \times d_v^h}{TP}$ V 解压,头聚合形式
Attention ✅ 128 $\frac{2BS^2 \times (d_{qk}^h + d_v^h)}{TP}$ Score 和加权求和
Output ✅ 128→1 $\frac{2BS \times H \times d_v^h \times d}{TP}$ 多头合并,投影回隐藏维度
Decode 阶段

步骤 1: Q 的 Down-Projection(查询压缩,无头概念)

与Prefill相同

步骤 2: Q 的 Up-Projection 和多头展开

第一步:线性投影展开维度

$$q_t^C = c^Q_t \times W^{UQ}$$
  • 输入 $c^Q_t$: $(B, 1, 1536)$
  • 权重 $W^{UQ}$: $(d^c_q, H \times d_{C}^h) = (1536, 128 \times 128) = (1536, 24576)$
  • 输出 $q_t^C$: $(B, 1, 24576)$

第二步:Reshape 成多头结构

$$q_t^C = \text{reshape}(q_t^C, (B, 1, H, d_C^h)) = (B, 1, 128, 128)$$
  • 现在有 $H=128$ 个头的显式结构
  • 每个头的 Non-RoPE 维度:$d_C^h = 128$

第三步:吸收矩阵$W^{UK}$到$$q_t^C$$

$$q_t^{C\_absorb} = q_t^C \times {W^{UK}}^T$$
  • 输入$$q_t^C :(B, 1, 128, 128)$$
  • 权重 $W^{UK}$: $(d^c_{kv}, H,d_C^h) = (512, 128,128)$
  • 输出$$q_t^{C\_absorb}:(B, 1, H, d^c_{kv})=(B, 1, 128, 512)$$

第四步:RoPE 部分

$$q^R_t = \text{RoPE}(c^Q_t \times W^{QR})$$
  • 输入 $c^Q_t$: $(B, 1, 1536)$
  • 权重 $W^{QR}$: $(d^c_q, H \times d_R^h) = (1536, 128 \times 64) = (1536, 8192)$
  • 输出 $q^R_t$: $(B, 1, 8192)$ → reshape 为 $(B, 1, 128, 64)$
  • 应用 RoPE 位置编码

第五步:拼接成完整Q

$$q_{t} = [q_t^{C\_absorb}; q_t^R] = (B, 1, 128, 576)$$

TP 切分(按列切分):

  • 输入 $c^Q_t$: $(B, 1, d^c_q)$
  • 权重 $W^{UQ}$: $(d^c_q, \frac{H}{TP} \times d_C^h)$,$W^{UK}$: $(d^c_{kv}, \frac{H}{TP}\times d_C^h)$,$W^{QR}$: $(d^c_q, \frac{H}{TP} \times d_R^h)$
  • 输出 $q_t$: $(B, 1, \frac{H \times d_{qk}^h}{TP})$ → reshape 为 $(B, 1, \frac{H}{TP}, d_{qk}^h)$

每个GPU计算量 (忽略RoPE位置编码): $\frac{2B \times d^c_q \times H \times (d_{qk}^h+d_R^h)+2B \times d^c_{kv} \times H \times d_{qk}^h}{TP}$

步骤 3: KV 的 Down-Projection

第一步与Prefill一致,计算:

$$c^{KV}_t = h_t \times W^{DKV}$$
  • 输入 $h_t$: $(B, 1, d) = (B, 1, 7168)$
  • 权重 $W^{DKV}$: $(d, d^c_{kv}) = (7168, 512)$
  • 输出 $c^{KV}_t$: $(B, 1, d^c_{kv}) = (B, 1, 512)$

利用先前压缩latent vector的KV Cache 存储

第二步:RoPE 部分

$$k^R_t = \text{RoPE}(h_t \times W^{KR})$$
  • 输入 $h_t$: $(B, 1, d)= (B, 1, 7168)$
  • 权重 $W^{KR}$: $(d, d_R^h) = (7168, 64)$
  • 输出 $k^R_t$: $(B, 1, 64)$ (注意只有一个头)

第三步:拼接成完整K

$$k_t = [c^{KV}_t; k^R_t] = (B, 1, 576)$$

TP 不能切分该步骤

每GPU的计算量: ${2B \times d \times (d^c_{kv} + d_R^h)}$

步骤 4: Attention 计算(MQA)

每个 GPU 上的 shape

  • $Q$: $(B, 1, \frac{H}{TP}, d_{kv}^c+d^h_R) = (B, 1, \frac{128}{TP}, 576)$
  • $K$: $(B, S, d_{kv}^c+d^h_R) = (B, S, 576)$
  • $V$: $(B, S, d_{kv}^c) = (B, S, 512)$

计算流程(128个头独立计算):

  1. Score 计算

    $$\text{Score} = \frac{Q \times K^T}{\sqrt{d_{qk}^h}}$$
    • 形状: $(B, \frac{H}{TP}, 1, S)$
  2. Attention 权重

    $$\text{Weights} = \text{Softmax}(\text{Score})$$
    • 形状: $(B, \frac{H}{TP}, 1, S)$
  3. 加权求和

    $$\text{Output} = \text{Weights} \times V$$
    • V此时不需要被升维还原成多头形式
    • 输出: $(B, \frac{H}{TP}, 1, d_{kv}^c)$

每个GPU计算量

  • Score: $\frac{H}{TP}\times2BS \times (d_{kv}^c+d^h_R) $
  • Attention×V: $\frac{H}{TP}\times2BS \times d_{kv}^c$
  • 总计: $\frac{H}{TP}\times2BS \times (2d_{kv}^c+d^h_R) $

步骤 5: V 的 Up-Projection应用到MQA结果

$$Out_{up} = \text{Output} \times W^{UV}$$

每个GPU计算

  • 输入 上一步MQA的Output: $(B, \frac{H}{TP}, 1, d_{kv}^c)=(B, \frac{H}{TP}, 1, 512)$
  • 权重 $W^{UV}$: $(\frac{H}{TP}, d^c_{kv}, d_v^h) = (\frac{128}{TP} ,512, 128)$
  • 输出 $Out_{up}$: $(B, 1, \frac{128}{TP}, 128)$

计算量: $\frac{2B \times d^c_{kv} \times H \times d_v^h}{TP}$

步骤 6: 输出投影(降维回隐藏维度)

$$y_t = Out_{up} \times W_O$$
  • 输入$Out_{up}$: $(B, 1, H \times d_{v}^h) = (B, 1, 16384)$
  • 权重 $W_O$: $(H \times d_{v}^h, d) = (16384, 7168)$
  • 输出 $y_t$: $(B, 1, d) = (B, 1, 7168)$

TP 切分

  • 输入: $(B, 1, \frac{H \times d_{v}^h}{TP})$
  • 权重 $W_O$: $(\frac{H \times d_{v}^h}{TP}, d)$
  • 输出: $(B, 1, d)$(部分结果)

计算量: $\frac{2B \times H \times d_{v}^h \times d}{TP}$

通信

  • All-Reduce: $B \times d = 7168 \times B$ 元素

Decode 阶段总结

步骤 涉及的头 (每 GPU) 计算量 (每 GPU) (B: Batch, S: History Len) 关键特征说明
Step 1: Q Down ❌ 无 $2B \times d \times d^c_q$ 查询压缩 输入 $h_t$ 投影到 latent 空间,无头概念。
Step 2: Q Up ✅ 128 $\frac{2B \cdot d^c_q \cdot H \cdot d_C^h}{TP} + \text{AbsorbCost}^*$ $+ \frac{2B \cdot d^c_q \cdot H \cdot d_R^h}{TP}$ Query 解压 + 吸收 K-Up 展开多头 Q,并吸收 $W^{UK}$ 权重; 生成 RoPE 部分并拼接。Q 处于"Latent KV"空间。
Step 3: KV Down ❌ 无 $2B \times d \times (d^c_{kv} + d_R^h)$ KV 生成 (单头) 仅生成 latent $c^{KV}$ 和 $k^R$; 不进行多头展开,直接存入 KV Cache。
Step 4: Attention ✅ 128 (Q) vs 1 (KV) Score: $\frac{H}{TP} \times 2B \cdot S \times (d^c_{kv} + d_R^h)$ Agg: $\frac{H}{TP} \times 2B \cdot S \times d^c_{kv}$ MQA (Multi-Query Attention) 多头 Q 关注共享的 Latent KV Cache; 计算复杂度随历史长度 $S$ 线性增长。
Step 5: V Up ✅ 128 $\frac{2B \cdot d^c_{kv} \cdot H \cdot d_v^h}{TP}$ V 解压 (后置) 仅对 Attention 的输出结果进行升维; 恢复多头 Value 的表达能力。
Step 6: Output ✅ 128→1 $\frac{2B \cdot H \cdot d_v^h \cdot d}{TP}$ 输出投影 多头合并,投影回隐藏维度 $d$; 需进行 All-Reduce 通信。
  • AbsorbCost: 指将 $W^{UK}$ 融合进 Q 的计算开销,通常是 $\frac{2B \cdot H \cdot d_C^h \cdot d^c_{kv}}{TP}$,取决于具体算子融合实现。

Prefill vs Decode

维度 Prefill 阶段 Decode 阶段 核心差异点
Attention 类型 MHA (Standard) MQA (Multi-Query) Decode直接对Latent vector做MQA
KV Cache 内容 $c^{KV}$ (Latent) $c^{KV}$ (Latent) 存储内容完全一致,Decode 复用 Prefill 生成的 Cache,无需转换。
KV Up-Proj 时机 Attention 之前 Attention 之后 (V) 融合进 Q (K) Prefill 显式还原所有 KV 头;Decode 避免还原历史 KV,只在结果上还原。
K 的形态 多头 $K$ $(B, S, H, d_{qk}^h)$ 单头 Latent $K$ $(B, S, 1, d^c_{kv}+d_R^h)$ Prefill 的 K 是物理多头;Decode 的 K 是Latent vector加上RoPE、逻辑广播。
计算复杂度 $O(S^2)$ $O(S)$ Prefill仍然可以考虑TP,但Decode使用TP会难以掩盖通信开销,此外KV Cache的效率很低。

MHA vs MLA 对比总结

  • MHA由于头是独立的,因此用TP只需要最后的通信开销。
  • MLA的Latent Vector需要在TP的每个rank做副本的保留,因此使用TP的效率会较低。
  • MLA的访存瓶颈被缓解,TP通信的影响会更明显。
  • MLA推理实践中很常用的是DP- Attention。
  • MLA的Latent Vector可以压缩KV Cache的大小,因此对访存密集的Decode会很有优势。

TP对FFN的影响

Prefill阶段和Decode阶段是一致的

区别是Decode的S为1

步骤1:第一层投影($d \to d_{ff}$)

计算:

  • 完整计算:$H = X \times W_1$

    • 输入 $X$: $(B, S, d)$
    • 权重 $W_1$: $(d, d_{ff})$(第一层FFN权重)
    • 输出 $H$: $(B, S, d_{ff})$
  • TP切分:权重矩阵按列切分

  • 每个GPU上的shape

    • 输入 $X$: $(B, S, d)$(广播到所有GPU)
    • 权重 $W_1$: $(d, \frac{d_{ff}}{TP})$(列切分)
    • 输出 $H$: $(B, S, \frac{d_{ff}}{TP})$
  • 每GPU计算量:$\frac{2BS \times d \times d_{ff}}{TP}$

通信:无

步骤2:激活函数

计算:各GPU独立计算(SwiGLU/GELU)

  • 每个GPU上的shape:输入和输出都是$(B, S, \frac{d_{ff}}{TP})$
  • 计算量:$O(BS \times \frac{d_{ff}}{TP})$(相对较小)

通信:无

步骤3:第二层投影($d_{ff} \to d$)

计算:

  • 完整计算:$Y = \text{Activation}(H) \times W_2$

    • 输入(激活后): $(B, S, d_{ff})$
    • 权重 $W_2$: $(d_{ff}, d)$(第二层FFN权重)
    • 输出 $Y$: $(B, S, d)$
  • TP切分:权重矩阵按行切分

  • 每个GPU上的shape

    • 输入: $(B, S, \frac{d_{ff}}{TP})$
    • 权重 $W_2$: $(\frac{d_{ff}}{TP}, d)$(行切分)
    • 局部输出: $(B, S, \frac{d_{ff}}{TP}) \times (\frac{d_{ff}}{TP}, d) \to (B, S, d)$(部分结果)
  • 每GPU计算量:$\frac{2BS \times d_{ff} \times d}{TP}$

通信:

  • All-Reduce:合并TP切分的结果
  • 通信量:$BS \times d$ 个元素(与Attention相同)
    • FFN后立即与Attention结果Add,通信模式一致

通信开销对比

各并行策略的通信量推导总结

约定:本节表格中的“需要同步的数据”指逻辑上的张量大小(即需要被传输的激活值元素个数)。 实际传输量指物理链路上每块 GPU 发送/接收的数据总量,需乘以与算法相关的常数因子。

并行策略 通信模式 需要同步的数据 (逻辑大小) 实际通信量 (每GPU, 数量级) 关键推导逻辑
TP All-Reduce $N \times d$ $\approx 2\frac{TP-1}{TP} \times N d$ 权重按行切分,每卡计算出部分和,需通过 Ring All-Reduce 累加。$N$ 为该算子的 token 总数。
EP All-to-All(和DP- Attention结合) $N \times k \times d$ $\approx 4 \times \frac{N k d}{EP} \times \frac{EP-1}{EP}$ MoE 层包含 两次 All-to-All (Dispatch + Combine)。每次逻辑数据量为 $N \times k \times d$ (每个 token 选 $k$ 个专家)。最后的系数是因为不需要和自己通信。
PP P2P Send/Recv $N \times d$ $\approx N \times d$ (单向) Stage 边界的点对点传输。前一个 Stage 计算完 $N$ 个 token 的激活值,发送给下一个 Stage。
DP 0 0 在纯推理阶段,数据并行副本之间无通信。

关键变量说明

  • Prefill 阶段:$N \approx B \times S$ (Batch $\times$ Sequence Length)
  • Decode 阶段:$N \approx B$ (Batch $\times$ 1)
  • 结论:在 Decode 阶段,所有策略的通信量相比 Prefill 都缩小了约 $S$ 倍

Prefill vs Decode 的 TP / EP / PP 通信量对比

下表显式区分了 PrefillDecode 阶段在不同并行策略下的通信特征。忽略 DP,仅关注单层/单次操作开销。

  • 记号:$B$ (Batch Size), $S$ (Seq Len), $d$ (Hidden Dim), $k$ (Top-k experts), $d_{act}$ (Activation Dim).
并行策略 阶段 逻辑通信数据大小 (单层) 粗略通信量 (每GPU) 特性分析
TP Prefill $BS \times d$ $\approx 1.75 \times BS d$ (TP=8) 通信量大,但在高算术强度下容易被计算掩盖。
Decode $B \times d$ $\approx 1.75 \times B d$ 通信量小,但计算量也小,延迟直接暴露,受限于 NVLink 带宽和延迟。
EP Prefill $BS \times k \times d$ $\approx 4 \frac{BSkd}{EP}\times \frac{EP-1}{EP}$ 跨节点的 All-to-All 通信。Prefill 阶段数据量大,极易触碰跨机互联带宽瓶颈。
Decode $B \times k \times d$ $\approx 4 \frac{Bkd}{EP}\times \frac{EP-1}{EP}$ 数据量虽小,但 All-to-All 的 启动延迟 (Latency) 和网络拥塞控制成为主要瓶颈。
PP Prefill $BS \times d_{act}$ $\approx BS d$ 巨大的激活值传输导致流水线 Bubble 难以填充,且首个 Token 延迟 (TTFT) 显著增加。
Decode $B \times d_{act}$ $\approx B d$ 通信量较小,主要问题是流水线带来的 串行依赖 导致的时延累积。
  • TP的实现简单,对MHA支持较好。但需要良好的通信性能。每一层都需要All Reduce通信。
  • EP用于MoE架构。优点是不需要GPU加载所有的专家,符合MoE减少内存加载的初衷。在DeepSeek中配合DP attention较为常见。每个MoE层都需要All to All通信,也需要关注。
  • PP只有在切分层需要通信。计算通信比会更高,适合显存不够但通信性能差的场景。

Static vs Continuous Batching

实际生产系统中,Batch 不再是静态的 $(B, S)$,而是动态的流。

维度 Static Batching (简化模型) Continuous Batching (实际模型) 核心差异
定义 假设 Batch 内所有请求状态一致(全 Prefill 或全 Decode)。 允许 Batch 内混合:$B_p$ 个请求在做 Prefill,$B_d$ 个在做 Decode。 资源利用率更高,但这增加了调度复杂性。
总 Token 数 Prefill: $T = B \times S$
Decode: $T = B$
$T_{total} = \sum_{i=1}^{B_p} S_i + B_d$ 通信量不再由 $B$ 决定,而是由当前 Step 的 有效 Token 总数 $T_{total}$ 决定。
TP 通信 每层固定传输 $BSd$ 或 $Bd$ 每层传输 $\propto T_{total} \times d$ $T_{total}$ 在不同 Step 间剧烈波动,带宽需求呈现脉冲式。
EP 通信 每层固定传输 $BSkd$ 或 $Bkd$ 每层传输 $\propto T_{total} \times k \times d$ EP 的 All-to-All 负载随 $T_{total}$ 波动,且专家负载可能不均衡。

Continuous Batching 的系统设计影响

引入 Continuous Batching 后,对并行通信系统的设计带来以下挑战:

  1. 通信带宽的动态性

    • 由于 $T_{total}$ 取决于新进来请求的 Prefill 长度 $S_i$,某个 Step 的通信量可能突然暴增。
    • 网络设计必须按 峰值 (Peak) 而非平均值规划,否则大 Prefill 进来时会造成严重的 Head-of-Line Blocking。
  2. 调度策略权衡

    • Prefill 优先:最大化 $T_{total}$,提高计算密度(算术强度),掩盖 TP/EP 通信延迟。缺点是显存瞬间暴涨,可能挤占 Decode 空间。
    • Decode 优先:保证低延迟,但 $T_{total}$ 较小,计算无法掩盖通信,GPU 即使利用率低也必须等待通信完成。
  3. 内存(KV Cache)管理

    • KV Cache 占用量公式变为:$\text{Mem}_{KV} = (\sum S_{curr}) \times L \times d_{kv} \times \text{Layers}$。
    • TP/PP 必须保证所有 Device 上的显存分配一致,而 EP 可能导致不同专家的 KV 分布不均(但在 MLA/MHA 共享 KV 的架构下,KV 通常不做 EP 切分)。

Chunked Prefill 对通信与流水线的影响

为了解决长序列 Prefill 阻塞 Decode 的问题,通常引入 Chunked Prefill:将长序列 $S_{total}$ 拆分为多个 $S_{chunk}$ (e.g., 512 tokens) 分批处理。

A. 对 TP / EP 通信量的影响

  • 总量不变,峰值削峰
    • 不分 Chunk:单次通信量 $B \times S_{total} \times d$,巨大的数据包可能导致网络拥塞。
    • 分 Chunk:单次通信量 $B \times S_{chunk} \times d$。虽然总传输字节数不变,但单次通信更小,更容易与计算 Kernel 进行 Overlap (重叠)

B. 对 PP (流水线并行) 的决定性作用

  • 消除流水线气泡 (Bubble)
    • 无 Chunk:一个长 Prefill 请求进入 Stage 1,后续所有 Decode 请求必须等待它完全流过整个 Pipeline,造成巨大的时延抖动。
    • 有 Chunk:Prefill 的 Chunk 可以与 Decode 请求 交错 (Interleaved) 进入流水线。
    • 效果:虽然单个 Prefill 请求的完成时间(TTFT)没有显著减少,但系统的 平均吞吐量 和 Decode 请求的 P99 延迟 得到显著优化。