打破 Agent 串行瓶颈: 无 KV 传输的双卡流水线并行

📅 2026/8/4 4:56:53
打破 Agent 串行瓶颈: 无 KV 传输的双卡流水线并行
日期: 2026-08-03场景: 5-Agent 串行推理链 · Qwen3-14B-AWQ · long-prefill一句话结论: 在双卡乒乓 append-prefill 下, 下游 TTFT 从约 600ms 降到约 13ms, 端到端相对单卡串行稳定快约11%, 且输出与串行逐字一致.代码与结果: https://github.com/xu-jia-ming/agent-dual-gpu-pipeline为什么要做这件事多智能体应用里, 常见模式是一条链:Agent A - Agent B - Agent C - Agent D - Agent E后一个 Agent 的 prompt, 依赖前一个 Agent 的完整输出. 于是大家习惯写成串行:A 做完 prefill decode把 A 的输出拼进 B 的 promptB 再从头 prefill decode...依此类推问题很直观:B 的 prefill 明明可以边收 A 的 token 边做, 却被硬生生排到 A 全部结束后才开始. A 每多生成一段时间, B 就多空等一段 prefill.如果把 上游 decode 和 下游 prefill 重叠起来, 端到端就有机会省下整段下游 TTFT.这不是新想法. Prefill/Decode 分离 (PD), chunked prefill, 推测解码, 都在不同层面做重叠. 我们想验证的是一个更轻的路径:不传 KV. 当前 Agent 在哪张卡建成 KV, 就在哪张卡 decode; 另一张卡专门给下一个 Agent 做增量 append-prefill.我们叫它双卡乒乓 (dual-GPU ping-pong).方案长什么样五个 Agent 交替落在两张 GPU 上 (CSDN 对 Mermaid 支持不稳定, 这里用 ASCII 时间线):Time -- ​ GPU0: [A decode] [C append-prefill]-[C decode] [E append-prefill]-[E decode] | ^ | ^ | A tokens | B tokens | C tokens | D tokens v | v | GPU1: [B append-prefill]-[B decode]-- [D append-prefill]-[D decode]--读法: 同一阶段里, 一张卡 decode, 对卡给下一跳 append-prefill; 箭头是 token 流, 不是 KV 拷贝. 奇数 Agent (A/C/E) 在 GPU0, 偶数 Agent (B/D) 在 GPU1.关键点有三个:Append-prefill下游请求先用静态 prefix 建起来; 上游每产出一段 token, 就增量 append 到下游 prompt, 并持续 prefill.Defer downstream等当前 Agent 出第一个 token 后再创建下游. 否则下游的静态 prefix prefill 会和当前 Agent 抢算力, 把首跳 TTFT 拖慢.乒乓落卡Agenti的 KV/decode 在GPU(i % 2); Agenti1的 append-prefill 在另一张卡. 这样 decode 和 prefill 不再打同卡争用.对照基线是同一套 5-Agent 链的单卡串行. 需要先说清楚:pipeline 用了 2 张卡, serial 用 1 张卡. 我们比较的是 用第二张卡做重叠 相对 单卡串行链 的收益, 不是 同等 GPU 数下的加速比. 对依赖链来说, 多一张卡如果只是再跑一个串行引擎, 本身也帮不上 E2E; 收益来自重叠, 不是来自 多买了一张卡随便用用.先说失败: 单卡重叠为什么不香在动手做双卡之前, 我们先在单卡上做了 append-prefill, 并加上 defer, 避免拖慢 A 的 TTFT.结果很有教育意义:设置相对串行 E2E输出是否与串行一致A 的 total单卡 defer约 -0.3% (几乎打平)否 (3/3 分叉)比串行慢约 560ms双卡乒乓约 -11%是 (逐字一致)与串行基本持平单卡上的故事是:下游 TTFT 确实被压下去了;但下游 prefill 和上游 decode抢同一张卡;省下来的 TTFT, 被上游变慢的 decode 吃掉;greedy (temperature0) 路径被扰动后, 输出还会和串行分叉.所以 能重叠 不等于 有端到端收益.争用才是单卡方案的主因.主实验结果: 双卡乒乓, 稳定约 11%主结果来自同日本地复跑 (两张 GPU, 10 次 measured 1 次 warmup, serial/pipeline 交替执行):模式平均 E2EminmaxSerial (单卡)19644.7 ms19618.019675.0Dual pipeline17497.0 ms17471.417592.5节省2147.7 ms (10.93%)10 次全部正收益, 波动很小. 不是 挑了最好的 3 次.正确性延迟好看之前, 先看对不对:10/10 runs: A-E 五个 Agent 输出与 serial逐字完全一致全部边的prompt_token_match通过: 引擎最终 prompt tokens encode(prefix upstream_output suffix)输出 token 数与 serial 对齐, 延迟比较是 quality-equivalent 的对我们来说, 这比 差不多对 更重要: 重叠路径没有偷偷改采样结果.机制有没有发生?看分 Agent 平均延迟:AgentSerial TTFTPipeline TTFTSerial totalPipeline totalA508.7 ms510.0 ms4432 ms4449 msB603.0 ms13.2 ms4543 ms3946 msC604.1 ms13.7 ms4546 ms3966 msD603.6 ms13.3 ms4543 ms3954 msE606.4 ms13.5 ms1581 ms986 ms说明: E 是最终响应 Agent,final_max_tokens64, 而 A-D 为 256, 所以 E 的 total 本来就更短; 单 token decode 速度与上游一致 (~15.5 ms/token).解读很直接:A 没被拖慢: defer 生效, 首跳和串行持平.B-E 的 TTFT 几乎消失: 轮到它们 decode 时, KV 基本已经在对卡上建好.E2E 省的就是这些被隐藏的下游 prefill. 粗算: 串行 B-E 的 TTFT 合计约 2400ms; 扣掉残留 TTFT (约 50ms) 和几次 finalize 开销 (约 4x45ms) 后, 和实测节省 ~2150ms 对得上.时间线上也能看到真实的增量 append, 而不是 提前把完整 prompt 塞进去假装重叠. 以上游 A-B 为例:A 首 token 后约 0.5s 创建下游;在 A 仍在 decode 的窗口内, 多次 append (约 1.6s / 2.6s / 3.6s / 4.4s);num_computed_tokens随 chunk 推进;finalize 时 prompt 已基本算完, B 的 TTFT 只剩约 13ms.一个必须老实写的局限我们也在另一组双卡上跑过 10 次: 正确性同样 10/10, 前几次也能到约 11%, 但后半段第二张卡的 decode 偶发变慢, 把10 次均值拉到只剩 0.35%.这说明两件事:机制本身成立-- 好的 run 上, 收益稳定出现, 输出也正确;工程稳定性仍敏感-- 第二张卡的 decode 抖动会直接打穿均值.所以对外表述我们用的是:在稳定环境下, 双卡乒乓可带来约 11% 的端到端收益, 并保持与串行逐字一致; 收益对第二张卡的 decode 稳定性敏感.另外还有几条边界:收益依赖long-prefill甜点区: 下游 prefill 要足够长, 才能被上游 decode 盖住.需要双倍权重显存(两份引擎).这不是通用 vLLM 整体加速, 而是Agent 串行链上的调度优化.对比口径是2 卡 pipeline vs 1 卡 serial, 请勿读成 iso-GPU speedup.我们学到了什么重叠要消灭争用, 不只是消灭空等.单卡上 TTFT 可以降, E2E 却可能打平, 还会弄坏 greedy 一致性.正确性必须和延迟一起报.exact match prompt token 一致性, 比单独报加速比更有说服力.轻量 PD 可以很实用.不传 KV, 不改模型, 只靠 对卡预建下一跳, 就能在链式 Agent 上挖出可见的 E2E 收益.均值会被尾部抖动吃掉.系统论文/博客里同时报 mean, 稳定子集和 failure mode, 比只报最好数字更耐打.复现思路代码, patch, 结果与文档都在 GitHub:https://github.com/xu-jia-ming/agent-dual-gpu-pipeline实验脚本在仓库的pipeline/下, 核心入口是:python3 pipeline/benchmark_internal_vllm_5agents.py \ --profile long-prefill \ --runs 10 \ --warmup-runs 1 \ --mode-order alternate \ --lazy-precreate \ --defer-downstream-until-first-token \ --dual-gpu-pingpong \ --pingpong-devices 0,1 \ --force-max-output-tokens \ --strict-output-tokens \ --gpu-memory-utilization 0.85 \ --output pipeline/results/dual_pingpong_local_gpu27_runs10.jsonl实现上依赖我们对 vLLM 的实验性 append-prefill 能力 (patch 形式). 详见仓库中的patches/与README.md.主结果文件:pipeline/results/dual_pingpong_local_gpu27_runs10.jsonl写在最后Agent 应用越来越像 多次 LLM 调用拼起来的工作流. 模型侧在变强, 但调度侧仍有大量空等: 下一个 Agent 干等上一个 Agent 说完, 才开始读它本来可以边看边读的上下文.这次实验让我们更有信心地说:在链式 Agent 场景里, 用一张额外的 GPU 做下一跳 append-prefill, 是一条足够轻, 又足够真实的优化路径 -- 前提是你把争用, 正确性和稳定性一起量清楚.如果这个方向对你有用, 欢迎一起挑刺, 复现, 或者把它推进成更稳的产品形态 (正式 PD, decode 优先调度, 更细的跨请求资源隔离).[更好的体验](https://pursueinf.cn/2026/08/03/da-po-agent-chuan-xing-ping-jing-wu-kv-chuan-shu-de-shuang/)