Unlimited-OCR 部署运行(12/13):RTX 3090 MoE triton autotune config 消除性能警告

服务跑通、输出正确之后,启动日志里可能还挂着一行恼人的警告:
Using default MoE kernel config. Performance might be sub-optimal!。它只影响性能、不影响正确性 ,但会污染日志,而且默认 config 在消费级卡上未必最优。本篇讲清三件事:警告从哪来、为什么盲拷 A100 配置会运行时崩溃、以及如何为 RTX 3090 生成一份安全且有效的 autotune config。
一、警告从哪来
Unlimited-OCR 是 E=64, moe_inter=896, hidden=2560, top_k=6 的 MoE 模型。SGLang 在找不到"匹配当前设备 + triton 版本 + 模型几何"的 autotune 配置时,会打印这条警告(以及 down-sampling 对应的第二条),并退回启发式默认 config。
配置文件的查找逻辑(简化):
configs/triton_{triton_version}/E={E},N={N},device_name={get_device_name().replace(' ','_')}{dtype}{block_shape}{per_channel_quant}{_down}.json
- 目录先按当前 triton 版本(本机
triton 3.7.1→triton_3_7_1)找; - 找不到再 fallback 到
supported_triton_versions = ["3.4.0","3.3.1","3.2.0","3.1.0"]; N = shard_intermediate_size // 2 = 896(当 moe_inter=896、tp=1);- 设备名由
torch.cuda.get_device_name(0)推出 =NVIDIA_GeForce_RTX_3090。
所以本机需要的文件名是:
E=64,N=896,device_name=NVIDIA_GeForce_RTX_3090.json
E=64,N=896,device_name=NVIDIA_GeForce_RTX_3090_down.json
(_down.json 给 down-sampling 复用 gate_up 调优,消除第二条警告。)
二、关键坑:不能盲拷 A100 配置

SGLang 自带 configs/triton_3_1_0/E=64,N=640/1280,device_name=NVIDIA_A100-SXM4-80GB.json 等 Ampere 调优。RTX 3090 = GA102 = sm86 (Ampere) ,与 A100 (sm80) 同架构族,block size 候选集通用 ------这很容易让人想"直接拷 A100 的过来改个文件名就用"。不要这么做。
| 维度 | RTX 3090 | A100 |
|---|---|---|
| 架构 | GA102 / sm_86 | GA100 / sm_80 |
| 每 SM 共享内存上限 | 100 KB(101376 B) | 164 KB |
A100 E=64 调优里有大量 config 的 shared memory 用量超过 101376 B ,在 3090 上 kernel 启动时会报 OutOfResources: shared memory → 推理直接崩。实测 6+ 个超限 config,典型特征:
BLOCK_SIZE_K = 256或BLOCK_SIZE_N = 256GROUP_SIZE_M >= 64配num_stages >= 4num_stages >= 5
例如 {16,64,256,...}、{64,256,128,...}、{16,128,128,G64,w4,s4} 等。
结论:盲拷 A100 配置到 3090 会运行时崩溃,必须剔除超限项再交付。 这正是"编译移植篇"里 第 06 篇 / 第 07 篇 "用不上的就别编 / 不超限才用"思路在 autotune 上的延续。
三、生成方法(gen_moe_config_3090.py)

策略(非盲拷、非纯默认):
- 以 A100 E=64 N=640 调优为基(N=640 最接近本模型 N=896,block 最保守;同 Ampere 族,候选集合法)。
- 对每个 M 条目用
is_risky()启发式标记超限项 (BLOCK_K>=256/BLOCK_N>=256/G>=64 & stage>=4/stage>=5),用保守安全 fallback(全部BLOCK_K<=128, BLOCK_N<=128, stage<=4)按 M 大小桶替换:M <= 8→{16,32,64,G16,w4,s4}8 < M <= 128→{16,128,128,G1,w4,s2~3}M > 128→{64~128,128,64,G1,w4,s3}
- 在真实 RTX 3090 上对每个唯一 config 跑
fused_experts实测 (含override_config+MoeRunnerConfig()),任一失败自动降级到最小安全 config 并重测------不靠"猜它能编过"。 - 文件名由
triton.__version__决定目录 (triton_3_7_1),N 由w2.shape[2]推出 = 896,device 名由torch.cuda.get_device_name(0)推出。 - 同时写主 config 与
_down.json,消除两条警告。
核心验证片段(真实 3090 实测,而非静态判断):
python
from sglang.srt.layers.moe.fused_moe_triton import override_config, fused_experts
from sglang.srt.layers.moe.moe_runner import MoeRunnerConfig
with override_config(cfg):
out = fused_experts(x, w1, w2, topk, moe_runner_config=MoeRunnerConfig())
# warmup 3 次后取中位数区间均值,记录该 config 的耗时
注意:triton kernel 按
(config, M)编译、缓存不跨 M 复用------逐 M 实测 autotune 极慢(每个 M 一次全量编译)。本方法只对"替换后保留的少数唯一 config"做实测,而非对所有 M 盲跑,正是为了避开这个坑。
四、验证结果

