muDNN 开发者指南
本文档介绍 muDNN 的架构、核心概念和使用方法。
C++ API 已标记为弃用。新应用请使用 C API,C++ API 将在未来版本中移除。
安装与环境准备
muDNN 随 MUSA SDK 一起发布。安装步骤可参考 MUSA SDK 安装指南。
在使用本文档中的示例前,请先完成 Linux Driver 和 MUSA Toolkit 安装,并确保相关环境变量已生效。
概述
什么是 muDNN
muDNN(Moore Threads Deep Neural Network Library,深度神经网络库)是面向 MUSA 平台的深度神经网络加速库,提供张量、卷积、归一化、激活、注意力等常用神经网络算子接口。
关键特性
- 提供 C API 形式的神经网络算子接口,新应用推荐优先使用 C API。
- 支持张量算子、卷积神经网络算子和高级算子等多类能力。
- 通过 handle、descriptor 和 workspace 模型管理执行上下文与算子参数。
- 针对 MTGPU 架构优化,适用于深度学习训练和推理场景。
muDNN 架构
API 层次结构
muDNN C API 由以下头文件组成:
mudnn.h - 主头文件
聚合所有 C API 导出接口,包含:
mudnn_version.h: 版本信息查询mudnn_ops.h: 张量算子和神经网络算子mudnn_cnn.h: 卷积神经网络算子mudnn_adv.h: 高级算子mudnn_graph.h: 图API
mudnn_ops.h - 基础算子
| 类型/函数 | 说明 |
|---|---|
mudnnHandle_t | muDNN 设备句柄,绑定到 MUSA 设备与流 |
mudnnTensorDescriptor_t | 张量描述符,描述数据地址、类型、格式、维度 |
mudnnFilterDescriptor_t | 滤波器/权重描述符 |
mudnnCreateXXXDescriptor | 创建各类描述符 |
mudnnDestroyXXXDescriptor | 销毁各类描述符 |
张量算子:
| 算子 | 函数前缀 |
|---|---|
| 一元算子 | mudnnUnary |
| 二元算子 | mudnnBinary |
| 三元算子 | mudnnTernary |
| 拼接 | mudnnConcat |
| 填充 | mudnnFill |
| 置换 | mudnnPermute |
| 归约 | mudnnReduce |
mudnn_cnn.h - 神经网络算子
| 算子类型 | 函数前缀 |
|---|---|
| 卷积 | mudnnConvolution |
| 池化 | mudnnPooling |
| 归一化 | mudnnBatchNorm, mudnnLayerNorm, mudnnGroupNorm |
| 激活 | mudnnActivation, mudnnSoftmax |
| Dropout | mudnnDropout |
mudnn_adv.h - 高级算子
| 算子类型 | 函数前缀 |
|---|---|
| 注意力 | mudnnMultiHeadAttention, mudnnScaledDotProductAttention |
| 损失 | mudnnCTCLoss |
| 矩阵乘法 | mudnnGemm |
快速开始
示例 1:LayerNorm(C API,使用非deprecated接口)
以下示例展示如何使用 muDNN C API 执行 LayerNorm 操作:
#include <stdio.h>
#include <stdlib.h>
#include <musa_runtime_api.h>
#include <mudnn/mudnn.h>
#define MUDNN_CHECK(cmd) \
do { \
mudnnStatus_t status = cmd; \
if (status != MUDNN_STATUS_SUCCESS) { \
fprintf(stderr, "muDNN error %d at %s:%d\n", status, \
__FILE__, __LINE__); \
exit(EXIT_FAILURE); \
} \
} while (0)
#define MUSACHECK(cmd) \
do { \
musaError_t err = cmd; \
if (err != musaSuccess) { \
fprintf(stderr, "MUSA error %d at %s:%d\n", err, \
__FILE__, __LINE__); \
exit(EXIT_FAILURE); \
} \
} while (0)
int main() {
mudnnHandle_t handle;
mudnnTensorDescriptor_t input_desc, output_desc;
mudnnTensorDescriptor_t mean_desc, variance_desc, gamma_desc, beta_desc;
mudnnLayerNormDescriptor_t layernorm_desc;
// 1. 创建 muDNN 句柄
MUDNN_CHECK(mudnnCreate(&handle));
// 2. 创建描述符
MUDNN_CHECK(mudnnCreateTensorDescriptor(&input_desc));
MUDNN_CHECK(mudnnCreateTensorDescriptor(&output_desc));
MUDNN_CHECK(mudnnCreateTensorDescriptor(&mean_desc));
MUDNN_CHECK(mudnnCreateTensorDescriptor(&variance_desc));
MUDNN_CHECK(mudnnCreateTensorDescriptor(&gamma_desc));
MUDNN_CHECK(mudnnCreateTensorDescriptor(&beta_desc));
MUDNN_CHECK(mudnnCreateLayerNormDescriptor(&layernorm_desc));
// 3. 设置张量维度 (N, C, H, W) - NCHW 格式
const int N = 4, C = 64, H = 32, W = 32;
// 设置输入张量描述符
MUDNN_CHECK(mudnnSetTensor4dDescriptor(
input_desc, MUDNN_TENSOR_NCHW, MUDNN_DATA_FLOAT, N, C, H, W));
// 输出/../gamma/beta 使用相同维度
MUDNN_CHECK(mudnnSetTensor4dDescriptor(
output_desc, MUDNN_TENSOR_NCHW, MUDNN_DATA_FLOAT, N, C, H, W));
MUDNN_CHECK(mudnnSetTensor4dDescriptor(
mean_desc, MUDNN_TENSOR_NCHW, MUDNN_DATA_FLOAT, N, C, H, W));
MUDNN_CHECK(mudnnSetTensor4dDescriptor(
variance_desc, MUDNN_TENSOR_NCHW, MUDNN_DATA_FLOAT, N, C, H, W));
MUDNN_CHECK(mudnnSetTensor4dDescriptor(
gamma_desc, MUDNN_TENSOR_NCHW, MUDNN_DATA_FLOAT, N, C, H, W));
MUDNN_CHECK(mudnnSetTensor4dDescriptor(
beta_desc, MUDNN_TENSOR_NCHW, MUDNN_DATA_FLOAT, N, C, H, W));
// 设置 LayerNorm 描述符
int axes[] = {1, 2, 3}; // 对 C, H, W 维度归一化
MUDNN_CHECK(mudnnSetLayerNormDescriptor(
layernorm_desc,
1e-5, // epsilon
MUDNN_LAYERNORM_MODE_ELEMENTWISE, // 模式
axes, // 归一化轴
3 // 轴数量
));
// 4. 分配设备内存
float *input_data, *output_data, *mean_data, *variance_data;
float *gamma_data, *beta_data;
size_t size = N * C * H * W * sizeof(float);
size_t channel_size = C * sizeof(float);
MUSACHECK(musaMalloc((void**)&input_data, size));
MUSACHECK(musaMalloc((void**)&output_data, size));
MUSACHECK(musaMalloc((void**)&mean_data, size));
MUSACHECK(musaMalloc((void**)&variance_data, size));
MUSACHECK(musaMalloc((void**)&gamma_data, channel_size));
MUSACHECK(musaMalloc((void**)&beta_data, channel_size));
// 5. 查询工作区大小
size_t workspace_size;
MUDNN_CHECK(mudnnGetLayerNormForwardWorkspaceSize(
handle, layernorm_desc, output_desc, mean_desc, variance_desc,
input_desc, gamma_desc, beta_desc, &workspace_size
));
void* workspace = NULL;
if (workspace_size > 0) {
MUSACHECK(musaMalloc(&workspace, workspace_size));
}
// 6. 执行 LayerNorm
MUDNN_CHECK(mudnnLayerNormForward(
handle, layernorm_desc,
output_desc, output_data,
mean_desc, mean_data,
variance_desc, variance_data,
input_desc, input_data,
gamma_desc, gamma_data,
beta_desc, beta_data,
workspace, workspace_size
));
// 7. 同步流
MUSACHECK(musaStreamSynchronize(musaStreamPerThread));
printf("LayerNorm executed successfully!\n");
// 8. 清理资源
MUSACHECK(musaFree(input_data));
MUSACHECK(musaFree(output_data));
MUSACHECK(musaFree(mean_data));
MUSACHECK(musaFree(variance_data));
MUSACHECK(musaFree(gamma_data));
MUSACHECK(musaFree(beta_data));
if (workspace) MUSACHECK(musaFree(workspace));
MUDNN_CHECK(mudnnDestroyTensorDescriptor(input_desc));
MUDNN_CHECK(mudnnDestroyTensorDescriptor(output_desc));
MUDNN_CHECK(mudnnDestroyTensorDescriptor(mean_desc));
MUDNN_CHECK(mudnnDestroyTensorDescriptor(variance_desc));
MUDNN_CHECK(mudnnDestroyTensorDescriptor(gamma_desc));
MUDNN_CHECK(mudnnDestroyTensorDescriptor(beta_desc));
MUDNN_CHECK(mudnnDestroyLayerNormDescriptor(layernorm_desc));
MUDNN_CHECK(mudnnDestroy(handle));
return 0;
}
编译命令:
编译依赖于 MUSA SDK 的 musa_asm 组件,请确保已正确安装。
mcc layernorm_example.c \
-I${MUSA_SDK_PATH}/include \
-L${MUSA_SDK_PATH}/lib \
-lmudnn -lmusa_runtime \
-Wl,-rpath,${MUSA_SDK_PATH}/lib \
-o layernorm_example
运行:
./layernorm_example
示例 2:GEMM(使用 muDNN)
以下示例展示如何使用 muDNN 执行矩阵乘法(GEMM)操作:
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <iomanip>
#include <iostream>
#include <vector>
#include <musa_runtime_api.h>
#include "mudnn.h"
#define MUDNN_CHECK(cmd) \
do { \
mudnnStatus_t status = cmd; \
if (status != MUDNN_STATUS_SUCCESS) { \
fprintf(stderr, "muDNN error %d at %s:%d\n", status, \
__FILE__, __LINE__); \
exit(EXIT_FAILURE); \
} \
} while (0)
#define MUSACHECK(cmd) \
do { \
musaError_t err = cmd; \
if (err != musaSuccess) { \
fprintf(stderr, "MUSA error %d at %s:%d\n", err, \
__FILE__, __LINE__); \
exit(EXIT_FAILURE); \
} \
} while (0)
namespace {
std::vector<int> ContiguousStrides(const std::vector<int>& shape) {
std::vector<int> stride(shape.size(), 1);
for (int i = static_cast<int>(shape.size()) - 2; i >= 0; --i) {
stride[static_cast<size_t>(i)] =
stride[static_cast<size_t>(i + 1)] * shape[static_cast<size_t>(i + 1)];
}
return stride;
}
void SetTensorDesc3D(mudnnTensorDescriptor_t desc, mudnnDataType_t dtype,
const std::vector<int>& shape) {
const std::vector<int> stride = ContiguousStrides(shape);
MUDNN_CHECK(mudnnSetTensorNdDescriptor(desc, dtype, static_cast<int>(shape.size()),
shape.data(), stride.data()));
}
} // namespace
int main() {
std::cout << "muDNN version: " << mudnnGetVersion() << std::endl;
MUSACHECK(musaSetDevice(0));
constexpr int batch = 1;
constexpr int m = 2;
constexpr int k = 3;
constexpr int n = 4;
const std::vector<float> h_a_f32 = {
1.0f, 2.0f, 3.0f,
4.0f, 5.0f, 6.0f,
};
const std::vector<float> h_b_f32 = {
1.0f, 2.0f, 3.0f, 4.0f,
5.0f, 6.0f, 7.0f, 8.0f,
9.0f, 10.0f, 11.0f, 12.0f,
};
std::vector<float> h_d(static_cast<size_t>(batch * m * n), 0.0f);
void* d_a = nullptr;
void* d_b = nullptr;
void* d_d = nullptr;
MUSACHECK(musaMalloc(&d_a, h_a_f32.size() * sizeof(float)));
MUSACHECK(musaMalloc(&d_b, h_b_f32.size() * sizeof(float)));
MUSACHECK(musaMalloc(&d_d, h_d.size() * sizeof(float)));
MUSACHECK(musaMemcpy(d_a, h_a_f32.data(), h_a_f32.size() * sizeof(float),
musaMemcpyHostToDevice));
MUSACHECK(musaMemcpy(d_b, h_b_f32.data(), h_b_f32.size() * sizeof(float),
musaMemcpyHostToDevice));
MUSACHECK(musaMemset(d_d, 0, h_d.size() * sizeof(float)));
mudnnHandle_t handle = nullptr;
mudnnTensorDescriptor_t a_desc = nullptr;
mudnnTensorDescriptor_t b_desc = nullptr;
mudnnTensorDescriptor_t d_desc = nullptr;
mudnnBatchMatMulDescriptor_t bmm_desc = nullptr;
MUDNN_CHECK(mudnnCreate(&handle));
MUDNN_CHECK(mudnnCreateTensorDescriptor(&a_desc));
MUDNN_CHECK(mudnnCreateTensorDescriptor(&b_desc));
MUDNN_CHECK(mudnnCreateTensorDescriptor(&d_desc));
MUDNN_CHECK(mudnnCreateBatchMatMulDescriptor(&bmm_desc));
SetTensorDesc3D(a_desc, MUDNN_DATA_FLOAT, {batch, m, k});
SetTensorDesc3D(b_desc, MUDNN_DATA_FLOAT, {batch, k, n});
SetTensorDesc3D(d_desc, MUDNN_DATA_FLOAT, {batch, m, n});
MUDNN_CHECK(mudnnSetBatchMatMulDescriptor(
bmm_desc, MUDNN_TENSOR_OP_MATH, false, false));
size_t workspace_size = 0;
MUDNN_CHECK(mudnnBatchMatmulGetWorkspaceSize(handle, bmm_desc, a_desc, b_desc,
nullptr, d_desc,
&workspace_size));
void* workspace = nullptr;
if (workspace_size > 0) {
MUSACHECK(musaMalloc(&workspace, workspace_size));
}
MUDNN_CHECK(mudnnBatchMatMulBias(handle, bmm_desc, a_desc, d_a, b_desc, d_b,
nullptr, nullptr, d_desc, d_d, nullptr,
nullptr, workspace, workspace_size));
MUSACHECK(musaMemcpy(h_d.data(), d_d, h_d.size() * sizeof(float),
musaMemcpyDeviceToHost));
MUSACHECK(musaDeviceSynchronize());
if (workspace != nullptr) {
MUSACHECK(musaFree(workspace));
}
MUDNN_CHECK(mudnnDestroyBatchMatMulDescriptor(bmm_desc));
MUDNN_CHECK(mudnnDestroyTensorDescriptor(a_desc));
MUDNN_CHECK(mudnnDestroyTensorDescriptor(b_desc));
MUDNN_CHECK(mudnnDestroyTensorDescriptor(d_desc));
MUDNN_CHECK(mudnnDestroy(handle));
MUSACHECK(musaFree(d_a));
MUSACHECK(musaFree(d_b));
MUSACHECK(musaFree(d_d));
return 0;
}
示例 3:BatchNorm(C API)
#include <stdio.h>
#include <stdlib.h>
#include <musa_runtime_api.h>
#include <mudnn/mudnn.h>
#define MUDNN_CHECK(cmd) \
do { \
mudnnStatus_t status = cmd; \
if (status != MUDNN_STATUS_SUCCESS) { \
fprintf(stderr, "muDNN error %d at %s:%d\n", status, \
__FILE__, __LINE__); \
exit(EXIT_FAILURE); \
} \
} while (0)
#define MUSACHECK(cmd) \
do { \
musaError_t err = cmd; \
if (err != musaSuccess) { \
fprintf(stderr, "MUSA error %d at %s:%d\n", err, \
__FILE__, __LINE__); \
exit(EXIT_FAILURE); \
} \
} while (0)
int main() {
mudnnHandle_t handle;
mudnnTensorDescriptor_t input_desc, output_desc;
mudnnTensorDescriptor_t scale_desc, bias_desc;
mudnnTensorDescriptor_t mean_desc, variance_desc;
mudnnBatchNormDescriptor_t bn_desc;
// 1. 创建句柄和描述符
MUDNN_CHECK(mudnnCreate(&handle));
MUDNN_CHECK(mudnnCreateTensorDescriptor(&input_desc));
MUDNN_CHECK(mudnnCreateTensorDescriptor(&output_desc));
MUDNN_CHECK(mudnnCreateTensorDescriptor(&scale_desc));
MUDNN_CHECK(mudnnCreateTensorDescriptor(&bias_desc));
MUDNN_CHECK(mudnnCreateTensorDescriptor(&mean_desc));
MUDNN_CHECK(mudnnCreateTensorDescriptor(&variance_desc));
MUDNN_CHECK(mudnnCreateBatchNormDescriptor(&bn_desc));
// 2. 设置张量维度 (N, C, H, W) NCHW 格式
const int N = 4, C = 64, H = 32, W = 32;
// 输入/输出张量描述符
MUDNN_CHECK(mudnnSetTensor4dDescriptor(
input_desc, MUDNN_TENSOR_NCHW, MUDNN_DATA_FLOAT, N, C, H, W));
MUDNN_CHECK(mudnnSetTensor4dDescriptor(
output_desc, MUDNN_TENSOR_NCHW, MUDNN_DATA_FLOAT, N, C, H, W));
// scale/bias 和 mean/var 描述符(每通道)
MUDNN_CHECK(mudnnSetTensor4dDescriptor(
scale_desc, MUDNN_TENSOR_NCHW, MUDNN_DATA_FLOAT, 1, C, 1, 1));
MUDNN_CHECK(mudnnSetTensor4dDescriptor(
bias_desc, MUDNN_TENSOR_NCHW, MUDNN_DATA_FLOAT, 1, C, 1, 1));
MUDNN_CHECK(mudnnSetTensor4dDescriptor(
mean_desc, MUDNN_TENSOR_NCHW, MUDNN_DATA_FLOAT, 1, C, 1, 1));
MUDNN_CHECK(mudnnSetTensor4dDescriptor(
variance_desc, MUDNN_TENSOR_NCHW, MUDNN_DATA_FLOAT, 1, C, 1, 1));
// 3. 分配设备内存
float *input_data, *output_data, *scale_data, *bias_data, *mean_data, *variance_data;
size_t input_size = N * C * H * W * sizeof(float);
size_t channel_size = C * sizeof(float);
MUSACHECK(musaMalloc((void**)&input_data, input_size));
MUSACHECK(musaMalloc((void**)&output_data, input_size));
MUSACHECK(musaMalloc((void**)&scale_data, channel_size));
MUSACHECK(musaMalloc((void**)&bias_data, channel_size));
MUSACHECK(musaMalloc((void**)&mean_data, channel_size));
MUSACHECK(musaMalloc((void**)&variance_data, channel_size));
// 4. 设置 BatchNorm 参数(使用 v5.1.0 API)
MUDNN_CHECK(mudnnSetBatchNormDescriptor(
bn_desc,
MUDNN_BATCHNORM_PER_ACTIVATION,
1e-5, // epsilon
true // isTraining = true for training mode
));
// 5. 查询工作区大小
size_t workspace_size;
MUDNN_CHECK(mudnnGetBatchNormForwardTrainingWorkspaceSize(
handle, bn_desc, input_desc, &workspace_size));
void* workspace = NULL;
if (workspace_size > 0) {
MUSACHECK(musaMalloc(&workspace, workspace_size));
}
// 6. 执行 BatchNorm(训练模式)- 使用 v5.1.0 API
// 注意:参数顺序为 outputDesc, output, inputDesc, input, ...
MUDNN_CHECK(mudnnBatchNormForwardTraining(
handle, bn_desc,
output_desc, output_data,
input_desc, input_data,
mean_desc, mean_data, // accumulated mean
variance_desc, variance_data, // accumulated variance
mean_desc, mean_data, // fresh mean (same buffer for simplicity)
variance_desc, variance_data, // fresh variance (same buffer for simplicity)
scale_desc, scale_data,
bias_desc, bias_data,
0.9, // momentum
workspace, workspace_size
));
// 7. 同步流
MUSACHECK(musaStreamSynchronize(musaStreamPerThread));
printf("BatchNorm executed successfully!\n");
// 8. 清理资源
MUSACHECK(musaFree(input_data));
MUSACHECK(musaFree(output_data));
MUSACHECK(musaFree(scale_data));
MUSACHECK(musaFree(bias_data));
MUSACHECK(musaFree(mean_data));
MUSACHECK(musaFree(variance_data));
if (workspace) MUSACHECK(musaFree(workspace));
MUDNN_CHECK(mudnnDestroyTensorDescriptor(input_desc));
MUDNN_CHECK(mudnnDestroyTensorDescriptor(output_desc));
MUDNN_CHECK(mudnnDestroyTensorDescriptor(scale_desc));
MUDNN_CHECK(mudnnDestroyTensorDescriptor(bias_desc));
MUDNN_CHECK(mudnnDestroyTensorDescriptor(mean_desc));
MUDNN_CHECK(mudnnDestroyTensorDescriptor(variance_desc));
MUDNN_CHECK(mudnnDestroyBatchNormDescriptor(bn_desc));
MUDNN_CHECK(mudnnDestroy(handle));
return 0;
}
核心概念
Handle(设备句柄)
mudnnHandle_t 是 muDNN 与 MUSA 设备交互的核心接口:
mudnnHandle_t handle;
mudnnCreate(&handle);
// 设置流
musaStream_t stream;
musaStreamCreate(&stream);
mudnnSetStream(handle, stream);
// 清理
mudnnDestroy(handle);
musaStreamDestroy(stream);
Tensor Descriptor(张量描述符)
张量描述符描述多维数组的元数据:
mudnnTensorDescriptor_t tensor_desc;
mudnnCreateTensorDescriptor(&tensor_desc);
// 4D 张量 (NCHW)
mudnnSetTensor4dDescriptor(
tensor_desc,
MUDNN_TENSOR_NCHW, // 格式
MUDNN_DATA_FLOAT, // 数据类型
N, C, H, W // 维度
);
// 2D 张量
mudnnSetTensor2dDescriptor(
tensor_desc,
MUDNN_DATA_FLOAT, // 数据类型
rows, cols // 维度
);
// 清理
mudnnDestroyTensorDescriptor(tensor_desc);
Workspace(工作区)
muDNN 算子需要临时工作区内存:
// 以 LayerNorm 为例:查询工作区大小
size_t workspace_size;
mudnnGetLayerNormForwardWorkspaceSize(
handle, layernorm_desc,
output_desc, mean_desc, variance_desc,
input_desc, gamma_desc, beta_desc,
&workspace_size
);
// 2. 分配工作区
void* workspace;
if (workspace_size > 0) {
musaMalloc(&workspace, workspace_size);
}
// 3. 使用工作区执行算子
mudnnLayerNormForward(
handle, layernorm_desc,
output_desc, output_data,
mean_desc, mean_data,
variance_desc, variance_data,
input_desc, input_data,
gamma_desc, gamma_data,
beta_desc, beta_data,
workspace, workspace_size
);
最佳实践
错误处理
始终检查 muDNN 函数返回状态:
#define MUDNN_CHECK(cmd) \
do { \
mudnnStatus_t status = cmd; \
if (status != MUDNN_STATUS_SUCCESS) { \
fprintf(stderr, "muDNN error %d at %s:%d\n", status, \
__FILE__, __LINE__); \
exit(EXIT_FAILURE); \
} \
} while (0)
资源管理
使用宏简化错误检查和资源清理:
// 在函数开始处创建所有资源
mudnnHandle_t handle;
mudnnTensorDescriptor_t input_desc, output_desc;
void* workspace = NULL;
MUDNN_CHECK(mudnnCreate(&handle));
MUDNN_CHECK(mudnnCreateTensorDescriptor(&input_desc));
MUDNN_CHECK(mudnnCreateTensorDescriptor(&output_desc));
// 使用 goto 统一清理
cleanup:
if (input_desc) mudnnDestroyTensorDescriptor(input_desc);
if (output_desc) mudnnDestroyTensorDescriptor(output_desc);
if (workspace) musaFree(workspace);
if (handle) mudnnDestroy(handle);
性能优化
- 选择合适的卷积算法(使用
mudnnFindConvolutionForwardAlgorithm) - 重用工作区内存,避免重复分配
- 批处理操作以减少内核启动开销
- 使用
mudnnSetStream绑定 MUSA 流以实现并发执行
与 AI 框架对接扩展
muDNN 可以与主流 AI 框架(PyTorch、TensorFlow)对接,使框架能够使用 muDNN 算子库在 MTGPU 上运行 AI 模型。
torch_musa - muDNN 对接扩展
本节介绍如何将 MTGPU 设备通过 muDNN 库和 torch_musa 集成到 PyTorch 框架中。
算子派发机制(Dispatch)
PyTorch 中的 dispatcher 是一个分发器,当执行一个 operator 时,dispatcher 会根据 tensor 的输入信息和其他信息(参数个数、返回值类型等)计算得到一个 dispatch key,然后根据 dispatch key 从 dispatch table 中找到对应的 kernel 函数指针,最后回调执行。
dispatch key 不仅有硬件后端,还有一些更抽象的概念如 autograd、tracing 等。当同一个算子有不同 dispatch key 的实现时,PyTorch 会调用优先级最高的 dispatch key 对应的 kernel 实现。