跳转至

批次不变性

Note

批量不变性目前处于测试阶段。部分功能仍在积极开发中。 请通过跟踪问题 #5487了解进展和计划改进。

Note

要安装批量不变性自定义算子库,请在构建 vllm-ascend 之前设置 VLLM_BATCH_INVARIANT=1。 有关安装说明,请参阅在现有 CANN 环境中安装。

本文档介绍如何在 vLLM-Ascend 中启用批次不变性。批次不变性确保模型的输出是确定性的,并且与批次大小或批次中请求的顺序无关。

动机

批次不变性在以下几种使用场景中至关重要:

  • 框架调试:确定性输出使得推理框架中的问题调试更加容易,因为无论批处理如何,相同的输入总是产生相同的输出。
  • 模型调试:通过确保不同批次配置下行为一致,帮助识别模型实现中的问题。
  • 强化学习 (RL):RL 训练通常需要确定性的推演以实现可复现性和稳定训练。
  • 大规模推理系统:将 vLLM 作为组件的系统受益于确定性行为,用于测试、验证和一致性保证。

硬件要求

批处理不变性支持 Atlas A2、A3 和 Ascend 950PR&950DT系列产品。

软件要求

批处理不变性需要为 Atlas A2、A3 和 Ascend 950PR&950DT系列产品定制算子。在从源码构建 vllm-ascend 之前设置 VLLM_BATCH_INVARIANT=1,以构建并安装所需的算子包。

batch_invariant_ops 的构建和安装过程包含两个阶段,如 build_batch_invariant_ops.sh 所示,必须按顺序执行:

  1. 安装算子运行包。它提供使用 AscendC 实现的设备端批量不变性算子。
  2. 构建并安装 batch_invariant_ops wheel。它提供调用 AscendC 算子的 PyTorch 扩展接口。

Note

预构建的 vllm-ascend wheel 不包含 csrc 目录或 csrc/build_batch_invariant_ops.sh,在安装该 wheel 时设置 VLLM_BATCH_INVARIANT=1 不会重新构建算子。手动安装算子需要匹配的 vllm-ascend 源码检出目录。以下命令中的 <vllm-ascend-source-dir> 指的是该检出目录,而不是 wheel 的 site-packages 目录。

从源码安装

选项 1:同时安装 vllm-ascend 和算子包

该环境变量由源码构建过程使用。在启用自定义内核编译时,它既适用于常规源码安装,也适用于可编辑源码安装:

cd <vllm-ascend-source-dir>

# Regular source installation
COMPILE_CUSTOM_KERNELS=1 VLLM_BATCH_INVARIANT=1 \
    pip install . --no-build-isolation

# Editable source installation
COMPILE_CUSTOM_KERNELS=1 VLLM_BATCH_INVARIANT=1 \
    pip install -e . --no-build-isolation

选项 2:如果 vllm-ascend 已安装,则安装算子包

获取与已安装包版本匹配的 vllm-ascend 源码树,然后从该源码树构建并安装算子包。

A2:

cd <vllm-ascend-source-dir>
bash csrc/build_batch_invariant_ops.sh ascend910b

A3:

cd <vllm-ascend-source-dir>
bash csrc/build_batch_invariant_ops.sh ascend910_93

Ascend 950PR&950DT系列产品:

cd <vllm-ascend-source-dir>
bash csrc/build_batch_invariant_ops.sh ascend950

使用 Docker 镜像

适用于 Ubuntu 和 openEuler 的 A2、A3 和 Ascend 950PR&950DT系列产品 Docker 镜像在从源码构建 vllm-ascend 时设置了 VLLM_BATCH_INVARIANT=1,因此镜像构建过程中会同时安装 AscendC 算子运行包和 batch_invariant_ops wheel 包。该构建时环境变量不会保留为运行时设置。在启动服务或运行离线推理时设置 VLLM_BATCH_INVARIANT=1 以启用批处理不变性。

快速检查

安装后,验证算子是否可用:

python -c "
import batch_invariant_ops
import torch
op = torch.ops.batch_invariant_ops.npu_matmul_batch_invariant
print(op)
assert 'npu_matmul_batch_invariant' in str(op)
"

启用批次不变性

可以通过将 VLLM_BATCH_INVARIANT 环境变量设置为 1 来启用批次不变性:

