MiniMax-M2.7:用 vLLM-Ascend 官方配方在8张昇腾 910B4 上做性能实测
本文来源: vLLM 生产工程(公众号:vLLM 生产工程)
原文链接: https://mp.weixin.qq.com/s/41NPRh8GHoWkgH6igkC5Fg
发布时间: 2026-08-27 08:55

作者: vLLM 生产工程 发布时间: 2026-08-27 08:55
咱们新系列开始啦。基于 vLLM Ascend 官方 CI 验证过的配方,咱们来补性能报告。
1. 引言:在8张昇腾 910B4 上部署 MiniMax-M2.7
MiniMax-M2.7 的 W8A8 量化权重,配方取自 vLLM-Ascend 官方文档🔗1 A2 单机一节,逐字照抄。部署形态:
- 62 层 · 48 Q / 8 KV 头 · 256 专家
- 主权重 216 GB,EAGLE3 草稿头 2.68 GB
- TP8 + 专家并行 · DP1,EAGLE3 一次 3 个候选
- KV 池 695,936 token(20.91 GiB/卡),每 token 252 KiB
max_model_len
204,800 —— 配方未写该参数,取的是模型自身上限
六档场景全部跑完,零错误率,抢占计数全为 0。
2. 测试方法
2.1 环境
| 加速卡 | 昇腾 910B4-1 × 8(单机),单卡 64 GB,节点内 HCCS |
| 驱动 | npu-smi 26.0.rc1 · torch_npu 2.10.0.post2 |
| 引擎 | vLLM 0.23.0 + vllm-ascend v0.23.0rc1 |
| 镜像 | sha256:e96491044bbc…(按内容摘要固定,不按标签) |
| 权重 | MiniMax-M2.7-w8a8-QuaRot |
| 压测工具 | sglang.bench_serving🔗2 0.5.17,打 /v1/chat/completions |
引擎和压测客户端同机,走回环地址。
关于压测工具的选择。 用 SGLang 仓库里的工具测 vLLM,看着别扭,但它是 OpenAI 协议层的负载生成器,只发 HTTP 请求、只按 SSE 分片计时,不碰引擎内部;源码里 vllm-chat 与 sglang-oai-chat 两个后端指向同一个请求函数、同一条路径,对 vLLM 没有单独的代码路径,也就谈不上偏向。
选它是因为六档里的多轮会话档需要 agentic-trace 做多轮串行回放,而 vllm bench serve 和 GuideLLM 都没有这个能力(前者的 timed_trace 是带时间戳的单轮,源码里还限定只能走 completions 后端)。既然有一档非它不可,其余五档就没有理由换工具 —— 混用工具会破坏表内可比性,尤其 GuideLLM 的 ITL 是逐 token 而这个工具的是逐分片,两者不能并排。
工具的数字也没有盲信:六档的吞吐都用服务端 /metrics 计数器增量重算过一遍,与工具自报值逐档吻合,差 0.00% 到 0.09%。
2.2 场景与口径
全部在 ignore_eos 与 temperature=0 下测得 —— 模型想停也得继续写,输出长度被强制拉满,贪心解码。
标「排队」的两档一次性灌进 1,000 条,而 --max-num-seqs 是 32,它们的 TTFT 量的是队列深度不是模型速度。
TPOT 和吞吐都带着 EAGLE3 的加成,没有关闭对照,和没开投机的系统比不对等。实测接受长度按档在 2.386(ShareGPT)到 2.608(多轮会话)之间,也就是一次猜 3 个平均落袋 2.4 到 2.6 个;由此得到的加速上界是 2.54 倍。
ITL 记的是一个解码步而不是一个 token —— 开了 EAGLE3 之后一步产出的两三个 token 挤在同一个流式分片里,所以 ITL 与 TPOT 不能相除。
输入吞吐含前缀命中的部分。多轮会话档扣掉命中后实际 prefill 只有 1,088 tok/s,不是表里的 13,271。
高生成量档那个 1,217.3 tok/s 也要打个折:它的 409,600 个输出 token 重分词后只剩 300,576,少了 27%,而同样口径、128 输出的两档重分词差值是 0 —— 长档的尾部退化成了重复文本。这个数要读作定长解码的吞吐上界,不是有效内容吞吐。
2.3 判据与样本量
容量档的 SLO 在开跑前冻结为 TTFT p95 ≤ 2,000 毫秒,取 MLPerf Inference server 场景 Llama2-70B 的约束原值。
分位数的可引用门槛同样取 MLPerf 的早停规则:p99 约需 662 条查询,p90 约 64 条。下表中带 * 的 p99 样本量不足,是真实的次序统计量但方差过大,不能用于 SLO 判定或跨轮比较。
2.4 复现命令
六档一行一档。<SG> 为本地 ShareGPT 语料路径,<TRACE> 为本地会话轨迹路径:
COMMON="--backend vllm-chat \ --base-url http://127.0.0.1:8077 \ --model /models/MiniMax-M2.7-w8a8-QuaRot \ --served-model-name MiniMax-M2.7 \ --warmup-requests 0 --output-details --disable-tqdm --seed 1024"# 定长档必须写 --random-range-ratio 1# random 档必须给 --dataset-path# 延迟python3 -m sglang.bench_serving $COMMON \ --dataset-name random --dataset-path <SG> \ --num-prompts 100 --max-concurrency 1 \ --random-input-len 1024 --random-output-len 128 \ --random-range-ratio 1# 高生成量python3 -m sglang.bench_serving $COMMON \ --dataset-name random --dataset-path <SG> \ --num-prompts 200 --max-concurrency 32 \ --random-input-len 1024 --random-output-len 2048 \ --random-range-ratio 1# 长上下文python3 -m sglang.bench_serving $COMMON \ --dataset-name random --dataset-path <SG> \ --num-prompts 100 --max-concurrency 16 \ --random-input-len 32768 --random-output-len 128 \ --random-range-ratio 1# ShareGPTpython3 -m sglang.bench_serving $COMMON \ --dataset-name sharegpt --dataset-path <SG> \ --num-prompts 1000# 吞吐python3 -m sglang.bench_serving $COMMON \ --dataset-name random --dataset-path <SG> \ --num-prompts 1000 \ --random-input-len 1024 --random-output-len 512 \ --random-range-ratio 1# 多轮会话python3 -m sglang.bench_serving $COMMON \ --dataset-name agentic-trace --dataset-path <TRACE> \ --num-prompts 16 --max-concurrency 8 \ --agentic-max-turns 32 --sharegpt-output-len 220每档之间调用 POST /reset_prefix_cache 清冷缓存,前后各采一次 /metrics 快照。容量档把 --max-concurrency 换成 --request-rate,泊松到达,九个速率点每点 300 秒。
3. 性能
吞吐
| 场景 | 并发 · n | 输出 tok/s | 输入 tok/s | 前缀命中 |
|---|---|---|---|---|
| 延迟 1024/128 | 1 · 100 | 72.9 | 609 | 0% |
| 高生成量 1024/2048 | 32 · 200 | 1,217.3 | 632 | 0.66% |
| 长上下文 32768/128 | 16 · 100 | 56.0 | 14,459 | 0% |
| ShareGPT(排队) | 不限 · 1,000 | 494.0 | 849 | 0.81% |
| 吞吐 1024/512(排队) | 不限 · 1,000 | 825.3 | 1,709 | 0.98% |
| 多轮会话 ≤32 轮 | 8 · 362 | 258.2 | 13,271 | 91.80% |
TTFT(毫秒)
| 场景 | p50 | p90 | p95 | p99 |
|---|---|---|---|---|
| 延迟 1024/128 | 355.6 | 369.7 | 377.4 | 388.6 * |
| 高生成量 1024/2048 | 443.1 | 2,094.1 | 2,098.7 | 2,102.3 * |
| 长上下文 32768/128 | 6,099.4 | 13,549.3 | 23,279.1 | 31,176.5 * |
| ShareGPT(排队) | 185,377.4 | 337,928.9 | 357,426.6 | 373,005.8 |
| 吞吐 1024/512(排队) | 300,626.4 | 546,708.1 | 579,147.5 | 603,587.2 |
| 多轮会话 ≤32 轮 | 477.2 | 760.1 | 813.6 | 1,077.9 * |
TPOT(毫秒,逐 token)
| 场景 | p50 | p90 | p95 | p99 |
|---|---|---|---|---|
| 延迟 1024/128 | 10.92 | 12.66 | 13.53 | 14.72 * |
| 高生成量 1024/2048 | 25.07 | 29.91 | 30.41 | 31.37 * |
| 长上下文 32768/128 | 241.6 | 300.7 | 317.4 | 337.0 * |
| ShareGPT | 60.06 | 85.42 | 103.2 | 178.8 |
| 吞吐 1024/512 | 38.44 | 43.51 | 45.22 | 49.19 |
| 多轮会话 ≤32 轮 | 26.35 | 33.25 | 36.38 | 43.54 * |
ITL(毫秒,逐解码步)
| 场景 | p50 | p90 | p95 | p99 |
|---|---|---|---|---|
| 延迟 1024/128 | 26.77 | 29.03 | 30.18 | 41.01 * |
| 高生成量 1024/2048 | 53.59 | 55.46 | 58.85 | 337.3 * |
| 长上下文 32768/128 | 64.51 | 1,978.8 | 1,999.2 | 2,038.8 * |
| ShareGPT | 55.38 | 377.0 | 399.2 | 418.0 |
| 吞吐 1024/512 | 53.55 | 313.7 | 340.8 | 371.2 |
| 多轮会话 ≤32 轮 | 43.91 | 63.04 | 334.2 | 391.7 * |

