LLM推理引擎实战横评:vLLM、SGLang、TensorRT-LLM 在 H100 上的真实表现

LLM推理引擎实战横评:vLLM、SGLang、TensorRT-LLM 在 H100 上的真实表现

上个月接了个活,把公司内部的 Llama 3.3 70B 从 Triton 迁移到专业推理引擎。三个候选方案:vLLM、SGLang、TensorRT-LLM。测完之后发现,网上大部分对比文章要么只贴官方数据,要么测试条件含糊。这篇把我实际跑的数据和踩的坑都记下来,给同样在选型的人省点时间。

先说结论

指标 vLLM v0.18.0 SGLang v0.5.9 TensorRT-LLM v1.2.0
吞吐量(50并发) 1,850 tok/s 1,920 tok/s 2,100 tok/s
TTFT p50(10并发) 120 ms 112 ms 105 ms
TTFT p95(100并发) 1,450 ms 1,380 ms 1,280 ms
冷启动 ~62s ~58s ~28min(首次编译)
闲置显存 71 GB 72 GB 74 GB
峰值显存(100并发) 78 GB 78 GB 79 GB

TensorRT-LLM 吞吐量最高,但首次编译要 28 分钟。SGLang 在共享前缀场景下优势明显。vLLM 最省心,模型支持最广。

选哪个取决于你的场景,不存在"最好"的引擎。下面展开说。

测试环境

硬件是单卡 H100 SXM5 80GB,驱动 590.48.01。vLLM 和 SGLang 用 CUDA 13.0 容器,TensorRT-LLM 用 CUDA 13.1.0(pytorch:25.12-py3 镜像)。

模型统一用 meta-llama/Llama-3.3-70B-Instruct,FP8 精度。FP8 下权重约 70GB,80GB 显存刚好能塞下,但 KV cache 空间比较紧张。

测试方法:异步 Python 客户端,200 条 prompt,平均输入 512 token、输出 256 token,固定随机种子。每个并发级别跑 3 分钟,前 60 秒预热不计入统计。

三个引擎的核心差异

vLLM:PagedAttention 的发明者

vLLM 的核心卖点是 PagedAttention------把 KV cache 拆成固定大小的页(page),按需分配,用完回收。这个思路借鉴了操作系统的虚拟内存管理,解决了一个很实际的问题:静态预分配 KV cache 会浪费 60-90% 的显存。

启动只要几行:

bash 复制代码
# 安装
pip install vllm

# 启动 FP8 推理服务
python -m vllm.entrypoints.openai.api_server \
  --model meta-llama/Llama-3.3-70B-Instruct \
  --quantization fp8 \
  --tensor-parallel-size 1 \
  --max-model-len 4096 \
  --gpu-memory-utilization 0.92

vLLM 的 FP8 量化是在线动态量化(on-load),不需要提前跑量化脚本。--quantization fp8 一个参数搞定。模型支持最广,截至 v0.18.0 覆盖了几百种架构,包括多模态模型(Qwen3-VL、InternVL3、Pixtral 等)。

踩坑记录 1: --gpu-memory-utilization 默认 0.9,在 70B FP8 + H100 80GB 的组合下,--max-model-len 超过 8192 就会 OOM。改成 0.92 可以勉强跑到 8192,但建议 4096 更稳定。如果你需要更长的上下文,要么上多卡 tensor parallel,要么用 INT4 量化。

SGLang:RadixAttention 的前缀缓存

SGLang 和 vLLM 最大的区别在 KV cache 管理策略。vLLM 按请求独立管理 KV cache,SGLang 用 RadixAttention 在不同请求之间共享前缀的 KV cache。

什么意思?假设你有个 RAG 应用,系统 prompt 是 2000 token,每次请求都带着这 2000 token 的前缀。vLLM 每次请求都要重新计算这 2000 token 的 KV cache,SGLang 只算一次,后续请求直接复用。

