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

大模型推理服务的批处理与动态 Batch 调度:从逐条推理到吞吐量优化 大模型推理服务的批处理与动态 Batch 调度从逐条推理到吞吐量优化一、推理服务的吞吐量瓶颈GPU 利用率的隐性浪费大模型推理服务的核心瓶颈不是单次请求的延迟而是 GPU 利用率。在逐条推理模式下每个请求独立送入 GPU 计算Batch Size 为 1GPU 的并行计算能力大量闲置。以 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( modelQwen/Qwen2.5-72B-Instruct, tensor_parallel_size2, # 2 卡并行 max_num_seqs64, # 最大并发序列数 max_num_batched_tokens32768, # 单 Batch 最大 Token 数 gpu_memory_utilization0.90, # GPU 显存利用率上限 enable_prefix_cachingTrue, # 启用前缀缓存复用 System Prompt ) # 推理端点vLLM 内部自动管理 Batch 调度 app.post(/v1/completions, response_modelInferenceResponse) async def completions(request: InferenceRequest): start_time time.monotonic() sampling_params SamplingParams( max_tokensrequest.max_tokens, temperaturerequest.temperature, top_prequest.top_p, ) # vLLM 的 add_request 自动进入 Continuous Batching 队列 outputs llm.generate( prompts[request.prompt], sampling_paramssampling_params, use_tqdmFalse, ) latency_ms (time.monotonic() - start_time) * 1000 output outputs[0] return InferenceResponse( textoutput.outputs[0].text, tokenslen(output.outputs[0].token_ids), latency_mslatency_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 内序列长度一致性。推理服务的优化不是单一维度的调参而是在吞吐量、延迟与显存利用率三个目标间的持续权衡。