Torch-MUSA
简介
MUSA 概述
MUSA (Metaverse Unified System Architecture) 是摩尔线程公司为摩尔线程 GPU 推出的一种通用并行计算平台和编程模型。它提供了 GPU 编程的简易接口,用 MUSA 编程可以构建基于 GPU 计算的应用程序,利用 GPU 的并行计算引擎来更加高效地解决比较复杂的计算难题。同时摩尔线程还推出了 MUSA 工具箱(MUSA Toolkits),工具箱中包括 GPU 加速库、运行时库、编译器、调试和优化工具等。MUSA Toolkits 为开发人员在摩尔线程 GPU 上开发和部署高性能异构计算程序提供软件环境。
更多详情,参见 MUSA 官方文档。
PyTorch 概述
PyTorch 是一款开源的深度学习编程框架,可以用于计算机视觉,自然语言处理,语音处理等领域。 PyTorch 使用动态计算,这在构建复杂架构时提供了更大的灵活性。PyTorch 使用核心 Python 概念,如 类、结构和条件循环,因此理解起来更直观,编程更容易。此外,PyTorch 还具有可以轻松扩展、快速实现、生产部署稳定性强等优点。
更多详情,参见 PyTorch 官方文档。
torch_musa 概述
为了摩尔线程 GPU 能支持开源框架 PyTorch,摩尔线程公司开发了 torch_musa。在 PyTorch
v2.0.0 基础上,torch_musa 以插件的形式来支持摩尔线程 GPU,最大程度与 PyTorch 代码解耦,便于代码维护与升级。torch_musa 利用 PyTorch 提供的第三方后端扩展接口,将摩尔线程高性能计算库动态注册到 PyTorch 上,从而使得 PyTorch 框架能够利用摩尔线程显卡的高性能计算单元。利用摩尔线程显卡 CUDA 兼容的特性,torch_musa 内部引入了 cuda 兼容模块,使 PyTorch 社区的 CUDA
kernels 经过 porting 后可运行在摩尔线程显卡上,而且 CUDA
Porting 的工作是在编译 torch_musa 的过程中自动进行,这大幅降低了 torch_musa 算子适配的成本,提高模型开发效率。同时,torch_musa 在 Python 前端接口与 PyTorch 社区 CUDA 接口形式上基本保持一致,这极大地降低了用户的学习成本和模型的迁移成本。
本手册主要介绍了基于 MUSA 软件栈的 torch_musa 开发指南。
torch_musa 核心代码目录概述
-
torch_musa/tests测试文件。 -
torch_musa/core主要包含 Python module,提供 amp/device/memory/stream/event 等模块的 Python 前端接口。 -
torch_musa/csrcC++ 侧实现代码;-
csrc/amp提供混合精度模块的 C++ 实现。 -
csrc/aten提供 C++ Tensor 库,包括MUDNN算子适配、CUDA-Porting算子适配等。 -
csrc/core提供核心功能库,包括设备管理、内存分配管理、Stream 管 理、Events 管理等。 -
csrc/distributed提供分布式模块的 C++ 实现。
-
m1000_gpu_model_zoo 模型仓库
m1000_gpu_model_zoo旨在演示如何基于 torch_musa 在 MTGPU 进行模型推理加速,帮助开发者在 MTGPU 上快速落地各种 AI 模型推理服务。
环境准备与部署
步骤 1:环境确认
确认操作系统版本
AIOS 1.4.1
确认 musa 和 musa-sdk 版本
musa 5.1.0
musa-sdk 5.1.0
可通过以下指令获得,关注 Version 字段
dpkg -s musa
dpkg -s musa-sdk
步骤 2:驱动更新
执行前提:只有在步骤 1 中检查发现 musa 或 musa-sdk 版本不符合要求时,才需要执行本步骤。如果版本已正确,请跳过步骤 2,直接进入步骤 3。
安装包 下载、安装命令和安装验证流程,请参考 MUSA 安装。
步骤 3:安装 torch、torch_musa、triton
安装脚本:
pip install ./torch-<package_version>.whl
pip install ./torch_musa-<package_version>.whl
pip install triton-<package_version>.whl
步骤 4:环境验证
python3 -c "import torch;import torch_musa;print(torch.musa.is_available())"
输出 True 证明 torch_musa 环境安装正确。
可能出现的问题:
输出 mudnn.so 找不到
执行:
export PATH=/usr/local/musa/bin:${PATH}
export LD_LIBRARY_PATH=/usr/local/musa/lib:${LD_LIBRARY_PATH}
输出 Error in cpuinfo: prctl(PR_SVE_GET_VL) failed
torch_musa 2.7 之前会存在该问题,不影响使用。
NumPy 报错,如 Failed to initialize NumPy 或者
numpy ModuleNotFoundError: No module named 'numpy'
执行 pip3 install numpy==1.26.4
报错 ImportError: libmccl.so.2: cannot open shared object file: No such file or directory
mccl 库在最新的 musa-sdk 中已经包含,请参考 MUSA 安装,更新 musa-sdk。
报错 MUSA driver initialization failed
设备需要连接显示器,输入用户名密码进入桌面。如果没有安全需求,推荐在"设置"->"用户"界面设置为自动登录。
编译安装
torch_musa 源码完全开源,开发者也可以根据实际需要编译源码安装。
编译安装前,需要安装 MUSA Toolkits 软件包、MUDNN 库、MCCL 库、muThrust 库、muAlg 库、muRAND 库、muSPARSE 库。具体安装步骤,请参见相应组件的安装手册。
依赖环境
编译流程
-
向
PyTorch源码打 patch -
编译
PyTorch -
编译
torch_musa
torch_musa 2.9.0 是在 PyTorch
v2.9.0 基础上以插件的方式来支持摩尔线程显卡。开发时涉及到对 PyTorch 源码的修改,目前是以打 patch 的方式实现的。PyTorch 社区正在积极支持第三方后端接入,相关
issue
下已有对应 PR。torch_musa 项目也在持续向 PyTorch 社区提交 PR,以减少编译过程中对 PyTorch 打 patch 的需求。
编译步骤
安装依赖
git clone --depth=10 https://github.com/MooreThreads/muThrust.git
cd muThrust
./mt_build.sh -i
git clone --depth=10 https://github.com/MooreThreads/muAlg.git
cd muAlg
./mt_build.sh -i
使用脚本一键编译(推荐)
在初次编译时,需要执行 bash build.sh
(先编译 PyTorch,再编译 torch_musa)。在后续开发过程中,如果不 涉及对 PyTorch 源码的修改,那么执行
bash build.sh -m(仅编译 torch_musa)即可。
MAX_JOBS=8 USE_MCCL=0 USE_KINETO=1 bash ./build.sh --clean --wheel
配置说明
| 示例 | 说明 |
|---|---|
-c/--clean | 清理构建产物后再构建 |
-w/--wheel | 生成 wheel 包并安装 |
-p/ --patch | 仅应用补丁,不构建 |
-d/--debug | Debug 模式构建 |
-t/--torch | 仅构建 PyTorch |
-m/--musa | 仅构建 Torch_MUSA |
--fp64 | 编译支持 fp64 数据类型的内核 |
-a/--asan | 启用 AddressSanitizer 内存检测 |
宏定义说明:
| 宏定义 | 默认值 | 说明 |
|---|---|---|
MAX_JOBS | 1 | 用于编译的 CPU 核心数 |
USE_MCCL | 1 | 是否使用 MCCL(MooreThreads 的通信库) |
USE_KINETO | 1 | 是否使用 Kineto 性能分析库 |
分步骤编译
如果不想使用脚本编译,那么可以按照如下步骤逐步编译。
- 在
PyTorch打 patch
# 请保证 PyTorch 源码和 torch_musa 源码在同级目录或者 export PYTORCH_REPO_PATH=path/to/PyTorch 指向 PyTorch 源码
bash build.sh --only-patch
- 编译
PyTorch
cd pytorch
pip install -r requirements.txt
python setup.py install
# debug mode: DEBUG=1 python setup.py install
# asan mode: USE_ASAN=1 python setup.py install
- 编译
torch_musa
cd torch_musa
pip install -r requirements.txt
python setup.py install
# debug mode: DEBUG=1 python setup.py install
# asan mode: USE_ASAN=1 python setup.py install
快速入门
常用环境变量
开发 torch_musa 过程中常用环境变量如下表所示:
| 环境变量示例 | 所属组件 | 功能说明 |
|---|---|---|
export TORCH_SHOW_CPP_STACKTRACES=1 | PyTorch | 当 python 程序发生错误时显示 PyTorch 中 C++ 调用栈 |
export MUDNN_LOG_LEVEL=INFO | MUDNN | 使能 MUDNN 算子库调用的 log |
export MUSA_VISIBLE_DEVICES=0,1,2,3 | Driver | 控制当前可见的显卡序号 |
export MUSA_LAUNCH_BLOCKING=1 | Driver | 驱动以同步模式下发 MUSA kernel,即当前 kernel 执行结束后再下发下一个 kernel |
常用 API 示例代码
torch_musa 中 Python API 基本与
PyTorch 原生 API
接口保持一致,极大降低了新用户的学习成本。
import torch
import torch_musa
torch.musa.is_available()
torch.musa.device_count()
a = torch.tensor([1.2, 2.3], dtype=torch.float32, device='musa')
b = torch.tensor([1.8, 1.2], dtype=torch.float32, device='cpu').to('musa')
c = torch.tensor([1.8, 1.3], dtype=torch.float32).musa()
d = a + b + c
torch.musa.synchronize()
with torch.musa.device(0):
assert torch.musa.current_device() == 0
if torch.musa.device_count() > 1:
torch.musa.set_device(1)
assert torch.musa.current_device() == 1
torch.musa.synchronize("musa:1")
推理示例代码
import torch
import torch_musa
import torchvision.models as models
model = models.resnet50().eval()
x = torch.rand((1, 3, 224, 224), device="musa")
model = model.to("musa")
# Perform the inference
y = model(x)
训练示例代码
1、常规训练
import torch
import torch_musa
import torchvision
import torchvision.transforms as transforms
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
## 1. prepare dataset
transform = transforms.Compose(
[transforms.ToTensor(),
transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])
batch_size = 4
trainset = torchvision.datasets.CIFAR10(root='./data', train=True,
download=True, transform=transform)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=batch_size,
shuffle=True, num_workers=2)
testset = torchvision.datasets.CIFAR10(root='./data', train=False,
download=True, transform=transform)
testloader = torch.utils.data.DataLoader(testset, batch_size=batch_size,
shuffle=False, num_workers=2)
classes = ('plane', 'car', 'bird', 'cat','deer', 'dog', 'frog', 'horse', 'ship', 'truck')
device = torch.device("musa")
## 2. build network
class Net(nn.Module):
def __init__(self):
super().__init__()
self.conv1 = nn.Conv2d(3, 6, 5)
self.pool = nn.MaxPool2d(2, 2)
self.conv2 = nn.Conv2d(6, 16, 5)
self.fc1 = nn.Linear(16 * 5 * 5, 120)
self.fc2 = nn.Linear(120, 84)
self.fc3 = nn.Linear(84, 10)
def forward(self, x):
x = self.pool(F.relu(self.conv1(x)))
x = self.pool(F.relu(self.conv2(x)))
x = torch.flatten(x, 1) # flatten all dimensions except batch
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
x = self.fc3(x)
return x
net = Net().to(device)
## 3. define loss and optimizer
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)
## 4. train
for epoch in range(2): # loop over the dataset multiple times
running_loss = 0.0
for i, data in enumerate(trainloader, 0):
# get the inputs; data is a list of [inputs, labels]
inputs, labels = data
# zero the parameter gradients
optimizer.zero_grad()
# forward + backward + optimize
outputs = net(inputs.to(device))
loss = criterion(outputs, labels.to(device))
loss.backward()
optimizer.step()
# print statistics
running_loss += loss.item()
if i % 2000 == 1999: # print every 2000 mini-batches
print(f'[{epoch + 1}, {i + 1:5d}] loss: {running_loss / 2000:.3f}')
running_loss = 0.0
print('Finished Training')
PATH = './cifar_net.pth'
torch.save(net.state_dict(), PATH)
net.load_state_dict(torch.load(PATH))
## 5. test
correct = 0
total = 0
# since we're not training, we don't need to calculate the gradients for our outputs
with torch.no_grad():
for data in testloader:
images, labels = data
# calculate outputs by running images through the network
outputs = net(images.to(device))
# the class with the highest energy is what we choose as prediction
_, predicted = torch.max(outputs.data, 1)
total += labels.size(0)
correct += (predicted == labels.to(device)).sum().item()
print(f'Accuracy of the network on the 10000 test images: {100 * correct // total} %')
2、混合精度 AMP 训练示例代码
import torch
import torch_musa
import torch.nn as nn
class SimpleModel(nn.Module):
def __init__(self):
super().__init__()
self.fc1 = nn.Linear(5, 4)
self.relu = nn.ReLU()
self.fc2 = nn.Linear(4, 3)
def forward(self, x):
x = self.fc1(x)
x = self.relu(x)
x = self.fc2(x)
return x
def __call__(self, x):
return self.forward(x)
DEVICE = "musa"
def train_in_amp(low_dtype=torch.float16):
model = SimpleModel().to(DEVICE)
criterion = nn.MSELoss()
optimizer = torch.optim.SGD(model.parameters(), lr=0.1)
# create the scaler object
scaler = torch.musa.amp.GradScaler()
inputs = torch.randn(6, 5).to(DEVICE) # 将数据移至 GPU
targets = torch.randn(6, 3).to(DEVICE)
for step in range(20):
optimizer.zero_grad()
# create autocast environment
with torch.musa.amp.autocast(dtype=low_dtype):
outputs = model(inputs)
assert outputs.dtype == low_dtype
loss = criterion(outputs, targets)
scaler.scale(loss).backward()
scaler.step(optimizer)
scaler.update()
return loss
if __name__ == "__main__":
train_in_amp(torch.float16)
使能 TensorCore 示例代码
在 M1000 上,当输入数据类型是 float32 时,可以通过设置 TensorFloat32 来使能 TensorCore,从而加速计算过程。TensorFloat32 的加速原理可以参考
TensorFloat-32
。
import torch
import torch_musa
with torch.backends.mudnn.flags(allow_tf32=True):
assert torch.backends.mudnn.allow_tf32
a = torch.randn(10240, 10240, dtype=torch.float, device='musa')
b = torch.randn(10240, 10240, dtype=torch.float, device='musa')
result_tf32 = a @ b
torch.backends.mudnn.allow_tf32 = True
assert torch_musa._MUSAC._get_allow_tf32()
a = torch.randn(10240, 10240, dtype=torch.float, device='musa')
b = torch.randn(10240, 10240, dtype=torch.float, device='musa')
result_tf32 = a @ b
C++ 部署示例代码
#include <torch/script.h>
#include <torch_musa/csrc/core/Device.h>
#include <cassert>
#include <iostream>
#include <memory>
int main(int argc, const char* argv[]) {
// Register 'musa' for PrivateUse1 as we save model with 'musa'.
c10::register_privateuse1_backend("musa");
torch::jit::script::Module module;
try {
// Load model which saved with torch jit.trace or jit.script.
module = torch::jit::load(argv[1]);
} catch (const c10::Error& e) {
std::cerr << "error loading the model\n";
return -1;
}
std::vector<torch::jit::IValue> inputs;
// Ready for input data.
torch::Tensor input = torch::rand({1, 3, 224, 224}).to("musa");
assert(input.is_privateuseone() == input.is_musa());
assert(input.device().is_privateuseone() == input.device().is_musa());
inputs.push_back(input);
// Model execute.
at::Tensor output = module.forward(inputs).toTensor();
std::cout << output.slice(/*dim=*/1, /*start=*/0, /*end=*/5) << std::endl;
return 0;
}
详细用法请参考 examples/cpp 下内容。
端侧推理与编译加速
M1000 具有强大的 GPU 算力,能够高效支持各类 AI 模型的端侧推理任务。PyTorch 提供了多种推理方式。这源于不同部署场景的差异化需求以及 PyTorch 自身的架构演进。本节将详细介绍 eager
mode、torch.compile、AOTI 三种主流的推理模式及其在 torch_musa 中的应用。
m1000_gpu_model_zoo中存放了在 M1000 上基于 torch_musa 推理的热门模型,可帮助开发者快速上手部署。
注意:
并非所有模型都能够通过
torch.compile和aoti部署,具体原因可以通过TORCH_LOGS进行排查。
eager mode (即时执行模式)
Eager
Mode 是 PyTorch 的默认执行模式,采用逐行解释执行的方式,操作在调用时立即执行。这种模式最适合模型开发、调试和原型验证阶段。
基本用法
import torch
import torch_musa
import torchvision.models as models
model = models.resnet18().to("musa").eval()
x = torch.randn(1, 3, 224, 224, device="musa")
with torch.no_grad():
output = model(x)
print(output)
torch.compile 模式
PyTorch 2.0 引入了 torch.compile,基于 TorchDynamo + TorchInductor
架构,实现零代码改动即可获得显著加速。TorchDynamo 从 Python 字节码捕获图结构,TorchInductor 将图编译为高效的后端代码。更多信息,可参考
torch.compile 官方文档
基本用法
import torch
import torchvision.models as models
model = models.resnet18().to("musa").eval()
model = torch.compile(model, mode="max-autotune")
dummy_input = torch.randn(1, 3, 224, 224, device="musa")
with torch.no_grad():
output = model(dummy_input) # compile the model
print(output)
相比 eager 模式,只需要增加一行代码,即可增加推理速度。
第一次编译需要花费较多时间,期间可能出现 warning
RuntimeWarning: MUSA backend: num_stages>1 requested without using TME; falling back to num_stages=1.
stages["ttgir"] = lambda src, metadata: self.make_ttgir(src, metadata, options, self.capability, self.warp_size)
这是由于 M1000 缺少 TME 支持,在 triton 算子 tuning 过程中,num_stages > 1 的情况会回退到 num_stages
= 1 。
AOTI (Ahead-Of-Time Inductor)
AOTI 将模型预编译成独立的共享库(.so),实现完全脱离 Python 环境运行,适合生产环境部署和端侧推理场景。更多资料可参考
AOTI 官方文档
方式一:导出为 pt2 格式
编译阶段(Python)
使用 torch.export.export() 将给定的 PyTorch 模型捕获到计算图中。然后
torch._inductor.aoti_compile_and_package()
使用 TorchInductor 编译导出的程序,并将编译后的构件保存到一个包中。该包采用 PT2 Archive
Spec 的格式。
import os
import torch
import torchvision.models as models
model = models.resnet18().to(device="musa").eval()
example_inputs=(torch.randn(1,3,224,224, device="musa"),)
exported = torch.export.export(model, example_inputs)
output_path = torch._inductor.aoti_compile_and_package(
exported,
package_path=os.path.join(os.getcwd(), "res18.pt2"),
inductor_configs={"max_autotune": True,},
)
推理阶段(Python)
在 Python 中提供了一个方便的实用 API torch._inductor.aoti_load_package() 来加载和运行 pt2 文件。
import os
import torch
device = "musa" if torch.musa.is_available() else "cpu"
model = torch._inductor.aoti_load_package(os.path.join(os.getcwd(), "res18.pt2"))
print(model(torch.randn(1, 3, 224, 224, device=device)))
推理阶段(C++)
提供一个 inference.cpp 文件,使开发者能够直接在 C++ 环境中使用 pt2 格式的模型进行推理。
#include <iostream>
#include <vector>
#include <torch/torch.h>
#include <torch/csrc/inductor/aoti_package/model_package_loader.h>
#include <torch_musa/csrc/core/Device.h>
#include <torch_musa/csrc/aten/musa/MUSAContext.h>
int main() {
// Register 'musa' for PrivateUse1 as we save model with 'musa'.
c10::register_privateuse1_backend("musa");
at::musa::lazyInitMUSA();
torch::inductor::AOTIModelPackageLoader loader("res18.pt2");
// Assume running on MUSA
std::vector<torch::Tensor> inputs = {torch::randn({1, 3, 224, 224}).to("musa")};
std::vector<torch::Tensor> outputs = loader.run(inputs);
std::cout << "Result from the first inference:"<< std::endl;
std::cout << outputs[0] << std::endl;
return 0;
}
对应的 CMakeLists.txt 文件如下,需要在 CMAKE_PREFIX_PATH
变量中增加 torch 和 torch_musa 库的设置文件。
cmake_minimum_required(VERSION 3.18 FATAL_ERROR)
project(aoti_example)
list(APPEND CMAKE_PREFIX_PATH "/home/mt/miniforge3/envs/torch_2.9/lib/python3.10/site-packages/torch/share/cmake")
find_package(Torch REQUIRED)
list(APPEND CMAKE_PREFIX_PATH "/home/mt/miniforge3/envs/torch_2.9/lib/python3.10/site-packages/torch_musa/share/cmake")
find_package(TorchMusa REQUIRED)
add_executable(aoti_example inference.cpp)
target_link_libraries(aoti_example "${TORCH_LIBRARIES}" torch_musa)
set_property(TARGET aoti_example PROPERTY CXX_STANDARD 17)
文件夹结构
aoti_example/
CMakeLists.txt
inference.cpp
指令
mkdir build && cd build
cmake ..
cmake --build . --config Release
方式二:导出为 .so 格式
pt2 格式实际是对 TorchInductor 编译获得的 .so、C++ 源码、mubin 算子进行了打包。也可以使用以下接口获取原始导出内容:
torch._inductor.aot_compile()。
编译阶段(Python)
import torch
import torchvision.models as models
model = models.resnet18().to(device="musa").eval()
example_inputs=(torch.randn(1,3,224,224, device="musa"),)
exported = torch.export.export(model, example_inputs)
output_path = torch._inductor.aot_compile(
exported.module(),
example_inputs,
options={"max_autotune": True,},
)
print(f"aot_compile result saved in : {output_path}")
获得 aot_compile 导出的文件路径。
aot_compile result saved in : /tmp/torchinductor_mt/cc3a7dazpfrgeedyqhxjbqxfd2cjkbcmgtq2fdkptltbljikw4bs/c7u6xtpw3lbe6dl2wnyks2vaofkezcbocbmo4amnswwk23234kku.wrapper.so
推理阶段(Python)
将 so_path 换成之前获得的 aot_compile 导出的文件路径。
import torch
from torch_musa._export import aot_load # CUDA: from torch._export import aot_load
so_path = "/tmp/torchinductor_mt/cc3a7dazpfrgeedyqhxjbqxfd2cjkbcmgtq2fdkptltbljikw4bs/c7u6xtpw3lbe6dl2wnyks2vaofkezcbocbmo4amnswwk23234kku.wrapper.so"
device = "musa" if torch.musa.is_available() else "cpu"
model = aot_load(so_path, device)
print(model(torch.randn(8, 10, device=device)))
推理阶段(C++)
提供 so_aot_inference.cpp
文件,用于推理以 .so 格式导出的 AOTI 结果。同样需要将 so_path 换成之前获得的 aot_compile 导出的文件路径。base_dir 为 .so 文件所在的文件夹,用于寻找编译好的 .mubin 算子。
#include <iostream>
#include <vector>
#include <torch/torch.h>
#include <torch_musa/csrc/core/Device.h>
#include <torch_musa/csrc/aten/musa/MUSAContext.h>
#include <torch_musa/csrc/inductor/aoti_runner/model_container_runner_musa.h>
int main() {
// Register 'musa' for PrivateUse1 as we save model with 'musa'.
c10::register_privateuse1_backend("musa");
at::musa::lazyInitMUSA();
std::string so_path = "/tmp/torchinductor_mt/cc3a7dazpfrgeedyqhxjbqxfd2cjkbcmgtq2fdkptltbljikw4bs/c7u6xtpw3lbe6dl2wnyks2vaofkezcbocbmo4amnswwk23234kku.wrapper.so";
std::string base_dir = "/tmp/torchinductor_mt/cc3a7dazpfrgeedyqhxjbqxfd2cjkbcmgtq2fdkptltbljikw4bs";
torch::inductor::AOTIModelContainerRunnerMusa runner(so_path, 1, "musa:0", base_dir);
// Run inference
std::vector<torch::Tensor> inputs = {torch::randn({1, 3, 224, 224}).to("musa")};
std::vector<torch::Tensor> outputs = runner.run(inputs);
std::cout << "Output shape: " << outputs[0].sizes() << std::endl;
std::cout << "Output first 10 values: " << outputs[0].flatten().slice(0, 0, 10) << std::endl;
return 0;
}
对应的 CMakeLists.txt 文件如下,需要在 CMAKE_PREFIX_PATH
变量中增加 torch 和 torch_musa 库的设置文件。
cmake_minimum_required(VERSION 3.10)
project(so_aot_inference LANGUAGES CXX VERSION 1.0.0)
list(APPEND CMAKE_PREFIX_PATH "/home/mt/miniforge3/envs/torch_2.9/lib/python3.10/site-packages/torch/share/cmake")
find_package(Torch REQUIRED)
list(APPEND CMAKE_PREFIX_PATH "/home/mt/miniforge3/envs/torch_2.9/lib/python3.10/site-packages/torch_musa/share/cmake")
find_package(TorchMusa REQUIRED)
add_executable(so_aot_inference so_aot_inference.cpp)
target_link_libraries(so_aot_inference "${TORCH_LIBRARIES}" torch_musa)
set_property(TARGET so_aot_inference PROPERTY CXX_STANDARD 17)
文件夹结构
so_aoti_example/
CMakeLists.txt
so_aot_inference.cpp
指令
mkdir build && cd build
cmake ..
cmake --build . --config Release
算子开发
PyTorch 采用定义和实现分离的方式构建算子单元;定义部分包含算子格式、实现方式、后端绑定和导出规则(见 aten/src/ATen/native/native_functions.yaml 文件);对于单个算子,官方为多种设备后端分别实现了计算逻辑。构建时,PyTorch 调用 torchgen 模块解析 yaml 文件,自动生成算子的接口 (*.h)和定义(*.cpp)文件,后者包含了具体实现与后端的绑定,最终在运行时完成注册。
基于 yaml 文件的格式规范,torch_musa 扩展 torchgen 的部分逻辑实现了 codegen 模块;开发者实现 MUSA 算子的计算逻辑后,只需在 torch_musa/csrc/aten/ops/musa_functions.yaml 文件中添加该算子的关键描述,编译时 codegen 模块可自动解析文件内容,生成算子的接口和定义文件,完成与 MUSA 后端的绑定(参考 PyTorch 官方建议,torch_musa 复用 PrivateUse1
key 实现算子注册)。
本节旨在对 PyTorch 的算子实现进行分类,帮助开发者判断是否需要手动适配,选择合适的方式实现算子逻辑,修改 musa_functions.yaml 文件实现绑定注册,完成 MUSA 算子的开发。
注解:
对于单个算子,多后端实现共享相同的接口,在 musa_functions.yaml 文件中该算子的接口定义字段(
func:)只需要列出函数名即可。
注解:
PyTorch 算子的 MUSA 后端 C++ 实现统一位于 at::musa 命名空间下,函数签名和接口定义一致, 禁止默认参数值。
注意:
本节不涉及算子正/反向计算关系的绑定;torch_musa 遵循 PyTorch 自动微分模块的设计与实 现,绑定规则可见 tools/autograd/derivatives.yaml 文件。
算子实现分类
实际上,PyTorch 算子就是 C++ 函数,不同后端提供对应的实现(包含引用第三方代码,比如 MUSA 的.mu 文件),绑定到同一个接口;当接口被调用时,框架根据运行时环境和传入参数,推理出目标后端,然后派发到该后端注册的实现函数完成计算。
由于尺寸变化、类型转换或数据依赖等原因,算子的实现通常会先在内部创建临时变量,计算结果写入 完成后作为结果返回,称为 functional 规则。以 isnan 算子为例, Python 接口为:
torch.isnan(input) -> Tensor
输出是 bool 类型,当输入是 fp32 时,产生类型转换,结果变量只能从内部创建。
当实现逻辑不受上述因素影响时,算子可通过多种规则实现调用,表现为可选的输出参数。以 tril 算子为例,Python 接口为:
torch.tril(input, diagonal=0, *, out=None) -> Tensor
可以看出,输出变量 out 是可配置的,支持外部传入。故产生了下列调用规则:
>>> a = torch.randn(3, 3)
>>> torch.tril(a)
functional 规则(默认),计算结果由实现内部创建并返回。
>>> a = torch.randn(3, 3)
>>> a.tril_()
inplace 规则,输入 a 是可读写的,计算完成后数据被覆盖。
>>> a = torch.randn(3, 3)
>>> b = torch.randn(3, 3)
>>> torch.tril(a, out=b)
out 规则,计算结果写入外部提前创建好的变量 b。
注意,PyTorch 算子区分调用规则。对于 tril 算子,native_functions.yaml 文件中对每种调用规则分别定义了 C++ 函数接口:
Tensor tril(const Tensor&, int64_t); // functional
Tensor& tril_(Tensor&, int64_t); // inplace
Tensor& tril_out(const Tensor&, int64_t, Tensor&); // out
Python 调用对应多种规则的 C++ 算子,称为一个 group
;调用后,在 group 内选择对应的 C++ 算子进行派发。从实现角度看,不同的调用规则除了预处理有区别之外,计算逻辑是一致的。因此,PyTorch 针对 group 算子引入了 structured 的实现方式,整体逻辑拆分为预处理和计算两个子过程,这样的好处是:
-
预处理过程屏蔽调用规则的逻辑差异,对外不区分后端,甚至算子类型。
-
计算过程不区分调用规则,每个后端只需要实现一个计算函数。
-
算子实现的外层 逻辑(预处理+ 计算)一致,可由
codegen生成。
structured 的实现方式可以减少代码体积,实现高效的跨规则/跨算子逻辑复用,减少算子实现的工作量。与之相反,只有一种调用规则的算子,对应 unstructured 的实现方式(不存在多种调用规则的逻辑区别),开发者可以尝试复用已有的 structured 子过程,也可以独立实现整个算子逻辑。
是否需要适配算子
以 tril 算子为例,可执行下面的 Python 程序进行验证:
import torch
import torch_musa
input_data = torch.randn(3, 3, device="musa")
result = torch.tril(input_data)
当 torch_musa 内部实现了该算子时,程序正常执行完毕,可以查看 result 的数据和属性:

