一座城市要办一场千人宴,总厨拿到一本厚厚的菜谱——每一页都写着完整做法的菜式。一个人根本做不完,于是总厨把菜谱按“菜系”拆成几册:川菜一册、粤菜一册、淮扬菜一册,分给几位分厨。每一位分厨手里都收到一模一样的核心食材,但只负责做自己那一册菜谱上的菜。等到开席时间,传菜员把各位分厨做好的菜统统端上同一张大桌,一桌完整的千人宴才算上齐。 这就是今天要讲的“列并行”——一种让多张 GPU 协作完成同一份计算任务的聪明分工方式。它把一份大计算按“输出列”切开分给多张卡,最后把各部分结果拼接成完整答案。它是大模型训练中最基础的并行招式之一,也是 Megatron-LM 这类明星框架的基石。接下来,我们把这场宴席的每一道工序拆开看:怎么切菜谱、怎么算菜、怎么上菜、怎么和激活函数配合,以及整套分工在千卡集群里如何运转。

岗位关联

掌握列并行,你就掌握了张量并行的半壁江山——这是你理解 Megatron-LM、万亿参数训练与推理加速的一把钥匙,也是大模型分布式训练面试中最高频的考点之一。

目录
  1. 可视化演示
  2. 切分原理:一册菜谱的分工之道
  3. 数学基础:矩阵乘法的列视野
  4. 通信机制:传菜员的 All-Gather
  5. MLP 应用:GeLU 的天然兼容与 f/g 融合
  6. 优缺点分析:分工的得与失
  7. 训练实践:千卡集群的厨师班底
  8. AI 应用场景
  9. Python 演示
  10. 常见误区
  11. 面试题

可视化演示

展示系统处理流水线、实时性能指标曲线及多方案对比分析,包含数据流转、吞吐延迟监控与方案评估。

本节配有交互动效(进入视口后自动加载,约 3KB)。

系统可视化 处理流程:输入→处理→模型→输出 性能监控:实时指标 P=L×T

切分原理:一册菜谱的分工之道

千人宴的后厨里,总厨不再一个人面对几百页菜谱。他把菜谱按菜系拆成三册——川菜一册、粤菜一册、淮扬菜一册,分别交给三位分厨。注意,这里切的是“菜的种类”,不是“食材的分量”:每一册菜谱都是完整的做法,只是包含的菜式变少了。这个朴素的直觉,正是列并行切分权重的核心思想。

在矩阵世界里,“菜谱”就是权重矩阵 W,它的每一列恰好对应输出向量的一个分量。列并行的第一步,是把 W 按列切成 p 块:第 i 块 W^{(i)} 的形状是 d × (h/p)。与此同时,输入 X 原封不动地复制给每一张 GPU——如同同样的食材同时送到每一位分厨手中。每张卡只拿一部分列,但食材是完整的,于是它能够独立算完自己负责的那几列输出。

切分的关键约束是输出维度 h 必须能被 GPU 数量 p 整除,否则某人拿到“半道菜谱”,谁也没法做出一道完整的菜。实践中,工程师会在切分前检查矩阵形状,确保每个分厨手里的菜谱都完整可用。

三位分厨各自翻开自己那册菜谱,手里的食材一模一样,做出来的菜却各不相同——这正是列并行的精髓:输入共享,输出分家。至于他们如何把菜“算”出来,请看下一节。

