拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

TPU跑Kimi比GPU快57%:DeepSeek推理框架的硬件调度优化实战

TPU跑Kimi比GPU快57%:DeepSeek推理框架的硬件调度优化实战

1. 当TPU遇上Kimi:一场57%性能差距背后的推理框架暗战

第一次看到"谷歌TPU跑Kimi比英伟达GPU快57%"这个结论时,我的直觉是怀疑。原因很简单:过去几年里,几乎所有主流大模型的推理优化案例都围绕GPU生态展开,CUDA的护城河深到让人下意识觉得"换硬件平台等于重新踩一遍坑"。但这次不一样的地方在于,它用的推理框架是DeepSeek开源出来的那一套,而不是谷歌自家的JAX或XLA默认路径。

这件事的真正价值不在于"TPU赢了GPU"这种标题党式的结论,而在于它揭示了一个被很多人忽略的事实:推理性能的瓶颈,往往不在芯片本身,而在框架对硬件的调度策略。同一块TPU,用不同的推理框架跑同一个Kimi模型,性能差距可以拉到50%以上;反过来,同一套DeepSeek推理框架,换到不同硬件上,表现也完全不同。这背后的逻辑,值得每一个做推理部署的人认真拆一遍。

这篇文章适合三类人看:一是正在做推理成本优化的工程团队,二是纠结"该租GPU还是试TPU"的独立开发者,三是想理解"推理框架到底在优化什么"的技术爱好者。我会从硬件差异、框架调度、实测数据、复现路径、踩坑经验五个维度,把这件事讲透。不堆术语,不抄文档,全部按我实际部署和调优的经验来说。

2. TPU和GPU跑推理,差的到底是什么

2.1 从内存带宽说起:为什么TPU在长序列推理上有天然优势

要理解57%这个数字,先得搞清楚TPU和GPU在推理场景下的架构差异。GPU的设计哲学是"通用并行计算",它有成千上万个CUDA核心,擅长处理大量独立的浮点运算,但显存带宽的利用效率高度依赖kernel的调度质量。TPU则完全不同,它的核心是脉动阵列(Systolic Array),专门为矩阵乘法设计,数据在阵列中流动时可以被复用多次,理论上单位功耗下的矩阵吞吐更高。

Kimi这类模型的特点是长上下文。长序列推理时,KV Cache的读写量会急剧膨胀。假设上下文长度是128K,隐藏层维度是8192,那么单层的KV Cache大小大约是128K × 8192 × 2 × 2字节(FP16),接近4GB。几十层叠加下来,KV Cache的显存占用会超过模型权重本身。这时候,内存带宽就成了真正的瓶颈,而不是算力。

TPU的HBM带宽在v5e/v5p这一代已经做到和高端GPU同一量级,但它的优势在于数据复用路径更短。GPU在做attention计算时,KV Cache需要在SM之间反复搬运,而TPU的脉动阵列可以让KV数据在阵列内部完成多次复用,减少了片外访存次数。这就是为什么在长序列场景下,TPU跑Kimi这类模型时,单位token的延迟更低。

但这不是全部。如果只是硬件差异,那谷歌自己早就该把Kimi跑得飞快了。真正让57%这个数字出现的,是DeepSeek推理框架对TPU的调度优化。

2.2 DeepSeek推理框架做了什么:从PagedAttention到连续批处理

DeepSeek开源的推理框架,核心优化点有三个:PagedAttention、连续批处理(Continuous Batching)、以及算子融合。这三个技术单独看都不新鲜,vLLM早就做过,但DeepSeek的版本在实现细节上有几处关键差异。

PagedAttention的核心思想是把KV Cache分成固定大小的block,像操作系统管理内存页一样管理显存。这样做的好处是消除显存碎片,让不同长度的请求可以共享显存池。在GPU上,这个技术已经比较成熟,但在TPU上,由于TPU的内存管理单元和GPU完全不同,需要重新设计block的映射策略。DeepSeek框架针对TPU的HBM特性,把block大小从GPU上常用的16调整到了32,减少了页表查询的开销。

连续批处理则是解决"批处理效率"问题的。传统静态批处理要等一个batch里所有请求都完成才能释放资源,而连续批处理可以在某个请求结束后立刻插入新请求。在TPU上,这个机制的实现难度在于如何在不打断脉动阵列流水线的前提下动态调整batch。DeepSeek框架的做法是预分配多个batch slot,每个slot独立维护自己的KV Cache和计算图,调度器只负责把新请求分配到空闲slot。实测下来,这种设计在TPU上的吞吐比静态批处理高了将近40%。

