大模型推理引擎对比:vLLM、TensorRT-LLM与SGLang的性能基准分析

一、推理引擎的核心竞争力

大模型推理的性能瓶颈不是算力,而是显存带宽和KV Cache管理效率。单张A100的算力为312 TFLOPS(FP16),但HBM带宽仅为2TB/s。对于自回归生成任务,每生成一个token都需要访问整个KV Cache,本质上是访存密集型(memory-bound)而非计算密集型(compute-bound)。

推理引擎的价值就在于:如何用更少的显存存储更多的KV Cache(提高并发),如何在有限的显存带宽下提高token生成速度(降低延迟),以及如何在并发和延迟之间找到最优平衡点。vLLM、TensorRT-LLM和SGLang分别从不同角度解决了这些问题。

graph TD
    subgraph vLLM
        V1[PagedAttention] --> V2[KV Cache分页管理]
        V2 --> V3[显存利用率提升2-4倍]
        V4[Continuous Batching] --> V5[动态合并请求]
        V5 --> V6[吞吐量提升5-10倍]
    end
    
    subgraph TensorRT-LLM
        T1[图编译优化] --> T2[算子融合+Kernel调优]
        T2 --> T3[NVIDIA GPU极致性能]
        T4[In-flight Batching] --> T5[请求级调度]
        T5 --> T6[延迟降低30-50%]
    end
    
    subgraph SGLang
        S1[RadixAttention] --> S2[前缀共享缓存]
        S2 --> S3[多轮对话场景5倍加速]
        S4[Structured Output] --> S5[约束解码]
        S5 --> S6[JSON/Regex输出保证]
    end
    
    style vLLM fill:#7B1FA2,color:#fff
    style TensorRT-LLM fill:#76B900,color:#000
    style SGLang fill:#E65100,color:#fff

二、PagedAttention与显存管理革命

vLLM的核心贡献是PagedAttention——将操作系统中的虚拟内存分页思想引入KV Cache管理。传统的KV Cache管理方式为每个请求预分配一块连续的显存区域,大小按最大序列长度预留。这种方式的显存浪费触目惊心:如果最大序列长度为4096而实际只用了500,超过85%的显存被浪费。

PagedAttention将KV Cache切分为固定大小的"page"(block),每个page可以非连续地存储在显存的任意位置。请求按需分配page,不再预留给最大序列长度。类比的话,这就是把KV Cache从"数组"变成了"链表"——失去了随机访问的便利,但获得了显存分配的灵活性。

工程上的细节是,PagedAttention允许不同请求之间共享同一个page——比如当多个请求共享相同的system prompt时,这个prompt的KV Cache只需要存一份。在多轮对话场景中,不同对话可能共享相同的上下文前缀,共享机制可以进一步压缩显存。

三、Continuous Batching与吞吐量优化

传统推理服务的batching方式是静态的——必须等一个batch中的所有请求都完成才能开始下一批。对于生成长度差异悬殊的场景(有的请求只需要20个token,有的需要2000个),这意味着短请求被长请求严重拖累。

Continuous Batching(也称In-flight Batching或Iteration-level Scheduling)的核心改进是:每个forward pass结束后,已经完成的请求立即退出batch,新请求立即加入。batch的组成在每个token生成步中动态变化。这相当于把调度粒度从"请求级"细化到了"迭代级"。

vLLM和TensorRT-LLM都实现了这个机制,但实现路径不同。vLLM在Python层面通过精细的异步调度实现,灵活但有小量开销。TensorRT-LLM在C++ Runtime中实现,调度更为高效,但灵活性受限——它的调度器与编译后的引擎紧耦合。

"""
Continuous Batching的核心调度逻辑示意
展示batch在每次迭代中的动态变化
"""