W=[ W (1)  ∥  W (2)  ∥  ⋯  ∥  W (p) ],W (i)∈R d×(h/p)\displaystyle W = \big[\,W^{\,(1)} \;\big\Vert\; W^{\,(2)} \;\big\Vert\; \cdots \;\big\Vert\; W^{\,(p)}\,\big], \qquad W^{\,(i)} \in \mathbb{R}^{\,d \times (h/p)}
这个式子说:把一张 d 行 h 列的大“菜谱”,竖直切成 p 册,每一册都有 d 行、h/p 列,每一册都是完整的“局部菜谱”。
  1. 权重矩阵$W$ 是 $d \times h$ 的权重矩阵,$d$ 是输入特征数(食材种类),$h$ 是输出特征数(菜式总数)。
    W∈R d×hW \in \mathbb{R}^{\,d \times h}
  2. 列拼接符号$\Vert$ 表示把多个矩阵按列方向拼接,就像把散开的三册菜谱按顺序放回书架。
    [ W(1)  ∥  W(2)  ∥  ⋯  ∥  W(p) ]\big[\,W^{(1)} \;\Vert\; W^{(2)} \;\Vert\; \cdots \;\Vert\; W^{(p)}\,\big]
  3. 切出的每一块$W^{(i)}$ 的维度是 $d \times \tfrac{h}{p}$,只含原来 $h$ 列中的 $h/p$ 列。
    W(i)∈R d×(h/p)W^{(i)} \in \mathbb{R}^{\,d \times (h/p)}
  4. 整除约束$h$ 必须能被 $p$ 整除;否则各块的列数不等,计算负载就会失衡。
    h mod p=0h \bmod p = 0

数学基础:矩阵乘法的列视野

三位分厨开始做菜:相同的食材 X 加上自己那册菜谱 W^{(i)},产出几道特色菜 XW^{(i)}。为什么把 W 竖着切开,各路乘法就能各算各的?答案藏在矩阵乘法的“分配律”里。

若把 W 按列分成两块 [W_a ∥ W_b],那么 X[W_a ∥ W_b] = [XW_a ∥ XW_b]——乘法可以“穿透”列切分线:先各算各的,再把结果并列拼起来。这个性质对任意 p 块都成立,于是列并行有了坚实的数学保证。

具体来说,设 X 的形状是 b × d(b 是批量大小,相当于一次宴席的桌数),W 是 d × h。完整计算 Y = XW 得到 b × h。列并行把 W 切成 p 块后,第 i 张卡只做一次 b × d 与 d × (h/p) 的乘法,得到 b × (h/p) 的部分结果。把 p 个部分结果沿列拼接,就得到与单卡完全一致的完整 Y。每张卡的计算量从 O(bdh) 降到 O(bdh/p)——典型的人多力量大。

分厨们用完全相同的食材,按各自的菜谱做出一盘盘特色菜。数学向总厨保证:只要各按各的菜谱做,最后拼桌时,这桌宴席的味道和总厨一个人掌勺时一模一样——不多一分,不少一毫。

Y=XW=[ XW (1)  ∥  XW (2)  ∥  ⋯  ∥  XW (p) ]\displaystyle Y = XW = \Big[\,XW^{\,(1)} \;\Big\Vert\; XW^{\,(2)} \;\Big\Vert\; \cdots \;\Big\Vert\; XW^{\,(p)}\,\Big]
这个等式是列并行的“合同书”:左边是单卡算出的完整结果,右边是 p 张卡分别算完再拼接的结果,两者数学上严格相等。
  1. 完整视角$Y = XW$,输入 $X$ 是 $b \times d$,权重 $W$ 是 $d \times h$,输出 $Y$ 是 $b \times h$。
    X∈Rb×d,W∈Rd×h,Y∈Rb×hX \in \mathbb{R}^{b \times d},\quad W \in \mathbb{R}^{d \times h},\quad Y \in \mathbb{R}^{b \times h}
  2. 分配律穿透矩阵乘法对列拼接满足分配律,$X$ 可以分别乘进每一块再并列。
    X [ W(1)  ∥  W(2) ]=[ XW(1)  ∥  XW(2) ]X\,\big[\,W^{(1)} \;\Vert\; W^{(2)}\,\big] = \big[\,XW^{(1)} \;\Vert\; XW^{(2)}\,\big]
  3. 局部计算第 $i$ 张卡只做 $XW^{(i)}$,得到 $b \times \tfrac{h}{p}$ 的部分输出。
    Y(i)=XW(i)∈R b×(h/p)Y^{(i)} = XW^{(i)} \in \mathbb{R}^{\,b \times (h/p)}
  4. 拼接合并把所有 $Y^{(i)}$ 沿列方向拼起来,恢复成 $b \times h$ 的完整输出。
    Y=[ Y(1)  ∥  Y(2)  ∥  ⋯  ∥  Y(p) ]Y = \big[\,Y^{(1)} \;\Vert\; Y^{(2)} \;\Vert\; \cdots \;\Vert\; Y^{(p)}\,\big]

