SiP 信号处理加速库 Strmm 算子实战:三角矩阵乘法 C++ 调用、编译运行与源码解析
【免费下载链接】sip本项目是CANN提供的一款高效、可靠的高性能信号处理算子加速库,基于华为Ascend AI处理器,专门为信号处理领域而设计。项目地址: https://gitcode.com/cann/sip
本文基于 SiP(Ascend SiP 信号处理加速库)仓库中example/A2/BLAS/strmm目录的官方 Demo 文档展开,完整讲解单精度三角矩阵乘法算子asdBlasStrmm的功能定义、接口参数、可运行的 C++ 示例代码,以及环境配置、库编译与 Demo 构建的完整操作流程,并结合仓库源码剖析其底层实现、参数校验与调用链路,帮助读者在 Atlas A2/A3 等昇腾平台上快速上手 Strmm 算子并完成工程化集成。
1. Strmm 算子功能:三角矩阵与一般矩阵的乘法
Strmm是 SiP 提供的单精度 BLAS 算子,其功能是将一个三角矩阵 A与一个一般矩阵 B相乘,得到新矩阵 C,计算结果为C被覆盖写(即输出直接写回 C 对应的设备内存)。其计算公式为:
$$ C = \alpha \cdot op(A) \cdot B \quad \text{(side = ASDBLAS_SIDE_LEFT)} $$ $$ C = \alpha \cdot B \cdot op(A) \quad \text{(side = ASDBLAS_SIDE_RIGHT)} $$
其中各符号含义为:
A:三角矩阵,仅存储下三角或上三角部分(由uplo指定);B:一般矩阵,shape 为[m, n];alpha:实数标量,矩阵乘法系数;op(A):对 A 的可选操作,可以是A本身(trans = ASDBLAS_OP_N)或其转置A^T(trans = ASDBLAS_OP_T)。
官方 API 文档中给出了一个直观的数值示例(见 Strmm 接口文档):输入下三角矩阵
A = [ 1, 0 ] [ 3, 4 ]一般矩阵
B = [ 1, 2 ] [ 3, 4 ]在side = LEFT、uplo = LOWER、trans = N、diag = N、n = 2、lda = ldb = ldc = 2、alpha = 2.345的调用下,输出 C 为:
C = [ 2.3450, 4.6900 ] [ 35.1750, 51.5900 ]这正对应C = 2.345 × A × B(下三角存储,A 的第一行上三角元素视为 0 参与乘法),可直接用于验证自己的调用是否正确。
1.1 产品支持情况
根据 接口文档 与 Demo 文档:
- 支持:Atlas A2 训练系列产品、Atlas A3 训练系列产品、Atlas 800I A2 推理产品、Atlas A3 推理系列产品;
- 不支持:Ascend 950PR/Ascend 950DT、Atlas 200I/500 A2 推理产品、Atlas 推理系列产品、Atlas 训练系列产品。
2. 接口签名与参数说明
Strmm 算子由两个接口配合使用,二者均声明于 blas_api.h:
// 初始化句柄对应的 Strmm 算子配置(计划绑定) AspbStatus asdBlasMakeStrmmPlan(asdBlasHandle handle); // 执行单精度三角矩阵乘法 AspbStatus asdBlasStrmm( asdBlasHandle handle, asdBlasSideMode_t side, asdBlasFillMode_t uplo, asdBlasOperation_t trans, asdBlasDiagType_t diag, const int64_t m, const int64_t n, const float& alpha, aclTensor* A, const int64_t lda, aclTensor* B, const int64_t ldb, aclTensor* C, const int64_t ldc);2.1 asdBlasMakeStrmmPlan 参数
| 参数名 | 输入/输出 | 描述 |
|---|---|---|
| handle(asdBlasHandle) | 输入 | 算子的句柄 |
返回状态码,具体参见 SiP返回码。
2.2 asdBlasStrmm 参数
| 参数名 | 输入/输出 | 描述 |
|---|---|---|
| handle(asdBlasHandle) | 输入 | 算子的句柄 |
| side(asdBlasSideMode_t) | 输入 | 指定矩阵 A 是乘法左侧还是右侧:ASDBLAS_SIDE_LEFT左侧、ASDBLAS_SIDE_RIGHT右侧 |
| uplo(asdBlasFillMode_t) | 输入 | 指定矩阵 A 的存储格式:ASDBLAS_FILL_MODE_LOWER下三角、ASDBLAS_FILL_MODE_UPPER上三角 |
| trans(asdBlasOperation_t) | 输入 | 指定是否对 A 转置:ASDBLAS_OP_N不转置、ASDBLAS_OP_T转置 |
| diag(asdBlasDiagType_t) | 输入 | 指定是否假定 A 对角线元素为 1:ASDBLAS_DIAG_NON_UNIT不假定、ASDBLAS_DIAG_UNIT假定 |
| m(int64_t) | 输入 | 矩阵 B 和 C 的行数 |
| n(int64_t) | 输入 | 矩阵 B 和 C 的列数 |
| alpha(float&) | 输入 | 公式中的乘法系数 |
| A(aclTensor*) | 输入 | 对应公式中的 A。数据类型支持 FLOAT32,数据格式支持 ND;当 A 为乘法左矩阵时 shape 为[m, m],为右矩阵时为[n, n] |
| lda(int64_t) | 输入 | 张量 A 中元素的间隔,当前约束:side 为 LEFT 时为 m,side 为 RIGHT 时为 n |
| B(aclTensor*) | 输入 | 对应公式中的 B。FLOAT32、ND,shape 为[m, n] |
| ldb(int64_t) | 输入 | 张量 B 中元素的间隔,当前约束为 m |
| C(aclTensor*) | 输出 | 对应公式中的 C。FLOAT32、ND,shape 为[m, n] |
| ldc(int64_t) | 输入 | 张量 C 中元素的间隔,当前约束为 m |
返回状态码,具体参见 SiP返回码。
2.3 约束说明
- 输入的元素个数
m、n当前覆盖支持[1, 8193]; - 当
side = ASDBLAS_SIDE_LEFT时,算子输入 shape 为[m, m]、[m, n],输出 shape 为[m, n]; - 当
side = ASDBLAS_SIDE_RIGHT时,算子输入 shape 为[n, n]、[m, n],输出 shape 为[m, n]; - 算子实际计算时,不支持 ND 高维度运算(不支持维度 ≥ 3 的运算);
- 从源码实现看(见第 5 节),当前版本对
diag仅接受ASDBLAS_DIAG_NON_UNIT,传入ASDBLAS_DIAG_UNIT会返回参数错误。
3. C++ Demo 完整实现:从数据准备到结果回读
example/A2/BLAS/strmm目录下的 example_strmm.cpp 提供了最小化的可运行示例,其参数配置为:side = ASDBLAS_SIDE_LEFT、uplo = ASDBLAS_FILL_MODE_LOWER、trans = ASDBLAS_OP_N、diag = ASDBLAS_DIAG_NON_UNIT、m = n = 5、alpha = 1.0、lda = ldb = ldc = m。示例输入矩阵 A 每行为行下标值(下三角存储形式)、B 每行为行下标值,用于直观观察乘法结果。示例中生成的数据不代表实际场景,可根据具体业务修改。
完整示例代码如下(与仓库文件一致,注释已合并):
#include <iostream> #include <vector> #include "asdsip.h" #include "acl/acl.h" #include "acl_meta.h" using namespace AsdSip; #define ASD_STATUS_CHECK(err) \ do { \ AsdSip::AspbStatus err_ = (err); \ if (err_ != AsdSip::ErrorType::ACL_SUCCESS) { \ std::cout << "Execute failed." << std::endl; \ return -1; \ } \ } while (0) #define CHECK_RET(cond, return_expr) \ do { \ if (!(cond)) { \ return_expr; \ } \ } while (0) #define LOG_PRINT(message, ...) \ do { \ printf(message, ##__VA_ARGS__); \ } while (0) int64_t GetShapeSize(const std::vector<int64_t>& shape) { int64_t shapeSize = 1; for (auto i : shape) { shapeSize *= i; } return shapeSize; } int Init(int32_t deviceId, aclrtStream* stream) { // 固定写法,acl初始化 auto ret = aclInit(nullptr); CHECK_RET(ret == ::ACL_SUCCESS, LOG_PRINT("aclInit failed. ERROR: %d\n", ret); return ret); ret = aclrtSetDevice(deviceId); CHECK_RET(ret == ::ACL_SUCCESS, LOG_PRINT("aclrtSetDevice failed. ERROR: %d\n", ret); return ret); ret = aclrtCreateStream(stream); CHECK_RET(ret == ::ACL_SUCCESS, LOG_PRINT("aclrtCreateStream failed. ERROR: %d\n", ret); return ret); return 0; } template <typename T> int CreateAclTensor(const std::vector<T>& hostData, const std::vector<int64_t>& shape, void** deviceAddr, aclDataType dataType, aclTensor** tensor) { auto size = GetShapeSize(shape) * sizeof(T); // 调用aclrtMalloc申请device侧内存 auto ret = aclrtMalloc(deviceAddr, size, ACL_MEM_MALLOC_HUGE_FIRST); CHECK_RET(ret == ::ACL_SUCCESS, LOG_PRINT("aclrtMalloc failed. ERROR: %d\n", ret); return ret); // 调用aclrtMemcpy将host侧数据复制到device侧内存上 ret = aclrtMemcpy(*deviceAddr, size, hostData.data(), size, ACL_MEMCPY_HOST_TO_DEVICE); CHECK_RET(ret == ::ACL_SUCCESS, LOG_PRINT("aclrtMemcpy failed. ERROR: %d\n", ret); return ret); // 计算连续tensor的strides std::vector<int64_t> strides(shape.size(), 1); for (int64_t i = shape.size() - 2; i >= 0; i--) { strides[i] = shape[i + 1] * strides[i + 1]; } // 调用aclCreateTensor接口创建aclTensor *tensor = aclCreateTensor(shape.data(), shape.size(), dataType, strides.data(), 0, aclFormat::ACL_FORMAT_ND, shape.data(), shape.size(), *deviceAddr); return 0; } int main(int argc, char** argv) { int deviceId = 0; aclrtStream stream; auto ret = Init(deviceId, &stream); CHECK_RET(ret == ::ACL_SUCCESS, LOG_PRINT("Init acl failed. ERROR: %d\n", ret); return ret); asdBlasSideMode_t side = asdBlasSideMode_t::ASDBLAS_SIDE_LEFT; asdBlasFillMode_t uplo = asdBlasFillMode_t::ASDBLAS_FILL_MODE_LOWER; asdBlasOperation_t trans = asdBlasOperation_t::ASDBLAS_OP_N; asdBlasDiagType_t diag = asdBlasDiagType_t::ASDBLAS_DIAG_NON_UNIT; const int64_t m = 5; const int64_t n = 5; float alpha = 1.0; int64_t lda = m; int64_t ldb = m; int64_t ldc = m; // 构造输入 A:每行填充为行下标 i const int64_t tensorASize = m * m; std::vector<float> tensorInAData(tensorASize, 0.0); for (int64_t i = 0; i < m; i++) { for (int64_t j = 0; j < m; j++) { tensorInAData[m * i + j] = i; } } // 构造输入 B:每行填充为行下标 i const int64_t tensorBSize = m * n; std::vector<float> tensorInBData(tensorBSize, 0.0); for (int64_t i = 0; i < m; i++) { for (int64_t j = 0; j < n; j++) { tensorInBData[n * i + j] = i; } } const int64_t tensorCSize = m * n; std::vector<float> tensorCData(tensorCSize, 0.0); // 打印侧信息(side/uplo/trans/diag 的枚举值)与输入矩阵,便于结果比对 std::cout << "side = " << static_cast<int32_t>(side) << std::endl; std::cout << "uplo = " << static_cast<int32_t>(uplo) << std::endl; std::cout << "trans = " << static_cast<int32_t>(trans) << std::endl; std::cout << "diag = " << static_cast<int32_t>(diag) << std::endl; std::cout << "------- input A -------" << std::endl; for (int64_t i = 0; i < m; i++) { for (int64_t j = 0; j < m; j++) std::cout << tensorInAData[i * m + j] << " "; std::cout << std::endl; } std::cout << "------- input B -------" << std::endl; for (int64_t i = 0; i < m; i++) { for (int64_t j = 0; j < n; j++) std::cout << tensorInBData[i * n + j] << " "; std::cout << std::endl; } std::vector<int64_t> aShape = {tensorASize}; std::vector<int64_t> bShape = {tensorBSize}; std::vector<int64_t> cShape = {tensorCSize}; aclTensor* inputA = nullptr; aclTensor* inputB = nullptr; aclTensor* outputC = nullptr; void* inputADeviceAddr = nullptr; void* inputBDeviceAddr = nullptr; void* outputCDeviceAddr = nullptr; // 三个张量均为 FLOAT32 类型 ret = CreateAclTensor(tensorInAData, aShape, &inputADeviceAddr, aclDataType::ACL_FLOAT, &inputA); CHECK_RET(ret == ::ACL_SUCCESS, return ret); ret = CreateAclTensor(tensorInBData, bShape, &inputBDeviceAddr, aclDataType::ACL_FLOAT, &inputB); CHECK_RET(ret == ::ACL_SUCCESS, return ret); ret = CreateAclTensor(tensorCData, cShape, &outputCDeviceAddr, aclDataType::ACL_FLOAT, &outputC); CHECK_RET(ret == ::ACL_SUCCESS, return ret); // ---- SiP 算子调用标准流程:Create → MakePlan → Workspace → SetStream → 计算 → Sync → Destroy ---- asdBlasHandle handle; asdBlasCreate(handle); size_t lwork = 0; void* buffer = nullptr; asdBlasMakeStrmmPlan(handle); asdBlasGetWorkspaceSize(handle, lwork); std::cout << "lwork = " << lwork << std::endl; if (lwork > 0) { ret = aclrtMalloc(&buffer, static_cast<int64_t>(lwork), ACL_MEM_MALLOC_HUGE_FIRST); CHECK_RET(ret == ::ACL_SUCCESS, LOG_PRINT("allocate workspace failed. ERROR: %d\n", ret); return ret); } asdBlasSetWorkspace(handle, buffer); asdBlasSetStream(handle, stream); ASD_STATUS_CHECK( asdBlasStrmm(handle, side, uplo, trans, diag, m, n, alpha, inputA, lda, inputB, ldb, outputC, ldc)); asdBlasSynchronize(handle); asdBlasDestroy(handle); // 结果回读到 host 并打印 ret = aclrtMemcpy(tensorCData.data(), tensorCSize * sizeof(float), outputCDeviceAddr, tensorCSize * sizeof(float), ACL_MEMCPY_DEVICE_TO_HOST); CHECK_RET(ret == ::ACL_SUCCESS, LOG_PRINT("copy result from device to host failed. ERROR: %d\n", ret); return ret); std::cout << "------- output C -------" << std::endl; for (int64_t i = 0; i < m; i++) { for (int64_t j = 0; j < n; j++) { std::cout << tensorCData[i * n + j] << " "; } std::cout << std::endl; } std::cout << "Execute successfully." << std::endl; // 资源释放 aclDestroyTensor(inputA); aclDestroyTensor(inputB); aclDestroyTensor(outputC); aclrtFree(inputADeviceAddr); aclrtFree(inputBDeviceAddr); aclrtFree(outputCDeviceAddr); aclrtDestroyStream(stream); aclrtResetDevice(deviceId); aclFinalize(); return 0; }示例代码的执行脉络可归纳为四个阶段:
- ACL 初始化:
aclInit→aclrtSetDevice→aclrtCreateStream,创建流用于后续算子执行; - 数据上卡:
CreateAclTensor封装了aclrtMalloc(设备内存申请)+aclrtMemcpy(Host→Device 拷贝)+aclCreateTensor(ND 格式、ACL_FLOAT类型张量构造),注意示例按连续布局自行计算 strides; - 算子调用:遵循 SiP BLAS 的标准调用序列
asdBlasCreate→asdBlasMakeStrmmPlan→asdBlasGetWorkspaceSize(如需则分配工作区)→asdBlasSetWorkspace→asdBlasSetStream→asdBlasStrmm→asdBlasSynchronize→asdBlasDestroy; - 结果回读与清理:
aclrtMemcpy将 C 从设备端拷回主机打印,随后销毁张量、释放显存、结束设备与 ACL 会话。
需要强调:该样例旨在提供快速上手、开发和调试算子的最小化实现,不推荐直接将示例代码作为业务代码用于生产环境。
4. 环境配置、编译与运行
4.1 配置 CANN 环境变量
source [CANN安装路径]/set_env.sh # 默认路径: source /usr/local/Ascend/ascend-toolkit/set_env.sh4.2 编译 SiP 加速库
进入 SiP 仓库根目录,执行编译并设置加速库环境变量:
cd ${SiP_root_path} bash build.sh source output/set_env.sh特别说明:
- 上述编译方式仅支持以 git 方式下载的加速库,zip 压缩包下载的加速库不支持该编译方式;
- 编译过程需要联网下载依赖库,编译环境必须联网;
- 该过程包含两个步骤:获取并编译
ascend-boost-comm(昇腾分布式通信加速库)组件,以及编译信号处理加速库本身。更多命令说明可参考 build.sh 与 编译与构建文档。
编译成功后,source output/set_env.sh会设置ASDSIP_HOME_PATH(以及其他相关环境变量),指向 SiP 输出目录,后续示例构建脚本依赖该变量。
4.3 构建并运行 Demo
进入示例所在目录,执行其构建脚本:
cd ${示例所在目录} # 即 example/A2/BLAS/strmm bash build.shbuild.sh 的实际行为是:先校验ASDSIP_HOME_PATH环境变量已设置且目录存在(兼容以latest/latest/结尾的路径),将其加入LD_LIBRARY_PATH,然后使用g++将example_strmm.cpp编译为可执行文件example并立即运行,运行完成后删除该可执行文件。其核心编译命令为:
g++ example_strmm.cpp \ -I${ASCEND_HOME_PATH}/include/aclnn \ -I${ASCEND_HOME_PATH}/include \ -L${ASCEND_HOME_PATH}/lib64/ -lascendcl -lopapi -lnnopbase \ -I${ASDSIP_HOME_PATH}/include \ -L${ASDSIP_HOME_PATH}/lib -lmki \ -L${ASDSIP_HOME_PATH}/lib -lasdsip \ -L${ASDSIP_HOME_PATH}/lib -lasdsip_core \ -L${ASDSIP_HOME_PATH}/lib -lasdsip_host \ -o example从链接项可以看出:示例依赖 CANN 侧的ascendcl/opapi/nnopbase,以及 SiP 侧的mki(计算内核接口)、asdsip(对外 API 层)、asdsip_core(算子核心实现)与asdsip_host(Host 端支持)等库,这与仓库的分层结构相吻合。
5. 源码级实现剖析:asdBlasStrmm 的校验、参数映射与内核调度
对外 API 的实现位于 core/blas/strmm.cpp,阅读该文件可以确认示例中每个参数在库内部是如何被消费与校验的。
5.1 计划绑定:asdBlasMakeStrmmPlan
asdBlasMakeStrmmPlan负责把BlasStrmmPlan实例绑定到 handle 上(计划对象定义见 BlasStrmmPlan.cpp):
- 校验 handle 非空,并防止重复绑定:若该 handle 已绑定过计划,将返回
ACL_ERROR_INVALID_PARAM(源码注释指明这是为了避免重复初始化导致的静默失败); - 通过
BlasPlanCache::MakePlan将BlasStrmmPlan存入计划缓存,随后MarkInitialized标记计划就绪。
因此示例中asdBlasMakeStrmmPlan(handle)必须在asdBlasStrmm之前调用一次,且不能对同一 handle 调用两次。
5.2 asdBlasStrmm 的执行流程
asdBlasStrmm的完整处理顺序为:
- 加锁:持有
blas_mtx互斥锁,保证多算子共用 handle 场景下的线程安全; - 计划存在性检查:通过
BlasPlanCache::doesPlanExist(handle)确认已执行过asdBlasMakeStrmmPlan,否则返回内部错误; - dtype 校验:
StrmmDtypeCheck强制 A、B、C 均为ACL_FLOAT(float32),否则返回ACL_ERROR_UNSUPPORTED_DATA_TYPE——这与接口文档“数据类型支持 FLOAT32”的约束一致; - shape 校验:
StrmmShapeCheck检查m、n在(0, UINT32_MAX]区间内,并按 side 计算 A 的元素数(LEFT 为m*m,RIGHT 为n*n),B、C 元素数必须为m*n,不匹配则返回ACL_ERROR_OP_INPUT_NOT_MATCH; - diag 校验:当前仅支持
ASDBLAS_DIAG_NON_UNIT,传入ASDBLAS_DIAG_UNIT直接返回ACL_ERROR_INVALID_PARAM; - ld 检查:
lda/ldb/ldc <= 0时记录 INFO 日志(当前约束下 ld 取 m 或 n); - 参数映射:将枚举参数编码为内核可识别的整型参数,并按 side/uplo/trans 的组合计算真实矩阵乘尺寸:
sideInt:LEFT → 0,RIGHT → 1;uploInt:UPPER → 1,LOWER → 0;- LEFT 情形:
realM = m,realK = m,transLeft = (trans == OP_N) ? 0 : 1; - RIGHT 情形:
realK = n,transRight = (trans == OP_N) ? 0 : 1; - 上三角(uploInt = 1)时额外置
lessFlag标记(case 2/3),下三角不置位; - 另外,
ASDBLAS_OP_C(共轭转置)在单精度场景下会被归一化为ASDBLAS_OP_T处理。
- 内核调度:组装
OpDesc(opName 为"StrmmOperation")与OpParam::Strmm参数结构,按 side 决定输入张量顺序(LEFT 为 [A, B],RIGHT 为 [B, A],输出为 [C]),最终通过RunAsdOpsV2在计划绑定的 stream 上执行 AscendC 内核,工作区使用plan.GetWorkspace()(即第 4 节中通过asdBlasSetWorkspace设置的缓冲区); - 异常处理:
dynamic_cast失败(计划类型不匹配)时捕获bad_cast并返回内部错误。
这一结构说明:asdBlasStrmm本质上是一层带完整参数校验的 Host API,把 BLAS 语义转换为StrmmOperation内核的执行描述,实际的三角矩阵乘法计算由编译进asdsip_core的 AscendC 算子完成。
5.3 单元测试佐证
test_strmm.cpp 中包含一个针对 shape 校验的负向用例(issue #133):构造元素数为m*n - 1的输出张量 C,调用asdBlasStrmm时应返回ACL_ERROR_OP_INPUT_NOT_MATCH而不是让内核越界写。该测试同时验证了asdBlasCreate→asdBlasMakeStrmmPlan→asdBlasStrmm的标准调用序列与aclTensor的[m, m]、[m, n]二维 shape 构造方式,可作为集成时排查参数错误的参考。
6. 使用注意事项与常见问题
- shape 以元素总数传入设备端,但接口语义为二维:示例中
aShape = {tensorASize}等将张量按一维总长度创建(ND 格式下元素数满足即可通过校验),但m、n语义仍是二维矩阵的行数列数,lda/ldb/ldc需按文档约束分别取 m(A 为左侧时)/ n(A 为右侧时)/ m / m; - 仅支持单精度:A、B、C 必须全部为
float32,混用 double 或半精度会在 Host 校验阶段被拒绝; - diag 当前仅支持 NON_UNIT:若算法上需要单位对角(unit diagonal)场景,需自行在对角线上填充 1 并以 NON_UNIT 调用;
- 不支持三维及以上 ND 张量:实际计算要求张量为二维(或元素总数合法的展平一维)布局;
- 计划一次性绑定:同一 handle 重复调用
asdBlasMakeStrmmPlan会报错;多算子复用同一 handle 时需注意 Plan 的互斥关系(源码中dynamic_cast到BlasStrmmPlan失败即返回内部错误); - 工作区:
asdBlasGetWorkspaceSize返回 0 时无需分配,但asdBlasSetWorkspace仍需按示例流程调用以保证计划状态完整; - 运行环境:示例与算子均要求已配置 CANN 环境变量、已完成 SiP 库编译(
ASDSIP_HOME_PATH可用),并在支持的产品(Atlas A2/A3 系列训练/推理产品、Atlas 800I A2 等)上运行; - 结果验证:调试阶段可参考第 1 节中官方文档给出的数值小例(alpha = 2.345 的 2×2 案例),将本地输出与其比对,快速判断 side/uplo/trans 组合是否符合预期。
7. 小结
SiP 的 Strmm 算子以“计划 + 句柄”的调用模型暴露了单精度三角矩阵乘法能力:asdBlasMakeStrmmPlan完成一次性的计划绑定,asdBlasStrmm在 Host 侧完成 dtype/shape/diag 等严格校验后,将 side、uplo、trans、diag 语义映射为StrmmOperation内核参数并调度执行。仓库中 Demo 文档、可运行示例、构建脚本、Host 实现 与单元测试 共同构成了一条从编译、调用到结果验证的完整路径,读者可按第 4 节的流程在本机完成一次最小化验证,再依据第 5 节的参数映射规则将算子嵌入实际信号处理流水线。
【免费下载链接】sip本项目是CANN提供的一款高效、可靠的高性能信号处理算子加速库,基于华为Ascend AI处理器,专门为信号处理领域而设计。项目地址: https://gitcode.com/cann/sip
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考