优化技术该插在哪一格:一套三层坐标,与一个 KV Cache 推导
0次浏览0条评论0个赞
Qwen3-235B 的 num_key_value_heads 是 4。这个数字看起来只是模型配置里的一个小项——其实它决定了一件很贵的事:
当张量并行度(TP)超过 4 时,KV Cache 会被整份复制 [1]。
于是可用 KV Cache 减半、最大并发下降、吞吐跟着掉 [2]。而你并没有多出任何算力,只是把同一份数据在更多卡上各存了一遍。
优化技术之间的耦合,比单项技术的优劣更决定结果——你选的每一层,都在为下一层的瓶颈创造条件。
一、为什么技术清单没有用
优化技术的内容会不断更新,但”该看哪里”的结构是稳定的。所以与其维护一张越来越长的清单,不如先建立一套坐标。
这个坐标系有三个相互正交的轴:
第一轴:任务流阶段。数据 / 训练 / 推理 / 验证 / 治理。它决定了你现在做的事属于哪一类问题。
第二轴:软硬件层级(L1–L7)。它决定了你要动的是哪一层。下表 L4 那一格会用到三个词,先各解释一次:量化(用更低比特表示权重与缓存,省显存)、蒸馏(让小模型学大模型的输出行为,以小代大)、剪枝(删掉影响小的参数)。三者都属于”用精度换资源”的算法层手段。
| 层级 | 名称 | 典型技术 |
|---|---|---|
| L7 | 业务与应用层 | Agent 系统、RAG 应用、提示工程 |
| L6 | 调度与编排层 | K8s、批调度、弹性扩缩容、负载均衡 |
| L5 | 推理引擎与服务层 | vLLM、TensorRT-LLM 等模型服务 |
| L4 | 模型与算法层 | 模型架构、并行策略、量化/剪枝/蒸馏 |
| L3 | 框架与编译器层 | PyTorch、torch.compile、XLA、MLIR |
| L2 | 编程与驱动层 | CUDA / ROCm / Ascend C、集合通信库 |
| L1 | 硬件与芯片层 | GPU/ASIC、HBM、CXL、InfiniBand |
第三轴:技术维度。算力 / 存储 / 网络 / 调度 / 数据 / 能源——它决定了这个优化主要在改善哪一种资源。
三层各取一个坐标,就是一次优化动作的完整定位。比如”把 MatMul 与 AllReduce 融合成一个算子”,坐标是:推理阶段 × L2–L3 之间 × 网络维度(辅以算力)。
二、坐标的三个用途
坐标系不是为了分类好看,它有三个具体用途。
用途一:定位”该不该在这里找”。症状和层级之间是有对应关系的。生成阶段慢、显存吃紧,问题几乎不可能在 L7;反过来,端到端响应慢而各层指标都正常,就要往 L6 的调度与排队上找。先定层,再选技术。
用途二:判断代价落在谁身上。这是坐标最有价值的地方。任何优化都不是免费的,而代价几乎总是落在另一个维度上:
- 计算通信融合 → 省网络时间,代价是算子融合带来的实现复杂度与兼容性约束;
- KV Cache 量化 → 省存储,代价是精度与算子支持;
- 增大批处理 → 提高吞吐,代价是单请求延迟与显存峰值。
用途三:回答”新技术该放哪”。新东西出现时,给它在三个轴上各取一个坐标,别先去清单里找同类——如果它落在某个已经有名字的格子里,说明它是替换项;如果落在一个空格里,那才是真正的新方向。
所以”这个优化该放哪一章”是错误的问题。正确的问题是:它在哪一层、主要动哪个维度、代价落在谁身上。
三、判据必须带阈值
坐标系说明”在哪里”,但还需要判据说明”是不是”。判据的关键在于必须是带阈值的量,否则会退化成感觉。
一套可用的判墙判据如下(阈值可按机器重标,但结构不变)[3]:
| 观测量 | 阈值 | 判到哪堵墙 | 动作方向 |
|---|---|---|---|
| 显存占用率(权重 + KV Cache) | > 90% | 显存墙 | 降并发、压缩 KV、量化 |
| 计算利用率 | > 85% 且吞吐未达预期 | 算力墙 | 优化算子、检查并行度 |
| 计算利用率 | < 50% 且显存很高 | 显存墙(表现成算力闲置) | 优先解决显存 |
| 显存带宽利用率 | > 70% | 带宽墙 | 减少访存、KV 优化、量化 |
这里最容易误判的是第三行:算力利用率低,通常第一反应是”算力没用满,去优化算子”——但如果显存同时很高,真正的原因是”卡在显存上,算力只能等着”。两个观测量必须一起看,单看一个会把方向搞反。
另外,采集本身要有清单。一份可用的采集至少要覆盖三类共十几项:性能 KPI(吞吐、TTFT 分布、Token 间延迟分布、成功率)、系统资源水位(显存、算力、带宽、排队深度)、负载与配置上下文(请求长度分布、并发、关键配置)。第三类最容易被漏掉,但它正是归因所需的参照系——没有它,两组数字无法比较。
四、贯穿线:优化是耦合的,不是叠加的
把前面三节连起来,会得到一条比”哪个技术更好”重要得多的判断:
优化技术不是可叠加的独立选项,它们之间有耦合——你选的每一个,都在为下一个瓶颈创造条件。
这条判断有一个非常干净的实例。张量并行本身是”算得快”的优化(把计算摊到多卡),却在推理阶段制造了一个新的存储问题:
- 为了让 KV 头能整除地分到各卡上,系统会复制 KV 头;
- 复制的直接后果是每张卡上可用的 KV Cache 变少;
- 于是最大并发与最大序列长度一起下降,吞吐反而受损。
也就是说:一个”算力维度”的优化,代价落在了”存储维度”上。这类跨维度的代价,只看单项技术的收益是看不见的——而这正是坐标系存在的理由。
下面用一个完整推导把它算清楚。
五、一个完整推导:TP 复制了 KV,DCP 把它切回来
5.1 问题的来源:TP 超过 KV 头数
在 GQA 模型里,Key/Value 头的数量 H = num_key_value_heads 远小于 Query 头数。这个设计本身就是为了压缩 KV Cache——但它同时引入了一个约束:
KV 头必须能整除地分到各张卡上。当张量并行度 TP > H 时,这个整除条件无法满足,系统只能复制 KV 头来凑齐。
后果有两个,都很实在:
- 每张卡可用 KV Cache 减少(极端情况下按
1/H缩减),最大支持序列长度下降; - 最大并发批数下降,吞吐降低。
注意这里的因果:冲突发生在并行度与模型的一个配置项之间,训练或推理框架本身没有做错什么。 H = 4 这样一个数字,就这样决定了 TP 能开到多少而不再浪费显存。
5.2 解法:沿序列维再切一刀
既然冗余来自”沿头维切分无法再切”,那就换一个维度切:把同一个请求的 KV Cache 沿序列维度再切分到多张卡上——这就是解码阶段的上下文并行(DCP)。
它的存储方式是交错的:同一个请求的 KV Cache 被拆到不同卡上,每张卡只保存其中一部分 token 的 KV。
交错粒度由框架参数控制(默认按单个 token 交错)。这样切分之后,原来被复制的 KV 头不再需要复制——同一份序列数据被分摊到多张卡上,冗余被直接从根上消除。
5.3 它不增加设备,但增加通信
这是 DCP 与前缀阶段上下文并行(PCP)最关键的区别,也是坐标系能立刻说清的一件事:
| Prefill 阶段上下文并行(PCP) | Decode 阶段上下文并行(DCP) | |
|---|---|---|
| 目标 | 摊薄 Prefill 计算,降 TTFT | 摊薄 KV Cache,提并发 |
| 是否增加设备 | 是——它扩展了设备规模 | 否——只在已有通信域内切分 |
| 代价 | 全量 KV 的聚合通信 / 分块收发 | 注意力计算时的跨卡通信 |
于是 DCP 的大小就有一个明确的有效范围:
dcp_size ∈ [1, tp_size / H],其中H = num_key_value_heads。
- 偏小 → KV 冗余较多,但通信少;
- 偏大 → 冗余降到最低(理论上可到
1/H),但通信增加; - 超过
tp_size / H在理论上仍可继续切分并加速计算,但会引入额外通信开销——是否值得,取决于你的瓶颈在存储还是在网络。
请注意最后这句的形态:它又把问题交回给了判据。坐标系告诉你这个手段在哪一层、动哪个维度;而”要不要用、用多大”仍然要回到第三节那组带阈值的观测。
六、什么时候该下到算子层
前面整套方法在宏观层就完成了大半。算子层是最后才去的地方,而且它有明确的前置条件。一条可用的三阶段路径是:
- 宏观参数——批大小、并行度、显存分配、序列长度上限;
- 应用与服务层——调度策略、缓存、请求编排;
- 算子与图模式——只有在前两层已收敛、硬件仍有余力时才进入。
第 3 步的进入条件必须写清楚,否则会变成”调不动就往下钻”。一个可判定的条件是:
计算利用率 < 85% 且显存 < 85%,但吞吐仍低于预期 → 说明硬件还有余力而瓶颈在实现细节,此时进算子层才有意义。
如果两个利用率都已接近上限,那说明宏观层已经到位,算子层也不会有大收益——继续往下钻只是浪费时间。先证明有余力,再往下走。
七、局限与边界
- 阈值(90% / 85% / 50% / 70%)是运行经验值,不是硬性物理界线。它们的作用是给出”该看哪边”的判据,换硬件、换模型、换负载都要重标;把某一台机器上的阈值当作通用常数是误用。
- DCP 的有效范围
[1, tp_size / H]依赖”KV 头数按整除复制”这一实现前提。不同框架对 KV 头的切分实现不同,范围也可能不同——推导的形态可迁移,具体边界必须按所用框架复核。 num_key_value_heads = 4是特定模型的值,这里用它是因为它足够小、冲突容易发生;换成 KV 头较多的模型,TP > H这个条件可能永远不触发。- 坐标系的价值是”不找错层”,它不保证”找到最优”。它把搜索空间缩小,但格子里该选哪个技术,仍然要靠实测量化。
- 不含本机实测数据:技术事实(GQA 的 KV 头复制、DCP 的交错切分与有效范围、三阶段路径)来自框架实现与公开资料;阈值来自运行经验。具体数值请以你自己机器上的测量为准。
结论:优化技术要插在坐标里,而不是堆在清单上
回到开头那个 4。
它之所以值得写在这里,是因为它示范了一件事:一个看起来只属于模型配置的小数字,可能会决定你的并行策略能开到多大、显存能装下多少并发。
所以我现在看一个优化时,顺序是固定的:
先给它取坐标(哪一层、动哪个维度、代价落在谁身上),再用量化判据确认瓶颈真的在那里,最后才比较具体技术。
顺序反了会怎样?你会得到一堆各自正确、合起来互相抵消的优化——换句话说,优化的顺序与耦合,比单项技术的优劣更决定结果;而坐标系的作用,就是让”代价落在谁身上”这件事在动手之前就可见。
参考文献
[1] Ainslie, J., Lee-Thorp, J., de Jong, M., Zemlyanskiy, Y., Lebrón, F., & Sanghai, S. (2023). GQA: Training Generalized Multi-Query Transformer Models from Multi-Head Checkpoints. arXiv:2305.13245:https://arxiv.org/abs/2305.13245(2026-10-11 实测可达)—— 第五节 num_key_value_heads 这一配置项及其与并行切分冲突的来源。
[2] Kwon, W., Li, Z., Zhuang, S., Sheng, Y., Zheng, L., Yu, C. H., Gonzalez, J. E., Zhang, H., & Stoica, I. (2023). Efficient Memory Management for Large Language Model Serving with PagedAttention. SOSP ‘23. arXiv:2309.06180:https://arxiv.org/abs/2309.06180(2026-10-11 实测可达)—— KV Cache 分页管理与显存占用的公开出处。
[3] Williams, S., Waterman, A., & Patterson, D. (2009). Roofline: An Insightful Visual Performance Model for Multicore Architectures. Communications of the ACM, 52(4). —— “先判断被哪一类上限卡住(算力还是带宽)“这一思路的来源;第三节的判墙判据沿用其形态。
关于证据的核验说明:这是一篇定位方法文章,不含本机实测数据。技术事实(GQA 的 KV 头复制、DCP 的交错切分与有效范围、计算通信融合)来自框架实现与公开资料 [1][2];四个阈值(90% / 85% / 50% / 70%)与算子层进入条件来自运行经验,不是物理界线——换硬件、换模型、换负载都要重标。num_key_value_heads = 4 是特定模型的值;dcp_size ∈ [1, tp_size / H] 依赖”KV 头按整除复制”这一实现前提,推导形态可迁移,具体边界须按所用框架复核。





评论
填个昵称就能发,不需要注册或登录