Skip to content

[2026春季][T2-1-3] surprisely - #498

Open
surprisely wants to merge 13 commits into
InfiniTensor:mainfrom
surprisely:2026-spring-surprisely-T2-1-3
Open

[2026春季][T2-1-3] surprisely#498
surprisely wants to merge 13 commits into
InfiniTensor:mainfrom
surprisely:2026-spring-surprisely-T2-1-3

Conversation

@surprisely

Copy link
Copy Markdown

摘要

  • 新增 auto|legacy|fused MoE 后端策略;auto 仅对受支持的 CUDA、FP16/BF16、非量化 Qwen3-MoE 选择 Fused MoE。
  • max_batch_size 贯通到 Paged CUDA Graph compiler,只捕获 scheduler 可达 batch,并稀疏化高 batch Graph;未捕获 shape 保留 eager fallback。
  • 为高并发 Qwen3-MoE 设置 4096-token prefill admission 默认值,降低激活峰值和 OOM 风险。
  • 普通 packed prefill 仅投影每个请求的末位 logits;非量化 Qwen3-MoE 的 LM head 按 TP rank 切分词表,并为 raw/non-greedy 调用保留完整 logits。
  • 非量化 Qwen3-MoE 的 CUDA Graph replay 跳过无状态 module-tree reset;其他模型保留原 reset 语义;cache 重配置后按新容量重建 Graph bucket。
  • 新增 7 个策略边界测试、公开 CLI/API 文档与 scripts/run_qwen3_moe_contest.sh 一键复现入口。

动机

Qwen3-30B-A3B-Thinking-2507 在 4×RTX 4090D、BF16、TP=4 的多并发/长文本服务中,主要受 Graph capture 常驻显存、packed-prefill 激活峰值和重复/全位置词表投影限制。未修改上游在 c64 长点会出现大量 server timeout。

服务端均为 InfiniLM,客户端均为 vLLM 0.9.2 openai-chatrequest-rate=inf。同点使用相同模型、TP、tokenizer、目标长度和 KV 容量;c64 短点使用 128 blocks,c64 长点使用 120 blocks 与 4096-token prefill budget。

未修改上游使用在 4×24 GiB 上可运行的 Paged eager,优化侧使用 Fused MoE + Paged Attention + device CUDA Graph。以下是端到端可服务栈对比,不冒充任一新增补丁的独立收益。

并发 / 输入 / 输出 上游输出 tok/s 优化输出 tok/s 上游总 tok/s 优化总 tok/s
1 / 32 / 256 24.48 116.25 27.45 130.84
64 / 32 / 256 36.29 1860.27 40.83 2093.00
64 / 256 / 1024 31.71 586.13 48.05 732.45

优化值均来自最终 HEAD 4a44bd3、seed 714 的 vLLM 0.9.2 openai-chat 重跑;c64 长点为三次运行的中位数,三次 output throughput 为 592.49 / 586.13 / 576.82 tok/s。c64 长点上游与优化侧实际输入均为 16335 tokens;优化侧三次均为 64/64 达到至少 1000 输出 tokens,上游仅 20/64 达到该长度,其余 44 个因 server timeout 提前结束。因此长点不视为等输出 token 数的延迟对比。

保留补丁的隔离结果:

  • max_batch_size 裁剪 Graph:b1/in32/out256 稳态延迟降低 3.87%,初始化降低 38.35%,峰值显存减少 2276~2284 MiB/GPU。
  • Last-token LM head:b1/in4096/out256 从 OOM 变为稳定运行,device Graph median 为 3396.78 ms。
  • Vocab-parallel LM head:三个配对点分别降低 5.09% / 3.64% / 6.76%,峰值显存减少 340~348 MiB/GPU。
  • Qwen3-MoE non-quant reset gate:三个配对点分别降低 2.54% / 1.04% / 2.58%。