class ContinuousBatchingScheduler:
    """请求级迭代调度器"""
    
    def __init__(self, max_batch_size: int, max_tokens_per_iter: int):
        self.max_batch_size = max_batch_size
        self.max_tokens_per_iter = max_tokens_per_iter
        self.running: List[Request] = []
        self.waiting: Deque[Request] = deque()
    
    def schedule(self) -> List[Request]:
        """每步迭代调用,返回本步参与forward的请求"""
        scheduled = []
        token_count = 0
        
        # 第一步:已完成请求退出
        newly_finished = [r for r in self.running if r.is_finished()]
        for req in newly_finished:
            self.running.remove(req)
            # 释放占用的KV Cache block
            req.release_kv_cache_blocks()
        
        # 第二步:从等待队列补充新请求
        while (len(scheduled) < self.max_batch_size 
               and token_count < self.max_tokens_per_iter
               and self.waiting):
            next_req = self.waiting.popleft()
            if next_req.can_allocate_kv_cache():
                scheduled.append(next_req)
                self.running.append(next_req)
                token_count += 1  # 每请求每步生成1个token
            else:
                # KV Cache不足,请求放回等待队列头部
                self.waiting.appendleft(next_req)
                break  # 不再尝试,因为后续请求也可能内存不足
        
        # 第三步:已有运行中的请求继续参与
        for req in self.running:
            if req not in scheduled:
                if token_count < self.max_tokens_per_iter:
                    scheduled.append(req)
                    token_count += 1
        
        return scheduled
    
    def step(self, logits: torch.Tensor, scheduled: List[Request]):
        """处理一个forward pass的输出"""
        for i, req in enumerate(scheduled):
            next_token = self.sample(logits[i])
            req.append_token(next_token)
            
            if next_token == req.eos_token_id or req.reached_max_length():
                req.mark_finished()

四、三维度基准测试对比

评价推理引擎性能需要从三个维度看:吞吐量(Throughput,tokens/s)、首Token延迟(TTFT,Time To First Token)和每Token生成时间(TPOT,Time Per Output Token)。

吞吐量维度,在A100-80GB上使用LLaMA-2-70B模型、QPS=32的并发场景下:vLLM v0.5.0的吞吐量约为2800 tokens/s,TensorRT-LLM约为3500 tokens/s(高出约25%),SGLang约为3100 tokens/s。TensorRT-LLM的领先优势来自NVIDIA原生的Kernel优化和图级编译,但这种优势在非NVIDIA GPU上无法复制。

TTFT维度,关键影响因子是prompt的prefill阶段效率。对于1024 token的prompt:vLLM的P50 TTFT约为180ms,TensorRT-LLM约为150ms,SGLang在RadixAttention命中前缀缓存时可以达到惊人的45ms。

TPOT维度,各引擎差距较小。在batch size=16条件下,三者的P50 TPOT都在18-22ms范围。TPOT主要受限于显存带宽,引擎层面的优化空间有限。

SGLang的差异化优势在于RadixAttention的前缀共享。在多轮对话(每轮共享system prompt和历史对话)和Few-shot prompting场景中,命中前缀缓存后TTFT可以降低5-10倍。如果你的业务场景大量涉及共享前缀(如客服对话、RAG应用),SGLang可能是更有性价比的选择。

五、总结

推理引擎的选择不是一个技术问题,而是一个场景适配问题。如果使用NVIDIA GPU且需要极致的单卡性能,TensorRT-LLM是首选——但需要接受它的部署复杂度和厂商锁定。如果需要快速部署和良好的生态支持,vLLM是当前最成熟的选择。如果业务中大量存在共享前缀场景(多轮对话、RAG),SGLang的RadixAttention可能带来数量级的TTFT改善。

更务实的选择是多引擎共存。不同模型、不同场景可以使用不同的推理引擎——vLLM用于通用推理服务,TensorRT-LLM用于性能敏感的热点模型,SGLang用于对话类应用。统一的上层路由+多引擎后端,在成本和性能上都比All-in-one方案更有竞争力。

Logo

免费领 150 小时云算力,进群参与显卡、AI PC 幸运抽奖

更多推荐