-
gen_moe_config_3090.py:替换 8 个超限 M(1, 2, 8, 16, 24, 256, 512, 1536),7 个唯一 config 全部在 3090 实测通过 (无OutOfResources、无崩溃)。 -
启动 SGLang 后日志确认两条加载行:
Using MoE kernel config from .../triton_3_7_1/E=64,N=896,device_name=NVIDIA_GeForce_RTX_3090.json. Using MoE kernel config from .../triton_3_7_1/E=64,N=896,device_name=NVIDIA_GeForce_RTX_3090_down.json.且
sub-optimal/Using default MoE警告计数 = 0。 -
一次完整 OCR 请求实测生成 36465 token,MoE 内核正确执行、无 OOM / 崩溃 → 配置有效。
交付文件位置:
.venv/Lib/site-packages/sglang/srt/layers/moe/fused_moe_triton/configs/triton_3_7_1/
E=64,N=896,device_name=NVIDIA_GeForce_RTX_3090.json
E=64,N=896,device_name=NVIDIA_GeForce_RTX_3090_down.json
五、如何适配你自己的 GPU
如果你不是 3090,按这个流程走:
- 先放任务打默认 config 跑一次,从日志拿到你的
device_name和 triton 版本目录名。 - 找同架构族(Ampere / Ada / Hopper...)的官方 A100 / 对应卡 config 作基。
- 查你的 GPU 每 SM 共享内存上限 (技术规格站可查),据此调整
is_risky()阈值------上限比 3090 高的卡可以放宽,比 3090 低的要更保守。 - 务必在真实卡上实测每个唯一 config,不要只做静态 shared-memory 预算。
六、小结与下一篇
本篇的要点:
- 警告来自"找不到匹配设备的 MoE autotune config",只影响性能。
- 盲拷 A100 配置会在 3090 上
OutOfResources崩溃------根因是 3090 共享内存上限(100KB)远低于 A100(164KB)。 - 正确做法:以同架构 A100 调优为基 + 剔除超限项 + 真实卡实测 + 主 /
_down双文件 → 警告归零、性能更贴合消费级卡。
第 13 篇 (本系列终篇)讲长文档 OCR 验证、gundam 图模式、两个 Windows 特有的运维坑(代理污染 health check、端口 10000 被遗留服务占用),以及给读者的完整使用指南------怎么用 OpenAI 兼容 API 实际调用这个服务。
系列导航(全 14 篇) (同前,略)
部署运行篇:09 正确启动 · 10 排障①乱码 · 11 排障②环境变量崩溃 · 12 MoE 性能调优(本篇) · 13 长文档验证与使用指南
参考资料与延伸阅读
以下为本文涉及的官方仓库、文档与规格站,建议发布前点一遍确认可达:
- Unlimited-OCR 官方仓库(模型与项目源码)
- SGLang 官方仓库
- SGLang 官方文档(启动参数 / OpenAI 兼容 API)
- flashinfer-windows(Windows 兼容 fork,编译前置)
- vllm-windows(同作者,可对照的 Windows 移植思路)
- PyTorch Windows CUDA 预编译索引(cu130)
- NVIDIA CUDA Toolkit 下载
- uv 官方文档(Python 环境治理)
- MSVC /Zc:preprocessor 标准预处理器
- MSVC 致命错误 C1001(编译器内部错误)
- nvcc -Xcompiler 转发 host 编译器选项
- CMake 生成器(Visual Studio / Ninja)
- RTX 3090 规格(GA102 / sm_86,共享内存 100KB)
- CUDA 共享内存上限与 dynamic_shared_memory 限制
- Windows 子进程环境变量块限制(CreateProcess / ~32KB)
- OpenAI 兼容 API 参考(推理调用)