变更类型

  • feat — 新增功能/API
  • fix — cache 重配置后重建 Graph bucket
  • perf — 性能优化
  • refactor — 仅重构
  • test — 新增测试
  • docs — 更新文档
  • build / ci
  • chore
  • Breaking change

涉及模型在支持平台上的测试结果

smoke c64long

smoke 截图展示最终 HEAD 的真模型正确性验证;c64long截图展示一次现场复测,输出吞吐为 602.42 tok/s、总吞吐为
752.75 tok/s。上方性能主表采用此前三次最终 HEAD 复测的中位数 586.13 tok/s 和 732.45 tok/s,两者测试配置一致,但不是同一条运行记录。

平台 模型 / 配置 结果
4×NVIDIA RTX 4090D,CUDA 12.8 Qwen3-30B-A3B-Thinking-2507,BF16,TP=4,Paged Attention + device CUDA Graph PASS:构建/import、7/7 策略测试、auto -> fused、真模型 smoke
同上 Paged Graph vs eager PASS:保存的 c64 结果 64/64 output IDs exact;b4 与跨 KV block 检查通过
同上 vLLM 0.9.2 赛题风格 HTTP burst PASS:c1 短点、c64 短点、c64 长点成功;超时和输出达标情况如上披露
其他 NVIDIA GPU / CUDA 版本 未测试 无对应硬件,不外推性能数据
CPU 及其他硬件后端 未测试 无对应硬件,不声称性能支持

性能影响与测试口径

  • 模型:Qwen3-30B-A3B-Thinking-2507,BF16,128 experts,top-8。
  • 硬件:4×RTX 4090D(24 GiB/卡),PCIe 4.0,TP=4,无 NVLink/P2P。
  • 客户端:vLLM 0.9.2 只负责随机请求、OpenAI Chat HTTP transport 和指标计算;推理服务端为 InfiniLM。
  • workload:request-rate=infnum-prompts=max-concurrency,seed 714;c1/c64 短点为最终 HEAD 单次值,c64 长点为最终 HEAD 三次中位数。
  • KV 容量:c64/in32/out256 为 128 blocks;c64/in256/out1024 为 120 blocks + 4096-token prefill budget;上游与优化侧同点一致。
  • 生成 token 数来自 vLLM benchmark JSON,超时与达标状态分开披露。

评审说明

  • InfiniCore 无源码改动。Fused MoE、Paged Attention 和 CUDA Graph 是框架原有能力,本 PR 不将其表述为新增 kernel。
  • moe_backend=auto 保留显式 Legacy override,仅对 CUDA FP16/BF16、非量化 qwen3_moe 选择 fused。
  • Vocab-parallel 本地 logits 仅用于受支持的 distributed-greedy 生成;raw logits、非 greedy、speculative/all-position、其他模型、TP=1 和量化模型均保留完整 logits。
  • Host 侧读取 packed input_offsets 前检查 CPU residency,避免解引用 device 地址。
  • Paged Graph 捕获 1~16、16 以上的 8 的倍数以及配置的最大 batch;其他 high-batch shape 使用原有 eager fallback。
  • GPTQ/AWQ 不纳入本 PR 性能结论;自动策略对量化模型回退 Legacy。
  • 完整参考资源、独立修改边界和开源协议见 REFERENCE.md

CI / ChatOps

创建 PR 后手动触发 Actions CI,或请维护者使用 /retest


提交前检查

标题、分支与提交

  • PR 标题使用赛事规定格式:[2026春季][T2-1-3] surprisely
  • 分支名使用赛事规定格式:2026-spring-surprisely-T2-1-3
  • 13 个提交均使用 Conventional Commits,可独立审查。
  • 分支基于最新 upstream/main,无 merge commit、fixup!squash!wip
  • N/A:赛事标题/分支格式优先于通用 PR 模板中的 Conventional Commit 标题和 <type>/... 分支规则。

