AI 技术
#连续批处理#LLM推理#vLLM#调度#延迟

连续批处理如何提升大模型吞吐:迭代级调度与延迟权衡

在线聊天服务中,静态批处理因输出长度差异导致GPU利用率低下。本文以聊天场景贯穿,解释连续批处理如何通过迭代级调度动态加入和完成请求,分析其对TTFT和TPOT的影响,并讨论最大批大小、队列策略等参数如何权衡吞吐与延迟,以及适用边界。

从排队到闲置:聊天服务为何浪费GPU

假设你运营一个在线聊天机器人服务,用户输入问题后,模型逐步生成回答。你租用了昂贵的GPU,却发现压力测试时GPU利用率只有35%左右。用户抱怨响应慢,但GPU明明有空闲算力。问题往往不在模型本身,而在调度方式。

传统深度学习推理中,批处理(batching)把多个输入打包成一个批次,一次性前向传播,输出大小固定。但大语言模型(LLM)生成文本是迭代式的:每个token依赖之前生成的token,输出长度不固定。一个聊天请求可能15个token就回答完毕,同一批次中的代码生成请求却需要800个token。静态批处理(static batching)把一批请求作为一个整体,直到所有序列都生成完毕才释放资源。短请求完成后,它的计算槽位和显存仍然被占用,只能填充(padding)等待长请求结束。这导致GPU利用率只有30%~60%,大量算力浪费在等待上。

动态批处理(dynamic batching)通过时间窗口(例如50ms)收集请求,减少了准入延迟,但一旦窗口关闭,批次仍作为一个整体运行,直到最后一个序列完成。问题没有根本解决。

迭代级调度:每次前向传播重新决策

连续批处理(continuous batching),又称迭代级调度(iteration-level scheduling)或飞行中批处理(in-flight batching),将调度决策从请求粒度细化到迭代粒度。调度器在模型的每次前向传播时运行一次,而不是每个请求一次。

具体流程如下:

  1. 扫描当前批次,找出已生成结束符(EOS)的序列。
  2. 释放这些序列的KV缓存块。
  3. 从等待队列中取出新请求,数量受显存和最大批大小限制。
  4. 将所有活动序列拼接成一个复合批次。
  5. 执行一次前向传播,每个序列生成下一个token。
  6. 重复上述过程。

关键在拼接步骤。静态批处理要求序列填充到相同长度,因为矩阵运算需要统一形状。连续批处理则构造一个带注意力掩码的“超级序列”,防止请求之间互相关注。没有填充,每个GPU的FLOP都用于处理真实token。这种拼接方式与FlashAttention的变长算子(variable-length kernel)自然集成,即使序列长度不同,也能在单个GPU算子调用中处理所有序列。

下图展示了连续批处理中批次构成的动态变化:

flowchart TD
    A[请求到达] --> B{队列非空?}
    B -- 是 --> C[调度器扫描运行中批次]
    B -- 否 --> D[等待新请求]
    C --> E{有序列完成?}
    E -- 是 --> F[释放KV缓存块]
    E -- 否 --> G[检查显存和最大批大小]
    F --> G
    G --> H{有可用槽位?}
    H -- 是 --> I[从队列取出新请求]
    H -- 否 --> J[保持当前批次]
    I --> K[拼接所有活动序列]
    J --> K
    K --> L[执行一次前向传播]
    L --> M[每个序列生成下一个token]
    M --> C

图中,每次前向传播后,调度器重新检查批次,完成序列立即被替换,新请求在下次迭代前加入。

预填充与生成:两种计算模式的调度挑战

连续批处理并非没有代价。LLM推理分为预填充(prefill)和生成(decode)两个阶段。预填充阶段处理整个输入提示,计算密集且并行度高;生成阶段逐token产生输出,内存带宽受限。两者计算模式不同,难以在同一批次中高效混合。

如果预填充请求和生成请求混在一起,预填充会占用大量计算资源,拖慢生成速度;而生成请求又会让预填充等待。连续批处理框架通常通过超参数管理这种混合,例如等待已服务比(waiting_served_ratio)控制等待队列中预填充请求与生成请求的比例。

以聊天场景为例,用户发送新消息时,系统需要先处理预填充,然后进入生成。如果新请求的预填充与正在生成的请求混批,可能增加生成请求的延迟。因此,调度器需要权衡:是优先让生成中的请求尽快完成,还是让新请求尽快开始。

内存管理:PagedAttention与连续批处理的协同

连续批处理解决了何时调度请求,但请求的KV缓存存储同样关键。KV缓存是LLM推理中存储历史token注意力键值的内存,每个请求的KV缓存大小随生成动态增长。

在PagedAttention出现之前,框架为每个请求预分配一块连续GPU显存,大小基于最大可能输出长度。这导致60%~80%的显存浪费:过度保留的槽位、对齐间隙,以及大多数序列不会用完最大分配空间。

PagedAttention(vLLM, SOSP 2023)将操作系统的虚拟内存分页模型应用于KV缓存管理。KV缓存被划分为固定大小的块(vLLM默认每块16个token),按需动态分配,而非预先分配。块表将每个序列的逻辑块映射到物理GPU显存位置,块不需要连续。显存浪费降至4%以下,仅浪费每个序列最后一个未填满的块。

