🧱 模块十二:推理运行时与跨框架部署(MUSA)

ONNX Runtime MUSA Plugin EP 构建与部署(out-of-tree / ORT 1.26)

高级·⏱ 85分钟·📅 2026-11-14
📌
本课定位(模块十二开篇)
此前学习中心的推理路线只有两条:vLLM-MUSA 与 SGLang-MUSA,两者都面向大语言模型。但真实生产环境里还有大量非 LLM 推理负载——CV 模型、传统 NLP 模型、推荐排序模型、语音前后处理——它们的事实标准运行时是 ONNX Runtime。2026-07-27 摩尔线程新建 MooreThreads/onnxruntime-musa 仓库(非 fork,窗口内 38 次提交),实现了面向 MUSA GPU 的 独立 Plugin Execution Provider。这是学习中心新设「推理运行时」模块的原因,本课是该模块第一课。

一、Plugin EP:为什么"不用重编 ONNX Runtime"很重要

ONNX Runtime 通过 Execution Provider(EP)机制支持不同硬件后端:CPU EP、CUDA EP、TensorRT EP、QNN EP 等等。传统上,新增一个硬件后端意味着把代码合进 ONNX Runtime 主仓、然后整体重新编译一份带该后端的 ORT。这条路的代价对使用方极其不友好:你不能再用 pip install onnxruntime 装官方包,必须使用厂商定制版本;ORT 每次升级你都要等厂商跟进重编;两个厂商的定制版还没法共存。

摩尔线程这次走的是out-of-tree Plugin EP 路线:遵循 onnxruntime-qnn 的产品骨架,基于 ORT 的 kernel-registry Plugin EP C API,最终产物是一个独立的动态库加一个 Python wheel,可以注册进社区官方的 stock pip install onnxruntime,无需重新编译 ONNX Runtime。

这个架构选择的价值可以用三个场景说明。场景一:升级解耦——ORT 发布新版本时,只要 Plugin EP C API 保持稳定,你可以先升 ORT、后升插件,而不必等厂商出整包。场景二:多后端共存——同一套环境里可以同时装 MUSA 插件与其他插件,按模型选择 EP。场景三:交付简化——给客户交付时不再需要分发几百 MB 的定制 ORT,只需一个插件包,客户原有的 ONNX Runtime 依赖声明完全不用改。

💡
和 torchada 的思想对照
回想 m6-l2 讲的 torchada:它的核心价值也是"不改上层、不重编上游",通过运行时映射让 CUDA 生态代码跑在 MUSA 上。Plugin EP 是同一种工程哲学在另一个生态里的体现——把适配层做薄、做成可插拔的边界,而不是分叉整个上游项目。当你评估任何国产硬件的生态方案时,"是否需要分叉上游"是一个极好的成熟度判据。

二、依赖与版本兼容矩阵(注意 MUSA 版本与其他课程不同)

⚠️
MUSA Toolkit 要求是 5.1.0,不是 5.2.0
本站绝大多数课程(m8-l12、m8-l13 等)的基线是 MUSA SDK 5.2.0,但 onnxruntime-musa 当前声明的依赖是 MUSA Toolkit 5.1.0。如果你在一台按 vLLM-MUSA 教程配好的 5.2.0 机器上直接构建,请务必先确认 MUSA_HOME 指向的版本,并做好构建失败的心理准备——这是跨课程操作时最容易被忽略的一处版本错配。
依赖项要求说明
MUSA Toolkit5.1.0默认路径 /usr/local/musa,可用 -DMUSA_HOME= 覆盖
链接库MUSA runtime / muBLAS / muDNN三者均需可用
C++ 标准C++20需要 std::span,项目已弃用 GSL
编译器GCC 11+ 或 Clang 14+低于此版本无法编译
ORT 源码submodule 固定于 tag v1.26.0commit 8c546c37,稀疏检出仅 include 目录
Python(运行时)>= 3.11注意与 Mooncake 的 cp310 要求不同
onnxruntime1.26.0社区官方包即可
onnx1.21.0
构建系统CMake入口脚本 ./build.sh

值得注意的是 submodule 的处理方式:项目并不需要完整的 ORT 源码树,只需要头文件来对接 Plugin EP C API,因此使用稀疏检出(sparse checkout)只拉取 include 目录。这既大幅缩短了首次拉取时间,也从工程上强化了"我们只依赖公开 API、不碰内部实现"这一边界约定。