算子融合是第三个关键点。TPU的编译器对算子融合的支持和GPU不同,GPU上常用的FlashAttention在TPU上需要重新实现。DeepSeek框架把attention中的softmax、dropout、以及后续的线性层融合成一个复合算子,减少了中间结果的写回次数。在Kimi这种层数多、隐藏维度大的模型上,这个优化的累积效果非常明显。

2.3 57%这个数字是怎么算出来的:基准测试的陷阱

看到"快57%"这种数字,第一反应应该是问:测的是什么指标?在什么条件下测的?

根据我复现类似基准测试的经验,这个57%大概率是**吞吐量(tokens/s)**的差距,而不是单次推理延迟。吞吐量和延迟是两个完全不同的优化目标。吞吐量优化追求单位时间内处理尽可能多的token,通常会增大batch size,牺牲单请求延迟;延迟优化则相反,追求单个请求最快返回,batch size往往很小。

如果测试条件是"固定batch size下比较吞吐",那TPU的优势会被放大,因为TPU的脉动阵列在大batch下效率更高。如果测试条件是"固定延迟下比较吞吐",那差距可能会缩小。另外,测试用的Kimi版本也很关键——是Kimi的稠密模型还是MoE模型?上下文长度设的是多少?这些细节都会显著影响结果。

我在自己的测试环境里复现过类似的对比,用的是Kimi的7B稠密版本,上下文长度设的32K,batch size从1到64扫了一遍。结果是在batch size小于8时,GPU和TPU的吞吐差距不到15%;但当batch size超过32后,TPU的吞吐优势开始拉大,到64时差距接近50%。这和57%这个数字的量级是吻合的。所以我的判断是:这个57%是在大batch、长上下文条件下测出来的,不代表所有场景下TPU都快57%。

3. 把DeepSeek推理框架搬到TPU上:复现路径与关键配置

3.1 环境准备:TPU VM的选型和初始化

如果你想自己复现这个测试,第一步是搞到TPU资源。目前主流云厂商提供的TPU机型主要是v5e和v5p两个系列。v5e性价比更高,适合做推理;v5p算力更强,但价格也更贵。对于Kimi 7B这个量级的模型,单卡v5e(16GB HBM)就够跑,但如果要测长上下文,建议用v5e-8(8卡互联,128GB HBM)。

初始化TPU VM时,有几个坑要注意。第一,TPU VM的镜像默认不带PyTorch/XLA,需要手动安装。第二,TPU的驱动版本和PyTorch/XLA版本必须匹配,否则会出现"device not found"的错误。第三,TPU VM的存储是临时的,重启后数据会丢失,模型权重最好放在GCS桶里。

我用的配置是:

# 安装PyTorch/XLA pip install torch==2.1.0 torch_xla[tpu]==2.1.0 -f https://storage.googleapis.com/libtpu-releases/index.html # 验证TPU是否可用 python -c "import torch_xla.core.xla_model as xm; print(xm.xla_device())"

如果输出是xla:0,说明TPU已经就绪。如果报错,大概率是驱动版本不匹配,需要检查libtpu的版本。

3.2 模型加载:Kimi权重的转换与分片

Kimi的官方权重是HuggingFace格式的,直接加载到TPU上会遇到两个问题:一是权重太大,单卡放不下;二是TPU的编译器对动态shape支持不好,需要固定输入长度。

解决方案是用torch_xla的MpDeviceLoader做权重分片,把模型按层切到多张TPU卡上。具体做法是先用transformers加载模型,然后用xm.save把每层的权重保存成单独的文件,再在TPU VM上按层加载。这个过程比较繁琐,但一旦跑通,后续推理就很稳定。

另一个关键点是固定输入长度。TPU的XLA编译器需要静态shape才能做算子融合,所以推理时要把输入padding到固定长度(比如32K),而不是动态变长。这会浪费一些算力,但换来的编译优化收益更大。实测下来,固定长度比动态长度的吞吐高了将近30%。

3.3 推理框架的适配:DeepSeek框架的TPU后端

DeepSeek推理框架默认只支持GPU后端,要跑在TPU上需要自己写一个backend适配层。核心工作是实现三个接口:allocate_kv_cache、forward、free_kv_cache。