通信机制:传菜员的 All-Gather

三道菜摆在三个厨房里,可客人要的是“一桌席”。这时传菜员上场——他从每个厨房端走一道菜,把菜拼到同一张大桌上。这个动作在分布式训练里叫 All-Gather(全收集)。

All-Gather 的语义很直白:p 张卡各自持有大小为 b × (h/p) 的部分输出(自己的菜),执行一次 All-Gather 后,每张卡都能拿到完整的 b × h 输出(整桌菜)。通信层面,每张卡要把它那份数据发给其余 p-1 张卡,同时收回 p-1 份别人的数据。

从通信量的角度,每张卡发收的总元素数约为 (p-1)/p × b·h。有意思的是,行并行末尾的那次 All-Reduce,通信量也是同一量级——从通信成本看,两者是“对称”的。真正的差异在于通信原语的语义:All-Gather 保留全部信息(列拼起来),All-Reduce 则把信息规约成一个结果。这个差异在 MLP 里会变成巨大的工程优势(见下一节)。

传菜员跑这一趟,满桌的菜才算真正成席。但传菜的通道只有那么宽——p 越大,传菜的花费越不可忽视。聪明的后厨会让传菜和做菜同时进行,这正是训练实践里“通信与计算重叠”技巧的由来。

CGPU=p−1p⋅bh\displaystyle C_{\mathrm{GPU}} = \frac{p-1}{p} \cdot b h
每张卡在一次 All-Gather 中,要发送 $(p-1) \times \frac{bh}{p}$ 个浮点数:把自己那份输出发给其余所有卡,并收回它们的输出。p 越大,通信占比越高。
  1. 每卡份额每个 GPU 持有 $\tfrac{b h}{p}$ 个元素的部分输出,$b$ 是批量大小,$h$ 是输出维度。
    bhp\frac{b h}{p}
  2. 发送自己那份每卡把自己这份发给其余 $p-1$ 张卡,共发出 $p-1$ 份。
    (p−1)⋅bhp(p-1) \cdot \frac{b h}{p}
  3. 接收他人份额同时每卡也收到其余 $p-1$ 份数据,收发对称。
    CGPU=(p−1)⋅bhpC_{\mathrm{GPU}} = (p-1) \cdot \frac{b h}{p}
  4. 与行并行对比行并行一次 All-Reduce 的通信量也在 $b h$ 量级,两者成本相当。
    Crow≈Ccol≈bhC_{\mathrm{row}} \approx C_{\mathrm{col}} \approx b h

MLP 应用:GeLU 的天然兼容与 f/g 融合

宴席里每道菜上桌前都要“试味”——咸淡是否合适。对列并行来说,试味可以在每个厨房里独立完成:分厨只尝自己做的菜,根本不需要等另外两盘菜端过来。这正是列并行与“逐元素”激活函数天然兼容的直觉来源。

GeLU 是 Transformer 里最常用的激活函数,它对输出向量的每个元素独立计算:g(z_j) 只依赖 z_j 自己,与其余元素无关。因此,把输出按列切成 p 块、对每块分别做 GeLU,和先做完整 GeLU 再切分,结果完全一致——数学上叫“逐元素操作的切分不变性”。

这个性质成就了 Megatron-LM 的经典 MLP 设计。MLP 的计算是 GeLU(X·W1)·W2,第一层权重 W1 用列并行,第二层权重 W2 用行并行。妙处在于“f/g 融合”:第一层列并行本来要 All-Gather 拼出完整中间结果,但第二层行并行恰好需要把中间结果按行分发——一拼一拆正好抵消,中间那次 All-Gather 被省掉了,整个 MLP 前向只需在最后做一次 All-Reduce,通信量几乎减半。

