采风全网
达观天下

首字延迟降下来之后,推理优化才刚开始

凌晨两点,监控面板上那条延迟曲线拱起一个包。白天它很平,平到没人看;到了这个时段,同一份权重、同一套接口,首字回来的时间却明显变长。值班的人先怀疑网络,再怀疑上游服务,最后把日志翻出来,发现模型侧的解码步数没变,变的是排队的人数和每段输入的长度。

这类夜晚大概是理解推理优化比较合适的入口:它很少是一个单点开关,更像是在几条互相牵制的线之间重新分配等待。

同一份权重,干的是两种活

一次请求进到模型里,前半段叫预填充,把整段输入一次性算完,并行度高,吃的是算力;后半段叫解码,一个一个吐 token,每一步都要把权重和缓存读一遍,算力大量闲置,卡在显存带宽上。两段的瓶颈不同,能用的手段也不同。

很多推理优化方案上线后效果不如预期,原因往往不在方法本身,而在于它只解决了其中一段。把预填充压得很短,用户感知到的可能仍然是解码那几百毫秒;反过来,解码侧做了花哨的调度,遇到长输入依然会堵。

攒批不是免费的午餐

把并发请求攒成一批送进 GPU,是提升吞吐最直接的做法,但每个请求都得等队伍里的其他人。队列长度、请求到达的抖动,会直接变成尾延迟。真实流量从来不均匀,它有成串的突发,也有走得极慢的长请求。

  • 先确认业务能容忍的等待上限,再决定批的大小和等待窗口
  • 排队策略常被忽略,但它往往比改写算子更容易见效
  • 长请求会拖住整批,需要拆分、隔离或者单独走一条路

吞吐和单请求延迟通常此消彼长,这不是调参能绕过去的物理事实,只能选一个自己承担得起的平衡点。

省下的算力,变成了显存

KV cache 的思路是别重复计算,把注意力的中间结果留在显存里。上下文越长,这份缓存越大,瓶颈就从算力挪到了显存容量和带宽上。分页管理、共享前缀复用、及时驱逐,都是在显存这一侧做文章。

关键在于命中率。前缀复用收益高的场景,通常是系统提示词长而稳定、多轮对话里历史变化缓慢;如果每次请求的前缀都不同,缓存带来的好处会被迅速稀释,剩下的只有管理开销。

精度和近似,账要有人算

降低数值精度能减少带宽占用和显存压力,但误差会在长链条生成里累积,短输出上看不出来的偏移,到了几百个 token 之后可能变得明显。投机采样换了一个思路:用小模型先起草,大模型负责校验,减少解码步数,代价是多一次前向和一份不确定的接受率。

这些手段的效果高度依赖任务分布,别人环境里测出来的数字,搬到自己的业务上经常对不上。做推理优化时,比较稳妥的做法是先把评测集固定下来,再谈收益,否则很容易拿别人的曲线安慰自己。

优化把瓶颈搬到了别处

一段流程变快,压力会顺势转移到下一段:网关、调度、特征拼接、后处理、日志上报。只盯模型侧的指标,容易得出过于乐观的结论。另一种常见误判,是把离线压测出来的吞吐当成线上能力——线上有突发、有长尾、有重试,这些在压测脚本里通常不会出现。

所以判断一次推理优化是否成立,我倾向于问一个很朴素的问题:这段延迟被挪到哪里去了,由谁承担。挪到队列里,是用户在等;挪到批处理窗口里,是请求在等;挪到缓存预热和离线编译里,是发布流程在等。没有哪种挪法是免费的,区别只在于它是否落在你能接受的位置。

首字延迟降下来当然值得高兴,但那通常只是开始。真正决定体验的,是那条曲线上你不愿意被人看见的另一段。

免责声明:市场有风险,选择需谨慎!此文仅供参考,不作买卖依据。如有侵权请联系删除。
文章名称:首字延迟降下来之后,推理优化才刚开始
文章链接:https://tech.cfdanews.com/ai/1659.html