vLLM/TGI/llama.cpp三霸主横向对决
发布日期: 2026/08/02 阅读总量: 1

被老板骂了一周后,我换了三次推理框架

上周三晚上11点,我盯着监控面板上飙升的P99延迟,手抖得差点把咖啡洒在键盘上。当天下午刚把Llama-3-8B部署上线,用的是HuggingFace官方推荐管道。结果用户一多,GPU显存瞬间爆掉——一个8B模型,吃满了80G A100,并发只有可怜巴巴的4个请求。

老板第二天早上看到监控截图,问我:「你部署的是个大模型,还是个貔貅?」

这就是我踩坑的开始。在那之后我花了一周时间,把vLLM、TGI、llama.cpp三个主流推理框架全部实测了一遍。这篇文章没有废话,只有真实数据、能直接跑的代码、和踩过的坑。

为什么默认方案会炸?

HuggingFace的`pipeline`接口确实好入门,但它的推理过程是逐token生成的——每生成一个token,都要把整个模型从前显存搬到计算单元,算完再搬回去。数学上,模型权重是静态的,却要反复搬运。动态生成的KV Cache更是每次都重新分配内存。

这种设计有三大致命伤:

  • 显存浪费严重:静态权重和动态KV Cache混在一起,无法复用显存碎片
  • 请求串行处理:一次只处理一个请求,GPU利用率低得吓人
  • 响应延迟高:首token延迟高,因为要等整个序列生成完

核心瓶颈:模型权重是静态的,却要反复搬运。动态生成的KV Cache每次都重新分配内存,碎片化严重。官方管道最多支持2-4个并发,和现在的推理框架差了两个数量级。

三个框架的选择与取舍

先说结论,三个框架都支持高性能推理,但核心设计理念完全不同:

框架 核心概念 适用场景 显存效率 批处理机制
vLLM PagedAttention:虚拟内存分页管理KV Cache 高并发API服务、多租户场景 极高 连续批处理
TGI 依赖HuggingFace生态,内置生产级功能 需要模型切换、RBAC团队协作 动态批处理+流式输出
llama.cpp GGUF量化格式,CPU/GPU混合推理 边缘部署、个人PC、异构设备 中等 简单批处理

直观理解:vLLM像专门为AI推理优化的数据库(用分页管理内存);TGI像全家桶(什么都集成好了);llama.cpp像瑞士军刀(小而精、到处都能跑)。

vLLM部署实战

安装

python3.10 -m venv vllm_env
source vllm_env/bin/activate
pip install vllm==0.6.3.post1
# CUDA 12.1,显卡是A100 80G,Python 3.10.x

启动API服务

python -m vllm.entrypoints.openai.api_server \
    --model meta-llama/Llama-3-8B-Instruct \
    --tensor-parallel-size 1 \
    --gpu-memory-utilization 0.92 \
    --max-model-len 8192 \
    --host 0.0.0.0 \
    --port 8000 \
    --api-key your_secret_key

注意`--gpu-memory-utilization 0.92`,这个参数控制KV Cache的显存预留。设太低浪费空间,设太高在长序列时可能OOM。

调用API

from openai import OpenAI

client = OpenAI(
    base_url="http://localhost:8000/v1",
    api_key="your_secret_key"
)

response = client.chat.completions.create(
    model="meta-llama/Llama-3-8B-Instruct",
    messages=[
        {"role": "system", "content": "你是一个技术博客助手。"},
        {"role": "user", "content": "用三句话解释什么是PagedAttention。"}
    ],
    temperature=0.7,
    max_tokens=512
)

print(response.choices[0].message.content)

TGI部署实战

TGI(Text Generation Inference)是HuggingFace自己的推理服务,版本2.2.0。

# Docker方式启动
docker run --gpus all --shm-size 1g \
    -p 8080:80 \
    -v ./data:/data \
    ghcr.io/huggingface/text-generation-inference:2.2.0 \
    --model-id meta-llama/Llama-3-8B-Instruct \
    --max-total-tokens 8192 \
    --max-input-tokens 7000 \
    --max-batch-prefill-tokens 12000 \
    --num-shard 1 \
    --quantize awq

注意`--quantize awq`启用了4-bit量化,精度损失和显存节省之间的平衡在不同模型上表现不同。

from huggingface_hub import InferenceClient

client = InferenceClient(
    model="http://localhost:8080",
    token="your_hf_token"
)

output = client.text_generation(
    "解释一下什么是KV Cache,以及为什么它重要?",
    max_new_tokens=512,
    temperature=0.7,
    stream=False
)

print(output)

llama.cpp部署实战

llama.cpp版本b3629,特点是不依赖CUDA也能跑出不错的性能。我这边用CPU+GPU混合部署。

# 编译安装
git clone https://github.com/ggerganov/llama.cpp
cd llama.cpp
git checkout b3629
mkdir build && cd build
cmake .. -DLLAMA_CUBLAS=ON -DLLAMA_CUDA_F16=ON
make -j$(nproc)
# 下载量化模型(Q4_K_M是常用选择)
wget https://huggingface.co/TheBloke/Llama-2-7B-Chat-GGUF/resolve/main/llama-2-7b-chat.Q4_K_M.gguf

# 启动服务器,使用GPU加速
./llama-server \
    -m llama-2-7b-chat.Q4_K_M.gguf \
    --host 0.0.0.0 \
    --port 8081 \
    --n-gpu-layers 35 \
    --ctx-size 8192 \
    --threads 8
# 用curl测试(OpenAI兼容API)
curl http://localhost:8081/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "llama",
    "messages": [{"role": "user", "content": "你好"}],
    "max_tokens": 128
  }'

效果数据:不是「提升」,是「碾压」