分厨在自己厨房里试完味,直接把菜递给下一道工序的摆盘师傅,中间的传菜员完全绕开了——直到整桌宴席上齐,才统一对一次账(All-Reduce)。这套“边做边传”的流程,正是 Megatron-LM 在千亿参数模型上保持高效的秘密武器。

GeLU(XW)=[ GeLU ⁣(XW (1))  ∥  ⋯  ∥  GeLU ⁣(XW (p)) ]\displaystyle \mathrm{GeLU}\big(XW\big) = \Big[\,\mathrm{GeLU}\!\big(XW^{\,(1)}\big) \;\Big\Vert\; \cdots \;\Big\Vert\; \mathrm{GeLU}\!\big(XW^{\,(p)}\big)\,\Big]
这把“试味勺”可以直接伸进每一盘菜里单独试味,不需要等整桌菜上齐。正是这个性质,让列并行与非线性激活可以无缝衔接。
  1. GeLU 定义$\mathrm{GeLU}(z) = z \cdot \Phi(z)$,其中 $\Phi$ 是标准正态分布的累积分布函数,逐元素计算。
    GeLU(zj)=zj⋅Φ(zj)\mathrm{GeLU}(z_j) = z_j \cdot \Phi(z_j)
  2. 分块不变性对第 $i$ 块部分输出做 GeLU,结果等于整体 GeLU 后取对应列。
    GeLU(XW(i))=[GeLU(XW)]i-th column block\mathrm{GeLU}\big(XW^{(i)}\big) = \big[\mathrm{GeLU}(XW)\big]_{i\text{-th column block}}
  3. 本地无等待每卡算完 $XW^{(i)}$ 立即做激活,不需要等 All-Gather 完成。
    Zi=GeLU(XW(i))Z_i = \mathrm{GeLU}\big(XW^{(i)}\big)
  4. 融合省通信$Z_i$ 直接喂给第二层行并行权重 $B^{(i)}$,中间 All-Gather 被吸收,仅在末尾 All-Reduce。
    Y=∑i=1pZi B(i)Y = \sum_{i=1}^{p} Z_i \, B^{(i)}

优缺点分析:分工的得与失

把菜谱分给十位厨师,一方面每位厨师的案板轻松了,另一方面后厨的传菜通道变得拥挤。任何工程方案都是取舍,列并行也不例外。

先说得。第一,显存大幅下降:权重 W 的 d·h 个参数平均摊到 p 张卡上,每卡只占 1/p;Adam 优化器的动量与方差也随参数同步减少——训练 175B 参数模型时,这一点是生死攸关的。第二,计算负载天然均衡:只要 h 能被 p 整除,每卡做的乘法量完全相等,不需要担心“有人忙死有人闲死”。

再谈失。第一,通信不可避免:每步前向都要一次 All-Gather,p 越大通信占比越高。第二,维度约束:h 必须能被 p 整除,这限制了 p 的上限。第三,收益递减:计算时间随 p 线性下降,通信时间却随 p 上升,两者交叉处就是最优并行度——所以实践中张量并行度通常取 2~8,更大的扩展交给数据并行维度。

十位厨师各守一块案板摊子,确实轻松;但传菜员的走廊只有那么宽。聪明的后厨会算一笔账:与其让一百个厨师挤在一个厨房里,不如把宴席拆成多场(数据并行),让每场只要三五位厨师配合(张量并行度 2~8)——这正是大模型训练里“混合并行”的基本哲学。

