推理服务为什么一上批量采样就开始输出不可复现:从 RNG State 到 Per-Request Stream 的工程实战

推理服务为什么一上批量采样就开始输出不可复现:从 RNG State 到 Per-Request Stream 的工程实战 一、批量采样上线后回归测试开始大面积失败在生产环境部署 LLM 推理服务时批量采样Batch Sampling是提升吞吐的核心手段。当多个请求被拼接进同一张量后一次前向传播即可产出多个结果GPU 利用率通常能提升 30% 到 50%。然而不少团队在刚刚开启这一优化后就发现同一 Prompt 的多次调用返回了不同文本缓存命中率骤降连回归测试也变得 flaky。图1推理服务的批量采样架构这种非确定性看似是模型固有的随机性实则往往源自 RNG State 在请求间的隐性共享。批量采样把独立请求塞进同一批次而底层 CUDA Kernel 中的随机数生成器如果未做隔离就会导致 Stream 相互污染。二、问题拆解为什么批量采样会泄漏随机状态2.1 单请求采样与批量采样的 RNG 差异单请求场景下每个推理调用拥有独立的 RNG Seed输出稳定可复现。进入批量采样后vLLM、TensorRT-LLM 等框架会把多个 Sequence 合并为一个 Batch调用一次sample()Kernel。此时若框架复用同一条 CUDA RNG Stream后一个请求会消费前一个请求留下的随机状态结果自然发生漂移。⚠️ 关键误区很多工程师认为设置temperature0就能消除随机性。实际上即便 Greedy Decode部分框架在 Top-K 处理时仍会触及 RNG只是概率分布被削峰后差异变小。2.2 状态泄漏的三条路径泄漏路径触发条件影响程度同 Stream 顺序消费Batch 内 Sequence 共享 RNG高Kernel Launch 异步重叠不同 Batch 间 Stream 复用中Checkpoint 恢复丢 Seed服务重启后 Seed 未持久化低[外链图片转存中…(img-UJuC8hbD-1779668516321)]图2RNG Stream 在 Batch 内的共享模型2.3 缓存失效与测试漂移的连锁反应输出不可复现直接击穿 Prompt Cache 的命中假设。当两次相同输入得到不同输出时基于 Hash 的语义缓存会判定为未命中导致后端重复计算。回归测试更是首当其冲同一用例在不同运行中可能通过也可能失败调试成本急剧上升。三、实战验证构建可复现的批量采样管线3.1 实验环境GPUNVIDIA A100 80GB框架vLLM 0.5.2模型Qwen2-7B-Instruct测试负载1000 条相同 PromptBatch Size 83.2 复现状态泄漏默认配置下运行批量推理记录每条请求的 output hashfromvllmimportLLM,SamplingParams llmLLM(modelQwen2-7B-Instruct)spSamplingParams(temperature0.7,top_p0.9,seed42)prompts[解释批量采样中的 RNG 泄漏]*8outputsllm.generate(prompts,sp)hashes[hash(o.outputs[0].text)foroinoutputs]print(fHash 去重后数量:{len(set(hashes))})# 往往 1在默认实现中即便显式传入了seed42Batch 内部仍可能出现多个不同输出因为 vLLM 的旧版本会把 Seed 应用到 Batch 级别而非 Sequence 级别。3.3 引入 Per-Request RNG Stream 修复思路是为每个 Sequence 分配独立的 Philox Stream确保随机状态按请求隔离# 伪代码在 Sampler 中为每个 Sequence 绑定独立 Seeddefsample_with_isolated_rng(logits,seq_seeds):results[]fori,seedinenumerate(seq_seeds):rngtorch.Generator(devicecuda)rng.manual_seed(seed)probssoftmax(logits[i])tokenmultinomial(probs,generatorrng)results.append(token)returntorch.stack(results)[外链图片转存中…(img-rgmBwhnz-1779668516322)]图3Per-Request Stream 隔离示意3.4 性能对比数据方案吞吐 (tok/s)输出一致性缓存命中率默认 Batch RNG1450低12%Per-Request Stream1380高89%同 Seed Greedy1420高91%✅ Per-Request Stream 仅带来约 5% 的吞吐下降却将缓存命中率从 12% 拉升到 89%实际端到端延迟反而更优。四、深度思考确定性与多样性的工程权衡在笔者看来批量采样的非确定性并非框架设计缺陷而是“性能优先”哲学下的默认取舍。vLLM 等框架为了最大化 Kernel 融合效率默认采用全局 RNG这在交互式对话场景中并无明显副作用。但一旦进入需要可复现性的生产链路例如 A/B 测试、缓存加速、回归验证这种取舍就会暴露风险。 核心判断确定性不该是采样参数的事后补丁而应是推理引擎的一级设计目标。Per-Request Stream 的额外开销远低于缓存失效带来的重复计算成本。另一个常被忽视的点是 Seed 的持久化。服务重启后如果 Seed 生成逻辑依赖时间戳或进程 ID之前的可复现链路就会断裂。建议将 Seed 与请求 ID 绑定并通过确定性哈希生成使 Seed 本身也成为请求语义的一部分。五、趋势预估从可复现推理到确定性服务未来 3 到 6 个月随着推理服务从“对话接口”演进为“生产管线组件”确定性推理将成为基础要求。笔者判断会出现以下趋势 主流推理框架将把 Per-Request RNG 作为默认选项而非高级配置。 语义缓存会与确定性采样深度绑定形成“可复现缓存”层。 模型评测中的 flaky 测试问题会推动行业建立“确定性推理基准”。同时完全确定性并不意味着牺牲多样性。通过在应用层为不同用户会话分配不同 Seed可以在保证单请求可复现的前提下维持系统级输出多样性。六、总结批量采样带来的 RNG State 泄漏是一个隐蔽但影响深远的工程问题。通过为每个请求分配独立的 RNG Stream并将 Seed 与请求语义绑定可以在几乎不损失吞吐的前提下重建可复现的推理管线。 你在部署批量采样时是否遇到过输出漂移的问题对于确定性推理与多样性的平衡你有什么实践经验欢迎在评论区交流。如果这篇文章对你有所帮助别忘了点赞收藏后续会持续更新更多推理优化的深度解析和实战干货。关注我带你玩转AI