FEATURED · 精选文章

CANN 信号处理加速库 swapLast2Axes 算子实战:C++ 示例运行与源码级解析

发布时间 / 2026/9/18 8:22:50
来源 / 创域科博编辑部
栏目 / 资讯中心
CANN 信号处理加速库 swapLast2Axes 算子实战:C++ 示例运行与源码级解析 CANN 信号处理加速库 swapLast2Axes 算子实战C 示例运行与源码级解析【免费下载链接】sip本项目是CANN提供的一款高效、可靠的高性能信号处理算子加速库基于华为Ascend AI处理器专门为信号处理领域而设计。项目地址: https://gitcode.com/cann/sip本指南以信号处理加速库SiP中 BASE 域的swapLast2Axes算子为主题完整讲解其数学原理、接口用法、编译环境配置、C Demo 构建与运行步骤并结合仓库源码深入剖析算子从 Host 侧接口到 Tiling、Kernel 的完整调用链。读者学完后将能够独立搭建 CANN 开发环境、编译 SiP 加速库、运行 example/A2/BASE/swap_last2_axes/example_swap_last2_axes.cpp 示例并理解该算子的维度约束与数据格式要求。一、算子概述与功能说明swapLast2Axes是信号处理加速库 BASE 域提供的基础算子核心功能是交换 Tensor 的最后两维本质上是围绕最后两个轴执行一次转置transpose操作与 PyTorch 中的x.transpose(-1, -2)语义等价可参见 sip_pta/test/base/swap_last2_axes.py 中的对照实现。其计算公式为$$ \text{outTensor}{bij} \text{inTensor}{bji} $$其中b为数据的批次号i为输入数据的行号j为输入数据的列号。也就是说输出张量中第(b, i, j)个元素取自输入张量中第(b, j, i)个元素。典型示例示例一输入为三维张量inTensor [[[1.0.j, 2.0.j, 3.0.j]]]shape 为[1, 1, 3]调用swapLast2Axes后输出为[[[1.0.j], [2.0.j], [3.0.j]]]示例二输入为inTensorshape 为[2, 2, 3][[[ 0.0.j, 1.0.j, 2.0.j], [ 3.0.j, 4.0.j, 5.0.j]], [[ 6.0.j, 7.0.j, 8.0.j], [ 9.0.j, 10.0.j, 11.0.j]]]调用swapLast2Axes后输出shape 变为[2, 3, 2][[[ 0.0.j, 3.0.j], [ 1.0.j, 4.0.j], [ 2.0.j, 5.0.j]], [[ 6.0.j, 9.0.j], [ 7.0.j, 10.0.j], [ 8.0.j, 11.0.j]]]从两个示例可以看出3 维张量的第 0 维批次维度保持不变最后两维被交换。2 维张量则等效于标准矩阵转置。二、函数原型与参数说明该算子在 include/base_api.h 中对外声明了两个接口。根据 docs/zh/API_Reference/base/swapLast2Axes.md 的说明使用swapLast2Axes算子前必须先调用swapLast2AxesGetWorkspaceSize获取 workspace 大小再调用swapLast2Axes执行计算。swapLast2AxesGetWorkspaceSizeAsdSip::AspbStatus swapLast2AxesGetWorkspaceSize( size_t *size)参数名输入/输出描述sizesize_t *输入/输出swapLast2Axes算子执行所需的 workspace 大小swapLast2AxesAsdSip::AspbStatus swapLast2Axes( const aclTensor * inTensor, aclTensor * outTensor, void * stream, void * workspace nullptr)参数名输入/输出描述inTensoraclTensor *输入输入张量对应公式中的inTensor。输入的最大元素数为 3600000000shape 在 [60000, 60000] 以内数据类型仅支持 COMPLEX64数据格式支持 ND输入维度限制为 2 或 3outTensoraclTensor *输出输出张量对应公式中的outTensor数据类型仅支持 COMPLEX64且需与inTensor一致若inTensor的 shape 为[k, x, y]则outTensor的 shape 为[k, y, x]数据格式支持 NDworkspacevoid *输入swapLast2Axes算子执行所需的 workspace 内存streamvoid *输入NPU 执行流aclrtStream两个接口的返回值均为AsdSip::AspbStatus状态码具体含义可参见 SiP返回码。约束说明算子实际计算时不支持维度大于 3 的 ND 高维度运算输入数据类型仅支持 COMPLEX64复数双精度数据格式仅支持 ND。三、环境配置CANN 与 SiP 编译1. 配置 CANN 环境变量示例运行依赖 CANNAscend CANN Toolkit环境需要先加载其环境变量source [CANN安装路径]/set_env.sh默认安装路径下命令为source /usr/local/Ascend/ascend-toolkit/set_env.sh2. 编译信号处理加速库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这是后续编译示例所必需的。3. 产品支持情况该示例适用于以下产品型号与 docs/zh/API_Reference/base/swapLast2Axes.md 中的产品支持矩阵一致支持Atlas A2 训练系列产品、Atlas A2 推理系列产品、Atlas A3 训练系列产品、Atlas A3 推理系列产品README 中描述的 Atlas A2/A3 训练系列、Atlas 800I A2 推理产品、Atlas A3 推理系列不支持Ascend 950PR/Ascend 950DT、Atlas 200I/500 A2 推理产品、Atlas 推理系列产品、Atlas 训练系列产品A910 等。四、示例代码逐段解读示例主程序位于 example/A2/BASE/swap_last2_axes/example_swap_last2_axes.cpp核心流程为ACL 初始化 → 创建 Host/Device 张量 → 查询 workspace → 调用算子 → 回拷结果 → 释放资源。下面分段解读关键逻辑。1. 头文件与状态检查宏#include iostream #include vector #include asdsip.h #include acl/acl.h #include acl_meta.h using namespace AsdSip;示例使用ASD_STATUS_CHECK宏统一检查算子返回状态使用CHECK_RET宏检查 ACL 接口返回码使用LOG_PRINT宏打印日志避免大量重复的错误处理代码。2. ACL 环境初始化Init 函数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; }Init是 CANN 开发的固定模板aclInit初始化 ACL 运行环境aclrtSetDevice指定使用的 NPU 设备示例中使用deviceId 0aclrtCreateStream创建执行流后续算子调用与数据搬运均在该流上排队执行。3. 创建 aclTensorCreateAclTensor 模板函数template typename T int CreateAclTensor(const std::vectorT hostData, const std::vectorint64_t shape, void** deviceAddr, aclDataType dataType, aclTensor** tensor) { auto size GetShapeSize(shape) * sizeof(T) * 2; // 调用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::vectorint64_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; }关键点说明GetShapeSize计算 shape 各维度乘积得到元素总数由于示例使用 COMPLEX64 复数类型每个复数由 2 个 float 组成内存大小计算为元素数 * sizeof(float) * 2aclrtMalloc在 Device 侧申请内存aclrtMemcpy将 Host 侧数据拷贝到 Device代码中手工计算连续张量的 strides最后一维 stride 为 1向前逐维累乘并以ACL_FORMAT_ND格式调用aclCreateTensor创建aclTensor描述对象。4. 主流程数据准备与算子调用int64_t row 3; int64_t col 2; const int64_t tensorSize row * col * 2; std::vectorfloat tensorInData; tensorInData.resize(tensorSize); for (int64_t i 0; i tensorSize; i) { tensorInData[i] 0.0 i; } std::vectorfloat tensorOutData; tensorOutData.resize(tensorSize); std::vectorint64_t inShape {row, col}; std::vectorint64_t outShape {col, row};示例使用 2 维张量输入 shape 为{3, 2}输出 shape 为{2, 3}即对 3×2 的复数矩阵做转置。数据按 0 到 11 线性填充0.0 i便于肉眼核对转置结果。README 中也特别说明示例中生成的数据不代表实际场景可根据具体使用场景进行数据修改。算子调用部分void* workspace nullptr; size_t lwork 0; swapLast2AxesGetWorkspaceSize(lwork); std::cout lwork lwork std::endl; if (lwork 0) { ret aclrtMalloc(workspace, static_castint64_t(lwork), ACL_MEM_MALLOC_HUGE_FIRST); CHECK_RET(ret ::ACL_SUCCESS, LOG_PRINT(allocate workspace failed. ERROR: %d\n, ret); return ret); } ASD_STATUS_CHECK(swapLast2Axes(input, output, stream, workspace));该段遵循先查 workspace、再执行算子的规范流程调用swapLast2AxesGetWorkspaceSize获得所需 workspace 大小若大于 0 则用aclrtMalloc申请随后将输入、输出、执行流与 workspace 一并传入swapLast2Axes完成计算。5. 结果回拷与打印ret aclrtSynchronizeStream(stream); CHECK_RET(ret ::ACL_SUCCESS, LOG_PRINT(aclrtSynchronizeStream failed. ERROR: %d\n, ret); return ret); ret aclrtMemcpy(tensorOutData.data(), tensorSize * sizeof(float), outputDeviceAddr, tensorSize * sizeof(float), ACL_MEMCPY_DEVICE_TO_HOST); CHECK_RET(ret ::ACL_SUCCESS, LOG_PRINT(copy output tensor from device to host failed. ERROR: %d\n, ret); return ret); std::cout row row , col col std::endl; std::cout ------- Input ------- std::endl; printTensor(tensorInData.data(), row, col); std::cout ------- Output ------- std::endl; printTensor(tensorOutData.data(), col, row);先通过aclrtSynchronizeStream同步等待流上任务完成再用aclrtMemcpy将 Device 侧结果拷回 Host 侧tensorOutData。printTensor函数按行 × 列遍历并以(real, imag)形式打印每个复数元素。6. 资源释放aclrtFree(inputDeviceAddr); aclrtFree(outputDeviceAddr); aclDestroyTensor(input); aclDestroyTensor(output); if (lwork 0) { aclrtFree(workspace); } aclrtDestroyStream(stream); aclrtResetDevice(deviceId); aclFinalize();按照与申请相反的顺序依次释放 Device 内存、销毁aclTensor描述、销毁流、复位设备并调用aclFinalize完成 ACL 环境收尾避免资源泄漏。五、运行 Demo编译与执行进入示例所在目录并执行构建脚本cd ${示例所在目录} bash build.sh示例目录下的 build.sh 会依次完成以下工作校验环境变量检查ASDSIP_HOME_PATH是否已设置且目录存在兼容以latest/或latest结尾的路径自动归一化配置动态库路径将$ASDSIP_HOME_PATH/lib追加到LD_LIBRARY_PATH编译示例使用g编译example_swap_last2_axes.cpp链接参数包括CANN 侧-I${ASCEND_HOME_PATH}/include/aclnn、-I${ASCEND_HOME_PATH}/include链接-lascendcl -lopapi -lnnopbase来自${ASCEND_HOME_PATH}/lib64/SiP 侧-I${ASDSIP_HOME_PATH}/include链接-lmki -lasdsip -lasdsip_core -lasdsip_host来自${ASDSIP_HOME_PATH}/lib运行并清理输出提示语Execute the example of SwapLast2AxesOperation, swap the last two dimensions of tensors:后执行./example运行结束后删除临时可执行文件。成功运行后程序会依次打印 workspace 大小lwork、输入矩阵、输出矩阵并输出Execute successfully.表示算子调用成功。六、源码级原理解析从 Host 接口到 Kernel1. Host 侧接口实现参数校验与算子下发swapLast2Axes的 Host 侧实现位于 core/base/swaplast2axes.cpp主要完成三步工作数据类型校验SwapLast2AxesDtypeCheck通过SIP_OP_CHECK_DTYPE_NOT_MATCH检查输入输出张量数据类型均为ACL_COMPLEX64否则返回ACL_ERROR_UNSUPPORTED_DATA_TYPE。形状校验SwapLast2AxesShapeCheck调用aclGetStorageShape获取输入输出张量的存储形状校验以下条件输出元素数不少于输入元素数outNum inNum否则返回ACL_ERROR_INVALID_PARAM输入输出维度数相等且满足1 dim 4即仅支持 2 维或 3 维否则返回ACL_ERROR_OP_INPUT_NOT_MATCH。这与 API 文档中输入 dim 限制为 2 或 3的约束一一对应。参数组装与算子下发将维度信息换算为算子参数int64_t dim static_castint64_t(inStorageDimsNum); param.batch 1; int64_t endDimIdx 2; int64_t frontDimIdx 1; for (int64_t i 0; i dim - endDimIdx; i) { param.batch * static_castuint32_t(storageDims[i]); } param.row static_castuint32_t(storageDims[dim - endDimIdx]); param.col static_castuint32_t(storageDims[dim - frontDimIdx]);即 3 维输入时batch取第 0 维row、col分别取倒数第二维和最后一维2 维输入时batch为 1。随后通过RunAsdOpsV2将算子下发到 NPU 执行。而swapLast2AxesGetWorkspaceSize的实现非常轻量AsdSip::AspbStatus swapLast2AxesGetWorkspaceSize(size_t size) { size ASYNC_WORKSPACE_SIZE; return ErrorType::ACL_SUCCESS; }该接口将 workspace 大小固定为异步执行所需的ASYNC_WORKSPACE_SIZE常量。2. 算子注册与 Kernel 选择算子类SwapLast2AxesOperation定义在 ops/base/swaplast2axes/swap_last_2axes_operation.cpp通过REG_OPERATION(SwapLast2AxesOperation)完成注册。其中两个关键方法GetBestKernel根据输入张量的数据类型选择核函数——FLOAT 类型选择SwapLast2AxesF32KernelCOMPLEX64 类型选择SwapLast2AxesC64KernelInferShapeImpl完成输出 shape 推导即把dims[dim-2]与dims[dim-1]互换同时保持 dtype 与 format 与输入一致。Kernel 侧定义在 ops/base/swaplast2axes/swap_last_2axes_kernel.cppSwapLast2AxesKernel继承KernelBase其CanSupport校验参数类型、输入/输出张量数量GetTilingSize返回sizeof(SwapLast2AxesTilingData)InitImpl调用SwapLast2AxesTiling生成 Tiling 信息。COMPLEX64 对应的SwapLast2AxesC64Kernel进一步校验输入输出 dtype 均为TENSOR_DTYPE_COMPLEX64并通过REG_KERNEL_BASE注册。3. Tiling 策略Tiling 实现位于 ops/base/swaplast2axes/tiling/swap_last_2axes_tiling.cpp从算子参数中取出batch、row、col写入SwapLast2AxesTilingData查询当前平台的 Vector Core 数量PlatformInfo::Instance().GetCoreNum(CoreType::CORE_TYPE_VECTOR)作为算子切分的块维度kernelInfo.SetBlockDim(maxCore)最大程度利用多核并行能力该算子无需额外 scratch 内存GetScratchSizes().push_back(0)这解释了为什么 workspace 只需满足异步执行的基础开销。从源码结构看NPU 侧核函数将基于 Tiling 数据中的 batch/row/col 对复数数据完成最后两维交换本质上等价于带批次维的矩阵转置通过多 Vector Core 并行分摊转置搬运的工作量。4. PyTorch 侧对照与测试验证仓库 sip_pta 提供了基于 PyTorch torch_npu 的 Python 封装测试用例 sip_pta/test/base/swap_last2_axes.py 验证了算子的正确性2D 复数张量(3, 2)调用torch_sip.swap_last2_axes(x)与x.transpose(-1, -2)的参考结果做torch.allclose数值比对rtol/atol 均为 1e-43D 复数张量(2, 4, 5)验证带批次维时的storageDims兼容性边界用例1D 张量输入应抛出异常验证了输入 dim 限制为 2 或 3的约束。PyTorch 参考实现x.transpose(-1, -2)与 C 示例的 3×2 复数矩阵转置结果一致进一步印证了算子语义。七、常见问题与注意事项数据格式与类型限制swapLast2Axes仅支持 COMPLEX64 类型与 ND 格式传入其他数据类型如 FLOAT16、INT32会触发ACL_ERROR_UNSUPPORTED_DATA_TYPE错误维度限制仅支持 2 维或 3 维输入1 维或超过 3 维的输入会被形状校验拒绝返回ACL_ERROR_OP_INPUT_NOT_MATCH输出 shape 声明创建输出张量时shape 必须预先声明为交换后的形态如输入{3, 2}对应输出{2, 3}且输出元素数不能少于输入元素数workspace 申请必须在调用算子前调用swapLast2AxesGetWorkspaceSize获取大小并申请内存即使大小为 0 也应保持接口调用流程完整编译前置条件示例构建依赖ASDSIP_HOME_PATH由source output/set_env.sh提供与ASCEND_HOME_PATH由 CANNset_env.sh提供两个环境变量缺一不可联网要求SiP 加速库编译需要联网下载依赖库ascend-boost-comm 等离线环境无法完成整库编译。八、相关参考示例代码example/A2/BASE/swap_last2_axes/example_swap_last2_axes.cpp示例构建脚本example/A2/BASE/swap_last2_axes/build.shAPI 接口声明include/base_api.h完整 API 文档swapLast2AxesHost 侧实现core/base/swaplast2axes.cpp算子注册与 Kernelops/base/swaplast2axes/swap_last_2axes_operation.cpp、ops/base/swaplast2axes/swap_last_2axes_kernel.cppTiling 实现ops/base/swaplast2axes/tiling/swap_last_2axes_tiling.cppPython 侧测试sip_pta/test/base/swap_last2_axes.py编译指南编译与构建返回码说明SiP返回码【免费下载链接】sip本项目是CANN提供的一款高效、可靠的高性能信号处理算子加速库基于华为Ascend AI处理器专门为信号处理领域而设计。项目地址: https://gitcode.com/cann/sip创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED — 相关阅读

相关资讯

LATEST — 最新资讯

最新发布

TODAY — 本日精选

新闻

WEEKLY — 本周精选

新闻

MONTHLY — 本月精选

新闻