三、Step-by-Step 构建流程

Step 1:工具链检查

# C++20 与编译器版本(必须 GCC 11+ 或 Clang 14+)
g++ --version
echo '#include 
int main(){ int a[3]={1,2,3}; std::span s(a); return s.size()==3?0:1; }' > /tmp/t.cpp
g++ -std=c++20 /tmp/t.cpp -o /tmp/t && /tmp/t && echo "C++20 std::span ok"

# CMake
cmake --version

# MUSA Toolkit:确认版本为 5.1.0
ls -d /usr/local/musa
cat /usr/local/musa/version* 2>/dev/null || mcc --version

Step 2:拉取仓库与初始化 submodule

git clone https://github.com/MooreThreads/onnxruntime-musa.git
cd onnxruntime-musa

# 稀疏检出 ORT v1.26.0(commit 8c546c37)的 include 目录
./scripts/init_onnxruntime_submodule.sh

# 核对仓库结构
ls musa/ep/src      # C++ Plugin EP 实现
ls musa/ep/python   # Python wheel 打包

Step 3:构建

# 默认构建(MUSA_HOME 取 /usr/local/musa)
./build.sh

# MUSA 安装在非默认路径时显式指定
./build.sh -DMUSA_HOME=/opt/musa-5.1.0

# 需要干净重建时
./build.sh --clean

Step 4:安装并注册到 stock ONNX Runtime

# 运行时依赖:注意 Python >= 3.11
python -V
pip install onnxruntime==1.26.0 onnx==1.21.0

