注意力后端的选择与自动降级:环境变量与命中证据

在 **** 的 ****(8 卡,单卡 64GB HBM,卡间互联 392 GB/s)上用 **** 的基础软件栈配合 vLLM 0.6.x 起 OpenAI 兼容服务。为固定执行路径,启动前显式 export VLLM_ATTENTION_BACKEND=FLASH_ATTN。服务正常起来,输出正确,但吞吐低于同规格基线,启动日志里却读不到任何「用了哪个注意力后端」的记录:既没有确认命中的行,也没有报错。这个版本里注意力后端由环境变量选择,但环境变量不是终局:selector.py 读到它之后还要经过平台分支与一组降级分支,任一分支命中即改写选择结果。而且证据是不对称的——只有命中非默认后端时才打印一行 info,命中默认的 FLASH_ATTN 时一行都没有,于是「日志里没有后端记录」这件事本身给不出结论。显式指定不等于命中。

机制:selector.py 的两级选择

vLLM 0.6.x 的注意力后端入口是 vllm/attention/selector.py 的 get_attn_backend(num_heads, head_size, num_kv_heads, sliding_window, dtype, kv_cache_dtype, block_size, is_blocksparse=False)。该函数带 lru_cache,实际选择交给 which_attn_to_use(...)。

选择分两级。第一级取进程内强制值 forced_attn_backend(由 global_force_attn_backend 设置),优先级高于环境变量;未设置时读环境变量 VLLM_ATTENTION_BACKEND(常量 STR_BACKEND_ENV_VAR,读入点 envs.VLLM_ATTENTION_BACKEND)。默认值是 _Backend.FLASH_ATTN。第二级是一组降级分支,在平台或输入约束不满足时改写已选值。

环境变量的合法取值是 _Backend 枚举成员名,大小写敏感,取错值抛:

ValueError: Invalid attention backend '<取值>'. Available backends:
FLASH_ATTN, XFORMERS, ROCM_FLASH, TORCH_SDPA, OPENVINO, FLASHINFER, PALLAS, IPEX (case-sensitive).
取值 实现类 适用平台 / 说明
FLASH_ATTN FlashAttentionBackend NVIDIA 默认;命中时不打印日志
XFORMERS XFormersBackend 降级目标
FLASHINFER FlashInferBackend fp8 KV cache 的推荐后端
ROCM_FLASH ROCmFlashAttentionBackend HIP 平台
TORCH_SDPA TorchSDPABackend 仅 CPU
OPENVINO OpenVINOAttentionBackend OpenVINO
IPEX IpexAttnBackend XPU
PALLAS PallasAttentionBackend TPU

该版本没有对应的 CLI 旗标,后端只能由环境变量或进程内强制值指定。

平台分支先行改写

which_attn_to_use 先处理平台分支。这些分支不看环境变量是否与之冲突,只在冲突时打一行 logger.info 再返回平台固定后端:

平台 返回 冲突时的提示
CPU TORCH_SDPA Cannot use %s backend on CPU.
OpenVINO OPENVINO Cannot use %s backend on OpenVINO.
XPU IPEX Cannot use %s backend on XPU.
TPU PALLAS Cannot use %s backend on TPU.
HIP ROCM_FLASH 原选 FLASH_ATTN 时先改写为 ROCM_FLASH;否则打 %s is not supported in AMD GPUs.

在非对应平台上设了环境变量,返回值与设置值不同,且只有一行 info 级提示。

NVIDIA 上的逐级降级

平台分支之后,若已选值仍是 FLASH_ATTN,逐条检查下列条件,命中任一条即改写为 XFORMERS:

条件 结果 日志片段(info 级)
算力 < 80(Volta / Turing) XFORMERS Cannot use FlashAttention-2 backend for Volta and Turing GPUs.
dtype 非 torch.float16 / torch.bfloat16 XFORMERS Cannot use FlashAttention-2 backend for dtype other than ...
kv_cache_dtype 以 fp8 开头 XFORMERS Cannot use FlashAttention-2 backend for FP8 KV cache.
block_size % 16 != 0 XFORMERS Cannot use FlashAttention-2 backend for block size not divisible by 16.
sliding_window is not None XFORMERS Cannot use FlashAttention-2 backend due to sliding window.
head_size 不在支持集,或 vllm.vllm_flash_attn 导入失败 XFORMERS ... because the vllm.vllm_flash_attn package is not found.

