实验二:推理吞吐量与 Continuous Batching 压测
1. 实验二:基础实验
注:
- 本实验所有机器均属于AutoDL 平台
www.autodl.com,执行前需自行注册账号并完成相关认证。- Happy Path:正常情况
- Sad Path:异常情况
1.1 第一阶段:实例租赁与环境准备
目标:获取一台 24GB 显存的机器(RTX 4090 为佳),并配置 PyTorch 环境。
-
租赁实例:
- 算力选型:在 AutoDL 算力市场,选择 RTX 4090 (24GB)。如果为了对比测试 OOM,也可以选 RTX 3090 (24GB) 或更小的卡(如 A4000 16GB,更容易触发 OOM)。
- 镜像选择:vLLM 对环境要求较高,建议选择 PyTorch 2.1.2 + CUDA 12.1 或 PyTorch 2.3.0 + CUDA 12.1 的镜像。
- 启动:开机后,点击“JupyterLab”进入控制台。
-
打开终端 (Terminal):
在 JupyterLab 页面底部点击“终端”图标。
-
安装依赖库:
hljs bash
1.2 第二阶段:模型下载(解决国内下载慢的问题)
目标:为了给 KV Cache 留出更多显存以测试“吞吐量极限”,我们使用 Int4 版本的 Qwen2.5-7B。
-
创建下载脚本:
在终端输入以下命令创建一个下载脚本:
hljs bash -
编辑并运行下载代码:
双击左侧文件列表中的 download_model.py,粘贴以下代码:
hljs python -
执行下载:
hljs bash记录下终端输出的模型最终路径(例如 /root/autodl-tmp/qwen/Qwen2-5-7B-Instruct),下一步代码要用。
1.3 第三阶段:构建实验代码 (The Experiment)
目标:将逻辑转化为可执行的 Python 脚本
-
创建主实验脚本:
hljs bash -
编写代码(请将 model_path 替换为你上一步下载的实际路径):
hljs pythonhljs output
1.4 第四阶段:执行与观测
你需要同时看代码输出和系统监控。
-
开启系统级监控(分屏):在 JupyterLab 中,新建第二个终端窗口。输入以下命令,实时观察 GPU 显存变化:
hljs bash -
运行实验:回到第一个终端,运行 Python 脚本:
hljs bash -
实验结果示例:点击示例代码右上角的执行按钮,查看示例结果
1.5 第五阶段:实验结果分析指南
当脚本运行时,请重点观察以下数据,验证“理论背景”
- 看 Happy Path 汇总表:
- 随着并发从 1 增加到 64,TPS 应该会快速上升,然后趋于平缓(这就是 vLLM 的 Continuous Batching 在发挥作用)。
- P99 TTFT 应该保持相对稳定,只有轻微增加。
- 看 Sad Path 结果:
- 对比 HappyPath (并发64) 和 SadPath_Burst (并发128)。
- 预期现象:Sad Path 的 P99 TTFT 可能会显著飙升(例如从 50ms 变成 500ms+)。这是因为并发超过了 GPU 的瞬时处理能力,请求被迫在调度队列中排队,导致“首字”迟迟出不来。
2. 实验二:进阶实验
为什么需要进阶实验:基础实验考虑了“量”的堆积,但忽略了“质”的冲突。在生产环境中,最可怕的不是请求多,而是请求长短不一。
2.1 进阶 1:长短文混合与 Chunked Prefill (The Prefill Bottleneck)
- 缺失点:Continuous Batching 解决了解码(Decode)阶段的空泡,但没解决预填充(Prefill)阶段的阻塞。如果一个超长 Prompt(比如 4000 token)进来,GPU 会全力计算它的 KV Cache,导致后面排队的 100 个短请求全部卡住,TTFT 瞬间爆炸。
- 操作:
- 构造混合流量:90% 的短请求(User: "你好") + 10% 的长文档总结请求(4k Context)。
- 开启/关闭 Chunked Prefill:这是 vLLM 的高级特性,将长 Prompt 切成小块分批计算,允许短请求“插队”。
- 观察:对比开启前后的 P99 TTFT。
- 锐评:这是 N 卡和国产卡拉开差距的地方。Chunked Prefill 需要极高频的 Kernel 切换,如果驱动层面的 Kernel Launch Latency 过高(这是国产驱动的通病),切分反而会导致总吞吐下降。
实验逻辑:我们模拟 1 个超长请求 (4K Context) 正在处理(Prefill 阶段),此时突发 20 个短请求。
- Case A (关):短请求必须等待长请求算完 KV Cache 才能开始,TTFT 会极高。
- Case B (开):长请求被切碎,短请求可以插队,TTFT 显著降低。
执行步骤
-
创建文件
exp2a_prefill_OFF.py(对照组:关闭特性)目标:测试短请求必须等待长请求算完 KV Cache 才能开始,TTFT 会极高。
hljs pythonhljs output -
运行文件
hljs bash -
观察结果:在页面中点击示例代码右上角的执行按钮,查看示例结果
-
创建文件
exp2a_prefill_ON.py(实验组:打开特性)目标:测试长请求被切碎,短请求可以插队,TTFT 显著降低。
hljs pythonhljs output -
观察结果
2.2 进阶 2:显存换入换出 (Swapping Penalty)
- 缺失点:你在 Sad Path 中提到了 KV Cache 耗尽,但 vLLM 不会直接崩,而是会触发 Swap-out(把 KV Block 搬到 CPU 内存)。
- 操作:
- 极度压榨显存:设置 gpu_memory_utilization = 0.95。
- 发送超过显存容量的并发长文本。
- 观察:
- 监控 musa-smi 或 nvidia-smi 的 PCIe 带宽利用率。
- 观察推理速度是否出现“波浪式”骤降(GPU 等待 CPU 数据搬回的时刻)。
- 技术解剖:这时候拼的不是算力,是 PCIe 4.0/5.0 的带宽。如果国产 CPU 搭配的 PCIe 控制器弱,这里就是瓶颈。
实验逻辑:设置 gpu_memory_utilization = 0.95,然后疯狂发送长文本,直到 vLLM 被迫将 KV Block 换出到 CPU RAM。此时通过观察 Token 生成间隔(TPOT)的抖动来验证 PCIe 带宽。
执行步骤
-
创建文件
exp3_swapping_extreme.py目标:测试 PCIe 瓶颈导致的 "Stalling" (停顿)。
hljs pythonhljs output -
运行文件(如果之前运行过 vLLM 脚本,必须重启 Kernel 或在终端 kill 掉之前的 python 进程,因为 vLLM 占住显存不放)
hljs bash -
观察结果:在页面中点击示例代码右上角的执行按钮,查看示例结果
3. 实验总结与核心知识点
3.1【核心结论】
一句话结论
3.2【技术解剖:显存三态】
- 技术1:xxxx
3.3【关键概念 (Knowledge Points)】
- 概念1:xxxx
一句话总结