# 安装构建产物 wheel(文件名以实际构建输出为准)
pip install dist/*.whl

# 确认 stock onnxruntime 未被替换
python -c "import onnxruntime as ort; print(ort.__version__, ort.__file__)"

Step 5:验证 EP 已注册并可用

python - <<'PY'
import numpy as np
import onnxruntime as ort

print("ORT version:", ort.__version__)
print("available EPs:", ort.get_available_providers())

# 用一个最小模型做端到端冒烟:构造 -> 推理 -> 与 CPU EP 比对
import onnx
from onnx import helper, TensorProto

X = helper.make_tensor_value_info("X", TensorProto.FLOAT, [4, 8])
W = helper.make_tensor_value_info("W", TensorProto.FLOAT, [8, 8])
Y = helper.make_tensor_value_info("Y", TensorProto.FLOAT, [4, 8])
node = helper.make_node("MatMul", ["X", "W"], ["Y"])
graph = helper.make_graph([node], "mm", [X, W], [Y])
model = helper.make_model(graph, opset_imports=[helper.make_opsetid("", 17)])
onnx.save(model, "/tmp/mm.onnx")

x = np.random.rand(4, 8).astype(np.float32)
w = np.random.rand(8, 8).astype(np.float32)

cpu = ort.InferenceSession("/tmp/mm.onnx", providers=["CPUExecutionProvider"])
ref = cpu.run(None, {"X": x, "W": w})[0]

# EP 名称以 get_available_providers() 实际返回值为准
musa_eps = [p for p in ort.get_available_providers() if "MUSA" in p.upper()]
if not musa_eps:
    print("MUSA EP not registered — check wheel installation")
else:
    sess = ort.InferenceSession("/tmp/mm.onnx", providers=[musa_eps[0]])
    out = sess.run(None, {"X": x, "W": w})[0]
    print("max abs diff:", float(np.abs(out - ref).max()))
PY

这个验证脚本刻意做成"先跑 CPU EP 拿参考值,再跑 MUSA EP 比对"的形式。对任何新硬件后端,能跑通不等于算对,数值比对必须是验证流程的一部分。MatMul 是最好的第一个测试算子,因为它同时覆盖 muBLAS 链路与内存搬运链路。

四、窗口内已实现的图优化与融合

根据仓库提交记录,2026-07-27 至 07-31 期间的 38 次提交主要集中在图优化与算子实现两个方向:

  • MHA fusion:把多头注意力的散装算子融合成单个 kernel,减少中间张量与访存往返。
  • ms_mha 算子:对应 ORT 的 Microsoft 域多头注意力算子实现。
  • fp32 flash attention:FP32 精度下的 FlashAttention 实现。
  • SDPA matcher 修复:修正 scaled-dot-product-attention 的图模式匹配,避免漏匹配导致回落到未融合路径。
  • linear fusion 与 reshape-gemm-relu fusion:常见的线性层组合融合。
  • strided view fusion:消除跨步视图带来的额外拷贝。
  • split 快路径:针对 split 算子的专用优化路径。
  • stream-ordered allocator 修复(MR115):流序内存分配器修复,与多流并发执行的正确性直接相关。
  • 构建加速:缩短编译时间。

从这份清单可以判断项目的成熟阶段:算子覆盖与图融合正在密集补齐,基础设施(分配器、构建)仍在修 bug。这意味着它适合用于评估验证与预研,把它直接放进生产关键链路需要充分的自测——尤其要覆盖数值比对与长稳测试。

五、排障与回滚

现象根因处理
编译报 std::span 未定义未启用 C++20 或编译器过旧升级到 GCC 11+/Clang 14+
CMake 找不到 MUSAMUSA_HOME 未设置或指向 5.2.0-DMUSA_HOME= 显式指向 5.1.0 安装目录
submodule 为空未执行初始化脚本./scripts/init_onnxruntime_submodule.sh
链接报缺 muBLAS/muDNNToolkit 安装不完整补装组件,检查 LD_LIBRARY_PATH
get_available_providers() 无 MUSAwheel 未安装或 ORT 版本不是 1.26.0核对 onnxruntime==1.26.0 并重装插件
Python import 失败Python 低于 3.11切换到 3.11+ 环境
数值与 CPU EP 不一致命中在修的融合路径逐个关闭图优化定位,向仓库提 issue

回滚极其简单,这正是 out-of-tree Plugin EP 架构的最大红利:只需 pip uninstall 掉插件 wheel,你的 onnxruntime==1.26.0 环境立刻回到纯 CPU EP 状态,业务代码一行不用改(前提是你的推理代码用 providers= 参数做了优雅降级,而不是硬编码 MUSA EP)。建议在生产代码里始终写成"优先 MUSA EP、失败则回落 CPU EP"的形式:

import onnxruntime as ort

def make_session(model_path):
    avail = ort.get_available_providers()
    prefer = [p for p in avail if "MUSA" in p.upper()]
    providers = prefer + ["CPUExecutionProvider"]
    sess = ort.InferenceSession(model_path, providers=providers)
    print("running on:", sess.get_providers()[0])
    return sess
🔭
观察项:xLLM 推理引擎与 MUSA 的协同
与本课同属"第三方推理运行时接入 MUSA"这条线的,还有 xLLM 推理引擎。摩尔线程于 开发者站新闻 中宣布与 xLLM 深度协同,但该条新闻正文托管于微信公众号且禁止抓取,技术细节目前无法核实。可确认的旁证是:xLLM 官方文档已将摩尔线程 MUSA 列为受支持后端,设备后端标识为 MUSA,可见性变量为 MUSA_VISIBLE_DEVICES,容器需按 GPU 数量逐个增加 --device=/dev/mtgpuN。由于该文档缺少镜像地址、启动命令与模型清单,本轮不单独成课,待官方材料补齐后再转化。
🔭
观察项:科学计算 MUSA 移植仓库群
与 onnxruntime-musa 同期,摩尔线程还批量新建了一批科学计算移植仓库:magma-musa(tag musa-2.9.0)、amgx-musa(musa-2.3.0)、cp2k-musa、relion-musa、lammps-musa、deepmd-kit-musa 等。需要注意:除 onnxruntime-musa 外均为上游 fork,且窗口内提交多为上游同步而非实质 MUSA 移植工作,移植进度目前无法量化,因此本轮仅作雷达跟踪,不转化为课程。
✓
本课小结
onnxruntime-musa 用 out-of-tree Plugin EP 打开了 MUSA 的第三条推理路线:遵循 onnxruntime-qnn 骨架、基于 ORT kernel-registry Plugin EP C API,产出单一动态库加 Python wheel,注册进社区 stock pip install onnxruntime,无需重编 ORT。构建侧四个硬约束务必记牢:MUSA Toolkit 5.1.0(与本站其他课程的 5.2.0 基线不同)、C++20 与 GCC 11+/Clang 14+(需 std::span,已弃用 GSL)、ORT submodule 固定在 v1.26.0(8c546c37,稀疏检出仅 include)、运行时 Python ≥ 3.11 + onnxruntime 1.26.0 + onnx 1.21.0。验证必须包含与 CPU EP 的数值比对,生产代码应写成"优先 MUSA EP、失败回落 CPU EP",这样回滚就只是一次 pip uninstall。来源:onnxruntime-musa README、提交记录。
2026-10-15 技术维护
本节为文档核验与教学实验设计,未在真实 MUSA GPU 上实测。开发分支、待合并方案与正式发布分别标明;不覆盖原课程的锁定环境。

待合并案例:一次推理成功还不够

截至 2026-10-15,PR #6 仍为开放状态,作者报告在 S5000 / MUSA 4.3.8 上验证 40 个公开 ONNX 模型,每个模型重复三轮,并检查数值相似度与 NaN/Inf。这里是 PR 作者自报结果,不代表主线已经提供相同覆盖,也不替换上文 ORT 1.26 的构建基线。ONNX Runtime MUSA PR #6

该方案涉及 MemcpyFromHost / MemcpyToHost、CPU 侧 Slice 元数据以及 Resize 的 CPU 回退等边界;说明中提到重复执行时的 memory-pattern 问题,因此课程验收应包括第二次及后续运行。ONNX Runtime MUSA PR #6

设计 CPU / GPU 分区与重复执行实验

  1. 前置条件:能运行原课程 CPU EP 数值对照。先记录模型哈希、opset、输入 shape / dtype、ORT 与插件 commit、CPU/GPU/OS/驱动/SDK 完整清单;本 PR 不足以补齐所有版本。
  2. 保留原来的可运行镜像,候选 PR 只进入隔离环境。未合并代码不得覆盖正式服务。
  3. 对固定输入运行 CPU 基线,再运行候选 EP;至少连续三次,并分别测试固定形状和模型实际支持的动态形状。
  4. 记录每轮输出误差、NaN/Inf、设备分配、CPU 回退节点及拷贝路径;相似度门槛应按模型任务预先定义,不把其他模型的阈值照搬。
  5. 量化 CPU 回退与数据搬运的端到端影响,不能用“GPU 注册成功”证明所有算子都在 GPU 上。
  6. 若第二轮报错、shape 变化失败或精度超限,保存最小复现并回滚整套环境。待修复后重跑全部轮次。

这是受 PR 问题启发的验证方法,而非本站已运行的 40 模型测试报告。提交记录时将“作者自报”“本地复现”“主线合并”“正式发行”分为不同栏位。ONNX Runtime MUSA PR #6

2026-11-14 技术维护
本节依据官方 Release 与文档整理,未在真实 MUSA GPU 上实测。正式发布、作者测量与本站验证分别标明;原课程的旧版本步骤保留作对照与回滚。

状态更新:PR #6 已合并并进入 v0.2.0

上一轮记录时 PR #6 仍开放。本轮核对:该 PR 已于 2026-10-26 合入 master(merge commit 932eb0c9058811da004f42b1e4bae0769082023d),并列入 2026-11-10 发布的 v0.2.0。同一 Release 还包括 grouped / depthwise 卷积支持(PR #3)和 CI 工作流(PR #9)。ONNX Runtime MUSA PR #6 ONNX Runtime MUSA v0.2.0 Release

PR #6 标题中的“40 public models pass on S5000”仍是作者报告的覆盖范围(作者测试条件为 MUSA 4.3.8),Release 本身没有给出逐模型清单;不要据此推断其他 GPU、SDK 或模型均已支持。ONNX Runtime MUSA PR #6

CI 只能按具体 run 说明

PR #9 新增 musa-ci.yml 与 musa-weekly-model-zoo.yml,作者列出单元测试、model zoo 与 CPU EP 对比、周度端到端性能。ONNX Runtime MUSA PR #9 2026-11-09 的周度运行在 self-hosted / musa / s5000 标签的 runner 上成功,对应 commit 7e12a22bec,即 v0.2.0 标签 commit。这只说明该 commit 在该 runner 上通过,不代表每个后续 commit 或其他平台都通过。Weekly Model Zoo run 42545489186

升级到 v0.2.0 的验收

  1. 保留原课程的构建产物与上一轮的 CPU 基线结果,作为回滚与对照。
  2. 按 v0.2.0 标签重新构建插件,记录 commit、ORT 版本与 MUSA SDK 版本;SDK 版本以仓库当前文档为准,与其他课程可能不同。
  3. 复用上一节设计的连续三轮实验,重点复查 Slice、Resize 回退和第二轮执行;新增卷积模型时加入 grouped / depthwise 用例。
  4. 记录栏位分为“Release 说明”“作者自报”“CI run”“本地复现”。本站尚未运行本地复现。