Mper_GPU=dhp(权重与优化器状态同比例缩减)\displaystyle M_{\mathrm{per\_GPU}} = \frac{d h}{p} \quad \text{(权重与优化器状态同比例缩减)}
权重显存从 $dh$ 降到 $dh/p$,这是列并行最直接的红利;但省下的显存要用通信时间来“偿还”,这就是工程天平的两端。
  1. 总参数量$W$ 含 $d \times h$ 个浮点参数,决定了单卡显存压力的量级。
    MW=dhM_W = d h
  2. 按卡均摊列切分后每卡只存 $\tfrac{d h}{p}$ 个参数。
    Mper_GPU=dhpM_{\mathrm{per\_GPU}} = \frac{d h}{p}
  3. 优化器同缩Adam 为每个参数额外存两份状态(一阶、二阶矩),同样除以 $p$。
    Mopt=2×dhpM_{\mathrm{opt}} = 2 \times \frac{d h}{p}
  4. 权衡关系计算量随 $p$ 等比例下降,通信量却随 $p$ 逼近 $b h$,一降一升,存在最优 $p$。
    T(p)=2bdhpF+(p−1)bhpBT(p) = \frac{2 b d h}{p F} + \frac{(p-1) b h}{p B}

训练实践:千卡集群的厨师班底

真正的千人宴不可能只靠一个后厨。现实中,训练一个千亿参数大模型需要上千张 GPU,它们被组织成“多厨房多班组”:数据并行负责复制多个配置相同的后厨,列并行/行并行负责让每个后厨内部的几位厨师分工协作。

Megatron-LM 的实践有一条经验法则:张量并行(含列并行)通常限制在单节点内部,8 卡以内。原因是节点内的 NVLink 带宽高达 900 GB/s,All-Gather 几乎“免费”;跨节点的慢速网络则交给数据并行和流水线并行处理。列并行作为张量并行的左半身,负责把矩阵乘法的“宽”切薄,让每个 Transformer 层都能在多卡上协同计算。

实际训练中,列并行有三个实践要点。第一,通信重叠:把 All-Gather 拆成细粒度小块,与下一层计算流水线重叠,让通信“藏在”计算背后。第二,负载均衡:保证 h 被 p 整除、批量 b 尽量大,摊薄通信占比。第三,混合并行:数据并行 × 张量并行 × 流水线并行构成三维网格。以 1024 卡训练 GPT-3 为例,常取数据并行 128 × 张量并行 8,模型层再切 1 段流水线——列并行只负责单副本内部的 8 卡分工。

千人宴的后厨调度,本质上就是在这三个维度上寻找平衡:后厨要复制多少份(数据并行)、每份内部几位厨师合作(张量并行)、菜肴按几道工序传递(流水线并行)。列并行作为“单副本内分工”的基石,决定了每一份后厨能容纳多少厨师、能做出多大的宴席——这也是它在大模型基础设施中不可替代的地位。

Ntotal=D×T×P\displaystyle N_{\mathrm{total}} = D \times T \times P
千卡集群被组织成三维并行网格:$D$ 是数据并行副本数,$T$ 是张量并行度(列并行在其中发挥作用),$P$ 是流水线阶段数。三者相乘等于总卡数。
  1. 总卡数分解$N_{\mathrm{total}}$ 张卡被组织成三维并行网格,每个维度各司其职。
    Ntotal=D×T×PN_{\mathrm{total}} = D \times T \times P
  2. 数据并行维$D$ 个副本分别处理不同批次的数据,副本间互不干扰。
    D=副本数D = \text{副本数}
  3. 张量并行维$T$ 张卡在一个副本内部分工,列并行和行并行都在这一维。
    T=单副本内卡数T = \text{单副本内卡数}
  4. 流水线并行维$P$ 按层切分,把不同 Transformer 层放到不同阶段。
    P=流水线阶段数P = \text{流水线阶段数}

AI 应用场景

场景一:Megatron-LM 千亿参数预训练

阶段:模型并行训练(分布式训练)

解决的问题:175B 参数的 GPT-3 单卡显存完全放不下(权重加优化器状态需要数 TB)。传统方式只能缩小模型或增大 batch,二者都不可接受;列并行把每个 Transformer 层的权重按列切开,让 8 张卡合算一层,显存压力瞬间摊薄,训练才能继续推进。

具体案例:用 1024 张 A100 训练 175B 模型时,每个节点内 8 张卡做张量并行:MLP 第一层用列并行、第二层用行并行,注意力层的 Q/K/V 与输出投影同样按列/行切分;跨节点再用数据并行与流水线并行。列并行让单层矩阵乘法从“一个人算不完”变成“八个人各算各的再拼起来”。

