合批与去重:先想清楚你到底在合什么
请求合并与去重在推理服务架构里常被当成一个词说,其实是两条完全不同的路径。合批(batching)解决的是“把同一次模型前向能算的东西放在一起算”,它提升的是 GPU 的利用效率,换来的是单条请求多等几毫秒到几十毫秒;去重则反过来,同类请求只算一次,后面的直接复用第一次的结果。判断能不能合并,看的是延迟预算;判断能不能去重,看的是业务对“完全一致答案”的容忍度。
线上经常混在一起做的情形是这样:数十个客户端在同一秒内拿着同一段系统提示、只差最后一个字的用户消息请求生成,这时自然想到能不能把 prompt 部分复用、把只差的那几位放进同一个 batch。vLLM 文档里写得很直接,PagedAttention 的核心动机之一就是面对“请求长度高度不均、并发持续变化”的负载时,把每个请求各自增长的 KV Cache 从粗放预分配转成可按块共享、可回收的资源。这是写少而固定的块级共享,不是自动按内容免费去重。如果直接在 prompt 层拿前缀比较做 hash 查表,就成了业务侧的额外功能,必须自己控制。
合批的收益曲线:5-8 倍和 32 条一组的条件
资料里给出的一个代表性数字是 batch size 从 1 提到 32,吞吐量增长 5-8 倍。这个量级在 decode 阶段并不离谱,注意力部分的收益主要来自 matrix-vector 推进变成 matrix-matrix 的分块计算。但这串数字很容易被误读:增长的前提是同样的请求被同时凑齐,而且没有任何一个被等待拖得过了头。
静态批处理的问题在这里暴露得最彻底。哪怕模型参数已经固定,padding to longest sequence 会让短请求一直趴在 batch 里等效于空跑,GPU 的算力有相当一部分打在 PAD token 上;更长的影响是延迟上界变成无界的——只要一条超长请求进了同批,剩下所有短请求都要等到最长那条完成。现在的框架都是动态批处理,vLLM、SGLang、NVIDIA Dynamo 都实现了,触发条件有两种:一条是等你把批次凑满到给定 token 数,另一条是计时器到期 max_batch_delay_ms。后者给出一条有限的延迟上界,但反过来,QPS 不够高的峰值口段,计时器每次到期触发都是小 batch,GPU 实际利用率也就那样。
这是合并这件事没法无偿支付的部分。真实情形里该关心的是 p95/p99 会不会因为定时器多出来的每批 2 到 10 毫秒出现平台台阶,而不是抄批量写到多大的参数表。我自己更倾向让写侧明确上报等待耗时,让监控看请求在批缓冲区里到底驻留了多久。
去重不是免费的:它在一条调用链上有多重身份
请求去重的直接起因不在于性能,在于请求真的会重复。腾讯云开发者社区那篇文章里列出的“四大典型重复场景”——重复提问、超时重发、批量任务重试、同一会话内重复提交——恰好对应 idempotency-key 系列实现里经常走的坑。最常被提到的一个数字是计时不同步导致的概率性穿透,这里更需要注意的是推理耗时通常从几百毫秒到几秒,客户端 timeout 往往只有 60 秒甚至更短,在超时宽限窗口内它会重发同一段内容。
对这种情形正确的做法是引入带状态的幂等键,并且把生命周期拆成 INIT 、PROCESSING、SUCCESS、FAILED 四段:请求带着 X-Idempotency-Key 到达,服务端先用 SETNX 占住状态;命中 PROCESSING 等状态一律不进入新一轮推理,否则并发两条相同的请求会各自拿走一份 GPU 的同时,先后两次写后槽;命中 SUCCESS 就直接返回缓存的完整生成结果。资料给出的一个参考是结果缓存只要带 expiration 到覆盖客户端重试周期,就不再触发计数。
跨实例部署时不能走进程内字典,Redis 是能拿来做单个逻辑锁和共享状态的结果。不过分布式锁也要注意:常见预设“锁超时见设置成 30s、比最慢一条推理还久”意味着更大的 tail 请求会在推理仍未收回时把这条锁续给下一次重试。我现在更倾向同时保留短锁作请求对齐、长过期作为写入屏障。批量场景使用子任务 ID 二层键也是来自同一个模板:batch_id 下 seq_id 的表,“已成功 + 已有输入才唯一”可以逐条重入,执行调度跳过 SUCCESS 的子请求。
把两类变更收敛到统一调度器来做
实际落地时最常见的失败情形是业务侧用一个模块负责去重键,而拼批发生在另一个 vLLM / Triton 层甚至上游网关。这样被幂等标记拦下来的请求等待结果时会占到批内存的消耗而不是重入推理。我们的折中是反着来:让去重表只作为合成后的公共池增量索引,也就是说没有用统一键 token 匹配时不做状态推进;一旦命中就直接在批执行器一侧塞进重用引用位置,不让业务侧对提示文本做 sql_variant 之类的库检索。维护窗口更被接入了连续的指标:命中和错过的比率、命中后时延是否包括超时,都打 trace。
合还是不合,比的是一个成本等式
合并增加等待、提升吞吐;去重跳过重复的预填充成本,但会产生缓存存储带出的不可重算责任,还会因过期时间不适合碰上结果不再有效的时刻。评测的价码很容易写:如果 max_batch_delay_ms 上的成本边际划算就开,如果显存吃紧就退回只保留 int8 缓存回退。对我来说先把事件打开、比对每条命中,再看合批实际以 merge_weight × prefix_match_percentage − normalized_load 打相对分,比读一整套机制简单。
转载请注明出处: 皮哈皮阿
本文的链接地址: https://blog.phpat.com/ai-dev/672.html
微信或手机浏览器扫一扫,随时随地随心阅读与分享
-
上下文工程:从 Prompt 到 Context 的工程化实践
客服 Agent 答错时,团队第一反应是换模型。把那次请求的上下文打印出来才发现,模型看到的是一份过期政策、两份互相矛盾的条款,还有一长串无关日志。上下文工程要解决的,就是这类问题。
2026/10/06
-
RAG 的下一站:Agentic Retrieval
用户问“我们去年买的套餐现在还能退吗”,经典 RAG 检索出一段通用退款政策就结束了。可这个问题至少要拆成订单时间、地区、套餐版本三件事。Agentic Retrieval 要解决的,就是这种“一次检索不够用”的场景。
2026/10/12
-
约束解码让 JSON 不再崩,但它会改坏推理
约束解码让 JSON 解析失败率归零,工程师可以删掉一堆重试和修复代码。但它会挤占模型的推理预算:强制格式在部分任务上带来10%以上的准确率下降。文章梳理 OpenAI、Anthropic、Gemini 和其他自托管引擎的现状,以及 schema 设计、两阶段模式与编译缓存几个实操要点。
2026/10/22
-
Agent 上下文压缩:四层策略与失败代价
把 Claude Code 的四段分层与 Hermes Agent 的双阈值压缩系统并排看重:200K 窗口下的 50%/85%、legacy 与 lean 尾部的实测差池、未处理到的摘要模型窗口溢出,以及工具对配对边界为什么是能不能跑的问题。
2026/10/23
-
推理模型输出被截断后怎么接回去
推理模型遇到输出截断,重试整条请求代价高;续写也有隐藏门槛。从识别边界、提示回灌到 prefill 与成本判断,聊聊工程上怎么做才不算亏。
2026/10/25
-
推理模型的并行思维链采样怎么用才不亏
并行思维链采样的成本不是白花的。结合推理模型测试时计算的几条实证结论,梳理best-of-N、束搜索、自洽性投票和自我修订在什么场景下划算、什么场景下只是在烧钱。
2026/10/26
京公网安备11011402053654 
@muffin:链接已更新,分类图像功能...
AI网址导航模版图标库更换指南
作者大大,图标库邀请链接失效了,麻烦更新...
AI网址导航模版图标库更换指南
太好看了,快点更新!
友情链接
@戴墨镜的刘备:哈喽,路过点赞。
vue.js组件之手写签名板vue-signature-simple
太好看了,快点更新!太棒了!👍真的很赞!
vue.js组件之手写签名板vue-signature-simple
你写得非常清晰明了,让我很容易理解你的观...
Emlog插件RSS订阅器发布