[控场AI]
· 7 分钟阅读· 3,771 字

被忽视的分词瓶颈:CPU如何拖慢大模型推理

被忽视的分词瓶颈:CPU如何拖慢大模型推理

GPU提速后,原本被视为免费的分词在Python GIL与智能体场景下成了推理新瓶颈。

长期以来,分词被视为大模型推理中可忽略的"舍入误差"级环节,但当GPU性能被压榨到极致,位于其前方的CPU分词步骤开始让昂贵的加速器空转等待。文章梳理了三重成因:GPU下游变快使相对瓶颈前移;SGLang、vLLM等引擎中分词虽由Rust/C++实现,却必须经过Python层,受GIL约束形成单线程队列;智能体多轮任务每轮重新分词累积历史,CPU工作量增长而GPU负担反因缓存缩小。佐治亚理工研究显示,充足CPU资源下首token延迟可缩短1.47至7.11倍。三方修复路径存在分歧:Hugging Face重写分词库并取得单线程3至30倍提速,佐治亚理工主张增加CPU核心,PyTorch则主张将分词彻底移出Python进程。但两项研究都无法确定空闲时间中分词的具体占比,根因判断也不一致,尚未形成统一结论。

在大模型推理的整个链路中,分词(Tokenization)长期被视为一个无需操心的环节。把句子切成模型能读的数字序列,这项运算与矩阵乘法相比简直微不足道,多年来也确实被当作"舍入误差"级别的存在。但当GPU越来越快,这个廉价步骤正在成为整台机器的新瓶颈。

大模型推理中的CPU瓶颈示意

分词为何从"免费"变成"昂贵"

Hugging Face在发布新一代分词库时给出了一个坦率的判断:分词在历史上从来不是瓶颈。但他们同时列出了这个判断失效的条件——同时服务大量请求,或者反复处理超长输入。

变化的核心不在于分词本身,而在于它下游的一切都变快了。模型推理的流水线顺序是固定的:文本先由CPU转成token,GPU才能开始工作。当GPU侧的性能被压榨到极致之后,位于它前方的"廉价"步骤就不再免费了。你花大价钱买来的加速器,最终在等一个从未被测量过的环节。

分词(Tokenization) 是大模型推理链路的第一步:将原始文本切分为模型词表中的最小单元(token),再映射为整数ID序列。主流大模型采用子词分词算法,如BPE(Byte Pair Encoding)、WordPiece或Unigram,它们在训练语料上统计出高频子串作为词表,从而兼顾词表规模与对未登录词的覆盖能力。分词的质量直接决定了序列长度、上下文利用效率以及模型对多语言、代码、数字等内容的处理表现。正因如此,分词器并非简单的字符串切分工具,而是一个需要与模型权重配套发布的组件。

从计算量上看,分词本质上是查表与少量字符串匹配操作,复杂度与序列长度近似线性,单位成本远低于Transformer中的自注意力或矩阵乘法。这正是它长期被归类为"免费"步骤的原因——在GPU算力尚且紧张、瓶颈集中在模型前向计算的年代,分词所在的CPU侧有充足的余量吸收这部分开销。只有当模型推理本身被大幅加速后,这条线性、单线程、位于关键路径前端的步骤才会显现出相对稀缺性。

Python的锁与生产环境的真实代价

据PyTorch博客上LightSeek Foundation的一篇分析,在SGLang和vLLM这两个主流推理引擎中,分词由Rust或C++编写的高速库执行,但每次调用都要经过Python层。而Python的全局解释器锁(GIL)规定同一时刻只有一个线程能执行Python代码,这就在每个请求的必经之路上设置了一个单文件队列。

小规模部署时没人会注意到这个问题。但当服务架构被重新设计以让GPU跑得更快——比如把prompt读取和回复生成分到不同机器,或者把模型的专用组件分散到整个集群——这个瓶颈就开始在生产环境中造成实际伤害。用原文的话说:花在锁后面的每一微秒,都是加速器空转等待输入的微秒。

全局解释器锁(GIL,Global Interpreter Lock) 是CPython解释器的一项机制:在任意时刻,同一个进程内只允许一个线程执行Python字节码。其初衷是简化内存管理与C扩展的线程安全实现,但代价是多线程无法真正并行执行Python代码,只能通过释放锁的间隙让出执行权。对于I/O密集型任务,线程仍可在等待时切换,效果尚可;但对需要在每次调用时进入Python层的操作,GIL就构成了一道串行化的关卡。

在SGLang、vLLM这类推理引擎中,分词库本身用Rust或C++实现,速度很快,但调度与调用依然要通过Python绑定层进入。这意味着即使底层分词函数是并行的,每次调用返回时都要重新竞争GIL。单次开销在微秒级别,看似可忽略,但在每秒处理成百上千请求的服务集群中,这些微秒会累积成可见的排队延迟。值得注意的是,Python 3.13起已提供实验性的自由线程构建(移除GIL),但主流推理框架尚未全面迁移,短期内GIL仍是实际部署中的结构性约束。

智能体场景放大了问题

佐治亚理工学院的研究从另一个角度发现了同样的"饥饿"现象,而且问题在行业演进的方向上更为严重。

