编程 vLLM 深度解析:LLM 推理性能的终极引擎——从 PagedAttention 到生产级部署的完整技术内幕

2026-05-18 08:22:35 +0800 CST views 13

vLLM 深度解析:LLM 推理性能的终极引擎——从 PagedAttention 到生产级部署的完整技术内幕

一、为什么 vLLM 是 2026 年 LLM 部署的事实标准

如果你在过去一年里部署过大语言模型,那你大概率用过 vLLM。这个由 UC Berkeley 团队开源的推理引擎,已经成为 LLM 服务部署领域的事实标准——从初创公司到科技巨头,从学术实验室到生产环境,几乎所有人都在用它。

为什么?因为它解决了一个极其关键的问题:GPU 显存利用率

在 vLLM 之前,部署 LLM 的主流方案(如 HuggingFace Transformers 的原生推理、TGI、FastChat 等)都有一个共同的痛点——KV Cache 的显存管理效率极低。传统的方案为每个请求预分配一块固定大小的连续显存空间来存储 KV Cache,这就导致了两个严重的问题:

  1. 内部碎片:请求实际使用的 KV Cache 大小会随着生成过程逐渐增长,但预分配的空间必须按最大可能长度来预留。大量预分配的显存被浪费。
  2. 外部碎片:请求结束后释放的空间大小不一,导致可用显存被打碎成无数小块,新的请求即使总显存够用,也找不到足够大的连续空间。

这两个问题直接导致了一个令人沮丧的现象:明明 GPU 还有 50% 的显存空闲,但推理服务却报 OOM(Out of Memory)错误。

vLLM 的核心创新——PagedAttention,从根本上解决了这个问题。它的思路直接借鉴了操作系统中的虚拟内存和分页机制,将 KV Cache 切分成固定大小的"页"(Block),不再要求连续存储,从而彻底消除了外部碎片,并大幅降低了内部碎片。

在 2026 年的今天,vLLM 已经进化到了 0.7.x 版本,支持的功能包括连续批处理(Continuous Batching)、前缀缓存(Prefix Caching)、多 LoRA 适配器、量化推理(GPTQ、AWQ、FP8)、多 GPU 张量并行和流水线并行等。同时,AMD 也推出了 vLLM-ATOM 插件,在 AMD Instinct GPU 上实现了对 DeepSeek-R1、Kimi-K2 等模型的推理加速。

这篇文章,我们将从底层原理到生产实战,全面拆解 vLLM 的技术架构。


二、PagedAttention:重新发明 KV Cache 的内存管理

2.1 传统方案的困境

要理解 PagedAttention 的精妙之处,我们首先需要理解传统方案的问题。

在 Transformer 的自注意力计算中,每个层的 Key 和 Value 向量需要被缓存下来,供后续的生成步骤使用。这就是所谓的 KV Cache。对于一个具有 L 层、隐藏维度为 d、注意力头数为 h 的模型,每个 token 的 KV Cache 大小为:

KV Cache per token = 2 × L × d × sizeof(dtype)

以 LLaMA-2-70B(80 层,8192 维度,BF16 精度)为例,每个 token 的 KV Cache 大小约为:

2 × 80 × 8192 × 2 bytes = 2.5 MB/token

如果序列长度为 4096,单个请求的 KV Cache 就需要约 10 GB。一个 A100-80G 显卡,理论上最多同时服务 8 个这样的请求——但实际中,由于内存碎片,往往只能服务 3-4 个。

传统方案的内存分配方式是:

请求1: [=======已使用======---------预留---------]
请求2: [=====已使用=====-----------预留-----------]
请求3: [=======已使用=======-------预留--------]

每个请求的 KV Cache 是一段连续的显存,按最大序列长度预分配。随着生成进行,"已使用"部分逐渐增长,"预留"部分逐渐缩小,但总的分配大小不变。

当请求结束时,释放的空间大小取决于该请求实际使用的 token 数。久而久之,显存变成了这样:

[已用][空][已用][空空][已用][空][已用][空空空][已用][空]

新的请求需要一块大的连续空间,但可用的空间被打碎了。这就是外部碎片。

2.2 PagedAttention 的解决方案

PagedAttention 借鉴了操作系统分页内存管理的思想:

  1. 页式分配:将 KV Cache 切分成固定大小的 Block(页),每个 Block 包含固定数量的 token 的 KV 向量。例如,Block 大小为 16 个 token。

  2. 页表映射:为每个请求维护一个页表(Block Table),记录该请求的 KV Cache 存储在哪些 Block 中。这些 Block 不需要物理连续。

  3. 按需分配:请求开始时只分配少量 Block,随着生成进行,按需分配新的 Block。请求结束时,Block 被回收,可以立即被其他请求使用。