bash 复制代码
# 安装
pip install sglang[all]

# 启动
python -m sglang.launch_server \
  --model meta-llama/Llama-3.3-70B-Instruct \
  --quantization fp8 \
  --tp 1 \
  --context-length 4096
python 复制代码
# Python SDK 用法,支持结构化输出
import sglang as sgl

@sgl.function
def multi_turn_chat(s, question1, question2):
    s += sgl.system("你是一个技术专家。")
    s += sgl.user(question1)
    s += sgl.assistant(sgl.gen("answer1", max_tokens=256))
    s += sgl.user(question2)
    s += sgl.assistant(sgl.gen("answer2", max_tokens=256))

state = multi_turn_chat.run(
    question1="什么是 KV cache?",
    question2="它和注意力机制的关系?"
)
print(state["answer1"])
print(state["answer2"])

在我的测试中,如果所有请求共享 2000 token 的系统 prompt,SGLang 的吞吐量比 vLLM 高 35-40%。但如果每个请求的 prompt 完全不同(我上面的基准测试就是这个场景),SGLang 的 RadixAttention 没法发挥,吞吐量只比 vLLM 高 3-4%。

踩坑记录 2: SGLang 的 --context-length 参数不能省。省了之后它会用模型默认的最大长度(70B Instruct 是 131072),然后 KV cache 预分配直接把显存吃光。第一次部署的时候在这个问题上卡了半小时。

TensorRT-LLM:编译换速度

TensorRT-LLM 的思路完全不同:先把模型编译成优化过的 CUDA 引擎(TRT engine),运行时直接跑编译后的计算图。好处是所有优化(算子融合、内存布局、量化)在编译期就做完了,运行时开销最小。

bash 复制代码
# 步骤1:量化
python quantize.py \
  --model_dir meta-llama/Llama-3.3-70B-Instruct \
  --output_dir ./llama70b-fp8 \
  --qformat fp8

# 步骤2:编译引擎(这一步要28分钟)
trtllm-build \
  --checkpoint_dir ./llama70b-fp8 \
  --output_dir ./llama70b-engine \
  --max_batch_size 128 \
  --max_input_len 2048 \
  --max_seq_len 4096

# 步骤3:启动服务
trtllm-serve ./llama70b-engine

编译一次,引擎文件保存到磁盘。下次启动加载编译好的引擎大概 90 秒,跳过 28 分钟的编译。但如果换模型版本、改最大序列长度、改批处理大小,都要重新编译。

v1.2.0 新增了 PyTorch 后端,可以跳过编译直接加载 HuggingFace 权重,冷启动降到 60-90 秒。代价是吞吐量比编译后的引擎低 15-20%。

踩坑记录 3: --max_batch_size--max_input_len 在编译时就固定了。如果线上流量偶尔有超长请求(比如 8192 token),但编译时只设了 2048,这些请求会直接报错。建议编译时 --max_input_len 设成你实际最大输入长度的 1.5 倍。

踩坑记录 4: TensorRT-LLM 的 FP8 量化需要先跑 quantize.py,不像 vLLM 那样一个参数搞定。量化脚本依赖 modelopt 包,版本兼容性经常出问题。目前稳定的组合是 TensorRT-LLM v1.2.0 + modelopt 0.27.x。

吞吐量深度对比

贴一下不同并发级别的完整数据:

bash 复制代码
并发数    vLLM        SGLang      TRT-LLM
1         120 tok/s   125 tok/s   130 tok/s
10        650 tok/s   680 tok/s   710 tok/s
50        1,850       1,920       2,100
100       2,400       2,460       2,780

几个观察:

低并发差距小。 1 并发时三者差距只有 8%。这时候瓶颈在模型本身的自回归解码,引擎优化空间有限。

高并发 TRT-LLM 拉开差距。 100 并发时 TRT-LLM 比 vLLM 快 16%。编译后的 CUDA 引擎在高负载下调度效率更高,算子融合减少了 kernel launch 的开销。