智能体(Agent)需要在多轮对话中逐步完成任务,历史记录不断累积。每一轮,整段历史都要在CPU上重新分词。话说回来,GPU的负担反而在缩小——因为大部分历史内容已经见过,其结果被缓存了。廉价步骤的工作量在增长,昂贵步骤的工作量在收缩。

该研究最值得记住的数据是:在获得充足CPU资源(而非被挤压的配额)的情况下,从发送请求到收到第一个token的等待时间,在测试的多种配置中缩短了1.47至7.11倍。也就是说,你等待回复开始前的那段停顿,可测量地,是一个处理器在切分文本。

三条修复路径与它们的分歧

面对同一个诊断,各方给出的方案也沿不同方向展开:

Hugging Face重建了分词库,在Apple M4 Max单线程下编码路径比v0.23快3到30倍,八worker扩展效率达到线性76%——不是八倍提速,但差距不大。

佐治亚理工的作者主张配置更多CPU核心。PyTorch那篇分析则主张把这项工作从Python进程中彻底移出。

有意思的是,两项研究都无法确定空闲时间中有多少归因于分词,而非输出重组、解析或在芯片上启动任务。两者对根因的判断也存在分歧:PyTorch文章将Python的锁视为根源,而佐治亚理工的研究发现,即使把工作拆分到独立进程、用预录制的指令序列喂给芯片,GPU仍然供血不足——所以GIL不可能是全部原因。Hugging Face的速度数据来自单台Apple笔记本芯片,而非服务集群,能否在实际瓶颈出现的地方弥合差距,尚未得到验证。

线性扩展效率 是衡量多核并行质量的常用指标:若使用N个worker,理想加速比为N倍,实际加速比除以N即为扩展效率。Hugging Face报告八worker下达到76%的线性扩展,意味着实际加速约为6倍,而非理论上的8倍。剩余损耗通常来自共享资源争用、任务分配不均或序列化开销。这一数字之所以重要,是因为分词这类短小、高频的任务很容易被调度开销吞掉收益——任务本身耗时太短,worker之间的协调成本反而可能成为新瓶颈,因此76%的效率在工程上已属可观。

同时,讨论中提到的首token延迟(TTFT,Time To First Token) 是衡量交互体验的关键指标,指用户发出请求到收到第一个输出token之间的时间。它与总吞吐量不同:一个系统可能整体吞吐很高,但TTFT很长,让人感觉"卡"。在智能体与流式对话场景中,TTFT直接决定用户感知的响应速度,因此佐治亚理工研究以TTFT作为核心度量,恰好对准了分词瓶颈最易暴露的位置——请求进入后、GPU开始计算前的那段等待。

历史给出的两种答案

游戏行业曾遇到类似问题:CPU向显卡提交绘图指令,当显卡变快后,通过旧编程接口提交指令这个动作本身成了让显卡空转的原因。显卡准备好了,CPU还在说话。Vulkan和Metal正是作为低开销接口被设计出来的,目的就是让这场对话变便宜。芯片没变,表达方式变了。

存储行业则走了另一条路。闪存变得足够快之后,操作系统对每个请求的处理——块层和完成中断——跟不上驱动器的节奏。解决方案不是更精简的请求方式,而是换一种安排:不再等待被通知工作完成,而是主动去查看。

图形学削薄了中间人,存储把交接移出了关键路径。分词正在同时被提供这两种方案。

Vulkan、Metal与低开销图形接口 的类比值得展开。在OpenGL等旧接口下,CPU每次提交绘图指令都要经过大量校验、状态管理与驱动转换,显卡往往在两次提交之间空转。Vulkan与Metal将这部分工作前移:允许应用预先构建命令缓冲区、批量提交、直接管理内存与同步,显著削减了每帧的CPU开销。其精髓不在于让显卡更快,而在于让CPU"说话"更省事,从而匹配已经提速的GPU。这与把分词移出Python进程、预先构造token序列的思路高度同构。

存储侧的轮询(polling)替代中断 则代表另一种哲学。传统中断模型下,设备完成I/O后主动通知CPU,但每次中断都伴随上下文切换与处理开销;当NVMe固态盘快到微秒级响应时,中断开销反而成为主导。轮询让CPU主动、频繁地查看完成队列,牺牲部分空闲时的能效,换取低延迟下的确定性与吞吐。两条路线一个削减中间层,一个改变交接方式,共同说明:当某一环节突然变快,配套的协调机制必须同步重构,否则瓶颈会平移到原本不显眼的接口处。

本可以建成的版本

一个不同的设计并不难想象。分词器本可以从这些推理引擎诞生的第一天起就活在Python进程之外,作为独立服务拥有自己的线程,传递token而非文本,永远不碰那把锁。代价是写推理引擎的人要维护更复杂的代码库和更慢的启动速度,喜欢一切都在一个Python文件里的研究者也会失去随意伸手修改的便利。

账单被推迟给了运行最大规模集群的人,以加速器空转时间的形式支付,只有当加速器快到能察觉时才到期。

那个舍入误差从来不是分词器本身。它是关于把它放在哪里的那个决定。

#大模型推理瓶颈#分词器性能#Python GIL#GPU利用率#LLM serving#Tokenization优化#CPU GPU disaggregation
分享:

相关推荐