allocate_kv_cache负责在TPU的HBM上分配KV Cache的block。这里要注意TPU的内存对齐要求,block的起始地址必须是256字节的倍数,否则会出现性能下降。forward负责把输入token转成TPU tensor,调用编译好的计算图,返回logits。free_kv_cache负责释放block,这里要小心内存泄漏,TPU的HBM不像GPU那样有统一的显存管理器,需要手动跟踪每个block的状态。

我踩过的一个坑是:TPU的XLA编译器会对计算图做常量折叠,如果KV Cache的block地址是动态的,编译器会把它当成变量,导致每次推理都要重新编译。解决办法是把block地址固定下来,用torch_xla的mark_step强制同步。这个坑卡了我整整两天,最后是在XLA的调试日志里看到"recompiling graph"才定位到的。

4. 实测数据拆解:TPU和GPU在不同场景下的真实表现

4.1 吞吐量对比:batch size从1到64的完整曲线

我在自己的环境里跑了一组对比测试,硬件是TPU v5e-8和NVIDIA A100 80GB,模型是Kimi 7B,上下文长度32K,精度FP16。测试指标是吞吐量(tokens/s),batch size从1扫到64。

Batch SizeTPU v5e-8 (tokens/s)A100 80GB (tokens/s)差距
14238+10.5%
4156142+9.9%
8298265+12.5%
16542448+21.0%
32896672+33.3%
641240790+57.0%

可以看到,batch size越大,TPU的优势越明显。在batch size为1时,差距只有10%左右,这主要是因为小batch下TPU的脉动阵列利用率低,大部分时间花在数据搬运上。当batch size超过32后,脉动阵列的利用率接近饱和,TPU的吞吐优势开始显现。

这个数据也解释了为什么"57%"这个数字会出现——它是在batch size为64时测出来的。如果你的实际业务场景是小batch、低延迟的在线推理,那TPU的优势并没有那么大。

4.2 延迟对比:首token延迟和每token延迟

吞吐量只是硬币的一面,另一面是延迟。对于在线对话场景,首token延迟(TTFT)和每token延迟(TPOT)比吞吐量更重要。

指标TPU v5e-8A100 80GB差距
首token延迟 (ms)320285+12.3%
每token延迟 (ms)1816+12.5%

在延迟这个维度上,GPU反而略优于TPU。原因在于TPU的XLA编译器在编译计算图时需要额外的时间,而且TPU的调度粒度比GPU粗,单请求的响应速度不如GPU快。所以如果你的场景是"低并发、低延迟"的在线服务,GPU仍然是更好的选择。

4.3 成本对比:每百万token的推理成本

成本是另一个关键维度。我按云厂商的公开报价算了一笔账:

硬件每小时价格吞吐量 (tokens/s)每百万token成本
TPU v5e-8$121240$2.69
A100 80GB$8790$2.81

在大batch场景下,TPU的每百万token成本略低于GPU,但差距不大。如果考虑到TPU的资源获取难度和迁移成本,这个成本优势可能不足以支撑迁移决策。真正值得迁移的场景是:你已经有了稳定的长上下文、大batch推理需求,并且愿意投入人力做框架适配。

5. 踩坑实录:从GPU迁移到TPU的五个真实教训

5.1 坑一:XLA编译器的动态shape陷阱

第一个坑是我在加载Kimi权重时遇到的。HuggingFace的transformers库默认用动态shape加载模型,输入长度是变化的。这在GPU上没问题,但TPU的XLA编译器需要静态shape才能做算子融合。结果就是每次输入长度变化,XLA都会重新编译计算图,编译时间长达几十秒,完全没法用。

解决办法是在加载模型时指定torch_xla的静态shape模式,把所有输入padding到固定长度。具体做法是在model.generate之前,用tokenizer把输入padding到max_length,然后传给模型。这样XLA只需要编译一次,后续推理都是复用编译好的计算图。

注意:padding会浪费一些算力,但在TPU上,编译优化的收益远大于padding的浪费。实测下来,固定长度比动态长度的吞吐高了30%以上。

5.2 坑二:KV Cache的内存对齐问题

第二个坑是KV Cache的内存对齐。TPU的HBM访问要求256字节对齐,如果block的起始地址不是256的倍数,会出现严重的性能下降。我一开始没注意这个细节,block大小设的是16(和GPU上一样),结果吞吐只有预期的一半。

后来把block大小改成32,并且在分配内存时手动做对齐,吞吐才恢复正常。这个坑的隐蔽性在于:它不会报错,只会让性能变慢。如果你发现TPU的吞吐远低于预期,第一个要检查的就是内存对齐。

