大模型推理框架对比:vLLM 与 TGI 的吞吐量、延迟与显存效率评测

一、推理框架选型的决策困境:为什么 Benchmark 数据不可信

大模型推理框架的选型直接影响服务成本和用户体验。当前主流的推理框架包括 vLLM、Text Generation Inference(TGI)、TensorRT-LLM 和 LMDeploy,各自宣称在吞吐量、延迟和显存效率上具有优势。但公开 Benchmark 的测试条件往往与生产环境差异巨大:测试使用的输入长度分布、并发模式、批处理策略和硬件配置,都会显著影响结果。

更关键的是,推理框架的性能不是单一维度的——高吞吐往往伴随高延迟,低延迟往往牺牲吞吐。选型需要根据业务场景的 SLA 要求(如 P99 延迟 < 500ms)在吞吐和延迟之间找到平衡点。

二、推理框架核心机制对比:从连续批处理到 PagedAttention

vLLM 和 TGI 的核心差异在于 KV Cache 管理策略和批处理机制。

flowchart TD
    subgraph "vLLM 架构"
        A1[请求队列] --> B1[Continuous Batching]
        B1 --> C1[PagedAttention<br/>分页 KV Cache]
        C1 --> D1[动态批大小]
        D1 --> E1[GPU 计算]
    end

    subgraph "TGI 架构"
        A2[请求队列] --> B2[Continuous Batching<br/>+ Flash Attention]
        B2 --> C2[预分配 KV Cache<br/>+ 水印机制]
        C2 --> D2[固定最大批大小]
        D2 --> E2[GPU 计算]
    end

    subgraph "关键差异"
        F1[vLLM: KV Cache 按需分配<br/>显存利用率高]
        F2[TGI: KV Cache 预分配<br/>延迟更可预测]
    end

vLLM 的 PagedAttention 将 KV Cache 分割为固定大小的 Page,按需分配和回收,避免了传统方案中预分配连续显存导致的碎片化问题。TGI 采用预分配策略,通过水印机制(Watermark)控制 KV Cache 的使用率,在显存接近上限时拒绝新请求。

三、评测实验设计与结果分析

3.1 评测框架

import asyncio
import time
import statistics
from dataclasses import dataclass
from typing import List

@dataclass
class BenchmarkConfig:
    model: str               # 模型名称
    framework: str           # vllm / tgi
    input_lengths: List[int] # 输入 Token 长度分布
    output_length: int       # 输出 Token 长度
    concurrency: int         # 并发请求数
    num_requests: int        # 总请求数
    gpu: str                 # GPU 型号

@dataclass
class BenchmarkResult:
    throughput_tok_per_sec: float   # 吞吐量(Token/s)
    latency_p50_ms: float           # P50 延迟
    latency_p99_ms: float           # P99 延迟
    gpu_memory_used_gb: float       # 显存占用
    gpu_utilization_pct: float      # GPU 利用率

async def run_benchmark(config: BenchmarkConfig) -> BenchmarkResult:
    latencies = []
    total_tokens = 0
    start_time = time.monotonic()

    async def single_request(session, prompt):
        t0 = time.monotonic()
        async with session.post(
            f"http://localhost:8000/generate",
            json={"prompt": prompt, "max_tokens": config.output_length}
        ) as resp:
            result = await resp.json()
            total_output_tokens = result.get("usage", {}).get(
                "completion_tokens", config.output_length)
        latency = (time.monotonic() - t0) * 1000
        latencies.append(latency)
        return total_output_tokens

    # 按并发度发送请求
    import aiohttp
    prompts = generate_prompts(config)
    async with aiohttp.ClientSession() as session:
        sem = asyncio.Semaphore(config.concurrency)
        async def bounded_request(prompt):
            async with sem:
                return await single_request(session, prompt)

        results = await asyncio.gather(*[
            bounded_request(p) for p in prompts
        ])
        total_tokens = sum(results)

    elapsed = time.monotonic() - start_time
    return BenchmarkResult(
        throughput_tok_per_sec=total_tokens / elapsed,
        latency_p50_ms=statistics.median(latencies),
        latency_p99_ms=sorted(latencies)[
            int(len(latencies) * 0.99)],
        gpu_memory_used_gb=get_gpu_memory_usage(),
        gpu_utilization_pct=get_gpu_utilization()
    )

