目标:部署一个VLLM大模型服务,验证速度提升。
内容:
使用同一个开源大模型,分别采用 Hugging Face Transformers 原生推理和 vLLM 服务化推理,在相同硬件、相同输入长度、相同输出长度下,对比单请求延迟、并发吞吐、TTFT、TPOT 和 Tokens/s,验证 vLLM 在在线推理场景中的性能提升。
一、第九周作业目标
本周重点掌握 5 个知识点:
- 学会下载并运行一个开源大语言模型。
- 使用 Hugging Face Transformers 实现基线推理。
- 使用 vLLM 部署 OpenAI Compatible API 服务。
- 学会进行并发压测。
- 对比不同推理框架下的延迟和吞吐量。
最终实验结构:
同一个模型 │ ├── Hugging Face Transformers │ ↓ │ 普通推理基线 │ └── vLLM ↓ OpenAI API Server ↓ Benchmark ↓ 延迟 / Tokens/s / 吞吐量二、为什么要学习 vLLM?
前面的课程重点基本都是模型训练,例如:
RNN LSTM Transformer 文本分类 序列标注 文本匹配但真正把大模型应用到线上,还会遇到:
模型推理太慢 GPU 利用率不高 并发能力差 显存容易浪费比如直接使用 Transformers:
model.generate(...)一两个请求问题不大,但如果线上同时来了 10、50、100 个请求,如何高效利用 GPU 就变得非常重要。
vLLM 的定位就是:
面向大语言模型的高吞吐推理和服务框架。
三、实验整体设计
建议选择一个能在本机 GPU 上稳定运行的模型。
如果使用 RTX 4060 Laptop GPU 这类显卡,显存通常会成为主要约束,因此建议优先使用:
Qwen/Qwen2.5-1.5B-Instruct如果显存更充足,可以考虑:
Qwen 3B / 4B 7B 量化模型为了保证实验公平,最重要的是:
基线和 vLLM 必须使用同一个模型。四、实验环境建议
| 项目 | 配置 |
|---|---|
| OS | Ubuntu 22.04 / Linux |
| GPU | RTX 4060 Laptop GPU |
| CUDA | 按实际环境填写 |
| Python | 3.10 / 3.11 |
| PyTorch | 按实际版本填写 |
| vLLM | 按实际版本填写 |
| Transformers | 按实际版本填写 |
| Model | Qwen2.5-1.5B-Instruct |
| dtype | float16 / auto |
| 输入长度 | 约 128 tokens |
| 最大输出 | 128 tokens |
| Benchmark Requests | 100 / 500 |
| 并发 | 1 / 4 / 8 / 16 |
如果实验机是 Windows,建议使用:
WSL2 + Ubuntu或者直接 Linux。
五、项目结构
week9/ ├── baseline_transformers.py ├── test_vllm.py ├── benchmark_client.py ├── requirements.txt ├── results/ │ ├── baseline.json │ ├── vllm_concurrency_1.json │ ├── vllm_concurrency_4.json │ ├── vllm_concurrency_8.json │ └── vllm_concurrency_16.json └── SUMMARY.md六、第一步:检查 GPU 环境
先执行:
nvidia-smi重点查看:
GPU 型号 CUDA Version 显存大小 显存占用然后 Python 中检查:
importtorchprint("CUDA available:",torch.cuda.is_available())print("CUDA version:",torch.version.cuda)iftorch.cuda.is_available():print("GPU:",torch.cuda.get_device_name(0))七、安装环境
建议创建独立虚拟环境:
conda create-nweek9-vllmpython=3.11-yconda activate week9-vllm安装:
pipinstallvllm pipinstalltransformers accelerate openai查看版本:
python-c"import torch; print(torch.__version__)"python-c"import vllm; print(vllm.__version__)"python-c"import transformers; print(transformers.__version__)"八、实验一:Transformers 原生推理基线
创建:
baseline_transformers.py代码:
importtimeimporttorchfromtransformersimportAutoTokenizer,AutoModelForCausalLM MODEL_NAME="Qwen/Qwen2.5-1.5B-Instruct"device="cuda"tokenizer=AutoTokenizer.from_pretrained(MODEL_NAME)model=AutoModelForCausalLM.from_pretrained(MODEL_NAME,torch_dtype=torch.float16,device_map=device)model.eval()prompt="请简单介绍一下 Transformer 模型的工作原理。"messages=[{"role":"user","content":prompt}]text=tokenizer.apply_chat_template(messages,tokenize=False,add_generation_prompt=True)inputs=tokenizer(text,return_tensors="pt").to(device)# Warmupwithtorch.no_grad():_=model.generate(**inputs,max_new_tokens=32)torch.cuda.synchronize()start=time.perf_counter()withtorch.no_grad():outputs=model.generate(**inputs,max_new_tokens=128,do_sample=False)torch.cuda.synchronize()elapsed=time.perf_counter()-start input_tokens=inputs.input_ids.shape[1]output_tokens=outputs.shape[1]-input_tokens tokens_per_second=output_tokens/elapsedprint("="*50)print("Input tokens:",input_tokens)print("Output tokens:",output_tokens)print(f"Latency:{elapsed:.3f}s")print(f"Generation speed:{tokens_per_second:.2f}token/s")result=tokenizer.decode(outputs[0][input_tokens:],skip_special_tokens=True)print("\nResult:")print(result)运行:
python baseline_transformers.py注意:作业报告必须填写自己实际测试的数据。
九、为什么一定要 Warmup?
第一次推理通常包含:
CUDA 初始化 Kernel 初始化 内存分配 模型缓存因此第一次调用:
model.generate()通常不能直接作为最终性能数据。
正确流程:
Warmup ↓ 正式 Benchmark十、实验二:启动 vLLM 服务
启动:
vllm serve Qwen/Qwen2.5-1.5B-Instruct或者:
vllm serve\Qwen/Qwen2.5-1.5B-Instruct\--host0.0.0.0\--port8000默认服务地址:
http://localhost:8000十一、验证服务是否启动成功
首先:
curlhttp://localhost:8000/v1/models然后测试 Chat Completions:
curlhttp://localhost:8000/v1/chat/completions\-H"Content-Type: application/json"\-d'{ "model": "Qwen/Qwen2.5-1.5B-Instruct", "messages": [ { "role": "user", "content": "请简单介绍Transformer。" } ], "temperature": 0, "max_tokens": 128 }'十二、使用 Python 调 vLLM 服务
创建:
test_vllm.pyimporttimefromopenaiimportOpenAI client=OpenAI(base_url="http://localhost:8000/v1",api_key="EMPTY")start=time.perf_counter()response=client.chat.completions.create(model="Qwen/Qwen2.5-1.5B-Instruct",messages=[{"role":"user","content":"请简单介绍一下 Transformer 模型。"}],temperature=0,max_tokens=128)elapsed=time.perf_counter()-start usage=response.usageprint("Prompt tokens:",usage.prompt_tokens)print("Completion tokens:",usage.completion_tokens)print(f"Latency:{elapsed:.3f}s")speed=usage.completion_tokens/elapsedprint(f"Output speed:{speed:.2f}token/s")print("\nResult:")print(response.choices[0].message.content)运行:
python test_vllm.py十三、只测一个请求,可能看不出 vLLM 的优势
如果 Transformers 和 vLLM 都只跑 1 个请求,你可能发现差异并不明显。
vLLM 真正需要重点验证的是:
并发请求下的整体吞吐量。
因此建议测试:
1 并发 4 并发 8 并发 16 并发十四、核心实验:并发性能测试
| 实验 | 并发 |
|---|---|
| Test 1 | 1 |
| Test 2 | 4 |
| Test 3 | 8 |
| Test 4 | 16 |
| Test 5 | 32(显存允许时) |
每次建议:
100 个 Prompt input ≈ 128 tokens output = 128 tokens十五、自己写并发 Benchmark
创建:
benchmark_client.pyimportasyncioimporttimeimportstatisticsfromopenaiimportAsyncOpenAI MODEL="Qwen/Qwen2.5-1.5B-Instruct"client=AsyncOpenAI(base_url="http://localhost:8000/v1",api_key="EMPTY")PROMPT="请解释 Transformer 中 Self-Attention 的基本工作原理。"asyncdefrequest_once():start=time.perf_counter()response=awaitclient.chat.completions.create(model=MODEL,messages=[{"role":"user","content":PROMPT}],max_tokens=128,temperature=0)elapsed=time.perf_counter()-start output_tokens=response.usage.completion_tokensreturnelapsed,output_tokensasyncdefworker(semaphore):asyncwithsemaphore:returnawaitrequest_once()asyncdefbenchmark(total_requests,concurrency):semaphore=asyncio.Semaphore(concurrency)start=time.perf_counter()tasks=[worker(semaphore)for_inrange(total_requests)]results=awaitasyncio.gather(*tasks)total_time=time.perf_counter()-start latencies=[x[0]forxinresults]total_tokens=sum(x[1]forxinresults)throughput=total_tokens/total_time request_per_second=total_requests/total_timeprint("\n=============================")print("Concurrency:",concurrency)print("Requests:",total_requests)print(f"Total time:{total_time:.2f}s")print(f"RPS:{request_per_second:.2f}")print(f"Output throughput:{throughput:.2f}token/s")print(f"Average latency:{statistics.mean(latencies):.2f}s")print(f"P50 latency:{statistics.median(latencies):.2f}s")print(f"Max latency:{max(latencies):.2f}s")asyncdefmain():forconcurrencyin[1,4,8,16]:awaitbenchmark(total_requests=100,concurrency=concurrency)asyncio.run(main())运行:
python benchmark_client.py十六、推荐:直接使用 vLLM 官方 Benchmark
可以查看:
vllm bench serve--help通过官方 benchmark 工具,可以更标准地测试:
模型 数据集 输入长度 输出长度 请求数量 请求速率 并发吞吐十七、真正应该关注哪些指标?
1. TTFT
Time To First Token即:
用户发送请求到看到第一个 Token 的时间。
这个指标直接影响用户对“响应快不快”的感知。
2. TPOT
Time Per Output Token表示第一个 Token 出来以后,平均生成一个 Token 需要多少时间。
例如:
TPOT = 30 ms大致相当于:
≈ 33 token/s3. End-to-End Latency
完整请求耗时:
请求开始 ↓ Prompt Prefill ↓ Decode ↓ 生成结束4. Throughput
这是验证 vLLM 优势的核心指标。
计算:
Speedup = vLLM Throughput / Baseline Throughput最终应该报告:
Speedup = X.XX ×必须来自实际测试结果。
十八、为什么 vLLM 吞吐通常会提升?
1. KV Cache
Transformer 在生成过程中,过去 Token 的 Key / Value 可以缓存下来,避免重复计算。
随着:
上下文长度 ↑ 并发用户 ↑KV Cache 的显存需求也会快速增大。
2. PagedAttention
可以将 KV Cache 的管理方式类比操作系统分页:
虚拟内存 ↓ Page ↓ 按页管理相比为每个请求预留连续空间,分页方式可以更加灵活地利用 GPU 显存。
3. Continuous Batching
假设三个请求:
A:生成 20 token B:生成 200 token C:生成 50 token当 A 完成后,可以及时将新请求加入,而不是等待整个固定 Batch 都结束。
因此在并发场景中:
GPU 利用率 ↑ 吞吐量 ↑十九、不要把“速度提升”简单理解为单请求更快
更准确的结论是:
vLLM 的主要优势通常体现在大模型在线 Serving 的 GPU 利用率、并发调度和总体吞吐能力上;具体单请求延迟是否改善,以及吞吐可以提高多少,需要结合模型、GPU、输入输出长度、并发量和配置通过实测确定。
二十、实验时一定要控制变量
必须保持:
同一个模型 同一个 GPU 同一个 dtype 相同 Prompt 相同输入长度 相同输出长度 相同 temperature例如:
Model: Qwen2.5-1.5B-Instruct temperature: 0 max_tokens: 128二十一、推荐测试矩阵
建议至少完成:
Transformers ├── concurrency 1 ├── concurrency 4 ├── concurrency 8 └── concurrency 16 vLLM ├── concurrency 1 ├── concurrency 4 ├── concurrency 8 └── concurrency 16形成:
2 个推理框架 × 4 个并发级别 = 8 组实验每组建议:
Warmup 10 正式测试 100二十二、建议最终实验结果表
单请求测试
| Framework | Latency | Output Tokens | Tokens/s |
|---|---|---|---|
| Transformers | 实测 | 128 | 实测 |
| vLLM | 实测 | 128 | 实测 |
并发吞吐测试
| 并发 | Transformers | vLLM | Speedup |
|---|---|---|---|
| 1 | 实测 | 实测 | x |
| 4 | 实测 | 实测 | x |
| 8 | 实测 | 实测 | x |
| 16 | 实测 | 实测 | x |
单位统一:
output tokens / second二十三、建议额外记录 GPU 数据
压测时开另一个终端:
watch-n1nvidia-smi记录:
| Framework | GPU Util | GPU Memory |
|---|---|---|
| Transformers | 实测 | 实测 |
| vLLM | 实测 | 实测 |
这样可以进一步验证:
性能提升是否来自更高的 GPU 利用率二十四、第九周最终实验结论模板
## 实验结论 本实验尝试将开源大语言模型部署为在线推理服务, 并比较 Hugging Face Transformers 与 vLLM 在相同模型和硬件条件下的推理性能。 实验重点考察单请求延迟、TTFT、TPOT、 Output Tokens/s、Requests/s 以及不同并发条件下的吞吐量。 单请求场景下,两种方案的性能差异需要结合实际测试结果分析。 随着并发请求数量增加,vLLM 可以通过更高效的 KV Cache 管理、PagedAttention 与 Continuous Batching 提升 GPU 利用率和整体吞吐能力。 本实验说明,大模型推理系统不能只关注模型本身, 还需要关注推理框架、并发调度、显存管理以及服务吞吐量。二十五、本周进阶实验
实验一:不同并发数
1 2 4 8 16 32绘制:
Concurrency → Tokens/s曲线,观察 GPU 最大吞吐点。
实验二:不同输入长度
128 512 1024 2048观察:
TTFT 显存 Throughput变化。
实验三:不同输出长度
32 128 512观察 Decode 阶段对性能的影响。
二十六、第九周作业验收标准
建议至少完成:
- 成功启动一个 vLLM 大模型服务。
/v1/chat/completions能正常返回结果。- 完成 Transformers 原生推理基线。
- 测试并发
1 / 4 / 8 / 16。 - 记录单请求延迟。
- 记录 Requests/s。
- 记录 Output Tokens/s。
- 最好记录 TTFT / TPOT。
- 对比 GPU 显存和利用率。
- 计算实际 Speedup:
vLLM Throughput ---------------- Baseline Throughput最终得到:
Speedup = X.XX ×总结
第九周最核心的知识点不是“把模型跑起来”,而是理解为什么同一个大模型,在不同推理框架和不同并发条件下,会表现出完全不同的服务能力。
通过本次实验,可以进一步理解:
大模型部署 ↓ 推理服务 ↓ KV Cache ↓ PagedAttention ↓ Continuous Batching ↓ 并发压测 ↓ 吞吐优化这也为后续学习大模型分布式推理、Tensor Parallel、多 GPU 部署和生产级 LLM Serving 打下基础。