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

115 lines
8.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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 开启。
已验证的基础镜像:
```text
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 主机上执行:
```sh
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 的镜像层缓存:
```sh
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` 换成实际容器名):
```sh
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 张卡:
```sh
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 取决于部署环境。
## 检查、直接安装与回滚
新镜像内可检查四个文件的状态:
```sh
nerdctl exec deepseek_v4_patched \
python /opt/ppu-deepseek-v4-pro-patch/scripts/apply.py --check
```
如果要对一个**尚未修改**的原始容器做临时验证,可以将本目录复制进去,再检查、安装;这只修改该容器的可写层,不能替代上面的镜像构建:
```sh
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` 可重复运行;已经是补丁版本时不再替换。容器内回滚四处源码:
```sh
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 模式以及生产性能尚未验证。原始未禁用辅助流的失败不能仅凭当前结果断定就是多流造成的。