范围与代码质量

  • 改动限于 Qwen3-MoE 服务策略、Graph 容量、LM-head/logits 路径、cache 重配置、测试和相关文档。
  • 无纯调试输出、profiling hook、注释掉的代码块或无归属 TODO。
  • 公开 API/CLI 已记录并有策略测试。
  • 修改文件保留单个行尾换行,git diff --check 通过。
  • 代码注释和错误消息使用英文。

C++ / Python

  • 未引入原始内存分配、未初始化读取或无边界检查的 host pointer 访问。
  • python scripts/format.py --ref upstream/main --check 和 Ruff 检查通过。
  • 未修改 csrc/models/llama_legacy/python/infinilm/auto_config.py
  • 新增 Python docstring 符合 PEP 257。

测试、构建与文档

  • 在 NVIDIA RTX 4090D 上从空构建目录完成编译、安装和 import。
  • examples/test_infer.py 等价单请求真模型 smoke 通过。
  • test/llm/test_moe_policy.py:7/7 通过。
  • N/A:未在最终 cleanup HEAD 重跑 examples/bench.pytest/bench/test_benchmark.py;本 PR 的性能口径为赛题参考的 vLLM HTTP 服务 benchmark。
  • N/A:未使用 scripts/test_perf.py;使用赛题参考的 vLLM 0.9.2 客户端。
  • README 已记录 --moe-backend=auto|legacy|fused,无 breaking change。
  • 创建 PR 后触发 CI 或请求 /retest

安全与合规

  • 仓库不含密码、token、内部 URL、私有服务器地址或个人数据。
  • 未复制第三方项目源码;技术思路和工具使用已在 REFERENCE.md 披露。
  • 新增 packed-index 和 local-logit 路径具有边界、device 与 sampling-mode 检查。

Expose auto, legacy, and fused policies. Auto selects fused only for non-quantized CUDA FP16/BF16 qwen3_moe workloads and preserves explicit legacy behavior elsewhere.
Propagate max_batch_size through the Python and C++ cache configuration so Paged CUDA Graph captures only scheduler-reachable batch shapes.
Keep exact graphs for batches 1 through 16 and coarse exact buckets above 16. Missing shapes retain the existing eager fallback and the configured maximum remains captured.
Use a 4096-token scheduler admission default only for high-concurrency Paged qwen3_moe serving. Explicit overrides, other models, and small capacities keep their prior behavior.
Project normal single-request prefill logits only at the sampled final position. sample_all_positions callers and decode retain complete logits semantics.
Shard the non-quantized Qwen3-MoE vocabulary projection across tensor-parallel ranks. Preserve complete-logit fallbacks for raw and non-greedy callers.
Skip recursive runtime-state reset only when the model is explicitly non-quantized. Quantized Marlin lock and workspace resets remain unchanged.
Derive the decode capture set from the current PagedKVCacheConfig on every compile so cache resets cannot retain stale batch limits or permanently discard larger buckets.
Exercise automatic backend gating, explicit compatibility overrides, quantization fallback, and the high-concurrency prefill budget boundaries.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR extends InfiniLM’s Qwen3-MoE serving stack with an auto|legacy|fused MoE backend policy, tighter paged CUDA Graph capture based on max_batch_size, and several Qwen3-MoE-specific performance/memory optimizations (prefill budgeting, last-token logits projection, vocab-parallel LM head/logits handling), alongside new tests, docs, and reproduction scripts.

Changes:

  • Add MoE backend policy resolution (auto|legacy|fused) and expose it via server CLI/API, with compatibility handling for the legacy boolean switch.
  • Propagate max_batch_size into paged KV/cache config and paged Graph compiler to limit captured shapes and reduce resident memory.
  • Optimize Qwen3-MoE inference paths (prefill admission defaulting, last-token logits, vocab-parallel logits for distributed greedy), plus add contest benchmark scripts and policy boundary tests.

Reviewed changes

