124 lines
9.4 KiB
Markdown
124 lines
9.4 KiB
Markdown
# 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 主机上。例如,在本机 `/Users/4paradigm/projects` 下运行:
|
||
|
||
```sh
|
||
scp -i llm_benchmark/bench-text_generation-tco/utils/id_rsa -r \
|
||
ppu_deepseek_v4_pro_patch root@172.28.4.222:/tmp/
|
||
```
|
||
|
||
在 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` 和 `VLLM_ENGINE_READY_TIMEOUT_S=3600`。后者为权重加载与首次 JIT 编译留出时间;默认的 600 秒在本机使 API 进程提前退出。
|
||
|
||
在新容器里启动已验证的配置(把 `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 模式以及生产性能尚未验证。原始未禁用辅助流的失败不能仅凭当前结果断定就是多流造成的。
|
||
|
||
历史排查细节与原始日志保存在同一工作区的 `llm_benchmark/bench-text_generation-tco/DEEPSEEK_V4_PPU_2026-10-20.md` 和 `ppu_debug_snapshot_2026-10-20/`。
|