Sun Ruoxi 8952261b8c update README
Signed-off-by: Sun Ruoxi <sunruoxi@4paradigm.com>
2026-10-21 16:02:59 +08:00
2026-10-21 15:52:23 +08:00
2026-10-21 15:52:23 +08:00
2026-10-21 15:52:23 +08:00
2026-10-21 15:52:23 +08:00
2026-10-21 15:52:23 +08:00
2026-10-21 15:52:23 +08:00
2026-10-21 15:52:23 +08:00
2026-10-21 15:52:23 +08:00
2026-10-21 15:52:23 +08:00
2026-10-21 15:52:23 +08:00
2026-10-21 16:02:59 +08:00

DeepSeek V4 Pro / PPU-ZW810E vLLM 补丁

这是 2026-10-20 至 2026-10-21 在 PPU-ZW810E 上验证过的 DeepSeek V4 Pro 推理补丁。它以原始 vLLM 镜像为基础,修改四个 vLLM Python 源文件;不修改 /model 的 checkpoint 或模型 config.json。当前验证环境是 vLLM 0.26.0、PyTorch 2.11.0、Python 3.12、16 张 PPU,TP=8、DP=2、EP 开启。

已验证的基础镜像:

swr.cn-north-4.myhuaweicloud.com/inference-engines/llm:v2.1.1-pytorch2.11.0-ubuntu24.04-cuda13.0-vllm0.26.0-py312

修复了什么

  1. **FP8 block linear 无法通过 PPU CUTLASS sm80 fallback。**原服务加载完 64 个权重 shard 后,在 cutlass_scaled_mm_sm80 / scaled_mm_c2x.cu:110 报错。运行时设置 VLLM_DISABLED_KERNELS=PPUCutlassFp8BlockScaledMMKernel,从候选列表中移除这个出错的 kernel;本次模型日志确认随后选择了 Marlin 的 FP8 weight-only 路径。PPU DeepGEMM 仍保留在候选列表中,其他形状或模型不保证总是选择 Marlin。Marlin 可能比原生 FP8 计算慢。
  2. 输出投影的 FP8 融合算子不支持该架构。_fused_inv_rope_fp8_quant_per_head 使用 fp8e4nv,而 PPU 编译器只支持 fp8e4b15、fp8e5。VLLM_PPU_BF16_O_PROJ=1 时,在 Marlin 打包前将 wo_a 的原始 block FP8 权重与 scale 解码成 BF16;输出投影使用 FP32 中间值完成 inverse RoPE、BF16 分组 bmm,然后调用现有 wo_b。这是 FP8 解码到 BF16,不是重新编码成 uint8 FP8。FP8 channelwise 路径也会遇到 fp8e4nv,因此没有采用。
  3. **compressor GEMM 触发设备参数错误。**原 BF16 输入、FP32 输出的 GEMM 在 PPU 上报 CUBLAS_STATUS_INVALID_VALUE;失败后再在同一流上转换 FP32 会继续报 invalid argument。补丁直接把两侧输入转换为 FP32,再执行普通 torch.mm,避免先调用已知失败的 GEMM。
  4. profile / 稀疏 MLA 预热仍有异步设备错误。曾在 MoE 加法以及 indexer 的 torch.empty 处看到 invalid argument;这些位置不一定是实际出错算子。仅把 VLLM_MULTI_STREAM_GEMM_TOKEN_THRESHOLD 设为 0 不会关闭注意力的辅助流。新增 VLLM_PPU_SERIAL_ATTENTION=1,使注意力、indexer、compressor 使用单流。该配置完成了启动和实际推理,但尚未单独证明多流是唯一根因。

代码位置:

文件 改动
src/vllm/model_executor/layers/quantization/fp8.py 在权重处理阶段为 wo_a 启用 BF16 解码
src/vllm/models/deepseek_v4/ppu/ops/o_proj.py BF16 输出投影与 FP32 compressor GEMM
src/vllm/models/deepseek_v4/ppu/flashmla.py 将 PPU 输出投影切换到 BF16 路径
src/vllm/models/deepseek_v4/attention.py 两处 compressor 调用与注意力单流开关

upstream/ 是从原始文件备份取得的精确版本,src/ 是成功运行的精确版本,patches/vllm.patch 是两者的统一 diff,manifest.json 记录两组 SHA-256。scripts/apply.py 在写入前核对全部四个目标文件和包内源码;遇到不同的 vLLM 版本会拒绝安装。首次安装会在目标文件旁生成 .ppu-dsv4.orig 备份。

该版本也接受 vllm serve ... --linear-backend marlin,它会要求所有适用的量化 linear 使用 Marlin;如果某种层没有可用的 Marlin kernel,启动会直接报错。它不是 VLLM_DISABLED_KERNELS=PPUCutlassFp8BlockScaledMMKernel 的等价替代,且本项目的完整模型成功验证没有使用该选项。--quantization marlin 是另一类量化配置,不能把当前 FP8 checkpoint 当作 Marlin 格式 checkpoint 来指定。

构建包含补丁的新镜像(推荐)

在 PPU 主机上执行:

cd /tmp/ppu_deepseek_v4_pro_patch
nerdctl build \
  --build-arg BASE_IMAGE=swr.cn-north-4.myhuaweicloud.com/inference-engines/llm:v2.1.1-pytorch2.11.0-ubuntu24.04-cuda13.0-vllm0.26.0-py312 \
  -t deepseek-v4-pro:ppu-bf16-serial .

若主机提示 BuildKit daemon 未运行,可启动仅用于本次构建的 BuildKit containerd worker,再指定其 socket;这样会复用 containerd 的镜像层缓存:

nohup buildkitd --oci-worker=false --containerd-worker=true \
  --containerd-worker-namespace default \
  --addr unix:///tmp/ppu-buildkit.sock --root /tmp/ppu-buildkit \
  > /tmp/ppu-buildkit.log 2>&1 < /dev/null &
nerdctl build --buildkit-host unix:///tmp/ppu-buildkit.sock \
  --build-arg BASE_IMAGE=swr.cn-north-4.myhuaweicloud.com/inference-engines/llm:v2.1.1-pytorch2.11.0-ubuntu24.04-cuda13.0-vllm0.26.0-py312 \
  -t deepseek-v4-pro:ppu-bf16-serial .

构建时的 RUN python .../scripts/apply.py --apply 会先验证原始镜像内四个文件的 SHA-256,再替换源码。nerdctl build 成功即表示补丁已写入新镜像,原镜像标签不会被覆盖。构建失败若提示 Installed source does not match this patch,说明基础镜像中的 vLLM 源码与本项目的原始版本不同;应先比较源码,不要强行跳过校验。

创建服务容器时,沿用原容器的 PPU 设备映射、权限、共享内存和网络配置,改用 deepseek-v4-pro:ppu-bf16-serial 镜像,并将模型挂载到 /model。这台机器原容器的模型挂载源是 /bmcp_lvm_fs/data/model/DeepSeek-V4-Pro,容器目标是 /model,共享内存配置为 64 GiB。PPU 设备如何注入由机器的容器运行环境决定,应复用原容器的创建参数。新镜像已经默认设置四个环境变量:VLLM_DISABLED_KERNELS、VLLM_PPU_BF16_O_PROJ、VLLM_PPU_SERIAL_ATTENTION、VLLM_MULTI_STREAM_GEMM_TOKEN_THRESHOLD。

在新容器里启动已验证的配置(把 deepseek_v4_patched 换成实际容器名):

nerdctl exec -d \
  -e CUDA_VISIBLE_DEVICES=0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 \
  deepseek_v4_patched \
  python /opt/ppu-deepseek-v4-pro-patch/examples/launch.py serve.log

启动器读取 examples/command-serial.json,将日志写到 /opt/ppu-deepseek-v4-pro-patch/examples/serve.log。它运行的核心命令如下;如果按原有编排系统直接运行 vllm serve,需同时传入镜像默认的四个环境变量,并明确指定全部 16 张卡:

vllm serve /model \
  --trust-remote-code --kv-cache-dtype fp8 --block-size 256 \
  --tensor-parallel-size 8 --data-parallel-size 2 --enable-expert-parallel \
  --no-enable-flashinfer-autotune \
  --tokenizer-mode deepseek_v4 --tool-call-parser deepseek_v4 \
  --enable-auto-tool-choice --reasoning-parser deepseek_v4 \
  --max-model-len 4096 --max-num-batched-tokens 1024 \
  --enforce-eager --port 18000

该启动器将 RLIMIT_MEMLOCK 设为 unlimited;如果容器运行时不允许此设置,需在创建容器时提供相应 memlock 限额。CUDA_VISIBLE_DEVICES 不在镜像中固定,因为实际可见的 PPU 取决于部署环境。

检查、直接安装与回滚

新镜像内可检查四个文件的状态:

nerdctl exec deepseek_v4_patched \
  python /opt/ppu-deepseek-v4-pro-patch/scripts/apply.py --check

如果要对一个尚未修改的原始容器做临时验证,可以将本目录复制进去,再检查、安装;这只修改该容器的可写层,不能替代上面的镜像构建:

cd /workspace/pytorch
nerdctl cp ./ppu_deepseek_v4_pro_patch deepseek_v4_original:/opt/ppu-deepseek-v4-pro-patch
nerdctl exec deepseek_v4_original python /opt/ppu-deepseek-v4-pro-patch/scripts/apply.py --check
nerdctl exec deepseek_v4_original python /opt/ppu-deepseek-v4-pro-patch/scripts/apply.py --apply

--apply 可重复运行;已经是补丁版本时不再替换。容器内回滚四处源码:

nerdctl exec deepseek_v4_original python /opt/ppu-deepseek-v4-pro-patch/scripts/apply.py --rollback

回滚后应停止服务并用原始环境变量重新启动;对于基于新镜像创建的容器,更直接的完整回滚方式是重新使用原始镜像。不要在正在执行推理的进程内热替换 Python 源码。

验证范围与限制

  • 四个源码文件均通过语法检查;patches/vllm.patch 可对 upstream/ 通过 git apply --check。安装脚本的幂等安装、回滚和错误版本拒绝有离线测试。
  • 合成尺寸的 BF16 wo_a 解码与输出投影相对参考计算的 L2 误差约 0.0016;(1024,7168) @ (7168,1024) 的 FP32 compressor GEMM 单卡测试通过。
  • 实际模型加载约 55 GiB/卡;TP8/DP2/EP 的服务通过 profile、KV cache 分配、稀疏 MLA 预热。一次短请求和四次并发短请求成功;两个 API 进程均处理了请求。
  • 长上下文测试中,1037、3085、3853、4045 输入 token 的请求各生成 32 token,均成功。一个 3619 输入 token 的聊天请求正确返回文中预埋的“蓝色”。测试后日志没有 ERROR。
  • 已验证配置使用 --enforce-eager 和 --max-model-len 4096。超过 4096 token、较高并发、CUDA graph 模式以及生产性能尚未验证。原始未禁用辅助流的失败不能仅凭当前结果断定就是多流造成的。
Description
No description provided
Readme 99 KiB
Languages
Python 99.7%
Dockerfile 0.3%