当测试环境打印如下报错信息:

可以确定,MUSA 后端缺少该算子的实现,原因为:
-
尝试调用 functional 规则的算子,即内部创建 result ,计算结果写入后返回,发现该实现缺失。
-
先创建 result,作为 out 参数传入 out 规则的算子调用,发现该实现也缺失。
-
没有其他可调用的算子,报错返回异常。
此时需要手动实现 tril 算子。考虑计算逻辑的完备性,建议把 tril group 内所有调用规则对 应的 C++ 算子全都实现。
实现 Structured 算子
对于 structured
group 内多种调用规则的 C++ 算子,PyTorch 使用多级继承的方式实现统一的 预处理 和 计算逻辑,继承类的命名遵循 torchgen 的规则,添加“structured”前缀。以 tril 算子的 CUDA 实现为例,规则如下(由底至上):
-
以 functional 算子名创建 meta::structured_tril 类(简称 meta 类,继承官方的
MetaBase基类),新增 meta 函数实现预处理。 -
以 out 算子绑定的后端实现名创建 native::structured_tril_cuda 类(简称 impl 类,继承 meta 类), 新增 impl 函数实现计算。
-
创建 structured_tril_cuda_functional 类(简称 functional 类,继承 impl 类),覆写基类方法实现结果 tensor 的创建。
-
创建 structured_tril_cuda_inplace 类(简称 inplace 类,继承 impl 类),覆写基类方法实现可读写 tensor 的校验。
-
创建 structured_tril_cuda_out 类(简称 out 类,继承 impl 类),覆写基类方法实现结果 tensor 的校验和尺寸变化。
-
functional/inplace/out 算子实现为分别实例化 3/4/5 中创建的子类,依次调用 meta 和 impl 函数, 最后返回结果。
对于算子实现部分,开发者只需完成 meta 类的预处理和 impl 类的计算逻辑开发,实现对应的函数;为了确保 codegen 正确生成算子文件和注册绑定, native_functions.yaml 文件中对三种调用规则的算子定义添加如下字段 :
- 不同调用规则的算子类(上述 3/4/5 中的类)继承同一个 impl 类,视为一种“规约”关系;impl 类 名固定引用 out 算子绑定的后端名,故在 functional/inplace 算子定义中添加“规约”字段,目标为 out 算子名。
- func: tril(Tensor self, int diagonal=0) -> Tensor
# 参考 out 算子的内容,生成 functional 类
# 标记该算子为 structured 的实现方式
structured_delegate: tril.out
- func: tril_(Tensor(a!) self, int diagonal=0) -> Tensor(a!)
# 参考 out 算子的内容,生成 inplace 类
# 标记该算子为 structured 的实现方式
structured_delegate: tril.out
- out 算子声明除了后端名外,需要增加
structured实现方式的标记,支持扩展字段描述基类信息,以 及传递预处理的中间变量映射关系。
- func: tril.out(Tensor self, int diagonal=0, *, Tensor(a!) out) -> Tensor(a!)
structured: True # 标记该算子为 structured 的实现方式
dispatch:
CUDA: tril_cuda # out 类和 impl 类实现的命名参考
# 选择要继承的基类,缺省默认为 MetaBase,自定义例如
# structured_inherits: TensorIteratorBase
# 预处理传递给计算函数的中间变量映射关系,缺省默认为原参数传递,自定义例如
# precomputed:
# - diagonal -> int var
导入上述定义字段后,构建时 torchgen 自动解析字段内容,生成上述 1-6 中所有的类定义和算子实现 (调用 meta 和 impl 函数),生成绑定和注册代码,完成算子的适配。
基于上述规范,在实现 structured 的 MUSA 算子时,考虑到 PyTorch 已经集成了 CUDA 的实现, meta/impl 函数可能都可以复用,故有三种 实现方法。
复用 meta/impl 函数(Legacy)
这类算子的 meta 预处理逻辑是通用的,impl 函数内部应用了 PyTorch 的 DispatchStub 机制,即 CPU/CUDA 后端分别把计算 kernel 注册到算子对应的 stub 中,运行时根据目标 device 在 stub 中找到对应的 kernel 完成计算。可直接复用 CUDA 的 impl 实现,只需要实现 MUSA
kernel,并注册到对应的 stub 中即可。
注解:
DispatchStub 的原理可参考
pytorch/aten/src/ATen/native/DispatchStub.h
源码文件
由于 MUSA 的编程模型实现了 CUDA 兼容,MUSA
kernel 的一种实现方法是通过 CUDA-Porting 工具完成,主要流程如下:
-
新建目录 build/generated_cuda_compatible,保存
Porting的 kernels 文件和依赖头文件。 -
把
PyTorch仓库中的CUDAkernels 和安装目录中的头文件复制到新建目录内(维持相对路径)。 -
Porting工具实施文本替换,如将cudaMalloc替换成musaMalloc,cuda_fp16.h替换成musa_fp16.h等。
上述步骤在编译时依次执行,结束后指定目录下出现目标 mu 文件,包含转换完成的 MUSA
kernel 和 stub 注册实现,将该文件添加到 musa_kernels 库包含的源文件集合中即可。以 lerp 算子为例,目标文件为 Lerp.mu,包含如下内容:
// 文件位置:build/generated_cuda_compatible/aten/src/ATen/native/musa/Lerp.mu
// lerp.Tensor group MUSA kernel
void lerp_tensor_kernel(at::TensorIteratorBase& iter) {......}
// lerp.Scalar group MUSA kernel
void lerp_scalar_kernel(at::TensorIteratorBase& iter, const c10::Scalar& weight) {
......
}
// lerp.Tensor group stub 注册
REGISTER_DISPATCH(lerp_kernel_tensor_weight, &lerp_tensor_kernel);
// lerp.Scalar group stub 注册
REGISTER_DISPATCH(lerp_kernel_scalar_weight, &lerp_scalar_kernel);
文件中已经包含计算和注册代码,需要在编译文件中做如下修改:
# 文件位置:/home/torch_musa/torch_musa/csrc/CMakeLists.txt
file(
GLOB_RECURSE
MU_SRCS
......
${GENERATED_PORTING_DIR}/aten/src/ATen/native/musa/Lerp.mu
......
)
编译完成后 musa_kernels 动态库中会包含 MUSA lerp 的 kernels,在运行时自动注册到 lerp
stub 中。 由于 MUSA 和 CUDA 设备的参数和架构存在差异,有时通过 CUDA-Porting 生成的 MUSA
kernels 在运行时会报错或者效率不 高,此时开发者可以选择手动修改或重写计算逻辑,即放弃在编译文件中加入 Porting 生成的 Lerp.mu 文件,在 torch_musa/csrc/aten/ops/musa 目录下创建 Lerp.mu 文件,手动实现 lerp
kernels 和 stub 注册。
对于这种 Legacy 的实现方式,musa_functions.yaml 中算子的定义只需要列出算子名,其他的 structured 关键字 codegen 模块自动与 PyTorch 保持对齐。以 Lerp 算子为例,内容如下:
- func: lerp.Scalar_out
- func: lerp.Scalar
- func: lerp_.Scalar
- func: lerp.Tensor_out
- func: lerp.Tensor
- func: lerp_.Tensor
只复用 meta 函数(LegacyMeta)
当算子的预处理逻辑通用,计算逻辑不使用 DispatchStub 时,开发者需要显式实现 MUSA 后端的 impl 函数。以 tril 算子为例,首先在 musa_functions.yaml 文件中添加算子接口定义:
- func: tril
- func: tril_
- func: tril.out
dispatch:
PrivateUse1: MusaTril
functional/inplace 规则的算子“规约”方式保持一致,列出算子名即可;与 Legacy 方式不同,out 算子需要显式指定后端实现名,让 codegen 模块自动生成 impl 类(在 at::musa 命名空间下)。实现计算逻辑时,可在 torch_musa/csrc/aten/ops 目录下新建 Tril.cpp 文件,实现如下函数:
// 文件位置:torch_musa/csrc/aten/ops/Tril.cpp
namespace at::musa {
TORCH_IMPL_FUNC(MusaTril)(const Tensor& self, int64_t k, const Tensor &result) { // 计算过程
}} // namespace at::musa
PyTorch 针对 structured 类和函数定义了一系列宏,此处 TORCH_IMPL_FUNC(MusaTril) 会自动展开为 void
structured_MusaTril::impl,与 codegen 生成的 impl 类函数保持一致。LegacyMeta 的实现方式不用修改编译文件,Tril.cpp 会自动被加入到 musa_kernels 库的源文件集合中。
自定义 meta/impl 函数(Customized)
如果在实现 MUSA structured 算子时遇到如下情况:
-
预处理逻辑和 CPU/
CUDA有区别。 -
meta 类需要继承不同的基类。
-
meta 函数需要传给 impl 函数自定义的中间值,与 CPU/
CUDA不同或 CPU/CUDA不传中间值。
开发者需要同时显式实现 meta 和 impl 函数(都在 at::musa 命名空间下)。以 tril 算子为例,首先在 musa_functions.yaml 文件中添加算子接口定义:
- func: tril
- func: tril_
- func: tril.out
structured_inherits: MyMetaBase # 集成的基类名
precomputed:
- diagonal -> int var # 需要传递的中间变量
dispatch:
PrivateUse1: MusaTril
out 算子定义中必须显式指定 structured_inherits(情况 2)或者 precomputed(情况 3)字段。考虑情况 2,meta 类的定义如下:
namespace at::musa {
struct TORCH_API structured_tril : public at::musa::MyMetaBase { void meta(const at::Tensor & self, int64_t diagonal);};} // namespace at::musa
meta 类的名字和 CPU/CUDA 一样,依靠命名空间实现隔离。需要额外满足情况 3 时,meta 类定义为:
namespace at::musa {
struct TORCH_API structured_tril : public at::musa::MyMetaBase { template <bool VAR = false> struct TORCH_API precompute_out { precompute_out<true> set_var(int64_t value) { static_assert(VAR == false, "var already set"); precompute_out<true> ret; ret.var = value; return ret; } int64_t var; }; using meta_return_ty = precompute_out<true>; meta_return_ty meta(const at::Tensor & self, int64_t diagonal);};} // namespace at::musa
codegen 模块会在 meta 类中生成一个嵌套模板子类 precompute_out,meta 函数返回值由 void 变为该子类的实例化,存储产生的中间变量。 impl 函数的 diagonal 参数由 var 代替,而非算子调用时传入的值。因此 impl 类定义为:
namespace at::musa {
struct TORCH_API structured_MusaTril : public at::musa::structured_tril { void impl(const at::Tensor & self, int64_t var, const at::Tensor & out);};} // namespace at::musa
开发者在实现 Customized 形式的 meta/impl 函数时需要注意函数签名和 codegen 生成的接口声明保 持一致。以 Tril.cpp 为目标文件,计算逻辑可实现如下:
// 文件位置:torch_musa/csrc/aten/ops/Tril.cpp
namespace at::musa {
TORCH_PRECOMPUTE_META_FUNC(tril)(const Tensor& self, int64_t diagonal) { // 参数校验
// 计算临时变量
int64_t var = .... // 打包临时变量
return TORCH_PRECOMPUTE_META_FUNC(tril).set_var(var);}
TORCH_IMPL_FUNC(MusaTril)(const Tensor& self, int64_t var, const Tensor &result) { // 计算实现
}} // namespace at::musa
计算函数的第二个参数是中间变量 var,非算子调用时传入的原始参数 diagonal,剩下的实现过程与 LegacyMeta 方式类似。
总结来看,MUSA structured 算子的开发难度为 Legacy < LegacyMeta <
Customized。当 impl 函数使用了 DispatchStub 机制时,可以通过 Porting-CUDA 快速实现基础 MUSA
kernels;遇到正确性或效率问题时,可结合 MUSA 设备的架构参数,自定义 impl 函数优化计算逻辑;如果要实现全新的预处理策略,再考虑自定义 meta 函数,普通情况下一般不会用到。
实现 UnStructured 算子
PyTorch 的 unstructured 算子一般以 functional 规则调用,实现逻辑相互独立,不显式抽象出预处理和计算子逻辑,而是在实现内部自组织。算子定义时,需要显式指定 MUSA 后端的派发名,和 structured 算子不同,这个名字就是实现绑定的函数名。以 nonzero 算子为例,参考 CPU/CUDA 声明格式, MUSA 后端可声明如下:
# CPU/CUDA 声明,来自 native_functions.yaml
# structured 默认为 false,标记为 unstructured 实现方式
- func: nonzero(Tensor self) -> Tensor
dispatch:
CPU: nonzero_cpu
CUDA: nonzero_cuda
# MUSA 声明,来自 musa_functions.yaml
# structured 缺省,默认和官方保持一致
- func: nonzero
dispatch:
PrivateUse1: Nonzero
算子声明可以显式指定 structured 为 false。由于官方实现方式默认是 unstructured,该字段也可以忽略不写,codegen 模块会自动对齐 CPU/CUDA 的设置。考虑灵活扩展,torch_musa 也支持将官方 structured 实现转换为 MUSA 后端的 unstructured 实现,以 add.Tensor 算子为例(Tensor +
Tensor),MUSA 的 unstructured 声明如下:
# Unstructured 声明,来自 musa_functions.yaml
- func: add.Tensor
dispatch:
PrivateUse1: AddTensor
- func: add_.Tensor
dispatch:
PrivateUse1: AddTensor_
- func: add.out
structured: false
dispatch:
PrivateUse1: AddTensorOut
如果算子的原始 structured 声明包含下列属性,在 MUSA 声明中需要显式处理:
-
structured:out 声明用 false 值覆盖,functional/inplace 声明缺省。 -
structured_delegate:functional/inplace 声明用 none 覆盖,out 声明缺省。 -
structured_inherits:out 声明用 none 值覆盖,functional/inplace 声明缺省。 -
precomputed:out 声明用 none 值覆盖,functional/inplace 声明缺省。 -
dispatch:所有规则声明都需要显式指定派发的后端名。
正确设置上述属性后,codegen 模块把 group 内每个调用规则的算子看成是实现独立的,生成注册函数时内部直接调用 MUSA 后端绑定名对应的实现函数,否则解析 yaml 文件会报错。在算子实现部分,可根据实际情况选择合适的逻辑策略。
复用公共函数(Legacy)
当算子的实现逻辑完全不区分后端时,CPU/CUDA 的实现会共用 PyTorch 仓库提供的一个基础函 数,内部逻辑可能只涉及非数据计算的视图转换,或者数据计算完全由依次调用其他算子完成。以 view_as_real 算子为例,实现逻辑只是复数拆开成两个浮点数的视图转换,没有数据计算,原始的官方 声明如下:
- func: view_as_real(Tensor(a) self) -> Tensor(a)
dispatch:
CPU, CUDA, MPS, Meta: view_as_real
可以看出 CPU/CUDA 等后端绑定了一个公共的 view_as_real 函数实现转换; MUSA 后端实现应该保持 一致,避免冗余代码,表现在算子声明上:
- func: view_as_real
dispatch:
PrivateUse1: view_as_real
在编译时, codegen 模块会在 unstructured 实现方式的前提下对比函数名,判断 出 MUSA 的绑定函数和 CPU/CUDA 一样,注册函数内部自动调用对应的公共函数,也称为 Legacy 实现。
接入 MUDNN(Customized)
如果算子的 MUSA 实现涉及数据计算,且 MUDNN 库提供了相应能力,可在实现逻辑内部直接调用 MUDNN 接口完成计算。使用 MUDNN 库的主要步骤如下:
-
参数校验,主要检查 device/dtype/defined_tensor 等。
-
添加 DeviceGuard。
-
参数转换,比如 conv 算子只支持连续 tensors,需要提前把输入/输出 tensors 转换为连续的。
-
创建输入/输出 MUTensors,以及
MUDNN的计算实例,配置计算参数。 -
调用实例接口完成计算,返回计算结果。
以 add.Tensor 的 functional 算子为例,可在 torch_musa/csrc/aten/ops 目录下创建 Add.cpp 文件,实现 AddTensor 函数:
#include <mudnn.h>
Tensor AddTensor(
const Tensor& self, const Tensor& other, Scalar const& alpha_scalar) {
// 检查 device
TORCH_CHECK(self.device().type() == kMUSA, "......");
TORCH_CHECK(self.device().type() == other.device().type(), "......");
// 检查 dtype
TORCH_CHECK(
self.scalar_type() == at::ScalarType::Float, "......");
......
// 添加 DeviceGuard
const c10::musa::MUSAGuard guard(self.device());
// 连续性转换
auto self_contig = self.contiguous();
auto other_contig = at::mul(other, alpha_scalar);
other_contig = other_contig.contiguous();
// 创建输出
auto output = at::empty(
infer_size_dimvector(self.sizes(), other.sizes()),
self.options());
// 创建 MUTensors
muTensor lhs = CreateMUTensor(self_contig);
muTensor rhs = CreateMUTensor(other_contig);
muTensor out = CreateMUTensor(output);
// 调用 MUDNN 接口
auto& h = GetMudnnHandle();
::musa::dnn::Binary op;
CHECK_MUDNN_STATUS(op.SetMode(::musa::dnn::Binary::Mode::ADD), "SetMode");
CHECK_MUDNN_STATUS(op.Run(h, out, lhs, rhs), "Run Add.Tensor");
return output;
}
通过 mudnn*.h 头文件可以查询 MUDNN 库的算子支持情况和接口定义,默认地址为 /usr/local/musa/include 目录。如果 MUDNN 不支持,也可以通过 CUDA-Porting 等方式手动实现 MUSA
kernels,在函数内部手工调用完成计算。
CPU 计算(Customized)
对于部分算子,如果 MUDNN 不支持,CUDA-Porting 也无法支持,可以临时中转到 CPU 后端实现该算子。主要逻辑是,先把 tensor 拷贝到 CPU 上,调用 CPU 算子完成计算,再将结果拷贝回 GPU。可以参考下述代码 :
Tensor AddTensor(
const Tensor& self, const Tensor& other, Scalar const& alpha_scalar) {
const auto cpu_dev = DeviceType::CPU;
const auto musa_dev = self.device();
auto cpu_self = at::empty(self.sizes(), self.options().device(cpu_dev));
auto cpu_other = at::empty(other.sizes(), other.options().device(cpu_dev));
return at::cpu::add(cpu_self, cpu_other).to(musa_dev);
}
在初次适配模型时,可以通过这种方式快速判断缺少哪些算子,然后再逐个适配,通过接入 MUDNN 或 自定义 kernels 的方式提高性能。
注意:
除了 MUSA 后端(PrivateUse1)外,其他 MUSA 相关后端(比如 QuantizedPrivateUse1)的实现方式一定是 unstructured 的。
注解:
以上代码仅作参考,不代表实际的实现逻辑。
第三方库 MUSA 扩展支持
本节主要介绍如何对 PyTorch 生态的第三方库进行 MUSA 扩展的构建(MUSAExtension),对应于 CUDAExtension。
为什么要对第三方库进行 MUSA 扩展的构建
以 mmcv 库(commit
id 为 0a2f60ba0198f8d567b536313bfba329588f9c3f) 为例,当测试代码出现如下报错 log 时,说明 mmcv 中没有构建 MUSA 扩展。此时需要对 mmcv 库进行 MUSA 扩展,使 mmcv 库运行在摩尔线程显卡上。
import numpy as np
import torch
import torch_musa
from mmcv.ops import nms
np_boxes = np.array([[6.0, 3.0, 8.0, 7.0], [3.0, 6.0, 9.0, 11.0],
[3.0, 7.0, 10.0, 12.0], [1.0, 4.0, 13.0, 7.0]],
dtype=np.float32)
np_scores = np.array([0.6, 0.9, 0.7, 0.2], dtype=np.float32)
np_inds = np.array([1, 0, 3])
np_dets = np.array([[3.0, 6.0, 9.0, 11.0, 0.9],
[6.0, 3.0, 8.0, 7.0, 0.6],
[1.0, 4.0, 13.0, 7.0, 0.2]])
boxes = torch.from_numpy(np_boxes)
scores = torch.from_numpy(np_scores)
# check if cpu can work
dets, inds = nms(boxes, scores, iou_threshold=0.3, offset=0)
# check if musa can work
dets, inds = nms(boxes.musa(), scores.musa(), iou_threshold=0.3, offset=0)

注意以上测试不要在 mmcv 根目录下进行,以免将当前目录下的 mmcv 包导入。