Copilot reviewed 23 out of 23 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
test/llm/test_moe_policy.py Adds policy/budget boundary tests for moe_backend=auto and Qwen3-MoE prefill budgeting.
scripts/run_vllm_bench_serve_client.py Adds a lightweight launcher for vLLM serving benchmark client with --extra-body support.
scripts/run_qwen3_moe_contest.sh Adds a one-command entrypoint to run correctness + contest-style serving benchmarks.
README.md Documents --moe-backend=auto|legacy|fused behavior for Qwen3-MoE.
python/infinilm/server/inference_server.py Threads moe_backend into the server’s AsyncLLMEngine construction.
python/infinilm/llm/model_runner/model_runner.py Passes max_batch_size into paged KV cache config creation.
python/infinilm/llm/llm.py Implements _resolve_moe_backend, adds Qwen3-MoE prefill budget defaulting, and updates LLM/AsyncLLMEngine args.
python/infinilm/infer_engine.py Adds allow_local_vocab_logits plumbing for distributed-greedy local logits consumption.
python/infinilm/cache/cache.py Extends PagedKVCacheConfig Python wrapper to include max_batch_size.
python/infinilm/base_config.py Adds --moe-backend CLI option and adjusts MoE-related argument wiring.
examples/test_infer.py Updates example to use new MoE policy parameters (skip_legacy_moe=None, moe_backend).
csrc/pybind11/engine/engine.hpp Exposes allow_local_vocab_logits to Python bindings.
csrc/pybind11/cache/cache.hpp Extends PagedKVCacheConfig binding to include max_batch_size.
csrc/models/infinilm_model.hpp Adds fields for last-token logits and vocab-parallel logits control; adds uses_vocab_parallel_logits().
csrc/models/infinilm_model.cpp Skips module-tree runtime reset for non-quantized Qwen3-MoE.
csrc/layers/causal_lm_templates/text_causal_lm.hpp Implements last-token-only logits path and vocab-parallel LM head + allgather as needed.
csrc/engine/rank_worker.hpp Adds allow_local_vocab_logits and greedy detection helper.
csrc/engine/rank_worker.cpp Adds distributed greedy over sharded vocab logits; adjusts graph usage gating.
csrc/engine/infer_engine.cpp Enforces CPU residency for input_offsets and computes last-token indices for packed prefill.
csrc/engine/compiler/static_batching_compiler.cpp Marks compiled inputs as local-vocab-logits-capable and rejects incompatible requests.
csrc/engine/compiler/paged_compiler.cpp Limits captured decode batch shapes by max_batch_size and sparsifies high-batch buckets.
csrc/cache/kv_cache.hpp Adds max_batch_size to PagedKVCacheConfig API.
csrc/cache/kv_cache.cpp Implements max_batch_size plumbing and preserves legacy 2-arg ctor default.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 191 to 199
self.engine = AsyncLLMEngine(
model_path=self.model_path,
device=self.device,
dtype=self.dtype,
tensor_parallel_size=self.tensor_parallel_size,
moe_ep_backend=self.moe_ep_backend,
moe_ep_size=self.moe_ep_size,
moe_backend=self.moe_backend,
cache_type=self.cache_type,
Comment on lines +120 to +127
export INFINI_ROOT
export CUDA_VISIBLE_DEVICES
export LD_LIBRARY_PATH="$INFINI_ROOT/lib:${LD_LIBRARY_PATH:-}"
if [ -n "$INFINICORE_DIR" ]; then
export PYTHONPATH="$INFINICORE_DIR/python:$REPO_DIR/python:${PYTHONPATH:-}"
else
export PYTHONPATH="$REPO_DIR/python:${PYTHONPATH:-}"
fi
@surprisely

Copy link
Copy Markdown
Author

@surprisely

Copy link
Copy Markdown
Author

Qwen3-MoE 长文本与多并发服务优化

1. 结果概览

在相同模型、Fused MoE、FlashAttention、Paged KV Cache、CUDA Graph 和客户端参数下:

  • B 完成参考主矩阵 18/18
  • A/B 均成功的 15 个点中,输出吞吐和总吞吐均为 15/15 正向
  • 输出吞吐几何平均提升 9.07%,总吞吐提升 9.07%,Mean TPOT 降低 8.30%
  • 小规模 C1 的 6/6 个点全部提升,输出吞吐提升 9.17%~12.35%
  • 长输出 C1/I32/O4096 的输出吞吐和总吞吐提升 11.03%
  • C16/I256/O256C1/I4096/O256C64/I32/O256 均由 A 的 CUDA OOM 改善为 B 可完成。

2. 主要改动

  • prefill 普通推理只对最后 token 执行 LM head;
  • TP vocab-parallel LM head 和分布式 greedy 路径;
  • 有界、稀疏的 CUDA Graph bucket 与 graph capture/运行时缓冲路径;
  • block table、KV cache 及高并发 prefill budget 等内存路径。

Legacy MoE 和 Fused MoE 均为 upstream 已有能力,auto 负责后端选择。本次 A/B 固定使用相同的 Fused MoE,测试其余运行时改动的综合效果。

3. A/B 与环境

项目 A:upstream Fused 基线 B:本 PR
InfiniLM revision ee4c810 4a44bd3
worktree upstream + 4 行 CLI-only adapter clean
MoE 参数 --skip-legacy-moe --moe-backend fused
实际 MoE 语义 upstream Fused MoE upstream Fused MoE
Attention FlashAttention 相同 FlashAttention
Paged KV Cache 开启 开启
CUDA Graph 开启 开启

upstream 服务入口解析了 --skip-legacy-moe,但没有继续传给 engine。A 的 4 行 adapter 只补齐参数透传,不包含计算、调度或性能优化:

 class InferenceServer:
     def __init__(
+        skip_legacy_moe: bool = False,
         ...
+        self.skip_legacy_moe = skip_legacy_moe
         ...
         self.engine = AsyncLLMEngine(
+            skip_legacy_moe=self.skip_legacy_moe,
             ...

 server = InferenceServer(
+    skip_legacy_moe=cfg.skip_legacy_moe,
     ...
项目 配置
OS Ubuntu 22.04.4 LTS
GPU 4 × NVIDIA GeForce RTX 4090 D,24564 MiB/GPU
NVIDIA Driver 580.105.08
CUDA Toolkit 12.8,V12.8.61
GPU 架构 sm_89
模型 Qwen3-30B-A3B-Thinking-2507
精度与并行 BF16,TP=4,DP=1,EP=1
服务端 Python 3.12.3,Torch 2.5.1+cu124
独立客户端 Python 3.12.3,vLLM 0.11.2,Torch 2.9.0+cu128
InfiniCore 1590de7
FlashAttention submodule 1084696
CUTLASS submodule 087c84d
benchmark seed 714

4. 测试矩阵

T2-1-3 指定 Qwen3-30B-A3B-Thinking-2507,测试风格参考 T2-1-2 长文本服务矩阵。题面参考矩阵为:

参考模型 并发数 C 输入长度 I 输出长度 O
8B 1、4 32、256、4096 256、1024、4096
8B 16、64 32、256 256、1024、4096
70B 1、4、16 32、256 256、1024、2048

本次 Qwen3-30B 主测试完整采用 70B 参考网格,共 3 × 2 × 3 = 18 点,并补充长输出、长输入和 C64 高并发:

类型 C I O num-blocks 点数 A B
主矩阵 1 32、256 256、1024、2048 18 6 6/6 成功 6/6 成功
主矩阵 4 32、256 256、1024、2048 48 6 6/6 成功 6/6 成功
主矩阵 16 32、256 256、1024、2048 80 6 3 成功、1 OOM、2 未运行 6/6 成功
长输出 1 32 4096 18 1 成功 成功
长输入 1 4096 256 18 1 OOM 成功
高并发 64 32 256 128 1 OOM 成功

num-blocks 按 4×24GB 设备容量设置,同一测试点的 A/B 完全相同。

5. 复现命令

先清除代理变量:

unset http_proxy https_proxy all_proxy ALL_PROXY

5.1 服务端

python python/infinilm/server/inference_server.py \
  --device nvidia \
  --model "$MODEL" \
  --dtype bfloat16 \
  --tp 4 \
  --dp 1 \
  --ep 1 \
  --moe-ep-backend disabled \
  --host 127.0.0.1 \
  --port 8103 \
  --max-batch-size "$C" \
  --max-new-tokens 4096 \
  --num-blocks "$NB" \
  --block-size 256 \
  --temperature 1.0 \
  --top-p 0.8 \
  --top-k 1 \
  --enable-paged-attn \
  --attn flash-attn \
  --enable-graph \
  --ignore-eos \
  "${ARM_ARGS[@]}"

A/B 只在源码和显式 MoE 参数上不同:

# A:upstream Fused
ARM_ARGS=(--skip-legacy-moe)

# B:PR Fused
ARM_ARGS=(--moe-backend fused)

5.2 客户端

vllm bench serve \
  --backend openai-chat \
  --model Qwen3-30B-A3B-Thinking-2507 \
  --tokenizer "$MODEL" \
  --host 127.0.0.1 \
  --port 8103 \
  --endpoint /v1/chat/completions \
  --request-rate inf \
  --seed 714 \
  --num-prompts "$C" \
  --max-concurrency "$C" \
  --random-input-len "$I" \
  --random-range-ratio 0 \
  --extra-body "{\"max_tokens\": $O}" \
  --disable-tqdm \
  --save-result \
  --result-dir "$OUT" \
  --result-filename result.json

与题面参考脚本相同,输出上限由 extra_body.max_tokens 指定;没有额外传入 --random-output-len--save-result 只负责保存 vLLM 的合法原始 JSON,不改变压测语义。

6. 测试结果

以下增幅均为 B 相对 A。聚合值为各配对点 B/A 的几何平均。

并发 可配对点 输出吞吐 总吞吐 逐点方向
C1 6/6 +10.64% +10.64% 6/6 正向
C4 6/6 +10.14% +10.14% 6/6 正向
C16 / I32 3/3 +3.95% +3.95% 3/3 正向
合计 15/15 +9.07% +9.07% 15/15 正向
  • 单点输出吞吐提升范围:2.91%~12.65%
  • Mean TPOT 15/15 改善,几何平均降低 8.30%
  • Mean TTFT 为 12 个点改善、3 个点变差。
长文本与容量场景 A B 结果
C1/I32/O4096 102.80 output tok/s 114.13 output tok/s 输出与总吞吐均 +11.03%
C16/I256/O256 CUDA OOM,0/16 完成 803.55 output tok/s,16/16 完成 由失败变为可服务
C1/I4096/O256 CUDA OOM 108.71 output tok/s,1/1 完成 由失败变为可服务
C64/I32/O256 CUDA OOM 2012.27 output tok/s,64/64 完成 由失败变为可服务

服务就绪显存:

并发 A 显存/GPU B 显存/GPU B 节省/GPU
C1 23251 MiB 20731 MiB 2520 MiB
C4 23443 MiB 20991 MiB 2452 MiB
C16 23827 MiB 21647 MiB 2180 MiB

7. 原始结果

results.csv

CSV 包含 18 个主矩阵点和 3 个补充点,以及 A/B 状态、请求完成数、输出吞吐、总吞吐、Mean TTFT、Mean TPOT 和逐点增幅。数据来自 vLLM --save-result JSON 与失败状态日志,并已完成公式复算和隐私审查。

说明:

  • 实验使用固定 seed 714,A 失败或未运行的点不计入吞吐平均;
  • A revision ee4c810 早于本 PR merge-base a949662,结果按 B 分支相对实测 A 基线报告,不作逐提交拆分;
  • OOM→成功作为可服务容量证据,不计算吞吐加速比。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants