发布时间:2026/9/3 7:47:34
AI 推理优化实践:vLLM Continuous Batching:5800 t/s 吞吐量的核心引擎 一、为什么需要 Continuous Batching1.1 三种 Batching 策略对比① 静态批处理 (Static Batching) — 传统方式 ┌──────────────────────────────────────────────┐ │ Req A: [Prefill][D][D][D][D][D][D][D][D][D][D] │ ← 生成长一直占着 │ Req B: [Prefill][D][D] │ ← 早就结束了slot 空着 │ Req C: [Prefill][D][D][D] │ ← 也结束了 │ Req D: [Prefill][D][D][D][D][D] │ └──────────────────────────────────────────────┘ → B/C 结束后必须等 A 完成才能处理新请求 → GPU 利用率极低大量 slot 空转 ​ ② 动态批处理 (Dynamic Batching) — TGI/DeepSpeed ┌──────────────────────────────────────────────┐ │ Req A: [Prefill][D][D][D][D][D][D][D][D][D][D] │ │ Req B: [Prefill][D][D] │ │ Req E: [Prefill][D][D][D] │ ← B 结束后 E 等到下一个 iter │ Req D: [Prefill][D][D][D][D][D] │ └──────────────────────────────────────────────┘ → 只在 iteration 边界检查请求状态 → 粒度还是太粗E 要等 1 个 iteration ​ ③ 连续批处理 (Continuous Batching) — vLLM ┌──────────────────────────────────────────────┐ │ Req A: [Prefill][D][D][D][D][D][D][D][D][D][D] │ │ Req B: [Prefill][D][D] │ │ Req E: [ ][Prefill][D][D][D][D] │ ← B 结束的下一 step 立刻插入 │ Req C: [Prefill][D][D][D] │ │ Req F: [ ][ ][Prefill][D][D] │ ← C 结束后 F 立刻插入 │ Req D: [Prefill][D][D][D][D][D] │ └──────────────────────────────────────────────┘ → 每个 step 都可以动态加入/移除请求 → GPU slot 始终被有效请求占满1.2 吞吐量对比Batching 策略并发 128 时吞吐量GPU 利用率静态批处理~1200 t/s25%-35%动态批处理~2500 t/s50%-60%连续批处理~5800 t/s85%-95%二、Continuous Batching 的核心机制2.1 迭代调度的本质Continuous Batching 的核心是一个迭代循环。每个 iteration即每生成一个 token都是一个独立的调度决策点2.2 三队列模型class Scheduler: def __init__(self, ...): # 三个核心队列 self.waiting: Deque[SequenceGroup] deque() # waiting: 新到达的请求尚未做 Prefill # 只有 Prefill 后才能开始 Decode ​ self.running: Deque[SequenceGroup] deque() # running: 正在 Decode 的请求 # 每个 step 为它们各生成一个 token ​ self.swapped: Deque[SequenceGroup] deque() # swapped: 被 swap out 到 CPU 的请求 # 显存不足时的临时中转站 请求生命周期: arrived → [waiting] → Prefill → [running] → Decode... → finished ↓ 显存不足 → [swapped] → 显存恢复 → [running]三、调度循环核心3.1 schedule()每步的调度决策class Scheduler: def schedule(self) - SchedulerOutputs: 每个 iteration 调用一次 决定这个 step 处理哪些请求 scheduler_outputs SchedulerOutputs() ​ # ── Step 1: 处理已完成的请求 ── # 上一 step 生成的 token 可能触发 EOS # 完成的请求从 running 移出释放 KV Cache Block self._schedule_finished_seqs() ​ # ── Step 2: 检查显存压力必要时抢占 ── if self._check_memory_pressure(): self._preempt_running_seqs() ​ # ── Step 3: 从 waiting 队列补充新请求 ── # 这是 Continuous Batching 的核心 # 每 step 都检查能否加入新请求 self._schedule_waiting_seqs() ​ # ── Step 4: 构建 batch ── # 将 running 队列中的请求组成一个 batch # 同时可能包含 waiting 队列中新请求的 Prefill self._build_batch(scheduler_outputs) ​ return scheduler_outputs3.2 Continuous Batching 的精髓每步都可以插入新请求def _schedule_waiting_seqs(self): 关键逻辑每个 step 都尝试从 waiting 队列拉取新请求 这就是 Continuous 的含义 — 不是等到整个 batch 完成才换人 而是每个 token 生成完后都可以换人 # 计算当前显存余量 free_blocks self.block_manager.get_num_free_gpu_blocks() ​ while self.waiting: seq_group self.waiting[0] ​ # 判断显存是否足够做 Prefill required_blocks self._get_prefill_blocks(seq_group) if required_blocks free_blocks: break # 显存不够停止补充 ​ # 从 waiting 移到 running self.waiting.popleft() self.running.append(seq_group) free_blocks - required_blocks ​ # 标记这个请求这个 step 做 Prefill seq_group.set_schedule_type(ScheduleType.PREFILL)3.3 两种抢占策略当显存不足时vLLM 需要从 running 队列中抢占请求释放显存def _preempt_running_seqs(self): 显存不足时从 running 队列尾部抢占请求 两种策略SWAP 或 RECOMPUTE while self._check_memory_pressure() and self.running: # 取最后加入的请求FIFO 的反面LIFO 抢占 seq_group self.running.pop() ​ if self.preemption_mode PreemptionMode.SWAP: # 策略 A: SWAP — 将 KV Cache 拷贝到 CPU self.block_manager.swap_out(seq_group) self.swapped.append(seq_group) ​ elif self.preemption_mode PreemptionMode.RECOMPUTE: # 策略 B: RECOMPUTE — 直接丢弃 KV Cache # 下次恢复时重新做 Prefill self.block_manager.free(seq_group) self.waiting.appendleft(seq_group) # 放回 waiting 头部 策略 A: SWAP running → [GPU KV Cache] 拷贝到 CPU → swapped 恢复: swapped → [CPU] 拷贝回 GPU → running 优点: 恢复快只需拷贝不需重新计算 缺点: 需要额外 CPU 内存 ​ 策略 B: RECOMPUTE running → 丢弃 [GPU KV Cache] → waiting 恢复: waiting → 重新 Prefill → running 优点: 不占 CPU 内存 缺点: 恢复慢要重新算 Prefill策略恢复延迟内存需求适用场景SWAP低~5msCPU 内存 GPU KV Cache 大小显存压力大但不极端RECOMPUTE高~200ms无显存极端不足3.4 Prefill 和 Decode 的分离vLLM 的一个重要优化是Prefill-Decode 分离调度。一个 iteration 要么全做 Prefill要么全做 Decodedef _build_batch(self, scheduler_outputs): 判断这个 step 做 Prefill 还是 Decode # 如果有新请求从 waiting 进入这个 step 做 Prefill has_new_seqs any( sg.is_prefill() for sg in self.running ) ​ if has_new_seqs: # Prefill step: 处理新请求的完整输入 # Prefill 是计算密集型GPU 利用率接近 100% batch self._build_prefill_batch() else: # Decode step: 为所有 running 请求各生成一个 token # Decode 是访存密集型GPU 利用率可能只有 30% # 这就是为什么 Continuous Batching 重要: # Decode step 可以塞入更多请求来提高 GPU 利用率 batch self._build_decode_batch()为什么要分离因为 Prefill 和 Decode 的计算特性完全不同Prefill首 token 生成: 输入: prompt几百到几千 token 计算: 并行处理所有输入 token GPU 利用率: ~95% (compute-bound) 耗时: 50-200ms ​ Decode后续 token 生成: 输入: 上一步生成的 1 个 token 计算: 只处理 1 个 token GPU 利用率: ~20-30% (memory-bound) 耗时: 5-20ms ​ → Decode 时 GPU 大量空闲 → Continuous Batching 利用这些空闲周期塞入更多请求四、连续批处理的数学模型4.1 吞吐量建模吞吐量 (batch_size × 1 token) / (decode_time_per_step) ​ 传统静态批处理: batch_size 32 (固定) 每步所有 32 个请求都 decode包括已完成的空 slot decode_time ≈ 15ms (固定) 但实际有效请求平均只有 18 个 吞吐量 18 / 0.015 1200 t/s ​ 连续批处理: batch_size 动态变化 (20-128) 每步所有 running 请求都 decode都是有效请求 decode_time ≈ 15ms (固定) 平均有效 batch_size ≈ 87 吞吐量 87 / 0.015 5800 t/s4.2 GPU 利用率建模Decode step 的 GPU 时间分解: ┌─────────────────────────────────────────┐ │ Kernel 执行 4ms (27%) ← 计算 │ │ 显存读取 10ms (67%) ← 访存 │ │ Kernel Launch 1ms (6%) ← 开销 │ └─────────────────────────────────────────┘ Total: 15ms ​ GPU SM 利用率 ≈ 27% → 浪费了 73% 的 GPU 计算能力 ​ Continuous Batching 的对策: 把更多请求塞进同一个 step → 显存读取量增加 (每个请求多读 KV Cache) → 但 Kernel 执行可以 batch 化 → SM 利用率提升到 60-80%五、实测数据5.1 测试环境GPU: NVIDIA A100 80GB × 1 模型: Llama-2-7B-Chat (FP16) vLLM 版本: v0.6.0 输入长度: 512 tokens (固定) 输出长度: 128 tokens (固定)5.2 不同并发下的吞吐量┌──────────────┬──────────┬──────────┬──────────┬──────────┐ │ 并发数 │ 静态 Batch│ 动态 Batch│ 连续 Batch│ 提升 │ ├──────────────┼──────────┼──────────┼──────────┼──────────┤ │ 1 │ 42 t/s │ 48 t/s │ 48 t/s │ 1.14x │ │ 8 │ 180 t/s │ 520 t/s │ 520 t/s │ 2.89x │ │ 16 │ 280 t/s │ 1050 t/s │ 1050 t/s │ 3.75x │ │ 32 │ OOM │ 2100 t/s │ 2100 t/s │ N/A │ │ 64 │ OOM │ 3500 t/s │ 3800 t/s │ N/A │ │ 128 │ OOM │ OOM │ 5800 t/s │ N/A │ └──────────────┴──────────┴──────────┴──────────┴──────────┘关键发现低并发1-8三种策略差距不大瓶颈在计算而非显存中并发16-64动态和连续批处理远超静态高并发128只有 Continuous Batching 能扛住吞吐量达 5800 t/s5.3 变长输出场景的对比真实场景中请求的输出长度是不均匀的这最能体现 Continuous Batching 的价值测试: 100 个请求输出长度分布 [32, 256, 64, 512, 128, ...] ​ 静态批处理 (batch32): Step 0-31: 所有 32 个请求同时 Decode Step 32: Req 3 完成 (32 token) Step 64: Req 7 完成 ... Step 512: 最后一个请求完成 (512 token) → 大量 step 中有效请求远小于 32 ​ 连续批处理: Step 0-31: 32 个请求 Decode Step 32: Req 3 完成 → 从 waiting 立刻拉入 Req 33 Step 64: Req 7 完成 → 拉入 Req 34 ... → batch 始终保持满载场景静态 Batch 吞吐连续 Batch 吞吐提升等长输出 (128)1200 t/s2100 t/s1.75x变长输出 [32-512]650 t/s3200 t/s4.9x极端变长 [16-1024]380 t/s2900 t/s7.6x结论输出越不均匀Continuous Batching 的优势越明显。六、调度器实战代码6.1 完整推理循环from vllm import LLM, SamplingParams ​ # 初始化 — vLLM 自动开启 Continuous Batching llm LLM( modelmeta-llama/Llama-2-7b-chat-hf, tensor_parallel_size1, gpu_memory_utilization0.90, max_model_len4096, # 调度相关参数 block_size16, # PagedAttention 块大小 enable_prefix_cachingTrue, # 前缀缓存优化 swap_space4, # CPU swap 空间 (GB) ) ​ # 模拟变长请求 prompts [ 写一首关于秋天的诗, # 短输出 详细解释 PagedAttention 的工作原理, # 长输出 什么是 RESTful API, # 中等输出 用 Python 实现快速排序, # 中等输出 总结一下 TCP 三次握手, # 短输出 ] * 50 # 250 个请求 ​ sampling_params SamplingParams( temperature0.7, top_p0.9, max_tokens512, ) ​ # vLLM 内部自动 Continuous Batching # 250 个请求会被动态调度不需要手动分批 outputs llm.generate(prompts, sampling_params) ​ # 统计吞吐量 total_tokens sum( len(o.outputs[0].token_ids) for o in outputs ) # vLLM 会打印吞吐量统计: # Throughput: 5234.5 tokens/s6.2 流式输出场景from vllm import LLM, SamplingParams ​ llm LLM(modelmeta-llama/Llama-2-7b-chat-hf) ​ # 流式生成 — Continuous Batching 在流式场景同样生效 sampling_params SamplingParams( temperature0.7, max_tokens512, streamTrue, # 流式输出 ) ​ # 多个请求并发流式 for output in llm.generate(prompts, sampling_params, use_tqdmFalse): for chunk in output: print(chunk.outputs[0].text, end, flushTrue)七、性能调优建议7.1 最大化吞吐量的配置llm LLM( model..., gpu_memory_utilization0.92, # 尽量用满显存 max_num_seqs256, # 最大并发数影响 batch 上限 max_model_len4096, block_size16, swap_space4, # 给 swap 留空间 # 吞吐量优化: enforce_eagerFalse, # 用 CUDA Graph 减少 launch 开销 enable_prefix_cachingTrue, # 前缀缓存 max_num_batched_tokens8192, # 单 step 最大 token 数 )7.2 参数对吞吐量的影响参数推荐值影响gpu_memory_utilization0.90-0.92越高 → 可支持更多并发 → 吞吐越高max_num_seqs128-256并发上限受显存限制max_num_batched_tokens8192单 step token 上限block_size16影响显存碎片和 kernel 效率swap_space4-8 (GB)Swap 策略的缓冲空间enable_prefix_cachingTrue共享前缀的请求加速7.3 延迟 vs 吞吐量的权衡追求延迟 (交互式场景): max_num_seqs 16-32 # 小 batch每个请求更快 max_num_batched_tokens 2048 → 牺牲吞吐量换取低延迟 ​ 追求吞吐量 (批量推理): max_num_seqs 128-256 # 大 batch max_num_batched_tokens 8192 → 牺牲延迟换取高吞吐量八、总结Continuous Batching 的三个核心贡献每步动态调度每个 token 生成后都可以加入/移除请求GPU slot 不空转Prefill-Decode 分离利用 Decode step 的 GPU 空闲周期塞入更多请求抢占与恢复显存不足时优雅降级Swap 或 Recompute 都能恢复优化点贡献的吞吐量提升连续批处理vs 静态3-5xPrefill-Decode 分离1.5-2x抢占与恢复防 OOM 崩溃可用性保障这三者加上 PagedAttention 的显存优化共同构成了 vLLM 5800 t/s 的技术基石。下一篇预告下一篇进入大数据领域对 Iceberg、Hudi、Delta Lake 三大数据湖格式进行深度压测对比在写入吞吐、读取性能、Time Travel、Compaction 效率四个维度横评附选型决策树。往期回顾AI 推理优化系列—vLLM PagedAttention 解析显存利用率从 40% 提升到 90% 的秘密。llama.cpp Q4 量化原理拆解10GB 显存跑 70B 模型的秘密。Kafka acks 机制性能实测acksall 在百万级吞吐下的延迟代价有多大。GPTQ vs AWQ vs GGUF三大量化方案性能与精度横评。Kafka 深度解剖 2消费者组再均衡 Rebalance 全流程。觉得有帮助请点赞收藏。关注专栏「AI大模型大数据硬件编程」每周更新大模型推理部署的深度实战内容。

相关新闻

2026/9/3 7:42:33

电力设备红外图像数据集构建与YOLOv8目标检测实战指南

简介:本资源是面向电力智能化检测领域的专业红外图像数据集,专为计算机视觉方向的算法工程师、电力AI应用研究者及高校科研团队设计,用于训练与验证电力设备部件的目标检测模型。数据集包含3930张高质量红外图像,覆盖避雷器、断路…

2026/9/3 7:42:33

基于Python与CNN的驾驶员疲劳检测系统:从原理到毕业设计实践

简介:本资源是一套完整的驾驶员疲劳检测与预警系统毕业设计实现方案,面向计算机、人工智能及相关专业本科生,解决行车过程中实时识别闭眼、打哈欠等疲劳特征并触发声光预警的实际问题。项目基于Python开发,核心采用轻量级卷积神经…

2026/9/3 7:42:33

无模型自适应控制(MFAC)Simulink实现与工程部署指南

简介:本资源是一套面向控制理论研究者与自动化工程师的无模型自适应控制(MFAC)实践工具,聚焦于无需被控对象数学模型即可实现高鲁棒性实时调节的先进控制策略,特别适用于参数时变、建模困难的非线性系统。压缩包共含2个…

2026/9/3 7:57:34

从单次脚本到自动化系统:构建稳健网络信息采集工作流

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/3 7:57:34

