§1 TL;DR #
Neo 将部分 decode attention 计算和 KVCache 从 GPU 卸载到本机 CPU,通过 asymmetric pipelining(非对称子批次重叠)和 load-aware scheduling(贪心回退到 GPU-only)提升在线推理吞吐。在内存受限的 T4 上达 7.5× 吞吐提升,A10G 上 26%,H100 上 14%,且不牺牲延迟。
§2 痛点 / 方法 / 结果 #
Q1 痛点 #
在线 LLM 推理中 GPU 吞吐高度依赖 batch size,但 KVCache 占用大量 GPU 内存导致实际 batch size 受限,GPU 计算单元严重闲置——这是 "GPU memory crisis"。先前 CPU offloading 方案(FlexGen、PowerInfer、HeteGen)用延迟换吞吐,不适合在线场景;FastDecode 完全卸载到 CPU 但需要 8 台远程 CPU 服务器处理单 GPU 的 attention,成本过高且 CPU 成为瓶颈。
核心 insight:decode attention 是 memory-bandwidth-bounded,GPU-CPU 在带宽上的差距(600 vs 200 GB/s)远小于算力差距(125 TFLOPS vs 1.2 TFLOPS),CPU 是 decode attention 的合理卸载目标。
Q2 方法 #
Asymmetric GPU-CPU pipelining(§3.1):
- KVCache 分为 GPU-cache 和 CPU-cache;每个请求的 KVCache 完整存放在一处。优先使用 GPU-cache 最大化 GPU 内存利用。
- 将请求分为两个 非对称子批次:
- Batch-0:所有 prefill 请求 + GPU-decode 请求 + 少量 CPU-decode 请求(long linear stage on GPU, short CPU attention)
- Batch-1:大部分 CPU-decode 请求(short linear stage, long CPU attention)
- 两个子批次交替执行:batch-0 的 GPU linear stage 与 batch-1 的 CPU attention 重叠,反之亦然。非对称设计使 GPU 始终繁忙,避免 symmetric pipelining 中 GPU 等待 CPU 的 bubble。
- Layer-wise KV swapping:每层计算后立即传输 KV,与下一层计算重叠。
Load-aware scheduling(§3.2):
迭代时间模型:
$$T \approx T_{tr} = L \times \left( \max\{T_{po_0} + T_{pr_0},\; T_{ca_1}\} + \max\{T_{po_1} + T_{pr_1} + T_{ga_0},\; T_{ca_0}\} \right)$$
约束条件(GPU 不空等 CPU):
$$T_{l_0} \geq T_{ca_1} \quad \text{and} \quad T_{l_1} + T_{ga_0} \geq T_{ca_0}$$
调度器每次迭代执行 6 步决策:初始化 → 调度 GPU-decode → 调度 prefill → 调度 CPU-decode → 裁减 prefill(避免 swap-out 开销使 CPU 空等)→ 贪心选择(两批次方案 vs GPU-only 方案取 $T_{tr}/x$ 更大者)。
Greedy 原则保证 Neo 永远不差于 GPU-only baseline。
核心技术壁垒:asymmetric batch division 的 insight——将 prefill(GPU 密集)和 CPU-decode 放在互补子批次中,使得两个 max 项中 GPU 端的 linear stage 总是足够长以覆盖 CPU attention,从而消除 pipeline bubble。这要求对每次迭代的 GPU/CPU 时间精确建模并动态调整批次构成。
Q3 结果 #
- T4 + LLaMA-2-7B:在线吞吐 7.5×(T4 仅 16GB HBM,baseline batch size 极小)
- A10G + LLaMA-3.1-8B:6.4%–26% throughput 提升,最高 CPU 配置(g5.16xlarge)达 79.3%
- 2×H100 + LLaMA-3.1-70B:14.3% throughput 提升
- 延迟:所有 percentile 与 GPU-only 基线持平
- vs FastDecode+:Neo 始终优于 baseline,FastDecode+ 在长输出时降至 baseline 的 <60%
§3 架构 / 方法图 #
sequenceDiagram
participant Sched as Request Scheduler (CPU)
participant PQ as Prefill WaitQueue
participant GQ as GPU Decode RunQueue
participant CQ as CPU Decode RunQueue
participant GPU as GPU (Linear + GPU Attn)
participant CPU as CPU (PACPU Attention)
Sched->>Sched: Per-iteration scheduling (6-step)
Sched->>GPU: Batch-0: prefill + GPU-decode + few CPU-decode
Sched->>CPU: Batch-1: most CPU-decode requests
par Batch-0 GPU linear ↔ Batch-1 CPU attention
GPU->>GPU: po₀ + pr₀ (linear stage, long)
CPU->>CPU: ca₁ (CPU attention, fits under GPU time)
end
par Batch-1 GPU linear + GPU attn ↔ Batch-0 CPU attention
GPU->>GPU: po₁ + pr₁ + ga₀ (linear + GPU attn)
CPU->>CPU: ca₀ (CPU attention for batch-0 CPU requests)
end
GPU->>Sched: Iteration complete, return to scheduling
关键组件:
- GPU-cache / CPU-cache:分页 KVCache,每个请求的 KV 完整存在于 GPU 或 CPU 一侧。GPU-cache 优先分配。
- PACPU (Paged-Attention-for-CPU):C++ torch extension → ISPC 编译的向量化 CPU attention kernel,支持 AVX2/AVX512/Neon,paged attention 算法减少碎片。
- Scheduling engine:每次迭代构建两种方案(asymmetric vs GPU-only),通过 $T_{tr}/x$ 比较选择更优方案(Greedy 原则)。
§4 作者证明 #
记号表 #
| 符号 | 含义 |
| $T$ | 单次迭代总时间 |
| $T_{tr}$ | Transformer 层时间(占 >95%) |
| $L$ | Transformer 层数 |
| $T_{po_x}$ | Batch-x 的 post-projection 时间 |
| $T_{pr_x}$ | Batch-x 的 pre-projection 时间 |
| $T_{ga_x}$ | Batch-x 的 GPU attention 时间 |
| $T_{ca_x}$ | Batch-x 的 CPU attention 时间 |
| $T_{l_x}$ | $T_{po_x} + T_{pr_x}$(batch-x linear 时间) |
| $x$ | 总 batch size |
方程物理意义 #
主方程 $T \approx L \times (\max\{T_{l_0}, T_{ca_1}\} + \max\{T_{l_1} + T_{ga_0}, T_{ca_0}\})$:
每层执行两个 pipeline stage。第一个 stage 中 GPU 执行 batch-0 的 linear ops,与 CPU 执行 batch-1 的 attention 重叠——取 max。第二个 stage 中 GPU 执行 batch-1 的 linear + batch-0 的 GPU attention,与 CPU 执行 batch-0 的 CPU attention 重叠。非对称性体现在 batch-0 的 linear stage 天然更长(包含 prefill + GPU-decode),能覆盖 batch-1 的 CPU attention。
约束 $T_{l_0} \geq T_{ca_1}$ 和 $T_{l_1} + T_{ga_0} \geq T_{ca_0}$ 保证 Hiding CPU 原则:GPU 永不空等 CPU。
6 项检查 #
- 迭代时间模型可验证:离线 profiling 估计各组件时间,线性插值。论文承认精度有限(短输出时 Neo 可能略差于 baseline)。
- Greedy 保证:每次迭代比较两种方案取最优——数学上保证 throughput ≥ GPU-only。实际中由于 profiling 误差和 scheduling 开销,存在轻微违反。
- CPU 带宽是绑定约束:sensitivity study 证实 throughput gain 正相关于 CPU 内存带宽而非核数(Fig 10a)。
- SwiftLLM vs vLLM 基线验证:单 GPU 性能持平;2-GPU 差 8.8%——多 GPU 结果存在基线偏弱问题。
- FastDecode+ 对比公平:作者自行实现 FastDecode+ 并赋予 Neo 的调度优势,仍然因完全卸载导致 CPU 过载。
- 层级 KV swap 开销未深度分析:论文提及 layer-wise swapping 但未定量剥离 PCIe 争用影响。
§5 实验与数据 #
在线延迟-负载曲线(Fig 6) #
| Setting | Neo Max Throughput Gain | Latency Profile |
| 2×H100 + 70B + AC | 14.3% (at 2s latency) | 低 RPS 时持平,高 RPS 时优势明显 |
| A10G + 8B + AC | 6.4% (at 2s latency) | 类似 |
| T4 + 7B + OSC | 563% (at 1s latency) | T4 HBM 极小,vLLM batch size 受限 |
- 短 output:Neo 可能略差(尝试 offload 的开销 > 收益)
- 中等 output:gain 达峰值(GPU/CPU 时间恰好平衡)
- 长 output:gain 逐渐下降,回落到接近 baseline
- 极长 output:偶尔略差于 baseline(profiling 不精确)
CPU 容量 sensitivity(Fig 10a) #
| EC2 Instance | CPU Cores | Peak BW Approx | Max Throughput Gain |
| g5.2xlarge | 4 | ~100 GB/s | 12.2% |
| g5.4xlarge | 8 | ~100 GB/s | 13.3% |
| g5.8xlarge | 16 | ~200 GB/s | 29.7% |
| g5.16xlarge | 32 | ~400 GB/s | 79.3% |
带宽翻倍则 gain 近似翻倍——验证了 CPU 内存带宽是绑定约束而非算力。
§6 论证链 #
| Step | Claim | Evidence | Strength |
| 1 | GPU 吞吐受限于 KVCache 导致的 batch size 瓶颈 | Fig 1 (NanoFlow), §2.1 引用 vLLM 数据 | Strong — well-established |
| 2 | Decode attention 是 memory-bandwidth-bounded,CPU 带宽差距远小于算力差距 | §2.2 定量比较 (600 vs 200 GB/s, 125 vs 1.2 TFLOPS) | Strong — 硬件规格事实 |
| 3 | Symmetric pipelining (FastDecode) 导致 GPU 内存浪费 + GPU 空等 CPU + 分批困难 | §3.1 三点分析 + Fig 3, 4 | Medium — 分析清晰但缺少 micro-benchmark 验证 |
| 4 | Asymmetric pipelining 消除上述三个问题 | §3.1 Fig 5 pipeline 设计 | Medium — 设计合理但验证主要通过端到端结果 |
| 5 | Load-aware scheduling 保证 never-worse-than-baseline | §3.2 Greedy 原则 + 方程推导 | Strong — 数学保证(受 profiling 精度限制) |
| 6 | 端到端验证:T4 上 7.5×,A10G 26%,H100 14% | Fig 6, 9, 10 | Strong — 多硬件、多模型、多 workload |
§7 实现 cross-reference #
- 基于 SwiftLLM 实现(~2K LoC 简化 vLLM),Neo 增加 4K Python + 1.5K C++
- CPU kernel:PACPU (C++ torch extension → ISPC),支持 AVX2/AVX512/Neon
- GPU kernel:替换 Triton-JIT 为 CUDA C++ 以减少 kernel launch overhead(Python GIL 阻塞问题)
- 多 GPU:Ray actors + NCCL,每个 actor 持有独立 CPU KVCache 分区
- 开源承诺但未给出 URL
关键实现细节:
- PACPU 的 Flash Decoding 并行策略:沿 request 维度分 partition,每个 task 访问连续内存 block,dispatch 到不同 CPU 线程,最后 reduce partial output。消除跨线程内存争用。
- Kernel launch overhead 是隐藏瓶颈:Python GIL 使 CPU data plane kernel 不能与 GPU control plane kernel launching 并行,被迫串行化。替换 Triton → CUDA C++ 是实测后的必要优化。
框架特定分析 #
系统范围 #
- 阶段覆盖:prefill + decode(不分离,共用 GPU 实例)
- Serving 模式:selective batching(继承 Orca),连续批处理
- 并行维度:TP(多 GPU),CPU offloading 作为额外并行维度
- 部署模式:单节点,仅使用本机 CPU(不使用远程 CPU)
调度与资源管理 #
- 调度粒度:iteration-level(每次迭代重新决策)
- 抢占策略:支持 GPU↔CPU 间 swap in/out(iteration 间)
- 过载策略:无显式过载处理(非 disaggregated,无 early rejection)
- KVCache 管理:paged attention 算法(继承 vLLM),GPU-cache + CPU-cache 分开管理
- Swap 触发:调度器根据 GPU 内存余量决定 swap 方向
胜负场景 #
| Workload Regime | Neo | GPU-only Baseline | Why |
| 小内存 GPU (T4 16GB) | 7.5× 吞吐 | batch size 极小 | 卸载大量 KVCache 到 CPU,释放 GPU batch capacity |
| 中等 GPU (A10G 24GB) | 6%–26% | 基线 | GPU 内存仍然受限但不极端 |
| 大内存 GPU (H100 80GB) | 14% | 基线 | GPU 已能容纳较大 batch,offloading 边际收益小 |
| 短 output | 持平或略差 | 基线 | offloading overhead > 收益 |
| 极长 output | 接近基线 | 基线 | 大部分迭代回退到 GPU-only 模式 |
| 强 CPU 配置 | 最高 79.3% | - | CPU BW 翻倍 → gain 翻倍 |
部署上下文 #
- Serving stage:prefill + decode 一体(非 disaggregated)
- 硬件亲和性:内存受限 GPU 受益最大(T4 >> A10G >> H100);CPU BW 越高越好
- 生态集成:基于 SwiftLLM(简化 vLLM),论文计划集成到 vLLM
- 迁移路径:替换 inference engine → SwiftLLM+Neo;未来可能为 vLLM 插件