## 海光 DCU(K100\_AI)上跑通第一个大模型:[Qwen3-0.6B](https://zhida.zhihu.com/search?content_id=279059623&content_type=Article&match_order=1&q=Qwen3-0.6B&zhida_source=entity) + [vLLM](https://zhida.zhihu.com/search?content_id=279059623&content_type=Article&match_order=1&q=vLLM&zhida_source=entity) 0.11 实战部署
### 写在前面
国产 AI 加速卡这两年越来越能打,但生态和文档比起 [CUDA](https://zhida.zhihu.com/search?content_id=279059623&content_type=Article&match_order=1&q=CUDA&zhida_source=entity) 还是差着一截——同样一句 `vllm serve`,在 N 卡上能直接跑,搬到 DCU 上往往要先踩几脚坑。这篇文章记录我在一台[海光 DCU](https://zhida.zhihu.com/search?content_id=279059623&content_type=Article&match_order=2&q=%E6%B5%B7%E5%85%89+DCU&zhida_source=entity) 服务器上,把最小的 Qwen3-0.6B 用 vLLM 跑通、并对外提供 OpenAI 兼容 API 的全过程。
为什么从 0.6B 起步?因为模型小(BF16 权重只有 1.5GB),单卡就能装下,排除了模型本身的问题,专门用来验证「驱动 → DTK → [PyTorch-DCU](https://zhida.zhihu.com/search?content_id=279059623&content_type=Article&match_order=1&q=PyTorch-DCU&zhida_source=entity) → vLLM」这一整条[推理链路](https://zhida.zhihu.com/search?content_id=279059623&content_type=Article&match_order=1&q=%E6%8E%A8%E7%90%86%E9%93%BE%E8%B7%AF&zhida_source=entity)是不是通的。链路一旦通了,后面再上更大的模型,基本就是调参数的事。
### 硬件与软件环境
| 项目 | 配置 |
| ----------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 加速卡 | 海光 K100\_AI × 8(HCU 0–7),单卡 64 GB HBM |
| CPU | Hygon C86-4G,192 核,8 个 NUMA 节点 |
| 内存 | 125 GB DDR4 |
| 系统 | [Ubuntu 22.04](https://zhida.zhihu.com/search?content_id=279059623&content_type=Article&match_order=1&q=Ubuntu+22.04&zhida_source=entity) / Kernel 5.15 |
| [计算栈](https://zhida.zhihu.com/search?content_id=279059623&content_type=Article&match_order=1&q=%E8%AE%A1%E7%AE%97%E6%A0%88&zhida_source=entity) | DTK 26.04([HIP](https://zhida.zhihu.com/search?content_id=279059623&content_type=Article&match_order=1&q=HIP&zhida_source=entity) 6.3.25521,对应 NVIDIA 的 [ROCm](https://zhida.zhihu.com/search?content_id=279059623&content_type=Article&match_order=1&q=ROCm&zhida_source=entity)) |
| 推理框架 | vLLM 0.11.0(DTK 26.04 专用 wheel,基于 torch 2.5.1) |
| 其它 | transformers 4.57、[flash-attn](https://zhida.zhihu.com/search?content_id=279059623&content_type=Article&match_order=1&q=flash-attn&zhida_source=entity)(DCU 版) |
几个关键认知先记下:
* 海光的 **DTK** 对应 NVIDIA 的 ROCm/HIP,装在 `/opt/dtk-26.04/`。每次开新终端要先 `source /opt/dtk-26.04/env.sh`,它会自动设好 `DTKROOT`、`ROCM_PATH`、`AMDGPU_TARGETS` 等[环境变量](https://zhida.zhihu.com/search?content_id=279059623&content_type=Article&match_order=1&q=%E7%8E%AF%E5%A2%83%E5%8F%98%E9%87%8F&zhida_source=entity)。
* PyTorch-DCU 保留了 `torch.cuda.*` 这套 API,业务代码几乎不用改;集合通信层把 RCCL 暴露成了 `nccl` 这个名字,所以直接 `dist.init_process_group(backend='nccl')` 就能用。
* 8 张卡按 NUMA 两两分组(HCU 0-1 → NUMA0,2-3 → NUMA3,4-5 → NUMA4,6-7 → NUMA7)。多卡训练记得 `numactl --interleave=all` 绑核;单卡推理不用管。
### 第一步:把模型权重拉到本地
国内直连 HuggingFace 基本不通,走 [ModelScope](https://zhida.zhihu.com/search?content_id=279059623&content_type=Article&match_order=1&q=ModelScope&zhida_source=entity)(魔搭社区)最稳。Qwen3-0.6B 的 BF16 权重只有 1.5 GB,一个 `model.safetensors` 文件就搞定:
```text
pip install modelscope
modelscope download --model Qwen/Qwen3-0.6B \
--local_dir /your/model/path/Qwen3-0.6B
```
下完之后目录里关键的就这么几个:`config.json`、`model.
safetensors`、`tokenizer.json`、`generation_config.json`。vLLM 会自动识别 `Qwen3ForCausalLM` 这个架构,不需要额外的 remote code。
### 第二步:一行命令启动 vLLM 服务
核心就一条命令,建议存成 `start.sh` 后台跑:
```text
#!/bin/bash
source /opt/dtk-26.04/env.sh
# 关键:Triton 的 AMD 后端在 DTK 下要找 ld.lld,而不是 lld
export TRITON_HIP_LLD_PATH=/opt/dtk-26.04/llvm/bin/ld.lld
# 指定单卡(HCU 0),0.6B 一张卡绰绰有余
export HIP_VISIBLE_DEVICES=0
exec vllm serve /your/model/path/Qwen3-0.6B \
--served-model-name Qwen3-0.6B \
--tensor-parallel-size 1 \
--dtype bfloat16 \
--max-model-len 8192 \
--enforce-eager \
--host 0.0.0.0 \
--port 8001
```
几个参数解释一下:
* `--enforce-eager`:关掉 CUDA Graph,DCU 上 eager 模式更稳,对 0.6B 这点性能损失可以忽略。
* `--dtype bfloat16`:显式[指定精度](https://zhida.zhihu.com/search?content_id=279059623&content_type=Article&match_order=1&q=%E6%8C%87%E5%AE%9A%E7%B2%BE%E5%BA%A6&zhida_source=entity),避免某些情况下被悄悄回退成 [fp32](https://zhida.zhihu.com/search?content_id=279059623&content_type=Article&match_order=1&q=fp32&zhida_source=entity) 导致[显存](https://zhida.zhihu.com/search?content_id=279059623&content_type=Article&match_order=1&q=%E6%98%BE%E5%AD%98&zhida_source=entity)翻倍。
* `--max-model-len 8192`:上下文长度上限,按业务需要调。
启动后盯着日志,看到下面这两行就算成功:
```text
INFO ... Application startup complete.
INFO ... Starting vLLM API server 0 on http://0.0.0.0:8001
```
### 第三步:用 OpenAI 兼容[接口测试](https://zhida.zhihu.com/search?content_id=279059623&content_type=Article&match_order=1&q=%E6%8E%A5%E5%8F%A3%E6%B5%8B%E8%AF%95&zhida_source=entity)
vLLM 暴露的是标准的 OpenAI Chat API,任何客户端——curl、Python SDK、Dify、NextChat——都能直接对接,不用改代码:
```text
# 1) 健康检查 + 列出已加载模型
curl http://localhost:8001/v1/models
# 2) 聊天补全
curl -X POST http://localhost:8001/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "Qwen3-0.6B",
"messages": [{"role":"user","content":"用一句话介绍你自己"}],
"max_tokens": 500,
"temperature": 0.6
}'
```
我跑了两组测试:一组中文自我介绍,一组简单算术(`23+45`)。中文回答通顺,算术也对——作为链路冒烟测试完全合格。
### 实测数据
单卡(K100\_AI,64 GB HBM)下的表现:
| 指标 | 数值 |
| ------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------- |
| 模型权重占用 | 约 1.12 GiB |
| KV cache[预分配](https://zhida.zhihu.com/search?content_id=279059623&content_type=Article&match_order=1&q=%E9%A2%84%E5%88%86%E9%85%8D&zhida_source=entity) | 约 54 GiB(vLLM 默认吃掉 0.9×显存做池子) |
| 服务就绪时间 | 约 50 秒 |
| 生成速度 | 220 tokens / 4 秒(max\_tokens=500 完整输出) |
必须说一句:0.6B 的能力天花板就在那——[复杂推理](https://zhida.zhihu.com/search?content_id=279059623&content_type=Article&match_order=1&q=%E5%A4%8D%E6%9D%82%E6%8E%A8%E7%90%86&zhida_source=entity)、长文写作别指望它。但作为「整条推理链路能不能跑」的验证,它足够了,而且加载快、好排查。
### 踩过的三个坑
**坑一:第一次启动直接报 `Free memory on device (5.36/63.98 GiB)`。**
明明是张空卡,怎么只剩 5 GB?原因是 vLLM 进程被 kill 之后,HIP 的 device memory 并不会立刻还给驱动(N 卡没这个毛病),要等几十秒,或者干脆换一张确定空闲的卡(改 `HIP_VISIBLE_DEVICES`)。我后来的习惯是:不同模型钉在不同卡上,互不干扰。
**坑二:多卡(TP≥2)启动崩在 `PassManager::run failed`。**
这是 Triton 的 AMD 后端硬编码去找 `/opt/rocm/llvm/bin/ld.lld`,而 DCU 用的是 DTK 路径。解法就是脚本开头那行 `export TRITON_HIP_LLD_PATH=/opt/dtk-26.04/llvm/bin/ld.lld`。**注意必须是 `ld.lld`,不能写成 `lld`**——因为 `lld` 看到 basename 是 `lld` 时会以为自己是通用驱动而拒绝执行链接。单卡 TP=1 其实根本不触发 Triton JIT,但写上不冲突,省得以后切多卡时再回头踩。
**坑三:`huggingface.co` 连不上。**
要么走 ModelScope,要么设 `export HF_ENDPOINT=https://hf-mirror.com`。另外有个隐藏雷:torch 2.5.1 是按 NumPy 1.x 的 ABI 编译的,环境里的 [numpy](https://zhida.zhihu.com/search?content_id=279059623&content_type=Article&match_order=1&q=numpy&zhida_source=entity) 必须 `<2`,否则 import 阶段就直接崩,报错还不太直观。
### 小结
国产 DCU 跑大模型推理,难的不是模型、也不是框架,而是「把 N 卡那一套思维定式掰过来」——路径不一样、[链接器](https://zhida.zhihu.com/search?content_id=279059623&content_type=Article&match_order=1&q=%E9%93%BE%E6%8E%A5%E5%99%A8&zhida_source=entity)不一样、显存回收机制也不一样。我的建议是:**先用 Qwen3-0.6B 这种小模型把链路打通、把坑踩平**,把启动脚本和环境变量固化下来,后面上 7B、32B 乃至[多模态模型](https://zhida.zhihu.com/search?content_id=279059623&content_type=Article&match_order=1&q=%E5%A4%9A%E6%A8%A1%E6%80%81%E6%A8%A1%E5%9E%8B&zhida_source=entity),就是顺水推舟。
下一篇我会记录在这同一台机器上部署 **MiniCPM-V 多模态模型**(看图说话)的过程,踩的坑和这篇又不一样,欢迎继续关注。
---
*环境信息:海光 K100\_AI × 8 / DTK 26.04 / vLLM 0.11.0 / Qwen3-0.6B(BF16)。*