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
修复了什么
- **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 计算慢。 - 输出投影的 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,因此没有采用。 - **compressor GEMM 触发设备参数错误。**原 BF16 输入、FP32 输出的 GEMM 在 PPU 上报
CUBLAS_STATUS_INVALID_VALUE;失败后再在同一流上转换 FP32 会继续报invalid argument。补丁直接把两侧输入转换为 FP32,再执行普通torch.mm,避免先调用已知失败的 GEMM。 - 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 和 VLLM_ENGINE_READY_TIMEOUT_S=3600。后者为权重加载与首次 JIT 编译留出时间;默认的 600 秒在本机使 API 进程提前退出。
在新容器里启动已验证的配置(把 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。传入文件名时将日志写到 examples/ 目录下该文件;不传文件名时,vLLM 的输出直接进入启动器的 stdout,例如在容器内前台运行 python /opt/ppu-deepseek-v4-pro-patch/examples/launch.py。它运行的核心命令如下;如果按原有编排系统直接运行 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 模式以及生产性能尚未验证。原始未禁用辅助流的失败不能仅凭当前结果断定就是多流造成的。