文档版本: v3.0 更新日期: 2026-03-26 适用环境: Ascend 910B / A2 / A3, TP=4 镜像版本:
vllm-ascend:v0.17.0rc1
| 项目 | 说明 |
|---|---|
| 镜像版本 | vllm-ascend-0.17.0rc1 |
| 模型路径 | /models/GLM-4.7-Flash |
| vLLM 仓库路径 | /vllm-workspace/vllm |
| vLLM-Ascend 仓库路径 | /vllm-workspace/vllm-ascend |
| 补丁文件 | vllm-v0.17.0rc1-glm47flash.patch、vllm-ascend-v0.17.0rc1-glm47flash.patch |
在当前仓库根目录执行以下命令,将两个补丁分别应用到 vllm 和 vllm-ascend:
cd /path/to/glm-4.7-flash
PATCH_DIR=$(pwd)
cd /vllm-workspace/vllm
git apply --check "${PATCH_DIR}/vllm-v0.17.0rc1-glm47flash.patch"
git apply "${PATCH_DIR}/vllm-v0.17.0rc1-glm47flash.patch"
cd /vllm-workspace/vllm-ascend
git apply --check "${PATCH_DIR}/vllm-ascend-v0.17.0rc1-glm47flash.patch"
git apply "${PATCH_DIR}/vllm-ascend-v0.17.0rc1-glm47flash.patch"ls /vllm-workspace/vllm/vllm/model_executor/models/glm4_moe_lite*.py
ls /vllm-workspace/vllm/vllm/transformers_utils/configs/glm4_moe_lite.pyHCCL_OP_EXPANSION_MODE=AIV vllm serve /models/GLM-4.7-Flash \
--served-model-name GLM-4.7-Flash \
--tensor-parallel-size 4 \
--max-model-len 32768 \
--port 8000HCCL_OP_EXPANSION_MODE=AIV vllm serve /models/GLM-4.7-Flash \
--served-model-name GLM-4.7-Flash \
--tensor-parallel-size 4 \
--max-model-len 32768 \
--enable-expert-parallel \
--speculative-config '{"method":"mtp","num_speculative_tokens":1}' \
--port 8013如需工具调用和推理解析,可追加 --enable-auto-tool-choice --tool-call-parser glm47 --reasoning-parser glm45。
说明:
--tensor-parallel-size 4,当前适配与主要测试结果均基于 TP=4。--max-model-len 32768,优先保证首次启动成功、较低显存压力和更快的启动速度。--max-model-len;模型上限约为 202752 tokens。HCCL_OP_EXPANSION_MODE=AIV 用于支持 200k 级长上下文;即使快速开始先跑 32k,仍建议保留该环境变量。num_speculative_tokens=1。num_speculative_tokens=2 可跑,但启动更慢,稳态 decode 不如 k=1。| 参数 | 值 |
|---|---|
| 模型名称 | GLM-4.7-Flash (glm4_moe_lite) |
| 模型规模 | 约 30B(MoE 架构) |
| 注意力机制 | MLA(Multi-Head Latent Attention) |
| 注意力头数 | 20 |
| KV 头数 | 20(与注意力头数相同) |
| 隐藏层维度 | 2048 |
| 层数 | 47 |
qk_nope_head_dim = 192 # Query/Key 非位置编码维度
qk_rope_head_dim = 64 # Query/Key 的 RoPE 维度
v_head_dim = 256 # Value 维度
kv_lora_rank = 512 # KV 压缩秩
q_lora_rank = 768 # Query 压缩秩| 参数 | DeepSeek V2/V3 | GLM-4.7-Flash |
|---|---|---|
qk_nope_head_dim | 128 | 192 |
qk_rope_head_dim | 64 | 64 |
v_head_dim | 128 | 256 |
| 注意力头数 | 128(2 的幂次) | 20(非 2 的幂次) |
核心结论: GLM-4.7-Flash 的 MLA 维度与 Ascend NPU 优化算子 npu_ring_mla 不兼容,不能直接复用 DeepSeek 系列的现成路径。
20 / 4 = 5,不是 2 的幂次,不满足 NPU MLA 算子的 group 约束。npu_ring_mla,因为该算子硬编码期望维度为 128 / 64 / 128,与 GLM 的 192 / 64 / 256 不一致。decode 阶段通过 head padding 将单卡本地头数从 5 补到 8,计算结束后再裁剪回原始头数:
# 在 AscendMLAImpl 内部,self.num_heads 表示单卡本地头数
local_num_heads = self.num_heads # TP=4 时为 5
need_head_padding = not _is_power_of_2(local_num_heads)
padded_num_heads = _next_power_of_2(local_num_heads) # 5 -> 8
# 对 Q 张量进行填充
if need_head_padding:
q_nope = torch.nn.functional.pad(q_nope, (0, 0, 0, pad_heads))
q_pe = torch.nn.functional.pad(q_pe, (0, 0, 0, pad_heads))
# 计算后裁剪回原始头数
if need_head_padding:
attn_output = attn_output[:local_num_heads]npu_fused_infer_attention_score由于 npu_ring_mla 维度不兼容,npu_fusion_attention 的 causal mask 又无法正确生效,prefill 最终切换到 npu_fused_infer_attention_score:
def _forward_prefill_fallback(self, q_nope, q_pe, k_nope, k_pe, value, ...):
# 合并 Q/K 的 nope 和 pe,满足 query_dim >= value_dim 约束
query = torch.cat([q_nope, q_pe], dim=-1) # [T, N, 256]
key = torch.cat([k_nope, k_pe], dim=-1) # [T, N, 256]
# 使用 BNSD 格式 + sparse_mode=3,逐序列计算
for seq_len in query_lens_list:
causal_mask = self._get_prefill_fallback_causal_mask(seq_len, query.device)
q_bnsd = query[start:end].transpose(0, 1).unsqueeze(0) # [1, N, S, D]
attn_out, lse = torch_npu.npu_fused_infer_attention_score(
query=q_bnsd, key=k_bnsd, value=v_bnsd,
input_layout="BNSD", sparse_mode=3, atten_mask=causal_mask,
softmax_lse_flag=True,
)
# chunked-prefix: 从 paged KV cache 取回历史上下文,
# 用 float32 logsumexp 风格合并,避免长序列重复问题
if chunked_context is not None:
for chunk_idx, toks in enumerate(chunked_context.seq_tot):
# npu_paged_cache_load -> kv_b_proj -> attention -> merge
merged_output = base_output * exp(base_lse - merged_lse) \
+ update_output * exp(update_lse - merged_lse)这个实现的关键约束如下:
| 配置 | 结果 |
|---|---|
sparse_mode=0 + pre_tokens/next_tokens | 输出错误 |
sparse_mode=3 + causal mask(大小为 max(2048, seq_len)) | 输出正确 |
| 补丁 | 文件路径 | 修改说明 |
|---|---|---|
vllm-v0.17.0rc1-glm47flash.patch | vllm/tokenizers/hf.py | 按模型 config.json 决定 fix_mistral_regex,避免数字/日期 token 拆分异常 |
vllm-v0.17.0rc1-glm47flash.patch | vllm/config/speculative.py | 兼容 checkpoint 中 num_nextn_predict_layers: 0 但实际含 MTP 权重的情况 |
vllm-v0.17.0rc1-glm47flash.patch | vllm/model_executor/models/glm4_moe_lite.py | 修正 get_spec_layer_idx_from_weight_name 以发现隐藏的 MTP 层 |
vllm-v0.17.0rc1-glm47flash.patch | vllm/model_executor/models/glm4_moe_lite_mtp.py | MTP 从 speculative_config.draft_model_config 读取 drafter config |
vllm-v0.17.0rc1-glm47flash.patch | vllm/transformers_utils/config.py | 注册 glm4_moe_lite config 入口 |
vllm-v0.17.0rc1-glm47flash.patch | vllm/transformers_utils/configs/__init__.py | 导出 Glm4MoeLiteConfig |
vllm-v0.17.0rc1-glm47flash.patch | vllm/transformers_utils/configs/glm4_moe_lite.py | 为目标 0.17.0rc1 环境补齐 Glm4MoeLiteConfig |
vllm-ascend-v0.17.0rc1-glm47flash.patch | vllm_ascend/attention/mla_v1.py | 添加 head padding、prefill fallback(含 chunked-prefix 支持) |
npu_ring_mla:不可用。原因是维度硬编码为 128 / 64 / 128,无法适配 GLM 的 192 / 64 / 256。npu_fusion_attention:不可用。原因是 TND 布局下 causal mask 无法正确生效,与 MLA metadata 也不匹配。npu_fused_infer_attention_score:已验证可用。在 BNSD 布局、sparse_mode=3、动态 causal mask(大小为 max(2048, seq_len))条件下结果正确。| # | 问题描述 | 典型报错 | 解决方案 |
|---|---|---|---|
| 1 | TP=8 头数不整除 | 20 must be divisible by 8 | 改用 TP=4 |
| 2 | NPU 算子要求 group 为 2 的幂次 | group num should be in 1,2,4,8... | 对 decode 路径做 head padding(5 -> 8) |
| 3 | npu_ring_mla 与 GLM 维度不兼容 | AtbRingMLAGetWorkspaceSize failed | 改用 npu_fused_infer_attention_score |
| 4 | 目标 0.17.0rc1 环境缺失 glm4_moe_lite.py config 文件 | 启动时 KeyError: glm4_moe_lite | 补丁新增该文件并注册 |
| 5 | checkpoint num_nextn_predict_layers: 0 但含 MTP 权重 | MTP drafter 初始化失败 | 补丁在 speculative config 中兜底为 1 |
| 6 | MTP drafter 误读 target config | num_nextn_predict_layers 被视为 0 | 改从 speculative_config.draft_model_config 读取 |
| 7 | fix_mistral_regex=True 导致数字/日期 token 逐位拆分 | 数值输出异常 | 按 config.json 中 transformers_version 自动决定 |
| 8 | 长序列重复 | prefill fallback 丢失 chunked-prefix 上下文 | 补丁增加 chunked-prefix logsumexp 合并 |
pkill -9 -f vllm && sleep 10 后重启服务。--max-model-len 或 --max-num-seqs。本节包含基础功能验证、开启 MTP 的在线服务吞吐测试,以及长上下文在线服务测试。不同表格可能来自不同测试场景,表内已注明关键配置。
以下为基础功能验证。vllm serve 启动后,直接调用 OpenAI 兼容接口 /v1/chat/completions 即可;若你使用的是 Graph 基线,端口为 8000,若使用 EP + MTP 快路径,端口为 8013。请求体中的 "model" 需要与启动参数 --served-model-name 保持一致。
BASE_URL=http://127.0.0.1:8000 # 若使用 EP + MTP 快路径则改为 http://127.0.0.1:8013
curl -s "${BASE_URL}/v1/chat/completions" \
-H "Content-Type: application/json" \
-d '{
"model": "GLM-4.7-Flash",
"messages": [
{"role": "user", "content": "What is the capital of France?"}
],
"temperature": 0,
"max_tokens": 32
}' | python -m json.tool正常情况下,可在返回结果的 choices[0].message.content 中看到 Paris。
如需再做一个最小续写验证,可将请求体改为:
{
"model": "GLM-4.7-Flash",
"messages": [
{"role": "user", "content": "Continue this sentence in one short line: San Francisco is a"}
],
"temperature": 0,
"max_tokens": 32
}以下数据为 2026-03-26 在本地 Ascend 环境复测的 vllm bench serve 结果:TP=4,request-rate=inf,temperature=0,--ignore-eos,1k input / 1k output。
| 场景 | 基线 | MTP k=1 | MTP k=3 | 观察 |
|---|---|---|---|---|
| 1k/1k@1 | 15.1 tok/s | 31.4 tok/s | 25.5 tok/s | k=1 最高 |
| 1k/1k@16 | 219.8 tok/s | 370.7 tok/s | 359.9 tok/s | k=1 最高 |
结论:
vllm-ascend-0.17.0rc1 + 本补丁 + 本地 Ascend 环境中,k=1 在本次复测的单并发和 16 并发场景下都优于 k=3。以下数据为 2026-03-26 在本地 Ascend 环境复测的 vllm bench serve 结果:TP=4,未启用 MTP,--max-model-len 202752,request-rate=inf,max-concurrency=1,temperature=0,--ignore-eos。
| 输入 Token 数 | 输出 Token 数 | 耗时 | 吞吐量 |
|---|---|---|---|
| 10k | 1k | 68.4s | 14.6 tok/s |
| 50k | 2k | 144.7s | 13.8 tok/s |
| 100k | 4k | 298.9s | 13.4 tok/s |
| 150k | 32k | 2348.9s | 13.6 tok/s |
以下数据为 2026-03-26 在本地 Ascend 环境复测的 vllm bench serve 结果:TP=4,启用 MTP k=1,--max-model-len 202752,request-rate=inf,max-concurrency=1,temperature=0,--ignore-eos。
| 输入 Token 数 | 输出 Token 数 | 耗时 | 吞吐量 | TTFT | TPOT |
|---|---|---|---|---|---|
| 10k | 1k | 34.7s | 28.8 tok/s | 1344.0 ms | 33.4 ms |
| 50k | 2k | 75.2s | 26.6 tok/s | 8140.9 ms | 33.6 ms |
| 100k | 4k | 157.7s | 25.4 tok/s | 18376.3 ms | 34.8 ms |
| 150k | 32k | 2123.4s | 15.1 tok/s | 27353.4 ms | 65.5 ms |
k=1 在 1k/1k 的单并发和 16 并发场景下都优于 k=3,相比 graph 基线带来约 69% - 108% 的吞吐提升。k=1 对 10k/1k、50k/2k、100k/4k 三组请求带来约 89% - 97% 的吞吐提升,但 150k/32k 仅提升约 11%。150k/32k 的 MTP 收益显著小于前面三组长上下文请求。