PagedAttention还支持物理块在序列间共享,通过写时复制(copy-on-write)语义。束搜索的分支、并行采样以及共享相同系统提示词的请求,在分歧前可以引用相同的物理KV块。对于束搜索,这可以减少高达55%的开销,吞吐量比非共享分配提高2.2倍。

SGLang通过RadixAttention进一步扩展:用基数树在不同请求间维护KV缓存,实现自动前缀重用。共享系统提示词、少样本示例或RAG上下文的请求可以重用彼此已缓存的KV块,无需重新计算。在大量前缀共享的工作负载中,推理速度可提升5倍。

在聊天服务中,用户通常共享相同的系统提示词(例如“你是一个友好的助手”),PagedAttention和RadixAttention可以显著减少重复计算。

延迟指标:TTFT与TPOT的权衡

连续批处理对延迟的影响需要区分两个指标:

  • TTFT(Time To First Token):从请求到达到生成第一个token的时间。
  • TPOT(Time Per Output Token):生成每个输出token的平均时间。

静态批处理中,新请求必须等待当前批次完成才能加入,TTFT可能很高。连续批处理允许新请求在下次迭代时加入,TTFT显著降低,尤其在中低负载下。

然而,TPOT可能受影响。当批次中混入预填充请求时,生成请求的计算资源被抢占,TPOT可能上升。调度器需要控制预填充与生成的比例,平衡TTFT和TPOT。

High-Flyer的实验(基于A100 GPU,OPT-13B模型)显示,在QPS=1时,连续批处理在所有百分位数上延迟都优于静态批处理;QPS=4时,优势缩小,因为系统饱和后,新请求立即注入的机会减少。

吞吐与延迟的权衡:参数与策略

连续批处理的核心参数包括最大批大小、队列策略和预填充/生成比例。

  • 最大批大小:增大批大小可以提高吞吐,但增加每个请求的TPOT,因为共享计算资源。
  • 队列策略:FIFO(先进先出)简单公平,但长序列可能阻塞短序列。优先级队列可以优先处理短请求,降低TTFT,但可能饿死长请求。
  • 预填充/生成比例:控制预填充请求与生成请求的混合程度,影响TTFT和TPOT的平衡。

下表对比了不同调度策略的适用场景:

策略吞吐TTFTTPOT适用场景
静态批处理稳定同质输出,离线批量
动态批处理中等方差,低并发
连续批处理(无内存优化)可能波动高方差,在线聊天
连续批处理 + PagedAttention很高较低高并发,长序列,共享前缀

在聊天场景中,输出长度方差大,连续批处理优势明显。但如果所有请求都生成恰好50个token,优势几乎为零。

失效模式:何时连续批处理会退化

连续批处理并非万能。以下场景中,其收益可能减弱甚至产生负面影响:

  • 低QPS:每秒个位数请求时,所有方法表现相近,调度开销可能超过收益。
  • 同质输出:离线批量推理,序列长度预先已知且统一,静态批处理更简单且具有竞争力。
  • 高并发饱和:系统满负载时,新请求无法立即注入,延迟上升,连续批处理优势缩小。
  • 内存碎片化:块大小固定可能导致碎片化,例如块大小16个token,序列生成17个token时,第二个块只使用1个token,浪费15个token空间。
  • 队头阻塞:基于内存的队头阻塞,当新请求需要大量KV缓存而显存不足时,可能阻塞后续请求。

实际部署中,需要监控GPU利用率、TTFT、TPOT、排队长度、KV缓存命中率等指标。当GPU利用率长期低于50%时,考虑调整批大小或队列策略;当TTFT过高时,检查预填充与生成比例。

替代方案与适用边界

与连续批处理相比,其他优化方法各有侧重。

  • 模型量化(如AutoGPTQ):减少模型显存占用,间接增大批大小,但可能损失精度。
  • FlashAttention:优化注意力计算的内存IO,与连续批处理正交,可叠加使用。
  • 投机解码(speculative decoding):用小模型草稿加速大模型生成,减少解码步数,但与批处理调度独立。

连续批处理最适合高方差、高并发的在线服务,如聊天、Agent、交互式助手。对于离线批量推理,静态批处理可能更简单。

仍未解决的问题

连续批处理虽已广泛应用,但调度问题尚未完全解决。如何动态调整最大批大小以适应负载变化?如何在不同租户间公平分配GPU资源?预填充与生成的混合比例如何自适应优化?这些问题仍需要进一步研究。

对于工程实践,建议从监控开始:记录TTFT、TPOT、GPU利用率、队列长度,分析瓶颈是调度还是内存。然后逐步调整参数,观察延迟分布的变化。连续批处理不是一劳永逸的解决方案,而是需要持续调优的系统设计。

资料来源

  1. vLLM: Easy, Fast, and Cheap LLM Serving with PagedAttention
  2. 持续批处理:LLM 服务中提升 GPU 利用率的最关键技术
  3. Continuous Batching:一种提升 LLM 部署吞吐量的利器