大模型推理服务的批处理与动态 Batch 调度:从逐条推理到吞吐量优化
大模型推理服务的批处理与动态 Batch 调度:从逐条推理到吞吐量优化

一、推理服务的吞吐量瓶颈:GPU 利用率的隐性浪费
大模型推理服务的核心瓶颈不是单次请求的延迟,而是 GPU 利用率。在逐条推理模式下,每个请求独立送入 GPU 计算,Batch Size 为 1,GPU 的并行计算能力大量闲置。以 A100 GPU 为例,Batch Size 1 时 GPU 利用率通常不足 20%,而 Batch Size 8-16 时可提升至 70% 以上。
批处理(Batching)是提升推理吞吐量的关键手段:将多个请求合并为一个 Batch 送入 GPU,利用矩阵运算的并行性摊薄计算开销。但批处理的引入带来新的挑战:请求到达时间不同,需要等待凑批,增加了尾延迟;Batch 内请求的序列长度差异导致 Padding 浪费;动态流量下 Batch Size 的最优值持续变化。动态 Batch 调度系统需要在吞吐量与延迟之间找到动态平衡点。
二、动态 Batch 调度的核心机制
flowchart TD
A[请求到达] --> B[请求队列]
B --> C[Batch 调度器]
C --> D{调度策略}
D -->|时间窗口到期| E[组装当前队列所有请求]
D -->|队列长度达到上限| F[组装最大 Batch]
D -->|最长等待超时| G[立即发送已有请求]
E --> H[Padding 与注意力掩码]
F --> H
G --> H
H --> I[GPU 推理]
I --> J[结果拆分与返回]
subgraph 连续批处理 Continuous Batching
K[迭代级调度]
K --> L[每步重新评估 Batch]
L --> M[已完成序列让出位置]
M --> N[新请求填入空位]
end
C --> K
静态批处理(Static Batching)在凑满 Batch 后一次性推理,Batch 内所有序列必须等待最长序列完成才能返回。连续批处理(Continuous Batching)在每次迭代(生成一个 Token)后重新评估 Batch:已完成的序列立即让出位置,新请求填入空位,显著降低平均等待时间。
三、工程实现:基于 vLLM 的动态 Batch 推理服务
# dynamic_batch_server.py — 动态 Batch 推理服务
from fastapi import FastAPI
from pydantic import BaseModel
import asyncio
import time
from typing import List, Optional
from vllm import LLM, SamplingParams
from vllm.entrypoints.openai.api_server import init_app
app = FastAPI()
class InferenceRequest(BaseModel):
prompt: str
max_tokens: int = 256
temperature: float = 0.7
top_p: float = 0.9
priority: int = 0 # 0=普通, 1=高优先级
class InferenceResponse(BaseModel):
text: str
tokens: int
latency_ms: float
# vLLM 引擎初始化:启用 Continuous Batching
llm = LLM(
model="Qwen/Qwen2.5-72B-Instruct",
tensor_parallel_size=2, # 2 卡并行
max_num_seqs=64, # 最大并发序列数
max_num_batched_tokens=32768, # 单 Batch 最大 Token 数
gpu_memory_utilization=0.90, # GPU 显存利用率上限
enable_prefix_caching=True, # 启用前缀缓存,复用 System Prompt
)
# 推理端点:vLLM 内部自动管理 Batch 调度
@app.post("/v1/completions", response_model=InferenceResponse)
async def completions(request: InferenceRequest):
start_time = time.monotonic()
sampling_params = SamplingParams(
max_tokens=request.max_tokens,
temperature=request.temperature,
top_p=request.top_p,
)
# vLLM 的 add_request 自动进入 Continuous Batching 队列
outputs = llm.generate(
prompts=[request.prompt],
sampling_params=sampling_params,
use_tqdm=False,
)
latency_ms = (time.monotonic() - start_time) * 1000
output = outputs[0]
return InferenceResponse(
text=output.outputs[0].text,
tokens=len(output.outputs[0].token_ids),
latency_ms=latency_ms,
)
# batch_metrics.py — Batch 调度指标监控
from prometheus_client import Histogram, Gauge, Counter
import time
from functools import wraps
# 核心指标定义
BATCH_SIZE = Histogram(
'inference_batch_size',
'Batch 大小分布',
buckets=[1, 2, 4, 8, 16, 32, 64]
)
QUEUE_LENGTH = Gauge(
'inference_queue_length',
'等待队列中的请求数'
)
INFERENCE_LATENCY = Histogram(
'inference_latency_ms',
'推理延迟分布',
buckets=[50, 100, 200, 500, 1000, 2000, 5000]
)
GPU_UTILIZATION = Gauge(
'gpu_utilization_percent',
'GPU 利用率'
)
TOKENS_PER_SECOND = Gauge(
'inference_tokens_per_second',
'每秒生成 Token 数'
)
def track_batch_metrics(func):
@wraps(func)
async def wrapper(*args, **kwargs):
# 记录队列长度
QUEUE_LENGTH.set(get_current_queue_length())
start = time.monotonic()
result = await func(*args, **kwargs)
latency = (time.monotonic() - start) * 1000
INFERENCE_LATENCY.observe(latency)
return result
return wrapper
# 动态 Batch Size 调优:基于延迟 SLO 调整最大 Batch
class AdaptiveBatchController:
def __init__(self, latency_slo_ms: float = 500):
self.latency_slo_ms = latency_slo_ms
self.max_batch_size = 16
self.min_batch_size = 1
def adjust(self, current_p99_latency: float):
"""根据 P99 延迟动态调整最大 Batch Size"""
if current_p99_latency > self.latency_slo_ms * 1.5:
# 延迟超标,减小 Batch Size
self.max_batch_size = max(
self.min_batch_size,
self.max_batch_size - 2
)
elif current_p99_latency < self.latency_slo_ms * 0.5:
# 延迟充裕,增大 Batch Size 提升吞吐
self.max_batch_size = min(64, self.max_batch_size + 2)
四、动态 Batch 调度的边界与权衡
Padding 浪费:Batch 内不同序列长度差异导致短序列需要 Padding 到最长序列的长度,浪费计算资源。vLLM 的 PagedAttention 通过虚拟内存管理减少 Padding 浪费,但极端长度差异(1:10 以上)仍会显著影响有效吞吐。建议对请求按长度分桶,相似长度的请求组成同一 Batch。
凑批延迟:等待凑批的时间增加了请求的尾延迟。在低流量时段,凑批等待可能成为延迟的主要来源。解决方案是设置最大等待时间(如 50ms),超时后即使 Batch 未满也立即发送,避免无限等待。
KV Cache 显存竞争:Batch 内所有序列的 KV Cache 共享 GPU 显存,长序列消耗大量显存,可能挤占其他序列的空间。vLLM 的 PagedAttention 按需分配 KV Cache 页,但显存不足时仍需拒绝新请求。需根据模型大小与 GPU 显存合理设置 max_num_seqs。
流式输出的兼容性:Continuous Batching 与流式输出(SSE)存在天然矛盾:流式要求 Token 级别的即时返回,而 Batching 需要等待整个迭代步完成。vLLM 通过迭代级调度解决了这一问题——每个 Token 生成后立即返回,同时维护 Batch 状态。
五、总结
动态 Batch 调度是大模型推理服务吞吐量优化的核心手段。Continuous Batching 通过迭代级调度实现"完成即让位",显著降低平均等待时间。工程落地的关键在于:PagedAttention 减少 Padding 浪费、自适应 Batch Size 控制器基于延迟 SLO 动态调优、最大等待时间避免凑批延迟、按长度分桶优化 Batch 内序列长度一致性。推理服务的优化不是单一维度的调参,而是在吞吐量、延迟与显存利用率三个目标间的持续权衡。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)