3.1 容量:每副本能扛多少
六档要么钉死并发、要么一次性灌完,都不是固定到达率。而这类测法测不出崩溃点 —— 系统再慢,在途请求也被并发数钉死,队列不会无限堆积。要回答容量问题得换开环:固定到达率,看队列什么时候开始发散。
我们另跑了一轮泊松到达的速率扫描:random 数据集,512 输入 / 256 输出,九个速率点,每点 300 秒,前后各采一次 /metrics 快照。SLO 在开跑前冻结为 TTFT p95 ≤ 2,000 毫秒,取的是 MLPerf server 场景 Llama2-70B 的约束原值。

六档场景全程零抢占。原因在下面这张图上:--max-num-seqs 32 与 KV 池容量两条约束,在 21,748 token 处交叉,而本轮没有一档的请求长度够得着。

结果是每副本 ### 2.25 请求/秒。服务端硬上界 2.76 请求/秒,越过 2.25 之后 TTFT p95 从 1,624 毫秒跳到 2,299 毫秒。
拐点两侧各复测了一次:2.25 两次都达标,2.42 两次都不达标。所以 safe_rps 取的是「两次都达标的最高速率」,而不是「下一档不达标所以上一档是上界」—— 后一种说法要依赖单次测量,而 TTFT p95 恰好是全表噪声最大的指标。
横轴用的是实测到达速率,不是设定值。压测客户端发不到设定速率(实测/设定在 0.840 到 0.955 之间),机制未查明;照设定速率画会把安全速率报高。
这个数是形态相关的,换负载必须重测。它同样在强制定长口径下测得,很可能偏保守。判定用的 TTFT p95 在同配置复跑下波动达 8.6% 到 22.9%,是全表噪声最大的指标,而这还是三重低估的下界(n=2、同 seed、未跨引擎重启),所以拐点只精确到相邻两档之间。
预登记里还有一条次 SLO(TPOT p95 ≤ 50 ms)。它在拐点附近实测平在 49 到 52 毫秒,与阈值重合,本实验分辨率下无法判定达标与否 —— 不能默认 2.25 请求/秒下 TPOT 也达标。
3.2 长上下文的延迟形状
长上下文档跑的是 random 数据集,32,768 输入 / 128 输出,并发 16,共 100 条请求。它的输入吞吐 14,459 tok/s 是六档最高,前缀命中率为 0,是最接近纯 prefill 能力的一档。
反常的是它的 ITL 分布:中位只有 64.5 毫秒,却有 22.7% 落在 1,500 毫秒以上,而 100 到 1,500 毫秒这一整段只占 9.3%。两个截然分开的峰,慢峰几乎是个定值。