fp8 KV cache 一条除 info 外多打一行 warning,建议改设 VLLM_ATTENTION_BACKEND=FLASHINFER。

命中证据的不对称

日志只在「非 FLASH_ATTN」时出现。FLASH_ATTN 命中时 get_attn_backend 不打印任何行;XFORMERS、FLASHINFER、ROCM_FLASH、TORCH_SDPA 等命中时各打一行:

# 命中非默认后端时的日志(片段,字段名示意)
INFO  Using XFormers backend.
INFO  Using Flashinfer backend.
INFO  Using ROCmFlashAttention backend.

「读到一行后端提示」说明发生了降级或指定了非默认后端;「一行都没有」只能说明走的是默认值 FLASH_ATTN,不能反推环境变量已生效。两条证据要分开读。

触发条件与现象

定位步骤

  1. 确认选择的来源:检查进程内是否调用了 global_force_attn_backend(优先级高于环境变量),再看 VLLM_ATTENTION_BACKEND 是否设置、拼写与大小写是否正确。
  2. 搜启动日志中的后端提示行:Using 、Cannot use FlashAttention-2 backend 等关键字;info 级输出可能被日志级别过滤,必要时先调高 VLLM_LOGGING_LEVEL。
  3. 若完全无提示,记为「默认 FLASH_ATTN 命中」,再逐条核对降级条件是否可能触发。
  4. 核对 block_size:确认生效值是 16 的倍数,命令行取值不等于生效值。
  5. 核对 kv_cache_dtype:fp8 与 FLASH_ATTN 不兼容。
  6. 核对模型特征:head_size、sliding_window。
  7. 用单变量对照复测:只改一项,观察后端提示行是否出现或消失。
# 固定路径选择并保留证据(参数名取自框架,取值按本机规格填)
export VLLM_ATTENTION_BACKEND=FLASH_ATTN
python -m vllm.entrypoints.openai.api_server \
  --model <model-path> \
  --block-size 16 \
  --kv-cache-dtype auto \
  --enforce-eager          # 先关图捕获,便于看清真实命中路径

--enforce-eager 不参与注意力后端选择,它只关闭图捕获。

处置

动作 作用 代价 / 前提
修正环境变量拼写与大小写 避免 ValueError 直接退出 只在取值合法时有效
--block-size 取 16 的倍数 满足 FLASH_ATTN 的块约束 受后端与碎片权衡约束
KV cache 保持 auto 避免 fp8 触发降级 放弃 fp8 的显存收益
改用 VLLM_ATTENTION_BACKEND=FLASHINFER fp8 KV cache 下命中预期后端 需确认版本内置 FlashInfer
逐条复核平台分支 确认环境变量不会被平台改写 平台固定后端不可改
固定后复测并与默认对照 确认命中路径真的变了 关闭图捕获的量级不代表稳态

记录字段

# 一条注意力后端选择记录(字段名示意)
selection:
  env_var: VLLM_ATTENTION_BACKEND
  env_value:              # 设置值
  forced_by_code:         # 是否被 global_force_attn_backend 覆盖
  default_backend: FLASH_ATTN
  resolved_backend:       # 实际命中
  degradation_hit:        # 命中的降级分支(无则记 none)
inputs:
  dtype:
  kv_cache_dtype:
  block_size:             # 生效值
  sliding_window:
  head_size:
  device_capability:      # 算力等级
platform:
  kind:                   # CPU / CUDA / HIP / TPU / XPU / OpenVINO
log:
  backend_line:           # 命中非默认后端时的那一行(原文)
  degraded_reason:        # Cannot use FlashAttention-2 backend ...(原文)
perf:
  prefill_stage:
  decode_stage:
status:

小结

vLLM 0.6.x 的注意力后端由环境变量 VLLM_ATTENTION_BACKEND 选择,入口是 selector.py 的 get_attn_backend,选择逻辑在 which_attn_to_use。环境变量不是终局:平台分支与一组 NVIDIA 降级分支会把已选值改写为 XFORMERS 或平台固定后端,证据只有一行 info 级日志。默认后端 FLASH_ATTN 命中时不打印任何行,因此「无日志」只等于「走了默认值」。判定顺序是先确认选择来源,再搜日志提示行,最后逐条核对 block_size、kv_cache_dtype、sliding_window、head_size 与平台。