凌晨的客服后台,对话框里光标闪着,四秒之后才吐出一行字。用户早在第二秒就切走了。这四秒和模型是否聪明关系不大,它属于推理过程本身的开销。
先把时间拆开看
一次生成通常分成两段:预填充把整段输入读进去,解码再一个字一个字往外送。前者像一次性把材料搬进屋子,后者像一件件手工交付。不少团队盯着“每秒吐出多少字”,却忽略了上下文一长,预填充会突然变重。真要动手,第一步不是换卡,而是弄清楚这几百毫秒落在了哪一段。
KV 缓存绕不开。它让算过的注意力结果不必重来,代价是显存被占住;上下文越长,占得越多,能同时接的请求就越少。分页管理、按需换出、压缩精度,都是围绕这块钱做文章。手段都有效,只是每一种都会在别处留下一点代价。
攒批的诱惑
把若干请求攒在一起算,单位成本立刻显得好看。麻烦在于排队:攒得越久,排在后面的请求等得越久。吞吐和单次响应在很多场景下互相拉扯,这跟工程水平无关,是目标本身在打架。
所以真正有效的推理优化,常常从区分请求开始:哪些是人在等,哪些可以慢慢跑。把交互式和离线批处理分开调度,往往比在参数上反复试探更直接。同一套集群里跑两种活,谁先谁后,需要一句明确的产品判断,而不只是压测数字。
精度换来的速度,代价落在别处
把权重压到更低的位宽,显存和带宽的压力立刻松一口气。但压缩带来的误差不是均匀分布的:长尾输入、罕见的表达、需要精确数字的问答,往往先松动。上线前只在常规样本上看效果,很难发现这一点。
另一类做法是让小模型先起草、大模型再校对。省下来的算力真实存在,代价是链路变长、调度更复杂,起草被否决时反而多花时间。这类方案值不值得用,取决于你的输出长度分布,而不是取决于它听起来多聪明。
有些系统不必这么急
不是所有场景都值得为几百毫秒投入人力。一份每晚跑的报表、一次离线的文档处理,慢一点没人察觉。反过来,交互式产品里,用户对首字出现的耐心,远低于对整段输出的耐心——先让第一个字尽快出来,往往比把总时长压短更值得做。推理优化的边界,很多时候由产品形态划出来,而不是由框架划出来。
还有一种常见的浪费,是在没测过基线的情况下先上复杂方案。没有基线,就没办法判断收益是来自优化,还是来自那天的流量刚好小。先把每段耗时、每类请求的占比量出来,再决定动哪一块;顺序反过来,多数工作会白做。
回到那个闪光的对话框。用户不会关心你用了几张卡、缓存怎么管,他只知道自己等了多久。推理优化最终要回答的,其实是这个很朴素的问题:这几百毫秒,省下来给谁。