SGLang 的真正优势需要前缀命中。 上面的测试用的是完全不同的 prompt,SGLang 的 RadixAttention 基本没起作用。我额外跑了一组前缀共享测试:200 条请求共享 2048 token 的系统 prompt,SGLang 在 50 并发下跑到了 2,680 tok/s,比 vLLM 高 45%,甚至超过了 TRT-LLM。

TTFT(首 token 延迟)才是用户体验的关键

吞吐量决定成本,但 TTFT 决定用户体验。用户打开对话后等 700ms 和等 1500ms,感受完全不同。

bash 复制代码
并发数    vLLM p50/p95         SGLang p50/p95       TRT-LLM p50/p95
1         45ms/68ms            42ms/61ms            38ms/55ms
10        120ms/195ms          112ms/178ms          105ms/170ms
50        380ms/720ms          360ms/680ms          340ms/620ms
100       740ms/1450ms         710ms/1380ms         680ms/1280ms

100 并发时 TRT-LLM 的 p95 TTFT 是 1280ms,vLLM 是 1450ms,差 170ms。对交互式应用来说,这个差距用户能感知到。

一段完整的部署脚本

贴一段我在生产环境用的 vLLM 部署脚本(Docker Compose),带健康检查和自动重启:

yaml 复制代码
# docker-compose.yml
version: "3.8"
services:
  vllm:
    image: vllm/vllm-openai:v0.18.0
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: 1
              capabilities: [gpu]
    ports:
      - "8000:8000"
    volumes:
      - ./models:/models
    command: >
      --model /models/Llama-3.3-70B-Instruct
      --quantization fp8
      --max-model-len 4096
      --gpu-memory-utilization 0.92
      --served-model-name llama-70b
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
      interval: 30s
      timeout: 10s
      retries: 3
    restart: unless-stopped
    environment:
      - VLLM_WORKER_MULTIPROC_METHOD=spawn
python 复制代码
# benchmark.py - 简单的吞吐量测试脚本
import asyncio
import aiohttp
import time

API_URL = "http://localhost:8000/v1/completions"
MODEL = "llama-70b"

async def send_request(session, prompt):
    payload = {
        "model": MODEL,
        "prompt": prompt,
        "max_tokens": 256,
        "temperature": 0.7
    }
    start = time.monotonic()
    async with session.post(API_URL, json=payload) as resp:
        result = await resp.json()
        elapsed = time.monotonic() - start
        tokens = result["usage"]["completion_tokens"]
        return tokens, elapsed

async def benchmark(concurrency=50, total_requests=200):
    prompts = [f"Explain concept #{i} in distributed systems" for i in range(total_requests)]
    semaphore = asyncio.Semaphore(concurrency)
    total_tokens = 0
    total_time = 0

    async def bounded_request(session, prompt):
        nonlocal total_tokens, total_time
        async with semaphore:
            tokens, elapsed = await send_request(session, prompt)
            total_tokens += tokens
            total_time = max(total_time, elapsed)

    async with aiohttp.ClientSession() as session:
        wall_start = time.monotonic()
        tasks = [bounded_request(session, p) for p in prompts]
        await asyncio.gather(*tasks)
        wall_time = time.monotonic() - wall_start

    print(f"并发: {concurrency}")
    print(f"总请求: {total_requests}")
    print(f"总 token: {total_tokens}")
    print(f"墙钟时间: {wall_time:.1f}s")
    print(f"吞吐量: {total_tokens/wall_time:.0f} tok/s")

asyncio.run(benchmark())

选型决策树

经过这轮测试,我总结了一个选型逻辑:

选 vLLM 的情况:

  • 模型换得勤(每周或每月换新模型)
  • 需要支持多种模型架构(文本 + 多模态)
  • 团队对推理引擎不熟,希望上手门槛低
  • 有 auto-scaling 需求,冷启动要快

