CANN SiP 信号处理加速库 Asum(绝对值求和)算子实战指南:从 C++ Demo 编译到源码级实现解析
【免费下载链接】sip本项目是CANN提供的一款高效、可靠的高性能信号处理算子加速库,基于华为Ascend AI处理器,专门为信号处理领域而设计。项目地址: https://gitcode.com/cann/sip
导读
本文以 CANN SiP(信号处理加速库)中 Asum 算子的 C++ 调用示例(example/A2/BLAS/asum/README.md)为主线,完整讲解单精度(Sasum)与复数(Scasum)绝对值求和算子的数学原理、环境配置、SiP 库编译、Demo 构建与运行全流程,并结合 core/blas/asum.cpp 与 ops/blas/asum 下的算子实现,剖析 Asum 从 Host 侧 API 到 Device 侧 Kernel 的完整调用链。读完本文,你将能够在 Atlas A2/A3 系列昇腾硬件上独立编译、运行并二次开发 Asum 算子,同时理解其多核并行、双缓冲与 Atomic Add 累加的底层实现思路。
一、算子功能与数学原理
Asum(Absolute Sum)是 BLAS Level 1 中的经典规约算子,功能为对输入向量的所有元素取绝对值后求和。SiP 提供两个接口分别对应实数与复数输入:
asdBlasSasum:单精度(float32)绝对值求和。
$$\text{result} = \sum_{i=0}^{n-1} |x_i|$$
示例:输入
x = [1, 2, -3, 4],输出result = 10。asdBlasScasum:复数(complex64)绝对值求和,对复数的实部与虚部分别取绝对值后再累加。
$$\text{result} = \sum_{i=0}^{n-1} (|\text{Re}(x_i)| + |\text{Im}(x_i)|)$$
示例:输入
x = [1+2i, 2-2i, -3+3i, 4-3i],输出result = (1+2)+(2+2)+(3+3)+(4+3) = 20。
两者的输出均为标量(shape 为[1]),数据类型统一为 float32。
二、环境配置与 SiP 加速库编译
2.1 CANN 环境变量配置
运行示例前需要先加载 CANN(Ascend CANN 工具包)环境变量:
source [CANN安装路径]/set_env.sh默认路径为:
source /usr/local/Ascend/ascend-toolkit/set_env.sh该命令会为后续编译提供ASCEND_HOME_PATH等关键环境变量(详见 example/A2/BLAS/asum/build.sh 中的引用方式)。
2.2 编译 SiP 信号处理加速库
进入 SiP 仓库根目录,执行:
cd ${SiP_root_path} bash build.sh source output/set_env.shoutput/set_env.sh会设置ASDSIP_HOME_PATH等加速库环境变量,供示例编译脚本使用。原文档特别说明了三点注意事项:
- 上述编译方式仅支持通过 git 下载的加速库;以 zip 压缩包方式下载的加速库不支持该编译方式;
- 编译过程需要联网下载依赖库,因此编译环境必须可以访问外网;
- 该编译过程包含两个步骤:获取并编译 ascend-boost-comm(昇腾分布式通信加速库)组件,以及编译信号加速库本身。更多命令介绍可查看 SiP 仓库根目录的 build.sh 文件。
更完整的编译命令说明请参考 docs/compilation_build.md。
2.3 运行 Demo
进入示例所在目录并执行构建脚本:
cd ${示例所在目录} bash build.sh即:
cd example/A2/BLAS/asum bash build.sh三、示例工程结构与构建脚本解析
3.1 目录内容
example/A2/BLAS/asum 目录下包含三个文件:
| 文件 | 说明 |
|---|---|
| README.md | 本示例的使用说明 |
| example_sasum.cpp | Sasum(float32 绝对值求和)示例,默认编译脚本可直接编译运行 |
| example_scasum.cpp | Scasum(complex64 绝对值求和)示例,需将编译脚本中的源文件替换后编译运行 |
3.2 构建脚本核心逻辑
example/A2/BLAS/asum/build.sh 的关键流程如下:
- 校验环境变量:要求
ASDSIP_HOME_PATH已通过source output/set_env.sh设置,且目录真实存在(兼容以latest结尾的路径写法);将$ASDSIP_HOME_PATH/lib加入LD_LIBRARY_PATH; - 编译链接:使用
g++编译示例源码,链接 ASCEND 侧库(-lascendcl -lopapi -lnnopbase)与 SiP 侧库(-lmki -lasdsip -lasdsip_core -lasdsip_host),头文件路径分别来自$ASCEND_HOME_PATH/include与$ASDSIP_HOME_PATH/include; - 运行并清理:执行生成的
example可执行文件,运行结束后删除临时产物。
从编译参数可以看出,示例程序同时依赖两个层面的组件:ACL(Ascend Compute Language)Runtime 层负责设备管理、内存管理与流管理;SiP 库(asdsip/asdsip_core/asdsip_host)提供asdBlas*高层 BLAS 接口;mki 库提供底层算子执行框架。
四、示例代码走读:从 ACL 初始化到算子调用
example_sasum.cpp 与 example_scasum.cpp 结构完全一致,仅输入数据类型与调用的算子接口不同。整个执行流程可划分为五个阶段:
4.1 ACL 初始化与流创建
Init()函数按固定写法完成 ACL 初始化:aclInit初始化 ACL 运行环境 →aclrtSetDevice(deviceId)指定设备(示例默认deviceId = 0)→aclrtCreateStream创建计算流stream。任一环节失败都会通过CHECK_RET宏打印错误并返回。
4.2 构造 Device 侧 Tensor
CreateAclTensor<T>()模板函数封装了"三段式"建张量流程:
- 申请 Device 内存:
aclrtMalloc按shape元素总数 × 元素大小申请显存(示例元素个数xSize = 8); - 拷贝 Host 数据:
aclrtMemcpy以ACL_MEMCPY_HOST_TO_DEVICE方向将输入数据搬入 Device; - 创建 aclTensor:计算连续 tensor 的 strides 后调用
aclCreateTensor,声明数据格式为ACL_FORMAT_ND。
数据格式方面:
- example_sasum 输入使用
std::vector<float>与aclDataType::ACL_FLOAT; - example_scasum 输入使用
std::vector<std::complex<float>>与aclDataType::ACL_COMPLEX64,输出result均为 float32。
4.3 Handle 生命周期与 Workspace 管理
示例完整展示了 SiP BLAS 算子"创建 → 绑 Plan → 申请 Workspace → 设置 Stream → 执行 → 同步 → 销毁"的标准流程(详见 docs/zh/API_Reference/BLAS/BLAS公共接口.md):
asdBlasHandle handle; asdBlasCreate(handle); // ① 创建全局唯一的 handle asdBlasMakeAsumPlan(handle); // ② 初始化 Asum 算子配置并绑定到 handle asdBlasGetWorkspaceSize(handle, lwork); // ③ 获取计算所需 workspace 大小 if (lwork > 0) { aclrtMalloc(&buffer, lwork, ACL_MEM_MALLOC_HUGE_FIRST); // 申请 workspace } asdBlasSetWorkspace(handle, buffer); // ④ 给 plan 设置 workspace asdBlasSetStream(handle, stream); // ⑤ 绑定运行流 ASD_STATUS_CHECK(asdBlasSasum(handle, n, inputX, incx, inputY)); // ⑥ 执行计算 asdBlasSynchronize(handle); // ⑦ 同步等待算子执行完成 asdBlasDestroy(handle); // ⑧ 销毁 plan 并释放资源,避免内存泄漏要点说明:
n = 8表示参与计算的元素个数,incx = 1表示相邻元素的内存地址偏移量(当前约束为 1);- Workspace 是算子计算所需的临时缓冲,示例先通过
asdBlasGetWorkspaceSize查询大小(示例场景下lwork通常为 0,即无需额外 workspace),再按需申请; ASD_STATUS_CHECK宏将返回值与AsdSip::ErrorType::ACL_SUCCESS比较,失败即打印 "Execute failed." 并退出,用于快速定位接口调用错误。
4.4 结果回读与资源释放
计算完成后,通过aclrtMemcpy以ACL_MEMCPY_DEVICE_TO_HOST方向将result从 Device 拷回 Host,打印计算结果;随后依次执行aclDestroyTensor、aclrtFree释放张量与显存,aclrtDestroyStream销毁流,aclrtResetDevice复位设备,最后aclFinalize结束 ACL 环境。
4.5 预期运行结果
- example_sasum:输入
x[i] = 1.0 + i(i = 0..7,即 1~8),输出result = 36; - example_scasum:输入
x[i] = {1+i, 3+i}(i = 0..7,即实部 1~8、虚部 3~10),输出result = 88。
注:示例中生成的数据不代表实际场景,读者可根据具体使用场景修改数据(如通过参数调整
n、xSize或初始化数值)。
五、API 与 Host 侧实现源码解析
5.1 三个接口的函数原型
Asum 相关接口定义(详见 docs/zh/API_Reference/BLAS/Asum.md):
AspbStatus asdBlasMakeAsumPlan(asdBlasHandle handle); AspbStatus asdBlasSasum( asdBlasHandle handle, const int64_t n, aclTensor * x, const int64_t incx, aclTensor * result); AspbStatus asdBlasScasum( asdBlasHandle handle, const int64_t n, aclTensor * x, const int64_t incx, aclTensor * result);参数说明汇总:
| 参数 | 输入/输出 | 描述 |
|---|---|---|
| handle(asdBlasHandle) | 输入 | 算子的句柄 |
| n(int64_t) | 输入 | 总的元素个数 |
| x(aclTensor *) | 输入 | 输入向量;Sasum 支持 float32,Scasum 支持 complex64;数据格式 ND,shape 为[n] |
| incx(int64_t) | 输入 | 相邻元素间的内存地址偏移量(当前约束为 1) |
| result(aclTensor *) | 输出 | 输出结果;数据类型 float32;数据格式 ND,shape 为[1] |
所有接口均返回AspbStatus状态码,具体取值参见 docs/zh/context/SiP返回码.md。
5.2 Host 侧实现的关键细节
core/blas/asum.cpp 中asdBlasSasum/asdBlasScasum/asdBlasMakeAsumPlan的实现揭示了几个值得注意的点:
- 类型与参数校验:
asdBlasSasum强制输入为ACL_FLOAT,asdBlasScasum强制输入为ACL_COMPLEX64,输出统一要求ACL_FLOAT;同时校验n > 0 && n <= UINT32_MAX,防止非法参数进入算子;tensor 格式必须为ACL_FORMAT_ND,否则返回格式不匹配错误码; - 复数按 2n 元素处理:复数 complex64 在内存中按"实部、虚部"交替存储。
asdBlasScasum实际以ELEMENTS_EACH_COMPLEX64 * n(即 2n)个元素进入公共实现asdBlasSasumImpl,并传入scaSum = 1标记复数场景(core/blas/asum.cpp 据此决定存储维度的匹配方式); - incx 容错:当
incx <= 0时实现内部将其修正为 1; - Plan 防重复绑定:
asdBlasMakeAsumPlan会检查 handle 是否已绑定 plan,防止重复初始化导致静默失败甚至 use-after-free(代码注释中提及 issue #129); - 执行入口:最终通过
RunAsdOpsV2以AsumOperation为算子名、OpParam::Asum(含n与incx)为参数下发计算。
六、算子侧实现:Tiling、Kernel 与多核并行
6.1 算子注册与 Kernel 选择
ops/blas/asum/CMakeLists.txt 将 asum_operation.cpp、asum_kernel.cpp 与 sasum tiling 编译为AsumOperation,并将 sasum/kernel/sasum.cpp 注册为名为SasumF32Kernel的 ascend910b mix 内核。
- asum_operation.cpp 中
AsumOperation::GetBestKernel直接返回SasumF32Kernel,输入输出维度均为 1; - asum_kernel.cpp 中
SasumF32Kernel::CanSupport校验输出 dtype 必须为TENSOR_DTYPE_FLOAT,InitImpl调用SasumTiling生成 tiling 数据,tiling 数据大小即sizeof(CommonTilingData)。
6.2 Tiling:多核切分与 Workspace 申请
sasum_tiling.cpp 的SasumTiling完成了三件事:
- 核数决策:查询 Vector 核数量,但上限截断为
DEFAULT_VECTOR_NUM = 40(即最多使用 40 个 Vector 核),核数为 0 时兜底为 1; - 数据切分:调用
ConfigCommonTilingData按核数将n个元素切分,为每个核生成offset(起始偏移)与calNum(计算数量),并以useCoreNum设置kernelInfo.SetBlockDim,即算子实际启动的核数; - Workspace 声明:向
kernelInfo注册WORKSPACE_SIZE = 16777216字节(16 MB)的 scratch 空间,供内核内ReduceSum规约使用。
6.3 Kernel:Abs + ReduceSum + 双缓冲 + Atomic Add
sasum_aiv.h 中SasumAIV<T>类实现了 Vector 核上的计算流水,其核心设计包括:
- 数据切块:单次迭代最大搬运
maxDataCount = 80 * 1024 / 4(80 KB,即 20480 个 float);Process()按整块 + 余数两段处理,余数部分走SingleIterationAligned,通过DataCopyPad补齐到 8 字节对齐再计算; - 双缓冲流水:输入队列
inQueue与输出队列outQueue均配置BUFFER_NUM = 2,配合TPipe实现 CopyIn 与 Compute 的重叠,隐藏搬运时延; - 规约计算:
Compute()中先用Abs对输入取绝对值,再调用ReduceSum在片内完成求和,并借助workBuf暂存规约中间结果; - 跨核累加:
Process()前后分别调用SetAtomicAdd<T>()与SetAtomicNone(),使多个 Vector 核的部分和通过 Atomic Add 安全累加到同一个输出地址outGM,从而得到全局总和; - 入口内核:sasum.cpp 中的
sasum函数从 tiling 缓冲区读取n、coreNum、每个核的offset与calNum,实例化SasumAIV<float>并调用Init/Process。
综上,从源码结构看,Asum 在 Device 侧是一个"Tiling 多核切分 → 每核 Abs + 局部 ReduceSum → Atomic Add 汇总"的典型规约算子,双缓冲与 16 MB workspace 的配置为大规模向量(文档标注 n 覆盖范围 [1, 6.71e+06])提供了可扩展的执行路径。
七、约束说明与产品支持情况
7.1 参数与格式约束
- 输入元素个数
n当前覆盖支持范围[1, 6.71e+06]; - 算子输入 shape 为
[n],输出 shape 为[1]; - 算子实际计算时,不支持 ND 高维度运算(不支持维度 ≥ 3 的运算),即当前版本仅面向一维向量场景;
incx当前约束为 1。
7.2 产品支持情况
根据 example/A2/BLAS/asum/README.md,Asum 示例适用于:
- Atlas A2/A3 训练系列产品
- Atlas 800I A2 推理产品
- Atlas A3 推理系列产品
对应地,docs/zh/API_Reference/BLAS/Asum.md 中列出了更细粒度的支持矩阵:Atlas A2 训练/推理系列产品与 Atlas A3 训练/推理系列产品支持该算子;Ascend 950PR/Ascend 950DT、Atlas 200I/500 A2 推理产品、Atlas 推理系列产品(310p)、Atlas 训练系列产品(910)均不支持。在使用前请根据实际硬件型号确认支持情况。
八、实践小结
本文围绕 CANN SiP 的 Asum 算子示例,覆盖了从环境搭建、库编译、Demo 运行到 Host/Device 双侧源码实现的完整链路。作为开发者,你可以:
- 直接复用 example/A2/BLAS/asum 下的两个示例,快速验证硬件与库环境是否就绪;
- 参考示例中的 handle/plan/workspace 三段式流程,迁移到 SiP 其他 BLAS 算子的调用(如 example/A2/BLAS 目录下的 caxpy、cgemm、dot 等);
- 若要深入定制性能,可从 ops/blas/asum/sasum/tiling/sasum_tiling.cpp 的核数与切分策略、sasum_aiv.h 的缓冲与规约方式入手,结合 docs/zh/API_Reference/BLAS/Asum.md 的参数约束进行针对性调优。
注意:示例代码定位为快速上手的最小化实现,不提供生产级安全保障,不建议直接作为业务代码使用;若将其应用于真实业务场景,安全责任需自行承担。
【免费下载链接】sip本项目是CANN提供的一款高效、可靠的高性能信号处理算子加速库,基于华为Ascend AI处理器,专门为信号处理领域而设计。项目地址: https://gitcode.com/cann/sip
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考