见过一个挺典型的场景:团队花了几周,把大模型的响应时间从几秒压到一秒出头,演示顺利,可月底的算力账单几乎没动。负责人有点困惑——不是变快了吗?问题在于,推理优化从来不是单一目标,“快”和“省”在很多情况下走的是两条路,甚至互相拉扯。
延迟和吞吐,是两把不同的尺子
用户感知的是首字延迟和出字速度;一台机器每秒能处理多少请求,则是另一回事。把多个请求攒成一批一起算,硬件利用率上去了,单位成本下来了,但排在后面的请求要多等一会儿。反过来,为了压低单请求延迟而放弃批处理,单次响应确实利索,机器却闲着。要不要做推理优化,先得说清楚优化的是哪把尺子:面向交互的产品和跑离线任务的系统,答案经常相反。
预填充和解码,瓶颈不在同一处
一次生成大致分两段。前半段把输入读完、算出缓存,计算密集,拼的是算力;后半段一个词一个词往外吐,访存密集,拼的是显存带宽和缓存管理。只看整条链路的总耗时,很容易把两段的优化手段搞混:在前半段加算力有用,到后半段就未必;后半段把缓存管好,收益往往直接落在延迟上。
- 量化把权重和部分激活压到更低精度,省显存也省带宽,但精度损失需要在真实任务上验证,不能只看困惑度。
- 缓存复用减少重复计算,长上下文和连续对话里效果明显,代价是显存占用上升,并发一高就容易撞上限。
- 投机解码用小模型先猜几个词再让大模型校验,出字快慢和任务类型关系很大,猜不中就白忙。
显存是很容易被忽略的那本账
很多人盯算力利用率,却忘了缓存和中间状态都住在显存里。显存一紧,能同时处理的请求数就掉,吞吐跟着掉,于是又回到堆卡的老路。把缓存按需换出、按请求长度动态分配,这类活不显眼,但在真实负载下常常决定系统能扛住多少并发。推理优化的难处也在这里:它更像系统调优,而不是换一个更强的模型。
不是所有场景都值得优化
低峰期流量不大,机器本来就闲,再抠延迟意义有限;反过来,高峰期的一点吞吐提升,可能直接决定要不要扩容。动手前先量清楚瓶颈在哪一段、峰值长什么样、能接受多大的精度折损,再决定用什么手段,比套用现成方案稳妥。另外,过度的工程复杂度本身有成本——为省一点算力引入一堆分支和缓存策略,维护起来未必划算。
回到开头那个团队。他们后来把指标拆成首字延迟、出字速度和单位请求成本三条分别看,才发现真正拖账单的是长上下文请求的缓存占用,而不是生成速度。这个结论不新鲜,但它说明一件事:推理优化不是把模型调快,而是先想清楚到底在为什么付费。