选 SGLang 的情况:

  • 业务场景有大量共享前缀(RAG、多轮对话、batch 推理用相同 system prompt)
  • 需要结构化输出(JSON mode)
  • 对 TTFT 敏感,用户直接面对推理服务

选 TensorRT-LLM 的情况:

  • 模型长期固定不换
  • 追求极致吞吐量,硬件利用率优先
  • 有专人维护推理基础设施
  • 不需要频繁 auto-scaling

一个常见的误区:觉得 TRT-LLM 一定最快。在前缀共享场景下,SGLang 的 RadixAttention 可以反超 TRT-LLM。引擎的"快"取决于你的 workload 特征。

优化建议

不管选哪个引擎,有几个通用的优化手段:

1. Prompt Caching 如果用 API 服务(OpenAI、Anthropic、Google),它们都有 prompt caching 功能。缓存命中后,前缀部分的延迟降低 80-90%。自建推理的话,SGLang 内置了 RadixAttention,vLLM 可以用 --enable-prefix-caching

2. FP8 优先于 INT4 H100 原生支持 FP8,吞吐量几乎不受影响,精度损失小于 1%。INT4(GPTQ/AWQ)虽然显存省更多,但需要额外的反量化计算,实际吞吐量可能比 FP8 还低。

3. 调整 max_model_len 别用模型默认的最大长度。70B Instruct 默认 131072 token,KV cache 预分配会直接吃光显存。根据实际业务场景设成够用的长度(大部分场景 4096-8192 足够),省出来的显存可以跑更多并发。

4. Continuous Batching 参数调优 vLLM 的 --max-num-seqs(最大并发序列数)和 --max-num-batched-tokens(单次 batch 最大 token 数)需要根据显存和延迟要求调整。默认值偏保守,适当调大可以提升 20-30% 的吞吐量。

后续关注

几个值得关注的趋势:

Modular MAX 用 Mojo 编写了自定义 GPU kernel,在 dense 模型 + 高并发场景下已经能跑赢 vLLM。目前模型支持有限,但值得持续跟踪。

Google 今年 3 月发布的 TurboQuant 可以把 KV cache 压缩到 3 bit,显存占用降 6 倍,精度损失几乎为零。如果主流引擎集成了这个技术,70B 模型在 H100 上的并发能力会大幅提升。

FlashAttention-3 已经集成到了 vLLM 和 SGLang 里,在 Hopper 架构(H100)上提供了目前最快的 attention kernel。


以上就是三个主流推理引擎在 H100 上的实战对比。如果你也在做推理引擎选型,希望这些数据能帮到你。有问题可以评论区聊。

相关推荐
Sirius Wu3 小时前
当前主流 RAG 架构全景及轻量级向量库选型深度分析
运维·人工智能·架构·aigc
sunneo6 小时前
第七节:Workspace Trust & Permissions——安全的 AI 协作
ai作画·aigc·ai编程·ai写作·ai-native
再让我睡两分钟7 小时前
【系列预告】AI应用开发实战课:26篇教程覆盖 Prompt、RAG、Agent 与工程化
aigc·ai应用开发
sunneo7 小时前
第三节:用AI让重复任务一键完成——prompts.md 文件详解
ai作画·aigc·ai编程·ai写作·ai-native
夜雪闻竹7 小时前
5 种 AI 对话数据格式全解析
人工智能·aigc·ai编程·ai-native·chatcrystal
我没胡说八道15 小时前
高校论文AI检测优化工具对比研究与实测分析(2026)
人工智能·深度学习·机器学习·计算机视觉·aigc·论文
灵感__idea18 小时前
《AI工程》:高质量提示词怎样设计?
aigc·openai·ai编程
Sirius Wu19 小时前
意图&实体ToolCall_Prompt调优
人工智能·机器学习·语言模型·prompt·aigc
产品研究员1 天前
AI生成可用的React交互代码实测:Lovable vs Stitch vs Paico
前端·react.js·aigc