2. vLLM基础回顾:架构、推理流程与关键组件
在深入排查推理结果一致性之前,必须先建立对 vLLM 工作方式的清晰认识。vLLM 并非简单地封装了 HuggingFace Transformers 的 generate() 接口,而是实现了一套完整的高性能推理服务系统。它的核心设计目标是在高吞吐场景下维持低延迟,而这套设计中的每一个环节——从调度器到显存管理——都可能成为影响结果一致性的潜在变量。
2.1 vLLM 整体架构与推理流程
vLLM 采用经典的客户端/服务端架构,服务端内部可划分为三个关键层次:API 层、调度执行层和显存管理层。一次完整的推理请求从进入服务到返回结果,会经历以下核心阶段:
- 请求接收与 Tokenize:HTTP 请求经 FastAPI 网关进入,由
LLMEngine接收,将输入文本转换为 token ID 序列。 - 序列分组与调度:
Scheduler将多个请求的序列按 continuous batching 策略动态组合成 step,决定当前迭代执行哪些序列。 - 模型前向计算:将 batch 中的 token 输入 GPU,执行 Transformer 层的并行计算,生成 logits。
- 采样与解码:根据
sampling_params(temperature、top_p 等)从 logits 中采样下一个 token。 - KV Cache 管理与更新:新生成的 token 对应的 Key/Value 张量被写入 PagedAttention 管理的显存块中。
- 结果返回:若序列触发停止条件(EOS、max_tokens),则从缓存中取出完整 token 序列并 Detokenize 返回。
2.2 PagedAttention 与 KV Cache 的机制
PagedAttention 是 vLLM 区别于传统推理框架的核心创新。传统框架为每个请求预分配固定大小的 KV Cache 显存,导致显存浪费严重;而 PagedAttention 将 KV Cache 划分为固定大小的物理块(block),通过逻辑块到物理块的映射表实现按需分配。这种设计带来了两个直接影响:
- 显存利用率提升:接近零浪费的显存管理,使得同一 GPU 上可以并发更多请求。
- 计算模式改变:Attention 计算不再连续,而是通过块索引间接访问内存,这引入了额外的访存开销,也使得浮点运算顺序与块布局强相关。
从一致性角度看,KV Cache 的物理布局并非固定不变。当显存碎片化或请求动态加入/退出时,新分配的物理块可能位于不同的显存地址。虽然数学上 Attention 结果与存储位置无关,但浮点数的加法不满足结合律,不同顺序的累加会产生 1e-6 量级的差异。在长序列生成中,这种微小差异会通过自回归过程逐步累积,最终导致 token 选择不同。
2.3 关键组件对结果一致性的影响矩阵
| 组件 | 功能定位 | 一致性影响机制 | 排查建议 |
|---|---|---|---|
| Scheduler | 动态组batch、决定prefill/decode | batch组成变化 → 矩阵乘法维度不同 → 累加顺序变化 | 固定请求数、固定并发压力测试 |
| PagedAttention | KV Cache块管理 | 物理块地址变化 → 访存顺序变化 → Attention累加差异 | 对比不同显存占用下的输出 |
| Sampler | 从logits采样token | 未固定seed时,随机数序列不同 | 设置 seed 并对比 |
| 模型权重 | 前向计算参数 | 权重加载顺序、量化方式影响数值 | 使用同一权重文件、关闭量化 |
2.4 典型场景:并发请求下的输出不一致
假设你向 vLLM 服务发送两次完全相同的请求,第一次单独发送,第二次与另外 7 个请求并发发送。你可能会发现两次输出存在差异。这并非 bug,而是 vLLM 动态调度机制的正常表现。具体原因如下:
# 场景模拟
# 请求A:单独发送,batch_size=1,prefill阶段计算完整
# 请求A':与B~H并发,batch_size=8,prefill阶段与其他请求混合计算
# 在 batch_size=1 时,Attention 计算为:
# out = softmax(Q @ K^T / sqrt(d)) @ V
# 在 batch_size=8 时,Q、K、V 矩阵的行数变为8倍,
# 矩阵乘法的分块策略(tiling)可能改变,导致浮点累加顺序不同。
这种差异在 fp16 精度下尤为明显,因为半精度浮点数的有效位数仅约 3 位十进制数字。若使用 fp32 或 bf16,差异会减小但不会完全消失。
seed,然后对比 --dtype float32 与默认 float16 的输出差异。若差异消失,则说明是浮点精度问题;若差异仍在,则需检查调度逻辑或权重加载。
2.5 常见误区与复盘
在理解 vLLM 架构时,初学者常陷入以下误区:
- 误区一:认为 vLLM 与 HuggingFace 的 generate() 完全等价。实际上,vLLM 重写了 Attention 计算和采样循环,即使设置完全相同的参数,输出也可能与 Transformers 库不同。
- 误区二:忽略 KV Cache 的显存碎片化。当服务运行较长时间后,物理块分配会变得碎片化,新请求获得的块位置与刚启动时不同,导致 Attention 计算顺序变化。
- 误区三:将采样随机性误认为模型错误。若未设置
seed,vLLM 每次采样都使用新的随机数,输出自然不同。这不是一致性问题,而是随机采样的预期行为。
seed 是否固定;② 对比单请求与并发请求的输出;③ 检查是否启用了确定性算法(torch.use_deterministic_algorithms(True));④ 尝试切换 dtype 观察差异是否缩小。只有完成上述步骤,才能定位到真正的根因。