场景二:大模型推理服务(张量并行推理)

阶段:模型部署与推理加速

解决的问题:70B 模型即使只加载权重也要 140 GB 显存,单张 A100(80 GB)放不下;推理服务还要求低延迟。列并行把权重分到多卡,一次前向由多卡协同完成,延迟可控且单卡显存压力骤减。

具体案例:vLLM、TensorRT-LLM 等推理框架部署 LLaMA-70B 时,用 TP=8 的张量并行(列并行 + 行并行组合),8 张 A100 协同计算一次前向,同时配合 KV cache 管理,把首 token 延迟压到秒级以内。对用户来说,请求被“悄悄”分给八位厨师,上菜依然是一整桌。

Python 演示

python30 行
import numpy as np  def column_parallel_forward(X, W_list):    """simulate column parallel: compute partial outputs then all-gather"""    partials = [X @ W_local for W_local in W_list]    Y = np.concatenate(partials, axis=1)    return Y, partials  def main():    np.random.seed(42)    batch, d_in, d_out, p = 4, 8, 9, 3    X = np.random.randn(batch, d_in)    W = np.random.randn(d_in, d_out)     # single GPU reference    Y_ref = X @ W     # split W by columns into p chunks (simulate p GPUs)    W_list = np.split(W, p, axis=1)    Y, partials = column_parallel_forward(X, W_list)     print("partial shapes:", [part.shape for part in partials])    print("gathered shape:", Y.shape)    print("match single GPU:", np.allclose(Y, Y_ref))  if __name__ == "__main__":    main()
目的:用 NumPy 模拟 3 卡列并行——把权重按列切分,各卡算部分输出,再 All-Gather 拼接,验证与单卡结果一致。
  1. W = np.random.randn(d_in, d_out)随机生成一个 d 行 h 列的权重矩阵,相当于总厨手里那本完整菜谱。
  2. Y_ref = X @ W单卡基准:总厨一个人做完整道菜,作为后续对比的“标准味道”。
  3. W_list = np.split(W, p, axis=1)把权重按列切成 p 份,模拟 p 个分厨各拿一册菜谱。
  4. partials = [X @ W_local for W_local in W_list]每张卡只做自己的局部乘法,产出的部分输出互不依赖。
  5. Y = np.concatenate(partials, axis=1)模拟 All-Gather:把三个部分输出沿列方向拼成完整输出。
  6. np.allclose(Y, Y_ref)验证拼接结果与单卡完全一致,这就是列并行正确性的数学保证。

常见误区

误区“并行就是把数据也切开分了呀,列并行应该同时把输入和权重都切成几份,一人拿一份吧?”

正解列并行中只有权重 W 按列切分,输入 X 是完整广播给每张卡的。原因很直接:第 i 张卡要计算 XW^{(i)},它手里的 X 必须完整,否则乘法都无法进行。不妨用宴席比喻:分厨们拿到的是同一批完整食材,只是各自负责不同的菜式。真正切输入样本的是数据并行,初学者常常把两者搞混。

为何常见:直觉上“并行 = 数据切分”,混淆了张量并行(切权重)和数据并行(切样本)两种完全不同的并行方式。

误区“每张卡算完自己那部分就已经是结果了,为什么还要通信?直接输出不就好了?”

正解每张卡算出的 Y^{(i)} 只是完整输出的一部分列,必须执行一次 All-Gather 把所有部分拼起来才能得到最终 Y。行并行同理需要 All-Reduce。可以这样想:每位分厨只做了自己那几道菜,客人要的是一整桌席——传菜员必须跑一趟,把菜拼上桌。计算确实无依赖,但“拼接结果”这一步通信省不掉。

为何常见:把“无依赖计算”误解为“无需通信”,忽略了输出合并本身就是一种通信。

误区“反正都是把矩阵切开分给多卡,列并行和行并行效果差不多,选哪个都行吧?”

