GPU 做 prefill、NPU 做 decode:HAWK 如何让跨架构 KV 搬运接近 GPU 直连,在线服务输出吞吐差最高 7.88%(arXiv 2610.29873v2 精读)
把 prefill 交给 GPU、把 decode 交给 NPU,这套异构拆分卡在一个具体动作上:KV 缓存要从 GPU 显存搬到 NPU 内存。韩国电子通信研究院(ETRI)与佐治亚理工的 HAWK(arXiv:2610.29873;v1 2026-10-21、v2 2026-10-23,下文「论文」均指 v2 正文)用共享的 Linux RDMA 栈,加上「按目标布局分配 NPU 侧缓冲区」与「把布局变换拆两段、让传输顺手完成对齐」,在 4 张 A100 PCIe 40 GB 与 4 张 Tenstorrent p150a 加速卡组成的商用系统上,让这一路搬运的延迟接近 GPU 到 GPU 直连(本文按论文 Figure 9 归一化读数反算,1280 MB 处约 0.6%)。本文区分三个不可直接混用的能效口径:kernel 算力能效(TFLOPS 每 ASIC kW)、decode 相位能效(输出 token/s 每 decode 设备 ASIC kW)、全服务设备能效(输出 token/s 每 prefill 与 decode 全部设备 ASIC kW)。按论文 v2 正文口径、以 GG-Direct(GPU 到 GPU、RDMA 直连)为基线,在相同 90% SLO 达成条件下,Llama 3.1 70B 的输出吞吐分别比基线低 0.48%(Chatbot)与 7.88%(Summarization);全服务设备能效为基线的 1.26 倍与 1.15 倍;按论文所列设备价格计算的成本效率为基线的 2.03 倍与 1.88 倍(不等于服务器整机墙电功耗口径)。截至本文 2026-10-29 核验,arXiv 摘要页仍显示 v1 的 1.72 倍成本效率,与 v2 正文的 2.03 倍冲突。所有数字均为论文口径转述,本文没有实测。
除另行标注外,本文引用的外部事实与页面核验时点为 2026-10-29(北京时间);除另注版本外,下文「论文」均指 2026-10-23 发布的 arXiv:2610.29873v2 正文。文中凡出自论文、官方文档或一手材料的数字,一律用 [n] 标注编号;凡本文按公式自己算出来的数字,一律标「本文自行推导」并写清假设。本文没有任何端到端实测:没有 A100 集群,也没有 p150a 板子。所有吞吐、延迟、能效数字都是论文报告的数值转述,或按公开常量复算的量级。
一、先把结论说清:这篇论文修的是「搬运」,不是「算力」
一句话版本:prefill 和 decode 是推理的两个阶段,本文讨论的是把它们分别部署到 GPU 节点与 NPU 节点;把两段拆到两种芯片上并不难,难的是拆完之后那条「把 KV 缓存送过去」的通道。
不严谨的比方。一家工厂有两道工序:前段是把整块原料一次性切好(prefill,所有输入 token 一次性并行处理,是稠密矩阵乘),后段是逐个零件往外送(decode,一次只生成一个 token,是矩阵乘向量)。前段吃算力,后段吃内存带宽。GPU 擅长前段,NPU 擅长后段——论文开篇就是拿这个观察做动机 [1](论文署名:第一作者来自佐治亚理工,其余五位作者来自韩国电子通信研究院 ETRI,见论文首页)。
但如果前段在一号车间、后段在二号车间,中间就要有一条传送带把半成品送过去。这条传送带在本文里就是 KV 缓存从 GPU 显存到 NPU 内存的搬运。论文测下来,这条传送带如果走「先落到主机内存、再从主机内存搬出去」的老路,延迟是同构 GPU 直连的 1.83 倍到 5.25 倍(传输量 10 MB 到 1.2 GB,这是论文在 GPU 到 GPU 之间测的)[1]。而如果强行直连,又会撞上第二个问题:两块芯片对同一个张量在内存里怎么摆,意见不一致。
HAWK 这个名字是 Heterogeneity-AWare Key-Value transfer 的缩写 [1]。它做的事可以拆成三件,后文按顺序讲:给 NPU 也接上一个 peer memory client(让网卡能直接读写 NPU 设备内存)、按 NPU 的张量布局去分配 RDMA 缓冲区、把布局变换拆成两段让传输过程顺手完成对齐。
先说清楚这篇论文的边界,免得读者把结论放大:单一 NPU 平台(Tenstorrent p150a,即 Tenstorrent 的 NPU 加速卡型号)、单一模型(Llama 3.1 70B)、单一并行度(TP=4)、单一网络(200 Gbps HDR InfiniBand),没有跨平台验证,也没有开源代码(论文写「plan to release」)[1]。所以本文的定位是「把一个真实系统的机制拆开讲清」,不是「NPU 已经能替代 GPU」。
读这篇论文的建议顺序:先看论文 Table 1 的四个数(动机),再看论文 §4.2 那个 OOM 的反面教材(为什么难),最后看论文 Table 8 与 Table 9(收益怎么量、量出来是多少)。 三处看完,这篇论文的骨架就清楚了;其余章节都是这三处的展开与证据。
二、先算一笔账:一次请求的 KV 到底有多大
要判断「搬运」值不值得专门写一篇论文,第一步是把被搬的东西量出来。设模型有 层、 个 KV 头、每个头维度为 、每个元素占 字节,则每 token、跨全部 L 层的 KV 占用是:
这里的 2 是 Key 与 Value 各一份。后文会区分三种体积:每 token 跨全部层的 KV(用来估算搬运量)、乘输入长度后的单请求 KV(用来对比传输时间)、模型权重(用来解释 decode 为什么容易被访存卡住)。代入 Llama 3.1 70B 的公开配置: 层、 个 KV 头(64 个查询头做 GQA 分组共享)、(由 hidden size 8192 除以 64 个查询头得到)、 字节(模型卡配置为 bfloat16)[3][4],得到:
也就是说,在本文的 bf16 假设下,每多一个输入 token,跨全部层的 KV 就多 320 KiB。再把它乘上论文用的两种负载的输入长度(Chatbot 用 ShareGPT,均值 783 token、P90 899;Summarization 用 LongBench,均值 2186、P90 2860)[1],并除以 200 Gbps InfiniBand 的线速( GB/s):
| 负载 | 输入 token(均值 / P90) | KV 体积(均值 / P90,MiB) | 线速下的纯传输时间(均值 / P90,ms) |
|---|---|---|---|
| Chatbot(ShareGPT) | 783 / 899 | 244.7 / 280.9 | 10.26 / 11.78 |
| Summarization(LongBench) | 2186 / 2860 | 683.1 / 893.8 | 28.65 / 37.49 |
注:KV 体积与传输时间两列为本文自行推导(按上面两个公式、上面四个公开配置常量计算,未计入协议开销、序列化与布局变换时间,也未考虑 TP 分片后单卡只搬 1/4;论文的传输量扫描覆盖 10 MB 到 1280 MB)[1][4]。
这张表回答了两个直觉问题。第一,被搬的东西不是 KB 级,是几百 MiB 级,所以「搬运」在实践中绕不开。第二,纯传输时间是十毫秒量级,而论文设定的 TTFT SLO 是 2000 ms(Chatbot)与 15000 ms(Summarization)[1],看起来占比不高——问题在于 KV 搬运落在首 token 之前的关键路径上,它不占用算力,却直接推迟第一个 token。同样十毫秒,在部分批处理任务里可能不显著,放在 P90 首字延迟的预算里就要算账。
顺带记一个对不上的细节:论文说「10 MB 的传输量对应 64 个输入 token」[1]。按上面的推导,bf16 下 10 MB 只对应约 30.5 个 token;要凑到 64 个 token,每 token 得是 156 KB 左右,即每个元素 1 字节(FP8/INT8 量化的 KV)。论文没有说明 KV 缓存的数据类型(只写「保留 Tenstorrent 软件栈默认的混合精度配置」)[1],所以本文按 bf16 推导,并把论文那句括注列为口径未闭合项。
为什么 decode 天生被带宽卡住:一个算术强度就够
这一小节要解释的是「为什么同样的芯片,前段能跑到 90% 利用率,后段只有 30%」。用算术强度(每读一字节能做多少次浮点运算)一句话就能说清。
判断瓶颈的通用判据是:把 kernel 的算术强度 (FLOP/字节)与设备的「峰值算力 ÷ 内存带宽」比值 相比, 时性能上限由带宽决定。下面先把 batch=1 单步 decode 的 算出来。
对一个稠密模型,每生成一个 token 需要约 次浮点运算( 为参数量,每个参数一次乘加记两次),同时至少要把权重读一遍:
代入 70B 稠密模型: FLOP,权重 148 GB [1],批大小为 1 时 KV 只有几百 MiB,可以忽略,于是
(本文自行推导,假设零缓存命中、不算注意力自身与激活的额外访存。)
不到 1 FLOP/字节意味着:每从内存读一个字节,只对应不到一次浮点运算(一次乘加按前文记两次 FLOP)。这解释了论文所测配置的 decode 为什么容易被访存卡住——GPU 在 decode 时张量核利用率最高只有 35.90%、DRAM 利用率爬到 64.09%(论文剖面数据)[1];但它不等于「任何加速器、任何 batch 都必然如此」,最终要看各设备的 。
那「把批开大」能不能救回来?能救一部分:权重只需读一遍,摊到更多 token 上。但代价是 KV 缓存随批大小线性增长:若批内 个请求的上下文长度均为 ,则 KV 容量约为 ( 为每 token 跨全部层的 KV),它会先撞上容量墙 [1];同时批量 decode 的运算量也随 增加,而权重的读取可以在批内复用。所以真实系统里「大 batch 把 GEMV 变成 GEMM」这条路有上限,这也是论文认为 NPU 那种「少开核、把计算压在近 DRAM 的核上」的设计思路有机会的原因。
三、为什么值得把两相拆给两种设备
先给口径导航:本文把三个能效分别叫 kernel 算力能效(TFLOPS 每 ASIC kW)、decode 相位能效(输出 token/s 每 decode 设备 ASIC kW)、全服务设备能效(输出 token/s 每 prefill 与 decode 全部设备 ASIC kW),三者不可直接混用。
论文用一张表把动机摆了出来:同一批请求(512 输入 token、128 输出 token、批大小 32,两侧各 4 张卡、TP=4、vLLM)分别跑在 A100 与 p150a 上 [1]。
| 指标(相位见各行) | GPU(A100) | NPU(p150a) | 比值(NPU/GPU) |
|---|---|---|---|
| Prefill 吞吐(输入 token/s) | 133.16 | 52.74 | 0.40(GPU 快 2.52 倍) |
| Decode 吞吐(输出 token/s) | 15.27 | 13.17 | 0.86(GPU 快 1.16 倍) |
| Decode 能效(输出 token/s 每 kW,设备 ASIC 功耗) | 77.11 | 107.20 | 1.39 |
| Decode 成本效率(输出 token/s 每千美元设备价) | 0.55 | 2.35 | 4.27(论文正文写 4.31) |
数据来自论文 Table 1 [1]。最后一行:用表里两个两位小数反算是 2.35 / 0.55 = 4.27,论文正文写 4.31,两处口径的差异原因本文未确认,引用时以论文正文口径 4.31 为准,同时保留反算值。
这张表就是整篇论文的立论基础:prefill 差 2.52 倍,decode 只差 1.16 倍,但 decode 阶段 NPU 更省电、更便宜。 于是把前段留在 GPU、后段换到 NPU,理论上能同时拿到「前段不慢」和「后段便宜」。
为什么 decode 阶段 NPU 能追上来?论文给了两组剖面数据。GPU 在 prefill 时 QKV 投影 kernel 的张量核利用率最高 92.92%、DRAM 利用率最高 19.26%(典型的计算受限);换到 decode,同一个 kernel 的张量核利用率掉到最高 35.90%、DRAM 利用率升到 64.09%(变成访存受限),同一层里其余 kernel 的张量核利用率只有 22% 到 39%、DRAM 利用率 36% 到 72% [1]。原因不复杂:batch 为 1 时是矩阵乘向量(GEMV),并行度低、数据复用少。
NPU 的应对方式是主动少开核:decode 阶段它用的核数比 prefill 少(论文举例 FFN kernel 是 8 核对 64 核),把计算集中到离 DRAM 更近的少数核上,从而把「已分配核的利用率」做得很高——QKV 投影 79.06%、FFN 81.46%,而 GPU 同一批 kernel 只有 11.93% 和 13.44% [1]。注意这是「占已用核峰值的比例」,不是绝对吞吐:论文对「少开核」的表述是「可能限制 kernel 吞吐」(may limit),Table 4 也没有给出单 kernel 的绝对吞吐;落到能效上,NPU 的 QKV 投影 kernel 算力能效(TFLOPS/kW)反而是 167.59 对 GPU 的 226.82,输给 GPU;只有在 attention(28.34 对 22.64)与 FFN(258.99 对 255.47)上才占优或打平 [1]。
这里必须打断一下,点出这篇论文最容易读错的地方:它有三个不可直接混用的「能效」口径。
| 口径 | 定义 | 论文位置 | GPU | NPU |
|---|---|---|---|---|
| kernel 算力能效 | 该 kernel 的 TFLOPS 除以 ASIC 功耗 | Table 4 | QKV 226.82 | QKV 167.59 |
| Decode 相位能效 | decode 输出 token/s 除以decode 设备 ASIC 功耗 | Table 1 | 77.11 | 107.20 |
| 全服务设备能效 | 输出 token/s 除以prefill + decode 全部设备的 ASIC 功耗 | Table 8 | 67.09(Chatbot) | 84.48(Chatbot) |
三个数字都叫「能效」,单位与分母都不同。混用会得出完全相反的结论——按第一个口径 NPU 在 QKV 上是输的,按第二、三个口径它是赢的。本文后面凡出现「能效」,都会写明是哪个口径。
四、真正的两道坎,以及主机中转的代价
论文把障碍拆成两条,这个拆法是全文的结构骨架 [1]。
第一道坎是通信栈不通。同构 GPU 系统里,NVIDIA 的 NIXL 一类软件栈可以让网卡通过 RDMA 直接读写 GPU 显存(靠 nvidia-peermem 把显存暴露给 RDMA 子系统),所以 KV 缓存能从 A 卡显存直接进 B 卡显存。换到 NPU,各家用的是自己的通信机制与驱动栈,跟 GPU 那一套不互通。
第二道坎是张量布局不同。GPU 上张量分布在多个 DRAM 通道,但映射在一段虚拟连续地址里(论文提到固定的 256 B 粒度交织),所以「一个连续 RDMA 缓冲区」就够用。NPU 不同:它显式控制哪个张量块放进哪个 DRAM 通道(为了内存局部性、减少片外流量),于是一个张量在物理上是分散在多个通道里的一堆块。论文引用 Google TPU 的 tiled 布局来说明这种差异在业界是常态 [1](TPU 是 Google 的张量处理加速器;此处只作布局差异的例子,不是 HAWK 的验证平台)。
flowchart LR
subgraph P["Prefill 节点(GPU)"]
A["A100 PCIe 40GB x4<br/>TP=4"]
B["RDMA 注册的<br/>连续 GPU 缓冲区"]
end
subgraph H["主机路径(HAWK 绕开)"]
C["主机内存<br/>两次拷贝 + 主机干预"]
end
subgraph D["Decode 节点(NPU)"]
E["每通道一个 RDMA 缓冲区<br/>按 NPU 张量布局分配"]
F["NPU KV cache pool<br/>拷入后无需再排版"]
end
A -->|"① 布局变换(GPU 上做)"| B
A -.->|"对照路径:主机中转;1.83x-5.25x 是同构 GPU 间的测值"| C
C -.-> E
B ==>|"② RDMA 直连 200 Gbps HDR IB"| E
E ==>|"③ 批量化传输 + 拷入 KV pool"| F这张图说明了什么:HAWK 的核心不是「更快的光纤」,而是把虚线那条主机中转路径删掉,同时让目标侧的缓冲区一落地就是 NPU 能直接吃的形状。
主机中转有多贵?论文在 GPU 到 GPU 之间量过:传输量从 10 MB 增到 1.2 GB 时,主机中转的延迟是直连的 1.83 倍到 5.25 倍 [1]。注意这是同构 GPU 之间的对比——也就是说,即使两块芯片的布局完全一样,只要中间过一趟主机,代价就已经是倍数级。异构还可能额外增加布局处理成本,具体要按实际路径去测。
顺带说一个反面教材,它能说明第二道坎有多硬。论文实现了一个对照配置:GPU 做 prefill、NPU 做 decode,直连 RDMA,但 NPU 侧用传统的一个连续缓冲区。这个配置在服务压测时直接 OOM 失败了——因为连续缓冲区不符合 NPU 的通道布局,内存使用在通道之间失衡,有的通道已经耗尽、别的通道还空着,后续张量分配不下去 [1]。论文没有把这个配置放进定量对比,因为它跑不完。一个「看起来只是少优化了一点」的实现选择,直接把系统跑挂,这是本文认为最值得记住的一条工程事实。
五、第一招:把 NPU 内存也挂上同一套 RDMA 栈
HAWK 的第一个动作是「接通道」:在 Linux 的 RDMA 栈(内核态 ib_core、用户态 libibverbs)下面,给 GPU 用现成的 peer memory client(nvidia-peermem),给 NPU 自己写一个 peer memory client [1]。
peer memory client 的接口很朴素,就是把五个回调补齐,让 RDMA 子系统在注册内存时能问「这块地址是不是你的、对应的物理页在哪、能不能映射成网卡能访问的 DMA 地址」[1]:
| 回调 | 作用 |
|---|---|
npu_acquire | 判断 RDMA 子系统请求的地址范围是否属于 NPU 设备内存 |
npu_get_pages | 取出该地址范围对应的 NPU 内存页 |
npu_dma_map | 把这些页映射成网卡可访问的 DMA 地址 |
npu_dma_unmap | 释放 DMA 映射 |
npu_put_pages | 释放取出的内存页 |
做完这一步,两侧的网卡就能直接读写对端设备内存,数据路径上不再有主机。论文把这个支撑做在 Tenstorrent 的驱动里(用户态驱动 v0.68.0、内核态驱动 v2.5.0),上层搬运逻辑写在 Mooncake Transfer Engine v0.3.6 之上——它是一个负责跨设备内存传输的开源传输引擎,HAWK 改的是它上面的「缓冲区配对 + 布局变换」这一层 [1][5]。
flowchart TB
APP["应用 / 传输引擎(Mooncake Transfer Engine)"] --> US["用户态:libibverbs"]
US --> KS["内核态:ib_core"]
KS --> NIC["RDMA 网卡(ConnectX-6)"]
KS --> PC1["GPU peer memory client<br/>现成(nvidia-peermem)"]
KS --> PC2["NPU peer memory client<br/>HAWK 新写(五个回调)"]
PC1 --> G["GPU 显存"]
PC2 --> N["NPU 内存(多 DRAM 通道)"]这张图说明了什么:异构搬运的第一性问题是「网卡能不能拿到对端设备内存的 DMA 映射」,谁能回答这个问题,谁就能进入同一条 RDMA 数据通路;HAWK 的贡献是把 NPU 侧这个回答补上了。
选择 Tenstorrent 作为 NPU 平台的理由,论文写得很直接:这是我们知道的唯一一个开源软件栈、允许做 HAWK 所需底层改动的 NPU 平台 [1]。这条限制很关键——它意味着论文的「可行性」结论部分依赖于「你手上那块 NPU 的驱动能不能改」。闭源驱动的加速卡,这一招未必用得上。
六、第二招:按目标布局分配缓冲区
接通道只解决了「能不能直连」,没解决「搬过去能不能直接用」。于是第二个动作是在 NPU 侧按目标张量布局分配 RDMA 缓冲区。
论文先用两个式子把两种内存表示区分开。NPU 张量是一组「指针—长度」对,每对指向一个 DRAM 通道里的连续块:
而传统连续缓冲区是单个「基址—长度」对:
这个差别带来两个后果 [1]。其一,搬过去不能直接用:即使把张量的维度顺序和元素顺序都改对了,落在连续缓冲区里的数据仍要再做一次搬运或调整才能变成 NPU 张量对象。其二,会用出内存碎片:连续缓冲区的布局不跟随 NPU 的通道划分,通道之间的占用会失衡,某些通道被占满、另一些还空着,后续张量分配就被卡住——上面那个 OOM 的对照配置就是这么挂的。
HAWK 的做法是:每个 DRAM 通道分配一个虚拟连续的缓冲区,并按目标布局组织它们(论文为此加了六个 C++ 接口:map_buffer_dma、get_device_num_bank、get_buffer_address_bank、get_buffer_size_bank、unmap_buffer_dma 等)[1]。
代价是什么?缓冲区从 1 个变成 个,管理开销和 RDMA 请求数都会上升。论文给的界是:缓冲区数量上限 = NPU 的 DRAM 通道数,通常十个以内,每个通道内部仍然是连续的,所以额外开销相对单缓冲方案「modest」(适度)[1]。在 p150a 上这个数是 8 [1]。
flowchart LR
subgraph G["GPU 侧(变换后)"]
G1["G' : [N_ch, -1, tile_chunk]<br/>每个通道的数据已聚成连续大块"]
end
subgraph B["NPU 侧缓冲区"]
B1["通道 0 缓冲区"]
B2["通道 1 缓冲区"]
B3["..."]
B4["通道 7 缓冲区"]
end
subgraph T["NPU KV pool"]
T1["按 tensor 布局落位<br/>可直接被 decode kernel 消费"]
end
G1 ==>|"每段对应一个通道,单次传输块变大"| B1
G1 ==> B2
G1 ==> B3
G1 ==> B4
B1 --> T1
B2 --> T1
B3 --> T1
B4 --> T1这张图说明了什么:把「一个连续缓冲区」换成「每通道一个缓冲区」,换来的不是更快的光纤,而是传输时就把目标布局对齐做掉——数据到达时已是目标形状;接收后仍要做一次设备内拷贝进 KV pool(见第八节),但不再需要布局重排。
七、第三招:把布局变换拆成两段,让传输顺手完成对齐
第三个动作解决一个矛盾:要在传输前把布局改对,就得做数据重排;而数据重排本身要花时间与带宽。论文把它拆成两段,中间夹一次传输 [1]。
先定义两个符号:tile 是 NPU 分块布局的基本单元, 是一个 tile 内的元素数;下面形状表达式里的 表示该维由总元素数自动推定,而不是一个固定值。
第一段在 GPU 上做,把张量重排成「每个目标通道的数据聚成一块连续区域」的形状:
这样做有两个好处:单个 RDMA 传输的块变大(请求数变少),而且 GPU 侧在 prefill 阶段还有内存带宽余量,重排的额外访存可以塞进这段余量里——这是论文明确解释的设计取舍 [1]。
第二段由传输本身完成。把变换后张量的每一段连续区域,映射到对应的每通道 NPU 缓冲区,用指针算术直接对齐:
其中 是通道下标, 是每一段在 里的字节跨度, 是该通道的 NPU 缓冲区。落到缓冲区的数据形状已经等于 NPU 张量要求的形状,于是 decode 侧在拷入 KV pool 后可以直接消费,不需要后置重排。
论文还解释了为什么不走另一条更「直接」的路:如果按目标布局逐小块映射(每个元素或每个 tile 单独一次 RDMA),单次传输会缩到几百字节,协议开销与请求处理开销吃掉全部收益 [1]。这条路看起来最「正确」,实际最慢——这是本文认为第二值得记住的工程事实:在搬运路径上,「对齐得越精细」不等于「越快」,要把传输粒度当成第一约束。
八、端到端流程:五步摘要,两处容易忽略的细节
论文用一段伪代码把流程固定下来(Algorithm 1),省略了第一个 token 的传递(那是一个整数):[1]
- decode 侧发起请求:
sendKVRequest(prefill, requestId)。 - prefill 侧按 requestId 取出 KV 张量,做第一段布局变换,向自己的 RDMA 缓冲区申请空间,把张量拷进去,然后把「总长度 + 缓冲区偏移」这些元数据发给 decode 侧。
- decode 侧按元数据把「GPU 缓冲区的一段」与「NPU 各通道缓冲区」两两配对,生成 RDMA 请求列表,批量提交(而不是逐条串行提交)。
- 传输完成后,decode 侧把收到的数据从 RDMA 缓冲区拷进 NPU 的 KV cache pool。
- 两侧各自释放占用的缓冲区区域。
两处容易忽略的细节。第一,元数据里带的是偏移,不是地址——意味着 GPU 侧的缓冲区是可以复用的环形/池化结构,不需要每次传输重新注册内存。第二,配对发生在 decode 侧而不是 prefill 侧:接收方才知道自己每个通道的缓冲区在哪、什么形状,发送方只需要交出一段连续数据。这个职责划分把「布局知识」留在了拥有该布局的设备一侧,是整篇论文里最容易迁移到别处的设计思想。
还有一处论文没展开、但工程上绕不开的:两侧缓冲区的生命周期管理。伪代码里只有「释放」两步(D9 与 P9),但实际上 NPU 侧每个通道的缓冲区被多个并发请求共用,谁先谁后、满了怎么办,论文没有写。这一段在实践中往往是 bug 高发区,读论文时应该把它当成「未给出」而不是「不存在」。
评测配置见下(论文 Table 7)[1]:
| 项目 | 配置 |
|---|---|
| Prefill 硬件 | 4 张 NVIDIA A100 PCIe 40 GB |
| GPU-only decode 硬件 | 4 张 NVIDIA A100 PCIe 80 GB |
| NPU decode 硬件 | 4 张 Tenstorrent p150a |
| 节点内互连 | prefill 侧 NVLink Gen 3 + PCIe Gen 4;NPU 侧以太网链路 |
| 节点间网络 | 200 Gbps HDR InfiniBand,每节点一块 ConnectX-6 |
| 服务框架 | vLLM v0.10.0,每相位一个实例,各自 TP=4 |
| 模型 | Llama 3.1 70B(论文记 148 GB 权重,即 137.8 GiB,本文自行换算) |
九、结果怎么读:三个层次,一个口径缺口
论文的四组对比是:GG-Host(GPU 对 GPU、主机中转)、GN-Host(GPU 到 NPU、主机中转 + NPU 侧布局调整)、GG-Direct(GPU 对 GPU、RDMA 直连)、GN-HAWK(GPU 到 NPU、HAWK)[1]。
先把三个倍数的分子分母摆出来,再读数字:延迟比 = GN-Host ÷ GG-Host(越大表示主机中转越慢);HAWK 加速比 = GN-Host ÷ GN-HAWK;服务能效比 = GN-HAWK ÷ GG-Direct。三者不是同一指标,下面的 8.91 与 3.79 属于第一个,1.26 倍属于第三个。
第一层,单次搬运延迟(Figure 9,全部归一化到 GG-Host)。 随传输量从 10 MB 增到 1280 MB:GG-Host 是基准 100%;GN-Host 的延迟是 GG-Host 的 8.91 倍降到 3.79 倍;GG-Direct 从 GG-Host 的 66.30% 降到 14.77%;GN-HAWK 从 77.25% 降到 14.68% [1]。翻成两句人话:小传输量时 HAWK 还吃亏(10 MB 处比 GG-Direct 慢约 16.5%,因为残留的布局变换成本摊不开),大传输量时两者就分不出来了(1280 MB 处 GN-HAWK 甚至比 GG-Direct 低 0.61%,论文 §1 引言写的是「0.65% 的差异」)[1]。这两个百分数都是本文按论文 Figure 9 的归一化读数反算,不是论文直接给出的比值。
然后是那个口径缺口。论文引言写「比朴素的主机中转快最多 27.69 倍」[1],但全文没有给出这个数字对应的传输量与测法。用 Figure 9 的两条归一化读数反算:10 MB 处约 8.91 / 0.7725 ≈ 11.5 倍,1280 MB 处约 3.79 / 0.1468 ≈ 25.8 倍。25.8 倍与 27.69 倍同量级、相差约 6.8%,但 25.8 这个反算值本身已被显示精度锁死:即使把 3.79 与 14.68% 都取到最宽的舍入边界,比值也只在约 25.77 到 25.86 之间,够不到 27.69,所以这个差额不能由显示值的四舍五入解释。 仅凭正文公开的端点归一化数值,无法确定 27.69 对应的数据点或原始值;本文只记录「无法复现到该精确值」,不据此判定论文数字对错。本文的处理是:引用时写「论文称最多 27.69 倍,按 Figure 9 反算约 25.8 倍」。
第二层,在线服务(Figure 10 到 13)。 SLO 设为 TTFT/TPOT = 2000/200 ms(Chatbot)与 15000/200 ms(Summarization)[1]:
以下相对差均以 GG-Direct 为比较基线;「90% SLO」在请求率一行指 90% TTFT-SLO 达成点(Figure 10),在输出吞吐一行指 90% TTFT/TPOT 联合 SLO 达成点(Figure 13)。
| 指标(判据见行内) | Chatbot | Summarization |
|---|---|---|
| GG-Host 掉到 90% TTFT-SLO 以下的请求率 | 0.40 req/s | 0.38 req/s |
| GN-HAWK 相对 GG-Direct 的请求率差(90% TTFT-SLO 处) | 2.90% | 5.90% |
| 输出吞吐差(90% TTFT/TPOT 联合 SLO 处,GN-HAWK 对 GG-Direct) | 0.48% | 7.88% |
| 请求率(90% TTFT/TPOT 联合 SLO 处,GG-Direct / GN-HAWK) | 0.65 / 0.65 req/s | 0.65 / 0.60 req/s |
这里有两个必须区分的数字,因为它们的分母不同:2.90% 与 5.90% 是「请求率差」(TTFT 维度,Figure 10),0.48% 与 7.88% 是「输出吞吐差」(Figure 13)。摘要里的 7.88% 指的是后者,不是「请求率差 7.88%」。另外 Summarization 的 SLO 达成处 GG-Direct 是 0.65、GN-HAWK 是 0.60,论文把这个 7.69% 的差归因于 NPU 略低的 decode 性能导致 TPOT 越界,而不是搬运开销 [1]。GN-Host 则根本没进 Figure 13——它在最低请求率下都达不到 90% SLO [1]。
第三层,能效与成本(Table 8、Table 9)。
| 指标(90% SLO 处) | 负载 | GG-Direct | GN-HAWK | 比值 |
|---|---|---|---|---|
| 全服务设备能效(输出 token/s 每 kW) | Chatbot | 67.09 | 84.48 | 1.26 |
| 全服务设备能效(输出 token/s 每 kW) | Summarization | 41.01 | 47.18 | 1.15 |
| 设备成本效率(输出 token/s 每千美元) | Chatbot | 2.05 | 4.15 | 2.03 |
| 设备成本效率(输出 token/s 每千美元) | Summarization | 1.25 | 2.35 | 1.88 |
这里就是版本冲突的现场。arXiv 摘要页上写的成本效率是 1.72 倍(那是 v1 摘要的口径,页面元数据在 v2 更新后仍停留在旧文本)[2];v2 的 PDF 正文与结论都写 2.03 倍,且与 Table 8 的 4.15 / 2.05 = 2.02 一致 [1]。引用时应采用 v2 的 2.03 倍,并注明摘要页显示值滞后。 本文按后者,同时把这个冲突留在核验记录里。
论文还做了一组换 GPU 的对照:把 decode 侧的 A100 换成 H100(记作 GGH-Direct)。这时 GN-HAWK 的吞吐只低 6.53%(Chatbot)与 6.50%(Summarization),能效高 1.04 倍,成本效率高 3.46 倍 [1]。这组数据比主结果更能说明问题:能效优势从 1.26 倍缩到 1.04 倍,原因是 H100 在 decode 时功耗更低;成本优势反而放大到 3.46 倍,原因是 H100 单价高(论文用的价格是 p150a 1400 美元、A100 40GB 4000 美元、A100 80GB 7000 美元、H100 16000 美元)[1]。换句话说:这套异构拆分的收益高度依赖「拿什么 GPU 对什么 NPU」,不是一个普适常数。 论文自己也把成本效率写成「参考而非定论」,因为设备价格随行情波动 [1]。
三个容易误读的地方
第一,「7.88%」是输出吞吐差,既不是延迟差也不是请求率差。同一组实验里另有两个不同的百分数:请求率差 2.90%(Chatbot)与 5.90%(Summarization)、SLO 达成处的请求率差 7.69%(Summarization)。这里既有输出吞吐差,也有请求率差;而请求率还必须区分「仅按 TTFT 取点」与「按 TTFT/TPOT 联合约束取点」两种情况,不能仅凭百分数互换,否则会得出「NPU 比 GPU 慢 7.88% 的请求率」这种错误结论。
第二,GN-Host 在 Figure 13 里缺席不是画图省略——它连最低评估请求率都达不到 90% SLO,因此没有「90% SLO 处的吞吐」可画 [1]。看到某条曲线只出现一半时,先想「它是不是根本没跑到那条线」,而不是「作者漏画了」。
第三,90% SLO 处的请求率在 Chatbot 上 GG-Direct 与 GN-HAWK 都写到 0.65 req/s。注意这里是「综合 SLO(TTFT 与 TPOT 同时达标)」口径,而 2.90% 是「单看 TTFT」口径的请求率差,两者不能互相解释:论文公开文本只报告 Figure 12 上两者都达到 0.65 req/s,并没有给出这两条曲线之间的精确差 [1]。
十、符号与术语表
| 符号 / 术语 | 含义 | 量纲 / 备注 |
|---|---|---|
| 模型的解码器层数 | Llama 3.1 70B 为 80 | |
| KV 头数(GQA 分组后的头数) | Llama 3.1 70B 为 8 | |
| 每个注意力头的维度 | 128 | |
| KV 元素字节数 | 论文未明示;本文按 bf16 取 2 | |
| 模型参数量 | 本文算术强度推导用 70B | |
| 批大小 | KV 体积随它线性增长 | |
| 每 token 的 KV 占用 | 本文推导 320 KiB | |
| NPU 的 DRAM 通道数(= 每通道缓冲区数上限) | p150a 为 8,论文称通常 ≤10 | |
tile_chunk | NPU tile 的元素数 | 论文未给出具体值 |
| prefill | 一次性处理全部输入 token 的相位 | 计算受限(GEMM) |
| decode | 逐 token 生成的相位 | 访存受限(GEMV) |
| PD 分离 | 把 prefill 与 decode 放到不同实例/节点 | 论文 Figure 3 |
| peer memory client | 把设备内存暴露给 RDMA 子系统的驱动模块 | GPU 侧现成,NPU 侧 HAWK 新写 |
| GG / GN | 前一位是 prefill 设备,后一位是 decode 设备 | GG=全 GPU,GN=GPU 到 NPU |
| 90% SLO 达成 | 使 90% 请求满足 TTFT/TPOT 约束的请求率 | 单位 req/s |
十一、边界与未解
按重要性排,本文认为这篇论文有六处边界需要读者自己接住。
第一,单平台。NPU 侧只有 Tenstorrent p150a,而且选它的理由是「驱动开源、能改」,这既是论文的诚实,也是它的限制:闭源驱动的加速卡未必能复现这套 peer memory client [1]。
第二,单模型、单并行度。只有 Llama 3.1 70B、只有 TP=4。论文自己写了原因:Tenstorrent 的软件栈把每个受支持的模型与特定设备类型、设备数量绑定 [1]。所以「7.88% 的差距」不是这个机制的固有常数,只是这一组配置下的读数。
第三,没有端到端质量退化数据。论文说异构配置的 ROUGE 与全 GPU 配置「几乎相同」[1],但没给具体分数,也没做更长上下文的生成质量对比。
第四,KV 数据类型未交代,由此产生第二节那个「10 MB 对应 64 token」与 bf16 推导(30.5 token)对不上的口径缺口。
第五,27.69 倍无法从公开数据复现(见第九节),只能反算到约 25.8 倍。
第六,没有和 MLA 一类的 KV 压缩方案交叉评估。Llama 3.1 70B 用的是 GQA(8 个 KV 头),如果换成 DeepSeek 系那种低秩压缩的 KV(每 token 的 KV 体积显著小于 320 KiB)[7],搬运量会小一个档次,「搬运是否是瓶颈」的结论要重算。论文没有讨论这一点,本文认为这是最值得后续补的一块。
如果只能记住一条「该怀疑什么」,本文的建议是:怀疑所有「倍数」类结论的可移植性。 论文里同一套机制对 A100 与 H100 的全服务设备能效比分别是 1.26 倍与 1.04 倍 [1]——这里要比较的是「各自相对基线的优势幅度」(多出 26% 与多出 4%),而不是说 1.26 这个总比值本身是 1.04 的几倍;那么对另一块 NPU、另一代网络、另一种 KV 宽度,这几个倍数会变成多少,论文没有、也无法给出答案。能被搬走的是机制(peer memory client + 布局感知缓冲区 + 两段式变换),不是这些数字。
十二、工程含义:什么时候值得动这件事
把上面的机制翻译成决策问题,本文给出这份清单(判断依据全部来自论文口径,不是实测):
- 值得考虑:decode 负载占比高、GPU 在 decode 阶段利用率长期偏低(论文里 QKV kernel 张量核利用率最高 35.90%),且手上 NPU 的驱动可改、能实现 peer memory client。
- 值得考虑:能接受「prefill 留在现有 GPU 集群、只为 decode 建一条新硬件池」,即不做整机替换。
- 要谨慎:如果你的 KV 缓存已经被压缩过(MLA、KV 量化到 1 bit 量级),先把每请求 KV 体积按第二节的公式重算一遍;搬运量降下来之后,这套优化能省下的纯传输时间也会同比下降,但端到端收益还要按目标体积重测。
- 要谨慎:如果 decode 侧的 GPU 不同于主实验里的 A100(论文另测了用 H100 做 decode 的配置:全服务设备能效比从 1.26 倍降到 1.04 倍)[1]。
- 不要照抄:不要直接照搬论文里那个导致 OOM 的「单一连续 RDMA 缓冲区 + 事后重排」配置;上异构前先验证目标平台的通道布局、分配策略与容量余量 [1]。
- 不要忽略:主机中转在同构 GPU 之间就已经是 1.83 到 5.25 倍的代价 [1];任何跨设备方案的第一优先级都是「把主机从中转路径上摘掉」。
一句话总结这篇论文的价值:它证明的不是「NPU 能替代 GPU」,而是**「异构拆分的可行性取决于你能不能把布局知识放到接收端、把对齐动作塞进传输过程」**——这是一个可以搬到别的异构场景(GPU 到 TPU、GPU 到 PIM、甚至同一机箱内的不同加速卡)的设计模式,而不只是一次硬件组合的性能报告。
12.1 如果 KV 已经压过:先把搬运量重算一遍
本文第二节那个 320 KiB/token 是按 bf16 假设计算的值。真实系统里 KV 常被压过,搬运量会同比下降,纯传输那一段的耗时也跟着下降,但端到端收益还得看固定开销占多大比例,需要按目标体积重新测。用第一节的定义式,只改元素字节数 :
| KV 元素宽度 | 每 token、单层( 字节) | 每 token、跨全部 80 层 | ShareGPT 均值请求(783 token) |
|---|---|---|---|
| bf16(2 字节) | 4,096 字节 | 320 KiB | 244.7 MiB |
| FP8(1 字节) | 2,048 字节 | 160 KiB | 122.3 MiB |
| 4 bit(0.5 字节) | 1,024 字节 | 80 KiB | 61.2 MiB |
(全部为本文自行推导,按 与上表常量计算。)这里正好对上第二节那个缺口:在「全模型未分片 + KV 为 1 字节 + 把 10 MB 按 10 MiB 理解」这组假设下,10 MiB 除以 160 KiB 恰好是 64 个 token,与论文括注完全一致;若按 TP=4 单卡 bf16(每 token 80 KiB)则是 128 个 token。所以「1 字节 KV」只是能精确解释 64 的一个合理猜测,论文没有披露足够信息确定实际口径,本文不把它写成唯一可能。
若换成 MLA 一类的低秩压缩 KV,每 token 的 KV 体积会显著小于 320 KiB(具体比例须按对应论文原文核对,本文不引用未核对的数字)[7]。结论要谨慎:KV 体积减少会降低纯传输那一段的时间,但端到端收益还取决于固定开销(布局变换、通道配对、落池拷贝)与服务本身的瓶颈,不能仅按字节数线性外推。
12.2 复现这份结果需要动到哪几层
论文列出的改动面总共四层,缺一层都跑不通 [1]:
- 驱动层:给 NPU 写一个 peer memory client(五个回调),并让 RDMA 子系统认这块内存;论文改的是 Tenstorrent 的用户态驱动 v0.68.0 与内核态驱动 v2.5.0。
- 缓冲区层:在 NPU 侧实现「每通道一个虚拟连续缓冲区」的分配与查询接口(六个 C++ 接口)。
- 传输引擎层:在 Mooncake Transfer Engine v0.3.6 上实现「变换后的 GPU 缓冲区段落 → 各通道 NPU 缓冲区」的配对与批量提交。
- 服务框架层:用插件把 NPU 执行栈接进 vLLM v0.10.0,并保持每相位一个实例、各自 TP=4 的形态。
对想在自己的硬件上评估这件事的团队,这份清单的价值大于那 7.88%:它把「要不要做」变成了「这四层里我有几层能改」。如果第 1 层改不动(闭源驱动),后面三层都无从谈起。
📋 来源与核验记录
本文所有外部事实均来自本轮实际打开并核对过的一手材料(核验时点 2026-10-29,北京时间)。为便于复核,每条来源单独给出 URL。
- HAWK 论文正文(v2 PDF,14 页 / 13 图 / 9 表) URL: https://arxiv.org/pdf/2610.29873v2 核对内容:Table 1、Table 3、Table 4、Table 5、Table 6、Table 7、Table 8、Table 9、Figure 9 至 Figure 13,以及 Algorithm 1 与 §4.1–§4.5、§5.1–§5.4。PDF 页脚标注 arXiv:2610.29873v2 [cs.AR] 23 Oct 2026。
- arXiv 摘要页(版本与摘要字段核对) URL: https://arxiv.org/abs/2610.29873 核对内容:提交历史 v1 为 2026-10-21 15:25:35 UTC、v2 为 2026-10-23 05:45:42 UTC(两份大小均为 1,178 KB);页面摘要字段仍写成本效率 1.72 倍,与 v2 正文的 2.03 倍冲突,本文按 v2 正文取值并在此记录冲突。
- Llama 3 模型家族技术报告(arXiv:2407.21783,The Llama 3 Herd of Models) URL: https://arxiv.org/abs/2407.21783 核对内容:Llama 3.1 70B 的 GQA 分组结构(64 个查询头、8 个 KV 头)。
- Llama 3.1 70B 公开配置(NousResearch 镜像;meta-llama 原仓库需授权) URL: https://huggingface.co/NousResearch/Meta-Llama-3.1-70B/blob/main/config.json 核对内容:本轮实际读取字段 num_hidden_layers=80、num_key_value_heads=8、num_attention_heads=64、hidden_size=8192、max_position_embeddings=131072、torch_dtype=bfloat16、vocab_size=128256;head_dim 字段未显式给出,按 8192/64 推得 128。
- Mooncake 论文(arXiv:2407.00079,KVCache-centric 分离式架构) URL: https://arxiv.org/abs/2407.00079 以及 HAWK 所依赖的传输引擎开源仓库(论文声明使用 Transfer Engine v0.3.6) URL: https://github.com/kvcache-ai/Mooncake
- SGLang 论文(arXiv:2312.07104),用于 PD 分离的系统背景 URL: https://arxiv.org/abs/2312.07104
- DeepSeek-V2(arXiv:2405.04434,MLA 低秩 KV 压缩),用于第 12.1 节的对照 URL: https://arxiv.org/abs/2405.04434
- NVIDIA A100 官方产品页,用于确认 A100 存在 PCIe 40 GB 与 80 GB 两种规格(论文 prefill / decode 两侧各用一种) URL: https://www.nvidia.com/en-us/data-center/a100/
- Tenstorrent 开源推理栈,用于确认论文所选 NPU 平台软件栈开源可改(这是论文选择该平台的前提) URL: https://github.com/tenstorrent/tt-inference-server
补充数字出处索引(逐个交代正文数字对应的论文位置):
- 0.48%:论文 Figure 13 与 §5.3.4,Chatbot 负载下 GN-HAWK 相对 GG-Direct 的输出吞吐差 [1]。
- 0.65%:论文 §1 引言的延迟差表述(1280 MB 处);按 §5.2 的 14.68% 与 14.77% 反算约 0.61%。
- 6.8%:本文自行推导,(27.69 − 25.82) / 27.69 ≈ 6.76%,用于说明 27.69 与公开端点反算值之间的相对差。
- 100%:Figure 9 的归一化基准(GG-Host 记作 100%),不是性能数字 [1]。
- 11.93% 与 13.44%:论文 Table 4 里 GPU 侧 QKV 投影与 FFN 投影的「compute utilization(占已用核算力峰值的比例)」[1]。
- 14.68% 与 14.77%:论文 Figure 9 在 1280 MB 处 GN-HAWK 与 GG-Direct 相对 GG-Host 的归一化延迟 [1]。
- 16.5%:本文自行推导,由 Figure 9 在 10 MB 处的两条归一化读数相除得到(77.25% 对 66.30%),不是论文直接给出的数字。
- 19.26%、35.90%、22%、36%、39%:论文 §3.1 的 GPU 剖面数据(prefill 时 QKV kernel 张量核利用率最高 92.92%、DRAM 最高 19.26%;decode 时张量核最高 35.90%、DRAM 最高 64.09%;同层其余 kernel 张量核 22% 到 39%、DRAM 36% 到 72%)[1]。
- 2.90% 与 5.90%:论文 Figure 10 与 §5.3.1,90% TTFT-SLO 处 GN-HAWK 相对 GG-Direct 的请求率差(Chatbot / Summarization)[1]。
- 6.50%:论文 Table 9 与 §5.4.3,Summarization 负载下 GN-HAWK 相对 GGH-Direct(H100 做 decode)的输出吞吐差 [1]。
- 6.53%、7.69%、7.88%、79.06%、81.46%:6.53% 为 Table 9 / §5.4.3 的 Chatbot 侧吞吐差;7.69% 为 §5.3.3 中 Summarization 在 90% SLO 处的请求率差;7.88% 为 Figure 13 / §5.3.4 的 Summarization 输出吞吐差(也是论文摘要里唯一的 7.88%);79.06% 与 81.46% 为 Table 4 里 NPU 侧 QKV 投影与 FFN 投影的 compute utilization [1]。
本文自行推导的部分(与官方数字分列):
- 每 token KV 体积 320 KiB、两种负载的 KV 体积(244.7 / 280.9 / 683.1 / 893.8 MiB)与 25 GB/s 线速下的纯传输时间(10.26 / 11.78 / 28.65 / 37.49 ms):按 与 计算,常量为来源 4 的配置与前文的 200 Gbps。
- decode 算术强度约 0.95 FLOP/字节:按 与 148 GB 权重计算,假设零缓存命中。
- 第 12.1 节三种 KV 宽度下的每 token 体积(320 / 160 / 80 KiB 与 244.7 / 122.3 / 61.2 MiB):只改元素字节数 b。
- 「10 MB 对应约 30.5 个 token」(bf16)与「约 61 个 token」(1 字节 KV):10 MB 除以每 token 字节数。
- 表 1 反向比值(2.52 / 1.16 / 1.39 / 4.27)、Table 8 与 Table 9 的比值复算(1.26 / 1.15 / 2.03 / 1.88 / 1.04 / 3.46):均由论文表内两位小数反算;其中 4.15 / 2.05 反算为 2.02、论文写 2.03,2.35 / 0.55 反算为 4.27、论文写 4.31,两处末位差异原因未确认,引用一律以论文正文口径为准。
- 25.8 倍(Figure 9 反算的上限)与 11.5 倍(10 MB 处):按「GN-Host / GG-Host = 8.91 到 3.79」与「GN-HAWK / GG-Host = 0.7725 到 0.1468」两组归一化读数相除得到,用于与论文引言的 27.69 倍对照。
- 模型权重的 GiB 换算(148 GB = 137.8 GiB):按十进制 GB 与二进制 GiB 的定义换算。