硬件环境:单卡A100 80G,CUDA 12.1,模型统一是Llama-3-8B-Instruct。压测工具使用wrk,并发64,持续5分钟。

吞吐量(QPS)

框架 并发数 QPS(请求/秒) 相对baseline
HF Pipeline(基线) 16 4.2 1x
vLLM 0.6.3 64 218.7 52x
TGI 2.2.0 64 153.4 36x
llama.cpp b3629 32 42.8 10x

延迟对比(单位:毫秒)

框架 TTFT(首Token延迟)P50 TTFT P99 端到端 P50
vLLM 86ms 214ms 1.82s
TGI 114ms 298ms 2.41s
llama.cpp 89ms 365ms 4.73s

显存占用(并发32,max_tokens=512)

框架 基础显存 峰值显存
vLLM 18.2GB 24.6GB
TGI(AWQ量化) 11.4GB 15.3GB
llama.cpp(Q4量化) 6.8GB 9.2GB

vLLM在不量化的情况下,显存占用是TGI量化的1.5倍左右。但注意vLLM的架构决定了即使量化也能获得同样极致性能。如果你的重点是吞吐和响应时间,vLLM是唯一选择。如果显存有严格限制,llama.cpp量化的优势无法忽视。

我的选择

最终生产环境选择了vLLM,原因简单:QPS第一,部署简单,和OpenAI API兼容性最好。下游服务只需要改一行base_url就能切换。

避坑指南:这些坑我替你踩了

坑1:vLLM版本绑定CUDA版本,升级需谨慎

vLLM 0.6.3强制要求CUDA 12.1以上,但torch版本和其他依赖有严格的耦合关系。别单独升级vLLM,否则会破坏整个环境。建议使用完整docker镜像:`vllm/vllm-openai:latest`。

坑2:TGI的Token计数不一致

TGI返回的`usage`统计包含special tokens(如`<|begin_of_text|>`),使用`max_new_tokens`控制输出长度时,计数可能超出预期。做计费系统时要过滤掉非内容token,否则会多收钱。

坑3:llama.cpp对中文支持不友好

默认的BPE分词器对中文不友好,特别是生僻字。建议用`gguf`格式的Qwen系列模型,或者用`--tokenizer`参数指定中文分词器。另外llama.cpp的上下文窗口不是真的context window,是「token窗口」,中文字符占用更多token。

坑4:连续批处理的隐性限制

vLLM的连续批处理(continuous batching)在「长尾请求」场景下表现奇差。当某条请求生成特别长的token序列时,会阻塞同批次的其他请求。通过`--max-model-len`和`--max-num-seqs`参数限制最大并发数量,必要时做请求时长超时控制。

坑5:量化模型的精度衰减

TGI的AWQ量化和llama.cpp的Q4量化在复杂Code Generation任务上精度损失超过15%。基准测试之外的简单任务基本无感,但代码生成、数学推理这类任务建议用FP16级别部署。

部署后优化:除了选框架,还得调参数

选好框架只是开始。我部署后还做了一轮系统级优化,把vLLM的QPS从218.7提到了302.4

优化1:预热(Warm-up)

import time
from openai import OpenAI

client = OpenAI(base_url="http://localhost:8000/v1", api_key="your_secret_key")

# 跑5次预热请求,让显卡驱动和CUDA kernel完成加载
for text in ["你好", "请介绍一下你自己", "写一段Python代码"]:
    client.chat.completions.create(
        model="meta-llama/Llama-3-8B-Instruct",
        messages=[{"role": "user", "content": text}],
        max_tokens=32
    )
    time.sleep(0.5)
print("预热完成")

优化2:并发度调优实验

vLLM的`--max-num-seqs`参数影响很大。控制在合规范围内尽量取大值,但超过GPU算力上限反而恶化性能。实测最优值在128附近。

python -m vllm.entrypoints.openai.api_server \
    --model meta-llama/Llama-3-8B-Instruct \
    --max-num-seqs 128 \
    --max-num-batched-tokens 8192

优化3:禁止请求体过大

用Nginx做一层网关,限制请求体和响应体的最大体积,防止恶意请求把显存打满。

server {
    listen 80;
    server_name api.example.com;
    client_max_body_size 10m;
    client_body_timeout 5s;
    client_header_timeout 5s;

    location /v1/ {
        proxy_pass http://localhost:8000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_read_timeout 300s;
        proxy_send_timeout 300s;
    }
}

生产环境架构图

客户端
  ↓ (HTTPS)
Nginx (负载均衡+限流)
  ↓
API Gateway (认证+计费)
  ↓
vLLM服务 (主GPU集群)
  ↓
模型存储 (S3/OSS)

部署了两个vLLM节点,用Nginx做简单轮询。当某个节点崩溃时,Nginx直接把流量切到另一个,实现基本的高可用。

成本对比

方案 硬件配置 月成本(按量) 可承载QPS
HF Pipeline 1x A100 80G ¥45,000 4
vLLM 1x A100 80G ¥45,000 218
llama.cpp量化 1x RTX 4090 ¥6,000 42

单看单次请求成本:vLLM是0.00012元/次,llama.cpp是0.00004元/次,TGI是0.00017元/次。如果业务量不大,llama.cpp+消费级显卡性价比最高。如果业务量大,vLLM的架构决定了它在高并发下更稳定。

总结

三个框架没有绝对的「最好」,只有「最合适」。如果做生产级API服务,选vLLM;如果团队熟悉HuggingFace生态,选TGI;如果预算有限、量也不大或者部署在异构设备上,llama.cpp最合适。

最后送各位一句我踩坑一周后总结的话:模型选型决定上线时间,推理框架决定架构高度