虚拟地址空间(请求视角):
请求1: Block[0] → Block[3] → Block[7] → ...
请求2: Block[1] → Block[4] → Block[8] → ...

物理显存(实际存储):
Block[0][Block[1]][Block[2]][Block[3]][Block[4]][Block[5]...
 ↑请求1     ↑请求2      ↑请求1      ↑请求1

这种方式有几个关键优势:

  • 消除外部碎片:Block 大小固定,任何空闲 Block 都可以被任何请求使用,不存在"找不到连续空间"的问题。
  • 减少内部碎片:只有最后一个 Block 可能不满,其他 Block 都是满的。最大浪费不超过一个 Block 的大小(如 16 个 token 的空间)。
  • 内存共享:对于共享相同前缀的请求(如系统提示词),可以使用 Copy-on-Write 机制共享 Block,进一步节省显存。

2.3 PagedAttention 的实现细节

在 vLLM 的实现中,PagedAttention 通过自定义的 CUDA Kernel 来实现。传统的注意力计算 Kernel 假设 Key 和 Value 是连续存储的,而 PagedAttention 需要根据页表进行间接寻址。

vLLM 实现了一个特殊的 paged_attention CUDA Kernel,它的工作流程如下:

# vLLM 内部的简化逻辑(伪代码)
def paged_attention_kernel(
    query,           # [num_heads, head_dim]
    key_cache,       # [num_blocks, block_size, num_heads, head_dim]
    value_cache,     # [num_blocks, block_size, num_heads, head_dim]
    block_tables,    # [max_num_blocks_per_seq] - 页表
    context_length,  # 当前序列长度
):
    output = zeros(num_heads, head_dim)
    
    for block_idx in range(num_blocks_needed):
        # 通过页表查找物理 Block
        physical_block = block_tables[block_idx]
        # 从物理 Block 中读取 Key 和 Value
        k = key_cache[physical_block]  # [block_size, num_heads, head_dim]
        v = value_cache[physical_block]
        # 执行注意力计算
        output += attention(query, k, v)
    
    return output

这个 Kernel 的关键优化点在于:

  • 使用 GPU 的共享内存(Shared Memory)来缓存当前 Block 的 Key 和 Value,减少全局内存访问次数
  • 支持 Flash Attention 风格的 tiling 技术,减少 HBM(高带宽内存)的读写次数
  • 使用 Warp 级别的并行来处理不同的注意力头

2.4 前缀缓存(Prefix Caching)

vLLM 从 0.4.0 版本开始引入了自动前缀缓存(Automatic Prefix Caching)。这是一个基于 PagedAttention 架构的自然扩展。

在许多实际场景中,多个请求会共享相同的系统提示词(System Prompt)。例如:

系统提示词:你是一个专业的代码助手,请用 Python 回答以下问题...(500 tokens)
用户问题1:如何实现一个LRU缓存?(30 tokens)
用户问题2:如何解析CSV文件?(25 tokens)

传统方案中,每个请求都需要为这 500 tokens 的系统提示词单独计算和存储 KV Cache。有了前缀缓存,vLLM 可以:

  1. 在第一个请求处理完系统提示词后,将对应的 KV Cache Block 标记为"可共享"
  2. 后续请求如果具有相同的前缀,直接复用这些 Block(使用引用计数)
  3. 当所有引用该 Block 的请求完成后,才释放 Block
# vLLM 前缀缓存的工作方式(概念示意)
# 请求1 处理系统提示词后:
block_table_req1 = [Block(0), Block(1), ..., Block(31)]  # 系统提示的 32 个 Block
# Block 0-31 被标记为可共享,引用计数 = 1

# 请求2 具有相同系统提示词:
block_table_req2 = [Block(0), Block(1), ..., Block(31), Block(32)]
# Block 0-31 的引用计数增加到 2
# Block 32 是请求2特有内容的 Block

这个优化在系统提示词较长的场景中效果尤为显著。实际测试表明,对于包含 1000+ token 系统提示词的对话场景,前缀缓存可以提升 2-3 倍的吞吐量。


三、连续批处理:告别静态批处理的效率陷阱

3.1 静态批处理的问题

传统的推理服务通常使用静态批处理(Static Batching):将一批请求打包在一起送入模型,等所有请求都生成完毕后,再处理下一批。

时间轴 →
请求1: [======预填充======][生成][生成][生成][生成][生成][生成]
请求2: [====预填充====][生成][生成][生成][生成][等待..........]
请求3: [=======预填充=======][生成][生成][生成][等待............]

批次结束 ←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←

问题很明显:短请求必须等待长请求完成,GPU 大量时间在空转。

3.2 连续批处理(Iteration-level Scheduling)

vLLM 实现了迭代级别的调度(Iteration-level Scheduling),也叫连续批处理。核心思想是:在每次迭代中,调度器都会检查是否有新的请求可以加入当前批次,以及是否有已完成的请求可以移出。

时间轴 →
迭代1: [请求1预填充][请求2预填充][请求3预填充]
迭代2: [请求1生成][请求2生成][请求3生成]
迭代3: [请求1生成][请求2生成][请求3生成][请求4预填充] ← 新请求立即加入
迭代4: [请求1生成][请求2完成✓][请求3生成][请求4生成]  ← 请求2完成,立即移出
迭代5: [请求1生成][请求3生成][请求4生成][请求5预填充] ← 新请求再次加入

这种方式的效果是:

  • GPU 利用率大幅提升:几乎每个迭代都在处理满负荷的计算
  • 首 Token 延迟降低:新请求不需要等待当前批次完成
  • 吞吐量提升:单位时间内处理更多请求

vLLM 的调度器在每次迭代中执行以下逻辑:

# vLLM 调度器的简化逻辑
def schedule():
    # 1. 从等待队列中挑选可以加入当前批次的请求
    for request in waiting_queue:
        if can_allocate(request):
            running_queue.add(request)
            allocate_blocks(request)
    
    # 2. 对运行中的请求执行一次前向计算
    for request in running_queue:
        if request.is_prefill:
            execute_prefill(request)
        else:
            execute_decode(request)
    
    # 3. 移除已完成的请求,释放 Block
    for request in running_queue:
        if request.is_finished:
            running_queue.remove(request)
            free_blocks(request)
    
    # 4. 处理内存压力:如果 GPU KV Cache 不够,将请求交换到 CPU
    if gpu_cache_usage > threshold:
        swap_to_cpu(low_priority_requests)
    elif gpu_cache_usage < threshold and swapped_requests:
        swap_from_gpu(swapped_requests)

3.3 Chunked Prefill:预填充的碎片化

在 vLLM 0.3.0+ 中引入了 Chunked Prefill 机制。传统的预填充(Prefill)阶段需要一次性处理整个输入序列,对于长输入(如长文档总结),这会导致:

  1. 单次预填充占用大量显存,可能挤占其他请求的空间
  2. 预填充期间,其他生成中的请求必须等待

Chunked Prefill 将长输入的预填充切分成多个 Chunk,每个迭代只处理一个 Chunk:

请求(输入 4096 tokens):
迭代1: [Chunk1: tokens 0-1023] + [其他请求的生成]
迭代2: [Chunk2: tokens 1024-2047] + [其他请求的生成]
迭代3: [Chunk3: tokens 2048-3071] + [其他请求的生成]
迭代4: [Chunk4: tokens 3072-4095] + [其他请求的生成]
迭代5: [生成token 1] + [其他请求的生成]

这样,长输入的请求不会"霸占"整个 GPU,其他短请求可以穿插执行。


四、vLLM 架构全景:从 API 到 GPU Kernel

4.1 整体架构

vLLM 的架构可以分为以下几层:

┌─────────────────────────────────────────────────┐
│                  API Layer                        │
│  OpenAI Compatible API / HTTP / gRPC             │
├─────────────────────────────────────────────────┤
│                 Frontend                          │
│  Request Parser / Tokenizer / Detokenizer         │
├─────────────────────────────────────────────────┤
│               Scheduler                           │
│  Priority Queue / Block Manager / Swapper         │
├─────────────────────────────────────────────────┤
│               Model Runner                        │
│  Prefill Engine / Decode Engine / Workers         │
├─────────────────────────────────────────────────┤
│              Backend / Kernels                     │
│  PagedAttention / Quantization / Sampling          │
├─────────────────────────────────────────────────┤
│               GPU / Hardware                       │
│  CUDA / ROCm / TensorRT / cuDNN                   │
└─────────────────────────────────────────────────┘

4.2 API 层:OpenAI 兼容

vLLM 提供了与 OpenAI API 完全兼容的服务端,这意味着你可以用任何支持 OpenAI API 的客户端直接对接 vLLM:

from openai import OpenAI

# 直接替换 base_url,其他代码不变
client = OpenAI(
    base_url="http://localhost:8000/v1",
    api_key="not-needed"  # vLLM 默认不需要 API Key
)

response = client.chat.completions.create(
    model="Qwen/Qwen3-8B",
    messages=[
        {"role": "system", "content": "你是一个专业的代码助手"},
        {"role": "user", "content": "用Python实现一个高效的LRU缓存"}
    ],
    max_tokens=2048,
    temperature=0.7,
)

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

支持的 API 包括:

  • POST /v1/chat/completions - 对话补全
  • POST /v1/completions - 文本补全
  • POST /v1/embeddings - 向量嵌入
  • GET /v1/models - 模型列表
  • POST /v1/tokenize / /detokenize - Token 化

4.3 Block Manager:显存管理的中枢

Block Manager 是 vLLM 的核心组件,负责管理 GPU 和 CPU 上的 KV Cache Block:

class BlockSpaceManager:
    def __init__(self, block_size, num_gpu_blocks, num_cpu_blocks):
        self.block_size = block_size          # 每个 Block 的 token 数(默认 16)
        self.num_gpu_blocks = num_gpu_blocks  # GPU 上可用的 Block 总数
        self.num_cpu_blocks = num_cpu_blocks  # CPU 上可用的 Block 总数
        
        # 空闲 Block 列表(用位图或 Free List 管理)
        self.free_gpu_blocks = list(range(num_gpu_blocks))
        self.free_cpu_blocks = list(range(num_cpu_blocks))
    
    def can_allocate(self, num_required_blocks):
        """检查是否有足够的空闲 Block"""
        return len(self.free_gpu_blocks) >= num_required_blocks
    
    def allocate(self, num_blocks):
        """分配指定数量的 Block"""
        allocated = self.free_gpu_blocks[:num_blocks]
        self.free_gpu_blocks = self.free_gpu_blocks[num_blocks:]
        return allocated
    
    def free(self, blocks):
        """释放 Block"""
        self.free_gpu_blocks.extend(blocks)
    
    def swap_out(self, blocks):
        """将 Block 从 GPU 交换到 CPU"""
        # 实际执行 GPU→CPU 的数据拷贝
        self.free_gpu_blocks.extend(blocks)
        allocated_cpu = self.free_cpu_blocks[:len(blocks)]
        self.free_cpu_blocks = self.free_cpu_blocks[len(blocks):]
        return allocated_cpu
    
    def swap_in(self, blocks):
        """将 Block 从 CPU 交换回 GPU"""
        self.free_cpu_blocks.extend(blocks)
        allocated_gpu = self.free_gpu_blocks[:len(blocks)]
        self.free_gpu_blocks = self.free_gpu_blocks[len(blocks):]
        return allocated_gpu

4.4 GPU KV Cache 容量计算

启动 vLLM 时,引擎会自动计算 GPU 可用多少 KV Cache Block:

可用 KV Cache 显存 = GPU 总显存 - 模型参数占用 - 框架运行开销
可用 Block 数量 = 可用 KV Cache 显存 / Block 大小

Block 大小的计算公式:

Block 大小 (bytes) = block_size × num_layers × 2 × num_heads × head_dim × sizeof(dtype)

以 LLaMA-2-70B(BF16)为例,block_size=16:

Block 大小 = 16 × 80 × 2 × 64 × 128 × 2 = 16,777,216 bytes ≈ 16 MB

在 A100-80G 上,扣除模型参数(约 140GB → 需要 2 张 A100)后,每张卡大约可以分配 2000+ 个 Block,对应 32000+ tokens 的 KV Cache。


五、量化推理:在精度和速度之间找到平衡

5.1 vLLM 支持的量化方案

vLLM 支持多种量化方案,从训练后量化(PTQ)到混合精度:

量化方案精度显存节省性能影响适用场景
FP16/BF1616-bit基准基准默认选择
FP88-bit~50%<1% 精度损失H100/Ada Lovelace GPU
GPTQ4-bit~75%1-3% 精度损失推理部署
AWQ4-bit~75%1-2% 精度损失推理部署
INT8 (W8A8)8-bit~50%<1% 精度损失通用场景
KV Cache 量化8-bitKV Cache ~50%较小影响长序列场景

5.2 FP8 量化:新一代 GPU 的杀手锏

FP8 是 NVIDIA H100(Hopper)和 Ada Lovelace(RTX 4090)架构引入的新数据格式,提供 E4M3(用于前向传播)和 E5M2(用于反向传播)两种编码方式。

在 vLLM 中启用 FP8 量化非常简单:

python -m vllm.entrypoints.openai.api_server \
    --model meta-llama/Meta-Llama-3-8B \
    --quantization fp8 \
    --dtype float16

FP8 的优势在于:

  • 硬件原生支持,计算速度几乎不降低
  • 精度损失极小(<1%),对大部分任务几乎无感知
  • 显存占用减半,可以在同等硬件上部署更大的模型或服务更多并发

5.3 AWQ:激活感知的 4-bit 量化

AWQ(Activation-Aware Weight Quantization)的核心洞察是:不是所有权重对模型输出都同样重要。AWQ 通过分析激活值来识别"重要"的权重通道,对这些通道保持较高精度,其他通道进行更激进的量化。

# 使用 AWQ 量化模型
python -m vllm.entrypoints.openai.api_server \
    --model TheBloke/Llama-2-70B-AWQ \
    --quantization awq \
    --dtype float16

5.4 KV Cache 量化

对于长序列场景,vLLM 支持 KV Cache 的 INT8 量化,可以将 KV Cache 的显存占用减半:

python -m vllm.entrypoints.openai.api_server \
    --model meta-llama/Meta-Llama-3-8B \
    --kv-cache-dtype float8 \
    --enforce-eager

六、多 GPU 并行:张量并行与流水线并行

6.1 张量并行(Tensor Parallelism)

张量并行是将模型的每一层参数切分到多个 GPU 上,每个 GPU 计算一部分,然后通过通信合并结果。

在 vLLM 中启用张量并行:

# 在 4 张 A100 上部署 LLaMA-70B
python -m vllm.entrypoints.openai.api_server \
    --model meta-llama/Meta-Llama-3-70B \
    --tensor-parallel-size 4

vLLM 使用 Megatron-LM 风格的张量并行实现:

  • 矩阵乘法沿列或行切分
  • 注意力计算中,QKV 投影沿列切分,输出投影沿行切分
  • 使用 NCCL 进行 GPU 间通信

张量并行的效率高度依赖于 GPU 间的通信带宽。在 NVLink 连接的服务器上(如 DGX A100),张量并行的效率可以接近线性。在 PCIe 连接的环境下,通信开销会显著增加。

实践建议:

  • 单机 8 卡(NVLink):TP=8,效果最佳
  • 单机 4 卡(NVLink):TP=4
  • 跨机部署:尽量避免跨机 TP,改用流水线并行

6.2 流水线并行(Pipeline Parallelism)

流水线并行将模型的不同层分配到不同的 GPU 上,数据依次流过各个阶段。

# 将 LLaMA-70B 分到 2 个节点,每个节点 4 张卡
# 节点1(TP=4, PP stage 0)
python -m vllm.entrypoints.openai.api_server \
    --model meta-llama/Meta-Llama-3-70B \
    --tensor-parallel-size 4 \
    --pipeline-parallel-size 2 \
    --pipeline-parallel-rank 0

# 节点2(TP=4, PP stage 1)
python -m vllm.entrypoints.openai.api_server \
    --model meta-llama/Meta-Llama-3-70B \
    --tensor-parallel-size 4 \
    --pipeline-parallel-size 2 \
    --pipeline-parallel-rank 1

流水线并行的挑战在于"气泡"(Bubble)问题——当一个阶段在计算时,其他阶段在等待。vLLM 通过微批次(Micro-batch)技术来减少气泡:

GPU 0: [微批次1层1-20][微批次2层1-20][微批次3层1-20][等待...]
GPU 1: [等待..][微批次1层21-40][微批次2层21-40][微批次3层21-40]

七、性能调优实战:从入门到精通

7.1 基础参数调优

以下是最重要的启动参数及其调优策略:

python -m vllm.entrypoints.openai.api_server \
    --model Qwen/Qwen3-8B \
    --served-model-name qwen3-8b \
    --max-model-len 8192 \                    # 最大序列长度
    --max-num-batched-tokens 32768 \           # 单批次最大 token 数
    --max-num-seqs 256 \                       # 最大并发请求数
    --gpu-memory-utilization 0.90 \            # GPU 显存使用上限(默认 0.9)
    --swap-space 4 \                           # CPU 交换空间(GB)
    --enable-prefix-caching \                  # 启用前缀缓存
    --dtype float16                            # 数据类型

参数调优指南:

--max-num-batched-tokens

  • 控制 GPU 算力的利用上限
  • 太小:GPU 利用率低,吞吐量低
  • 太大:显存不足,触发 OOM
  • 调优策略:从 2048 开始,逐步增加直到 GPU 计算利用率 > 90%

--max-num-seqs

  • 控制最大并发请求数
  • 影响延迟(请求数越多,单请求等待时间越长)
  • 调优策略:根据业务 SLA 的延迟要求来设置

--gpu-memory-utilization

  • 控制显存使用上限(默认 0.9)
  • 留 10% 显存给 CUDA 上下文和其他运行时开销
  • 如果显存充裕,可以设到 0.95

7.2 监控指标解读

vLLM 内置了 Prometheus 指标导出,以下是关键指标:

# 核心性能指标
vllm:num_requests_running      # GPU 上正在运行的请求数
vllm:num_requests_waiting      # 排队等待的请求数
vllm:num_requests_swapped      # 被交换到 CPU 的请求数

# 缓存指标
vllm:gpu_cache_usage_perc      # GPU KV Cache 使用率
vllm:cpu_cache_usage_perc      # CPU KV Cache 使用率
vllm:cpu_cache_usage_perc      # 前缀缓存命中率

# 延迟指标
vllm:e2e_request_latency_seconds          # 端到端请求延迟
vllm:time_to_first_token_seconds          # 首 Token 延迟(TTFT)
vllm:time_per_output_token_seconds        # 每 Token 输出时间(TPOT)
vllm:generation_throughput_tokens_per_sec # 生成吞吐量

故障排查指南:

症状关键指标可能原因解决方案
TTFT 高num_requests_running 飙高批次太大,预填充排队降低 max_num_seqs
TPOT 高gpu_cache_usage_perc > 95%KV Cache 不足,频繁换入换出增加 GPU 或启用 KV 量化
吞吐量低num_requests_waiting 持续 > 0调度瓶颈增加 max_num_batched_tokens
OOM-显存不足降低 max_model_len 或使用量化
延迟抖动num_requests_swapped 频繁变化GPU↔CPU 交换增加 swap_space 或 GPU 数量

7.3 搭建 Prometheus + Grafana 监控

#!/bin/bash
# docker-compose.yml
cat > docker-compose.yml << 'EOF'
version: '3.8'
services:
  prometheus:
    image: prom/prometheus:latest
    container_name: prometheus
    ports:
      - "9090:9090"
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml
      - prometheus_data:/prometheus

  grafana:
    image: grafana/grafana:latest
    container_name: grafana
    ports:
      - "3000:3000"
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=admin
    volumes:
      - grafana_data:/var/lib/grafana
    depends_on:
      - prometheus

volumes:
  prometheus_data:
  grafana_data:
EOF

cat > prometheus.yml << 'EOF'
global:
  scrape_interval: 15s

scrape_configs:
  - job_name: 'vllm'
    static_configs:
      - targets: ['host.docker.internal:8000']
EOF

docker compose up -d

八、生产级部署架构

8.1 Docker 部署

# 拉取官方镜像
docker pull vllm/vllm-openai:latest

# 启动服务
docker run --gpus all \
    -v ~/.cache/huggingface:/root/.cache/huggingface \
    -p 8000:8000 \
    --ipc=host \
    vllm/vllm-openai:latest \
    --model Qwen/Qwen3-8B \
    --served-model-name qwen3-8b \
    --max-model-len 8192 \
    --gpu-memory-utilization 0.92 \
    --enable-prefix-caching \
    --dtype float16

重要:--ipc=host 不能省略。vLLM 使用 PyTorch 的多进程数据加载器,需要足够的共享内存。默认的 Docker 共享内存(64MB)会导致服务崩溃。

8.2 Kubernetes 部署

apiVersion: apps/v1
kind: Deployment
metadata:
  name: vllm-qwen3-8b
spec:
  replicas: 2
  selector:
    matchLabels:
      app: vllm
  template:
    metadata:
      labels:
        app: vllm
    spec:
      containers:
      - name: vllm
        image: vllm/vllm-openai:latest
        command:
          - --model
          - Qwen/Qwen3-8B
          - --served-model-name
          - qwen3-8b
          - --max-model-len
          - "8192"
          - --gpu-memory-utilization
          - "0.92"
          - --enable-prefix-caching
        resources:
          limits:
            nvidia.com/gpu: 1
            memory: "32Gi"
        ports:
        - containerPort: 8000
        readinessProbe:
          httpGet:
            path: /health
            port: 8000
          initialDelaySeconds: 120
          periodSeconds: 30
        livenessProbe:
          httpGet:
            path: /health
            port: 8000
          initialDelaySeconds: 300
          periodSeconds: 60
        volumeMounts:
        - name: model-cache
          mountPath: /root/.cache/huggingface
      volumes:
      - name: model-cache
        persistentVolumeClaim:
          claimName: model-cache-pvc
---
apiVersion: v1
kind: Service
metadata:
  name: vllm-service
spec:
  selector:
    app: vllm
  ports:
  - port: 80
    targetPort: 8000
  type: LoadBalancer

8.3 多模型部署与 LoRA 适配器

vLLM 支持通过 LoRA(Low-Rank Adaptation)在同一个基础模型上服务多个微调版本:

python -m vllm.entrypoints.openai.api_server \
    --model meta-llama/Meta-Llama-3-8B \
    --enable-lora \
    --lora-modules \
        lora-code=/path/to/code-lora \
        lora-math=/path/to/math-lora \
        lora-legal=/path/to/legal-lora \
    --max-lora-rank 16

调用时指定 LoRA 适配器:

response = client.chat.completions.create(
    model="meta-llama/Meta-Llama-3-8B",
    messages=[...],
    extra_body={"lora_name": "lora-code"}
)

这种方式的优势是:

  • 只需加载一次基础模型
  • 不同 LoRA 适配器共享 KV Cache Block
  • 切换适配器的开销极小(毫秒级)

8.4 vLLM-ATOM:AMD GPU 的推理加速

2026 年 5 月,AMD 推出了 vLLM-ATOM 插件,为 AMD Instinct GPU 提供推理优化。该插件的核心特性:

  • 零代码修改:不改动 vLLM 的 API 和工作流
  • 自动优化:后台自动接管计算优化
  • 模型支持:DeepSeek-R1、Kimi-K2、gpt-oss-120B 等

这标志着 vLLM 生态从纯 NVIDIA 生态扩展到了 AMD ROCm 生态,对于需要降低 GPU 采购成本的团队来说是个好消息。


九、vLLM vs 其他推理框架对比

特性vLLMTGI (HuggingFace)SGLangLMDeploy
PagedAttention
连续批处理
前缀缓存
张量并行
流水线并行
多 LoRA
量化(GPTQ/AWQ/FP8)
Chunked Prefill
CUDA Graph
AMD ROCm 支持
Speculative Decoding
RadixAttention
OpenAI 兼容 API
社区活跃度⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐

选择建议:

  • 通用部署首选:vLLM —— 功能最全面,社区最活跃
  • 需要 RadixAttention(细粒度缓存):SGLang
  • 纯 NVIDIA 生态 + HuggingFace 集成:TGI
  • 国产模型(如 InternLM):LMDeploy

十、性能基准测试

10.1 基准测试方法

"""
vLLM 性能基准测试脚本
使用 locust 进行压力测试
"""
from locust import HttpUser, task, between

class LLMUser(HttpUser):
    wait_time = between(1, 3)
    
    @task
    def chat_completion(self):
        self.client.post("/v1/chat/completions", json={
            "model": "qwen3-8b",
            "messages": [
                {"role": "system", "content": "你是一个专业的编程助手。"},
                {"role": "user", "content": "请解释 Go 语言中 goroutine 的调度机制。"}
            ],
            "max_tokens": 512,
            "temperature": 0.7
        })

10.2 参考性能数据

以下是基于公开基准测试的参考数据(单卡 A100-80G,BF16):

模型并发数吞吐量 (tokens/s)TTFT (ms)TPOT (ms)
LLaMA-3-8B12,100150.48
LLaMA-3-8B3212,500250.39
LLaMA-3-8B12818,200350.38
LLaMA-3-70B (TP=4)11,800450.56
LLaMA-3-70B (TP=4)328,900600.42
Qwen3-8B3211,800200.41
Qwen3-32B (TP=2)329,200500.45

十一、常见陷阱与解决方案

陷阱 1:OOM 错误

症状torch.cuda.OutOfMemoryError: CUDA out of memory

排查步骤

# 1. 降低最大序列长度
--max-model-len 4096  # 从 8192 降到 4096

# 2. 降低显存使用上限
--gpu-memory-utilization 0.85  # 从 0.9 降到 0.85

# 3. 使用量化
--quantization awq  # 或 fp8

# 4. 增加张量并行
--tensor-parallel-size 2  # 从 1 增加到 2

# 5. 启用 KV Cache 量化
--kv-cache-dtype float8

陷阱 2:首 Token 延迟过高

症状:首个 Token 返回时间 > 5 秒

排查方向

  1. 检查 num_requests_running 是否过高
  2. 检查是否有大量长预填充请求在排队
  3. 启用 Chunked Prefill:--enable-chunked-prefill
  4. 降低 max_num_seqs 以减少批处理大小

陷阱 3:吞吐量不达预期

排查方向

  1. 检查 GPU 利用率:nvidia-smi 查看 Volatile GPU-Util
  2. 如果利用率低,增加 max_num_batched_tokens
  3. 检查前缀缓存命中率,如果不命中,检查请求是否真的共享前缀
  4. 确保 --enforce-eager 未启用(CUDA Graph 可以提升 10-30% 吞吐)

陷阱 4:Docker 部署崩溃

症状:容器启动后立即退出

最常见原因:忘记 --ipc=host

# 正确
docker run --gpus all --ipc=host -p 8000:8000 vllm/vllm-openai:latest ...

# 错误(缺少 --ipc=host)
docker run --gpus all -p 8000:8000 vllm/vllm-openai:latest ...

十二、总结与展望

vLLM 从 2023 年开源至今,已经从一个学术项目成长为 LLM 推理部署的基础设施。它的成功不仅仅是技术上的——PagedAttention 的优雅设计、OpenAI 兼容 API 的务实选择、以及活跃的开源社区,共同构成了 vLLM 的核心竞争力。

在 2026 年,我们可以看到几个明确的发展趋势:

  1. 异构计算支持:随着 AMD vLLM-ATOM 的推出,vLLM 正在从 NVIDIA-only 扩展到多硬件平台,这将降低部署成本

  2. 更智能的调度:从简单的 FIFO 调度到基于优先级、SLA 感知的智能调度,vLLM 的调度器会越来越"聪明"

  3. 推理成本持续下降:通过 FP8/INT4 量化、KV Cache 压缩、投机解码(Speculative Decoding)等技术,每 token 的推理成本正在快速下降

  4. 长上下文优化:随着模型上下文窗口的扩展(128K、1M tokens),KV Cache 的管理效率变得越来越重要,PagedAttention 的优势会更加明显

  5. Agent 场景的深度优化:AI Agent 的多轮对话、工具调用等场景对推理引擎提出了新的要求,vLLM 正在针对性地优化这些场景

对于正在或即将部署 LLM 服务的团队来说,vLLM 不是一个可选项——它是必选项。理解它的原理、掌握它的调优方法、建立完善的监控体系,是每一个 LLM 基础设施工程师的必修课。


参考资料

  • vLLM GitHub 仓库:https://github.com/vllm-project/vllm
  • PagedAttention 论文:《Efficient Memory Management for Large Language Model Serving with PagedAttention》
  • vLLM 官方文档:https://docs.vllm.ai
  • AMD vLLM-ATOM 公告:https://www.amd.com/en/developer/vllm-atom.html

推荐文章

一些高质量的Mac软件资源网站
2024-11-19 08:16:01 +0800 CST
php机器学习神经网络库
2024-11-19 09:03:47 +0800 CST
pin.gl是基于WebRTC的屏幕共享工具
2024-11-19 06:38:05 +0800 CST
Grid布局的简洁性和高效性
2024-11-18 03:48:02 +0800 CST
Redis函数在PHP中的使用方法
2024-11-19 04:42:21 +0800 CST
JavaScript设计模式:观察者模式
2024-11-19 05:37:50 +0800 CST
支付宝批量转账
2024-11-18 20:26:17 +0800 CST
PHP 微信红包算法
2024-11-17 22:45:34 +0800 CST
Vue3中的Scoped Slots有什么改变?
2024-11-17 13:50:01 +0800 CST
markdown语法
2024-11-18 18:38:43 +0800 CST
实用MySQL函数
2024-11-19 03:00:12 +0800 CST
WebSQL数据库:HTML5的非标准伴侣
2024-11-18 22:44:20 +0800 CST
JavaScript中的常用浏览器API
2024-11-18 23:23:16 +0800 CST
Python设计模式之工厂模式详解
2024-11-19 09:36:23 +0800 CST
html5在客户端存储数据
2024-11-17 05:02:17 +0800 CST
10个几乎无人使用的罕见HTML标签
2024-11-18 21:44:46 +0800 CST
PyMySQL - Python中非常有用的库
2024-11-18 14:43:28 +0800 CST
程序员茄子在线接单