正解两者通信原语不同(All-Gather 拼接列 vs All-Reduce 求和),与后续算子的衔接也不同。列并行输出是“完整列块”,可以直接接逐元素激活函数;行并行输出是“部分和”,必须先 All-Reduce 才能继续。在 Megatron 的 MLP 里,第一层必须用列并行、第二层必须用行并行,才能实现 f/g 融合省掉中间通信——交换位置会破坏整个融合结构。

为何常见:只盯着单层矩阵乘法觉得“对称”,没看到跨层衔接时通信模式的关键差异。

误区“GPU 加到 100 张,每张只用原来的 1/100,速度应该提升 100 倍吧?”

正解p 受限于输出维度 h(必须整除,且 p 不能超过 h);通信量随 p 增长,p 超过一定值后计算收益被通信吞掉。实践中张量并行度通常取 2~8,更多的卡通过数据并行和流水线并行扩展。千卡集群的扩展靠“三维并行”组合拳,不是单靠列并行一维加到底。

为何常见:忽略了通信带宽是瓶颈,也混淆了张量并行(切模型)与数据并行(切数据)的扩展方式。

误区“反正都是卡与卡之间通信,All-Gather 和 All-Reduce 不就是名字不同吗?”

正解语义差异巨大。All-Reduce 输出是“规约后的单一结果”,每张卡拿到的都相同(例如部分和相加);All-Gather 输出是“拼接后的完整张量”,保留全部信息。行并行需要 All-Reduce(部分和必须相加),列并行需要 All-Gather(不同列必须拼接)。MLP 融合之所以成立,正是靠这两种原语语义上的互补。

为何常见:集合通信概念繁多,初学者容易只记名字不记语义,导致张量并行两种切分的原理也记混。

面试题

Q1. 列并行中,权重矩阵 W 是如何划分给各 GPU 的?

单选题 · easy

  • A.按行切开,每卡拿到若干完整行
  • B.按列切开,每卡拿到若干完整列← 正确答案
  • C.按二维块状网格切分
  • D.整块复制,不做任何切分

答案:B。列并行按列切分权重,即沿输出特征维度把 W 竖切成 p 块,每张卡持有 d × (h/p) 的局部权重。A 是行并行的做法,C 是二维切分(实践中少见),D 根本不是并行。

Q2. 在列并行中,输入 X 是如何发送到各 GPU 的?

单选题 · easy

  • A.按样本批次拆分发给各卡
  • B.按特征维度拆分发给各卡
  • C.完整复制广播给每一张卡← 正确答案
  • D.随机洗牌后再平均分卡

答案:C。列并行中 X 必须完整广播给每张卡,因为每张卡要计算 XW^{(i)},输入矩阵不完整就无法做局部乘法。A 是数据并行的思路,B 是行并行需要讨论的切法,D 无意义。

Q3. 列并行计算完成后,各 GPU 的部分输出如何得到最终结果?

单选题 · mid

  • A.用 All-Reduce 把所有部分求和
  • B.用 All-Gather 沿列方向拼接← 正确答案
  • C.用 Reduce-Scatter 求全局平均
  • D.直接输出任意一张卡的结果

答案:B。每张卡算出的部分输出是完整 Y 的不同列块,需要用 All-Gather 沿列拼接。A 是行并行的做法(部分和相加),C 是另一种规约型通信,D 会丢数据。传菜员是把不同菜拼上桌,而不是把菜倒进同一个锅里煮。

Q4. 为什么列并行可以直接对部分输出应用 GeLU 激活函数?

单选题 · mid

  • A.GeLU 逐元素操作,切块后结果不变← 正确答案
  • B.GeLU 是线性操作,满足分配律
  • C.GeLU 只在输出拼接后执行一次
  • D.每张卡都持有完整的输出矩阵

答案:A。GeLU 对每个元素独立计算,输出某一列的值只依赖输入同一列的元素,所以分块后逐块做 GeLU 与整体做再切分完全等价。B 错在 GeLU 是非线性的;C、D 都违背列并行“先局部后拼接”的基本流程。就像试味只需尝自己那盘菜,不必等整桌菜上齐。

Q5. 关于列并行的通信开销 All-Gather,下列说法正确的是?