慢峰的量级和一条 32K 提示的 prefill 耗时对得上。按该档 14,459 tok/s 折算,33,077 token 约需 2.3 秒。
再看配方:--max-num-batched-tokens 是 32,768,而单条提示实测 33,077 token(标称 32,768 加上聊天模板)。一条提示的 prefill 就能占满整个批次预算。引擎 V1 架构把 prefill 切块和 decode 混在同一个调度步里,于是 decode 步之间持续插进大块 prefill。该档每条请求只输出 128 个 token,整条请求的生命周期几乎全泡在混合批里。
这个链条讲得通,但讲得通不等于成立。我们只改这一个参数、其余逐字不动,同一档重跑了三次。

| --max-num-batched-tokens | 8,192 | 16,384 | 32,768(官方) |
|---|---|---|---|
| 输入吞吐 (tok/s) | 14,258 | 14,405 | 14,470 |
| 整批耗时 (s) | 229.8 | 227.5 | 226.5 |
| ITL 均值 (ms) | 492.7 | 502.5 | 510.7 |
| ITL 中位 (ms) | 519.9 | 199.6 | 64.9 |
| ITL p95 (ms) | 673.7 | 1,207.5 | 1,995.5 |
| ITL p95 ÷ 中位 | 1.3× | 6.0× | 30.7× |
| ITL ≥ 1,500 ms 的比例 | 0.00% | 0.2% | 22.5% |
批次预算变化 4 倍,总量三项全部守恒:输入吞吐差 1.5%,整批耗时差 1.5%,ITL 均值差 3.6%。而分布形状彻底翻转:中位相差 8 倍,p95 相差 3 倍,那个 2 秒的慢峰完全消失。
prefill 的总工作量是守恒的,它必须插进 decode 流里。这个参数决定的只是你以什么粒度承受它 —— 是大部分步很快、22.5% 的步卡两秒,还是所有步都均匀地不快。
官方文档把 --max-num-batched-tokens 列在 OOM 时的调节顺序里,排在 --max-num-seqs 之后。从这组数据看,它同时是个延迟整形参数,而这一面文档没提。
- 交互式长上下文、对卡顿敏感,就降低批次预算。换来可预测的吐字节奏,吞吐几乎不付代价,代价在中位数变慢。
- 能接受偶发两秒停顿、要大部分时候快,就保持官方的 32,768。
- 两者兼得做不到。三个工作点的 ITL 均值守恒在 3.6% 以内,总时间就那么多。
降低批次预算会连带把 KV 池撑大(695,936 到 767,872 token),所以这个对照还差一步才干净。我们用 --num-gpu-blocks-override 把 8,192 那一档的 KV 池钉回 696,064,和官方档逐字相同,重跑:全部 ITL 指标变化不超过 2.1%,抢占仍为 0。KV 池容量不是成因,批次预算才是。
官方 32,768 那一档同时是与首轮的复现对照,全部指标吻合在 3.3% 以内。
有一条同因没能分离:图模式配的是 FULL_DECODE_ONLY,只覆盖纯 decode 批,调度步一旦混进 prefill 就回落逐算子执行 —— 而批次预算恰好也决定混合批出现的频率,两者绑在一起,拆不开。
4. 部署
配方取自官方文档🔗1 A2 单机一节,逐字照抄。下面是实际跑的那一份,四处偏离已标出,全是环境适配,不是调优。
export LD_PRELOAD=/usr/lib/aarch64-linux-gnu/libjemalloc.so.2:$LD_PRELOADexport HCCL_OP_EXPANSION_MODE="AIV"export HCCL_BUFFSIZE=512export TASK_QUEUE_ENABLE=1export PYTORCH_NPU_ALLOC_CONF=expandable_segments:Trueexport HCCL_INTRA_PCIE_ENABLE=1export HCCL_INTRA_ROCE_ENABLE=0export OMP_PROC_BIND=falseexport OMP_NUM_THREADS=1# 偏离 1(本地追加):容器内 8 个加速卡设备节点全部可见,运行时认的是这个变量export ASCEND_RT_VISIBLE_DEVICES=0,1,2,3,4,5,6,7# 偏离 2(本地追加):不设它,POST /reset_prefix_cache 返回 404export VLLM_SERVER_DEV_MODE=1# 偏离 3:两处权重路径改为本地路径vllm serve /models/MiniMax-M2.7-w8a8-QuaRot \ --served-model-name MiniMax-M2.7 \ --host 0.0.0.0 --port 8077 \ --trust-remote-code \ --tensor-parallel-size 8 \ --quantization ascend \ --enable-expert-parallel \ --max-num-seqs 32 \ --seed 1024 \ --max-num-batched-tokens 32768 \ --compilation-config '{"cudagraph_mode": "FULL_DECODE_ONLY"}' \ --gpu-memory-utilization 0.85 \ --additional-config '{"enable_cpu_binding":true}' \ --model-loader-extra-config '{"enable_multithread_load":true,"num_threads":16}' \ --speculative_config '{"method": "eagle3", "model": "/models/MiniMax-M2.7-eagle-model-short", "num_speculative_tokens":3}'官方在同一段里还给了三条宿主机内核参数,要单独在宿主上执行,改之前记原值、跑完还原:
sysctl -w vm.swappiness=0sysctl -w kernel.numa_balancing=0sysctl -w kernel.sched_migration_cost_ns=50000我们照抄了这三条,但没做开关对照,给不出它们值多少性能。
Pod 需要 hostNetwork 与特权模式,生产环境应改用设备插件。冷启动到就绪 141 秒(编译 77 秒),编译缓存命中后 52.8 秒 —— 这两个数决定探针超时怎么配。
--enable-prefix-caching 官方没写,但引擎 V1 默认开启,所以本轮是开着的。三个解析器官方标为「若要用命令行测试工具调用则追加」,不属于基础配方,本轮没开;要对外提供工具调用能力这三项必须加,加了之后输出 token 的计数口径不变。
5. 结论
官方 A2 单机配方在这台八卡机器上照抄就能跑,不需要打补丁。并发 32 时每用户 39.9 tok/s、整机输出 1,217 tok/s,32K 输入的输入吞吐 14,459 tok/s,每副本安全速率 2.25 请求/秒。
跑长上下文交互式负载的话,--max-num-batched-tokens 值得单独调一次。它不提速,但能把两秒的停顿摊平成均匀的慢 —— 官方文档只把它列在 OOM 的调节顺序里,没提这一面。
版本基线:vLLM 0.23.0 配 vllm-ascend v0.23.0rc1,昇腾 910B4-1 单机八卡,权重为官方发布的 W8A8 量化包。换权重、换引擎版本、换硬件,结论都可能变,需要重新测量。
附录:各项测试的口径一览
| 测试 | 数据集 / 规格 | 样本量 | 并发或速率 | 工具 |
|---|---|---|---|---|
| 延迟 | random 1024 / 128 | 100 | 并发 1 | sglang.bench_serving 0.5.17 |
| 高生成量 | random 1024 / 2048 | 200 | 并发 32 | 同上 |
| 长上下文 | random 32768 / 128 | 100 | 并发 16 | 同上 |
| ShareGPT | ShareGPT V3 真实对话 | 1,000 | 不限并发 | 同上 |
| 吞吐 | random 1024 / 512 | 1,000 | 不限并发 | 同上 |
| 多轮会话 | agent 编码轨迹,16 会话 ≤32 轮 | 362 轮 | 并发 8 | 同上 |
| 容量 | random 512 / 256 | 9 点 × 300 s | 泊松到达 0.5–3.0 rps | 同上,--request-rate |
| 批次预算敏感性 | random 32768 / 128 | 3 × 100 | 并发 16 | 同上 |
全部在 ignore_eos + temperature=0 下测得。除多轮会话档外每档一轮;多轮会话两轮,批次预算三档各一轮外加一组固定 KV 池的对照。
免责:本文为单次实验的测量记录,除多轮会话档外各档只跑一轮,不构成普适结论,亦不构成选型建议。数据取自特定版本、特定硬件与特定配方,换任一条件均需重新测量。
参考链接
🔗1 https://docs.vllm.ai/projects/ascend/zh-cn/latest/tutorials/models/MiniMax-M2.html
🔗2 https://github.com/sgl-project/sglang/blob/main/python/sglang/bench_serving.py
🔗3 https://github.com/vllm-project/vllm-ascend
🔗4 https://huggingface.co/datasets/anon8231489123/ShareGPT_Vicuna_unfiltered
本文转载自微信公众号「vLLM 生产工程」,仅供学习交流使用。
觉得内容不错?我要