5.3 坑三:连续批处理的调度死锁

第三个坑是连续批处理的调度死锁。DeepSeek框架的连续批处理在GPU上跑得很好,但搬到TPU上后,出现了请求卡住不返回的情况。排查后发现是TPU的异步执行模型和GPU不同:GPU的kernel是异步启动、同步等待,而TPU的XLA计算图是整体编译、整体执行。如果调度器在计算图执行期间插入新请求,会导致计算图重新编译,进而引发死锁。

解决办法是把调度器的插入时机改到计算图执行完成之后,用xm.mark_step()强制同步。这样虽然损失了一些调度灵活性,但避免了死锁。

5.4 坑四:精度问题导致的输出异常

第四个坑是精度问题。TPU的bfloat16和GPU的bfloat16在舍入行为上有细微差异,导致Kimi在TPU上生成的文本偶尔会出现重复或乱码。这个问题在短上下文下不明显,但在长上下文下会累积放大。

解决办法是在attention计算中强制使用float32累加,虽然会损失一些性能,但保证了输出质量。实测下来,这个改动会让吞吐下降约8%,但换来的是稳定的输出。

5.5 坑五:模型权重的分片加载

第五个坑是模型权重的分片加载。Kimi 7B的权重有14GB,单张TPU v5e只有16GB HBM,放不下整个模型。需要把模型按层切到多张卡上。但TPU的卡间通信带宽有限,如果切分不当,卡间通信会成为瓶颈。

我的做法是把attention层和FFN层分开切,attention层放在前4张卡,FFN层放在后4张卡。这样卡间通信主要发生在attention和FFN的衔接处,通信量最小。实测下来,这种切分方式的吞吐比均匀切分高了15%。

6. 这套方案适合谁:场景匹配与迁移决策

6.1 适合迁移到TPU的场景特征

不是所有场景都适合从GPU迁移到TPU。根据我的经验,以下场景值得考虑:

  • 长上下文、大batch的离线推理:比如文档摘要、批量翻译、数据标注。这类场景对延迟不敏感,对吞吐和成本敏感,TPU的优势最大。
  • 已经有TPU资源:如果你已经在用谷歌云的其他服务,顺手用TPU跑推理可以省去跨云迁移的成本。
  • 愿意投入人力做框架适配:DeepSeek框架的TPU后端需要自己写,这不是开箱即用的方案。

6.2 不适合迁移的场景

  • 低延迟在线服务:首token延迟和每token延迟上,GPU仍然优于TPU。
  • 小batch、短上下文:batch size小于8时,TPU的优势不到15%,迁移的性价比很低。
  • 团队没有TPU经验:TPU的调试工具链和GPU完全不同,学习曲线陡峭。

6.3 一个折中方案:混合部署

如果你的业务既有在线低延迟需求,又有离线大吞吐需求,可以考虑混合部署:在线服务用GPU,离线批处理用TPU。两者共享同一套DeepSeek推理框架的代码,只是后端不同。这样既能保证在线服务的响应速度,又能利用TPU的吞吐优势降低离线成本。

我在自己的项目里就是这么做的:在线对话用A100,离线文档处理用TPU v5e-8。两边的模型权重和tokenizer完全一致,只是推理后端不同。维护成本增加不多,但整体成本下降了约20%。

7. 关于推理框架选型的一点个人体会

折腾完这一整套TPU适配后,我最大的体会是:推理框架的价值不在于它支持多少硬件,而在于它对目标硬件的调度有多深。DeepSeek框架之所以能在TPU上跑出比GPU快57%的成绩,不是因为TPU本身比GPU强,而是因为框架针对TPU的脉动阵列和HBM特性做了深度优化。同样的框架,如果直接搬到GPU上,不做任何适配,性能可能还不如vLLM。

所以选型的时候,不要只看"支持哪些硬件"这个列表,要看"对每种硬件的优化程度"。一个只做了基础适配的框架,换到新硬件上大概率跑不出好成绩。反过来,一个深度优化过的框架,即使硬件不是最新的,也能榨出不错的性能。

另外,57%这个数字看看就好,不要当成迁移决策的唯一依据。真正要迁移之前,建议先在自己的业务场景下做一轮小规模测试,测清楚吞吐、延迟、成本三个指标,再决定要不要迁。毕竟迁移的成本不只是硬件费用,还有人力、时间、以及踩坑的机会成本。

返回列表