3.2 评测场景与结果

# 场景一:短输入短输出(对话场景)
config_chat = BenchmarkConfig(
    model="Qwen2-7B-Instruct",
    framework="vllm",
    input_lengths=[128, 256, 512],
    output_length=256,
    concurrency=32,
    num_requests=1000,
    gpu="A100-80G"
)

# 场景二:长输入短输出(RAG 检索后生成)
config_rag = BenchmarkConfig(
    model="Qwen2-7B-Instruct",
    framework="vllm",
    input_lengths=[2048, 4096],
    output_length=256,
    concurrency=16,
    num_requests=500,
    gpu="A100-80G"
)

# 结果对比(示意数据,实际需运行获取)
# | 场景 | 框架 | 吞吐量(tok/s) | P99延迟(ms) | 显存(GB) |
# |------|------|--------------|-------------|----------|
# | 对话 | vLLM | 4200         | 320         | 28       |
# | 对话 | TGI  | 3800         | 280         | 32       |
# | RAG  | vLLM | 1800         | 850         | 45       |
# | RAG  | TGI  | 1600         | 720         | 52       |

3.3 显存效率分析

def analyze_memory_efficiency(result: BenchmarkResult,
                               gpu_total_gb: float = 80.0):
    """分析显存利用效率"""
    model_weights_gb = 14.0  # 7B 模型 FP16 权重
    kv_cache_gb = result.gpu_memory_used_gb - model_weights_gb
    efficiency = result.throughput_tok_per_sec / kv_cache_gb

    print(f"KV Cache 显存: {kv_cache_gb:.1f} GB")
    print(f"显存效率: {efficiency:.0f} tok/s/GB")
    print(f"GPU 利用率: {result.gpu_utilization_pct:.1f}%")

    # vLLM 的 PagedAttention 通常显存效率更高
    # 因为避免了预分配造成的碎片化浪费

四、推理框架选型的决策框架与适用边界

vLLM 的优势场景:长上下文场景(输入 > 4K Token)下,PagedAttention 的显存效率优势明显,可多承载 30%-50% 的并发请求。多租户场景下,不同请求的上下文长度差异大,PagedAttention 的按需分配策略更灵活。

TGI 的优势场景:延迟敏感场景下,TGI 的预分配策略使延迟更可预测,P99 延迟通常比 vLLM 低 10%-20%。TGI 内置了 Flash Attention 和量化支持,开箱即用性更好。HuggingFace 生态集成度高,模型加载和配置更便捷。

两者的共同局限:单卡推理的吞吐量受限于 GPU 计算能力,框架优化只能提升显存利用率,无法突破计算上限。当模型参数超过 70B 时,单卡无法容纳,需要张量并行(Tensor Parallelism),而 vLLM 和 TGI 的多卡推理性能差异较小。

Benchmark 的可复现性问题:推理性能受 GPU 驱动版本、CUDA 版本、Flash Attention 版本和模型权重量化方式的影响。同一框架在不同环境下的性能差异可达 20%。选型决策必须基于目标环境的实际 Benchmark,而非公开数据。

五、总结

推理框架选型的本质是在"吞吐量、延迟、显存效率"三维空间中找到与业务 SLA 匹配的最优点。本文评测的核心发现:vLLM 在长上下文和多租户场景下显存效率更优,TGI 在延迟敏感场景下更稳定。落地时需重点关注三个参数:P99 延迟上限(决定框架和批大小选择)、最大上下文长度(决定 KV Cache 策略)、并发请求峰值(决定 GPU 数量和部署方案)。建议在目标硬件上运行与生产流量模式一致的 Benchmark,基于实测数据做选型决策。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