无剪辑版背后:原始素材公开与演播室反应的内容分发逻辑

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/3 7:57:34

Python实现燃气轮机性能仿真:从布雷顿循环到工程实践

简介:本资源是一套面向能源动力工程专业学生与热力系统仿真初学者的燃气轮机Brayton循环Python建模实践包,聚焦热力学性能计算、参数敏感性分析与可视化呈现,解决传统教学中理论抽象、缺乏交互式仿真工具的问题。压缩包共69个文件&#xff0c…

2026/9/3 7:57:34

SEBAL模型MATLAB实现:地表蒸散发遥感反演实操指南

简介:本资源是面向水文学、遥感与GIS领域初学者及科研人员的SEBAL蒸散发估算入门实践包,聚焦地表能量平衡建模与MATLAB实现,解决遥感影像驱动的区域蒸散发定量反演问题。压缩包为RAR格式,仅含1个核心文件——SEBAL_manual.m&#…

2026/9/3 7:52:34

HDMI TX接口设计实战:从TMDS信号到PCB布局与故障排查

在实际音视频产品里,HDMI TX 接口是主控芯片往外输出画面最常用的接口之一。它表面上看就是几根差分线加一个连接器,但真正落地时,会涉及物理信号、协议时序、EDID、HPD、DDC、驱动配置和信号完整性等多个层面。很多项目在原理图阶段看不出问…