export VLLM_BATCH_INVARIANT=1

在线推理(服务器模式)

启动启用了批次不变性的 vLLM 服务器:

VLLM_BATCH_INVARIANT=1 vllm serve Qwen/Qwen3-8B \
    --no-enable-chunked-prefill \
    --no-enable-prefix-caching \
    --block-size 128

然后使用 OpenAI 兼容的客户端:

from openai import OpenAI

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

# These requests will produce deterministic outputs
# regardless of batch size or order
response = client.completions.create(
    model="Qwen/Qwen3-8B",
    prompt="The future of AI is",
    max_tokens=100,
    temperature=0.7,
    seed=42,
)

print(response.choices[0].text)

离线推理

对于具有批次不变性的离线批次推理:

import os
os.environ["VLLM_BATCH_INVARIANT"] = "1"

from vllm import LLM, SamplingParams

prompts = [
    "The future of AI is",
    "Machine learning enables",
    "Deep learning models can",
]

sampling_params = SamplingParams(
    temperature=0.7,
    max_tokens=100,
    seed=42,
)

llm = LLM(
    model="Qwen/Qwen3-8B",
    tensor_parallel_size=1,
    enable_prefix_caching=False,
    enable_chunked_prefill=False,
    block_size=128,
)

# Outputs will be deterministic regardless of batch size
outputs = llm.generate(prompts, sampling_params)

for output in outputs:
    prompt = output.prompt
    generated_text = output.outputs[0].text
    print(f"Prompt: {prompt!r}")
    print(f"Generated: {generated_text!r}\n")

调度限制

批处理不变性不支持分块预填充(chunked prefill)、前缀缓存(prefix caching)以及请求抢占(驱逐和重新计算)。

这些调度特性不会被自动禁用。你必须在配置中显式禁用分块预填充和前缀缓存,并将禁用分块预填充与 KV 缓存块大小 128 搭配使用——启动服务时同时传入 --block-size 128 和 --no-enable-chunked-prefill,离线推理时同时设置 block_size=128 和 enable_chunked_prefill=False——如上述示例所示。

当 KV 缓存耗尽时会触发请求抢占:被抢占的请求会被驱逐并在稍后重新计算。重新计算的预填充包含抢占之前生成的 token,因此注意力机制会通过预填充(P)路径而非原本的解码(D)路径处理这些 token——P 和 D 的计算无法对齐,从而破坏批处理不变性。为降低抢占发生的概率,可增加可用的 KV 缓存,或降低单请求及并发压力:

  • 减小 --max-num-seqs,使更少的请求共享 KV 缓存。
  • 将 --max-model-len 设置为你的工作负载所需的最小值,并限制单请求的输出长度(max_tokens)。
  • 提高 --gpu-memory-utilization,为 KV 缓存留出更多内存。

使用启动日志(GPU KV cache size 和 Maximum concurrency for ... tokens per request)根据 KV 缓存容量来规划你的工作负载,并关注引擎统计信息:GPU KV cache usage 接近 100% 表示即将发生抢占。详情请参阅抢占常见问题。

已测试模型

批次不变性已在以下模型上进行了测试和验证:

Model Atlas A2 Atlas A3 950PR&950DT Products
Qwen/Qwen3-1.7B ✅ ✅ ✅
Qwen/Qwen3-8B ✅ ✅ ✅
Qwen/Qwen3-30B-A3B ✅ ✅ ✅
Qwen/Qwen3-235B-A22B ✅ ✅ ✅
Qwen/Qwen3.5-35B-A3B ✅ ✅ ❌

其他模型也可能适用,但这些模型已明确验证。如果您在特定模型上遇到问题,请在 GitHub 问题跟踪器 上报告。

实现细节

当启用批次不变性时,vLLM:

  1. 对注意力和其他操作使用确定性内核实现
  2. 确保不同批次大小下数值行为一致
  3. 禁用可能引入非确定性的某些优化

Note

与默认的非确定性模式相比,启用批次不变性可能会影响性能。这种权衡是故意的,以保证可复现性。

未来改进

批次不变性功能正在积极开发中。计划中的改进包括:

  • 支持更多 NPU 系列
  • 扩展模型覆盖范围
  • 性能优化
  • 额外的测试与验证

有关最新状态和贡献想法,请参见 跟踪问题。