多选题 · mid

  • A.每卡需向其余各卡发送自身输出← 正确答案
  • B.每卡通信量约为 (p-1)/p 乘以 bh← 正确答案
  • C.列并行完全不需要通信开销
  • D.通信量与行并行一次 All-Reduce 相当← 正确答案

答案:ABD。All-Gather 中每张卡把自己那份 b×(h/p) 输出发给其余 p-1 张卡,同时收回它们的数据,单卡通信量约为 (p-1)/p × bh。行并行一次 All-Reduce 也是同一量级,所以两者通信成本相当。C 完全错误,任何形式的张量切分都需要集合通信来合并结果。

Q6. Megatron-LM 的 MLP 中,两层权重 W1 和 W2 分别怎么切?

单选题 · mid

  • A.第一层列并行,第二层行并行← 正确答案
  • B.第一层行并行,第二层列并行
  • C.两层都用列并行切分
  • D.两层都用行并行切分

答案:A。MLP 计算为 GeLU(XW1)W2。第一层用列并行,使每卡产出完整列块并直接本地做 GeLU;第二层用行并行,使每卡算部分和后 All-Reduce。两者组合才能实现 f/g 融合,省掉中间一次 All-Gather。顺序一旦颠倒,GeLU 就无法在部分输出上本地计算。

Q7. 使用 p 张卡做列并行,每张卡的权重显存约为原来的多少?

单选题 · easy

  • A.和单卡一样,保持完全不变
  • B.缩减为原来的 p 分之一← 正确答案
  • C.增加为原来的 p 倍
  • D.缩减为原来的二分之一

答案:B。权重按列切成 p 块后,每张卡只存 d×(h/p) 个参数,显存降为 1/p;Adam 优化器状态也按同比例缩减。这就是列并行让千亿参数模型“装得下”的根本原因。D 只是固定减半,忽略了 p 的效果。

Q8. 关于列并行与行并行的对比,下列表述正确的是?

多选题 · hard

  • A.列并行输出是完整列块,用 All-Gather← 正确答案
  • B.行并行输出是部分和,用 All-Reduce← 正确答案
  • C.两者通信量处于同一数量级← 正确答案
  • D.列并行完全不触发任何通信

答案:ABC。列并行把输出列分给不同卡,最终用 All-Gather 拼接;行并行把输出行分给不同卡,部分和用 All-Reduce 累加;两者单卡通信量都在 bh 量级,成本相当。D 错,列并行每步前向都需要一次 All-Gather。两者的差异不在通信量,而在通信原语与后续算子的衔接方式。

Q9. 在千卡训练集群中,限制列并行扩展规模的最主要因素是?

单选题 · hard

  • A.输入维度必须能被 GPU 数整除
  • B.输出维度需整除且通信占比上升← 正确答案
  • C.激活函数无法在部分输出上计算
  • D.千卡集群不支持 All-Gather 通信

答案:B。列并行要求输出维度 h 能被 p 整除,且 p 增大时通信占比上升、加卡收益递减,所以张量并行度通常取 2~8。A 错在输入是广播的,不参与切分;C 错在 GeLU 逐元素特性反而让列并行更兼容;D 错在千卡集群完全支持 All-Gather,只是跨节点代价高。

Q10. 关于 MLP 中列并行与行并行的 f/g 融合,下列说法正确的是?

多选题 · hard

  • A.中间一次 All-Gather 被第二层吸收← 正确答案
  • B.融合后 MLP 前向只需一次 All-Reduce← 正确答案
  • C.融合之后完全不再需要任何通信
  • D.融合依赖 GeLU 逐元素的切分不变性← 正确答案

答案:ABD。第一层列并行本要 All-Gather,但第二层行并行恰好需要切分后的中间结果,两者抵消,只剩末尾一次 All-Reduce,通信几乎减半。D 正确:GeLU 的逐元素特性是“先激活再传给第二层”而非“先拼完整再激活”的前提。C 错误,MLP 末尾的 All-Reduce 以及反向传播中的通信依然存在,只是中间少了一次。