2026/9/1 16:02:17

vSound小提琴数字处理器实操指南:从接线到演出的完整配置

电小提琴或者原声小提琴插电演出,第一个绕不开的坎就是声音难听。原声琴的共鸣和空气感一旦进了拾音器,出来的往往是一坨干瘪、发尖、带着奇怪塑料味的信号。我当初第一次把琴接上乐队调音台,直接被主唱吐槽"你这声音像在锯钢丝"。…

2026/9/2 9:00:32

传感器接口IC如何攻克生物化学传感的微弱信号难题?

1. 从电极到比特流:为什么生物化学传感必须依赖专用接口IC 做生物化学传感的人都有过类似的经历:明明传感器本身性能很好,信号输出却一塌糊涂——噪声大、漂移明显、重复性差,怎么调都达不到预期。很多时候问题并不在传感器&#…

2026/9/2 8:41:06

STM32F411CEU6多通道ADC采集:扫描模式+DMA实现详解

1. 多通道 ADC 的用武之地把“Multichannel ADC”和“STM32F411CEU6”这两个关键字放在一起,其实就是嵌入式开发里最常遇到的一类需求:用一块不算贵的 MCU,同时采集多路模拟信号。STM32F411CEU6 是 48 引脚的 Cortex-M4F 主控,主频…

2026/9/3 0:02:06

零基础装 OpenClaw 小龙虾 AI:Windows 一键部署教程与避坑要点

Windows 部署 OpenClaw 完整教程|本地 AI 智能体 5 分钟落地,环境配置一次搞定 版本说明:Windows 3.1.0 / Mac 2.7.9 写在前面 近两年开源 AI 领域有一款被称作「数字员工」的工具持续走热,它就是 OpenClaw,圈内人更习…

2026/9/3 0:02:06

Hermes Agent 本地部署新方案:Windows 整合包减少依赖报错

Windows 本地部署 Hermes 太麻烦?这版一键包 5 分钟快速跑通 很多人想体验 Hermes Agent,但真正开始部署时,往往会卡在环境配置这一步。 需要安装各类依赖、调试运行环境、处理路径问题,还容易遇到命令行报错、系统拦截、文件缺…

2026/9/3 0:02:06

实测 OpenClaw 一键包,5 分钟完成本地自动化环境搭建

OpenClaw 本地 AI 自动化工具部署指南|使用一键包规避环境配置难题 痛点:部署 AI 自动化工具常常要处理 Python、Node.js 各类依赖,版本冲突、环境配置耗费大量时间,OpenClaw 提供一键安装包,降低部署门槛。 适配系统&…

2026/9/2 1:15:22

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/2 1:15:22

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/2 1:15:20

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…