CANN ops-math 中 aclnnLog2 与 aclnnInplaceLog2 算子接口使用指南
2026/9/21 2:41:06 网站建设 项目流程

CANN ops-math 中 aclnnLog2 与 aclnnInplaceLog2 算子接口使用指南

【免费下载链接】ops-math本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。项目地址: https://gitcode.com/cann/ops-math

本文基于 CANN ops-math 开源仓库中 math/log/docs/aclnnLog2&aclnnInplaceLog2.md 编写,结合 math/log 目录下的 op_api 实现源码与可运行示例,系统讲解以 2 为底的对数算子在 CANN 单算子 API 场景下的两种调用方式(普通输出与 Inplace 原地计算)、两段式接口的完整参数语义、返回码约束,并给出可直接编译运行的完整示例代码。读完本文,你将能够独立完成 aclnnLog2 / aclnnInplaceLog2 的接口选型、入参构造、workspace 申请、算子执行与结果回拷的全流程编码。

1. 算子功能与产品支持情况

aclnnLog2aclnnInplaceLog2完成以 2 为底数的逐元素对数计算,计算公式为:

$$ output_i = log_2(self_i) $$

两者实现完全相同的数学功能,区别仅在于结果的存放方式:

  • aclnnLog2:需要新建一个输出张量对象out来存储计算结果,输入self保持不变;
  • aclnnInplaceLog2:无需新建输出张量对象,直接在输入张量selfRef的内存中原地写回计算结果,可节省一份输出张量的 Device 侧内存。

该接口位于 ops-math 仓库的数学算子(math)模块,与 math/log/README.md 中描述的底层 Log 算子(支持 base/scale/shift 属性的通用对数算子)对应:aclnnLog2等价于固定底数 base=2、scale=1、shift=0 的 Log 特化接口。仓库中同族的接口还包括 aclnnLog&aclnnInplaceLog 与 aclnnLog10&aclnnInplaceLog10。

产品支持情况如下:

产品是否支持
Ascend 950PR / Ascend 950DT支持
Atlas A3 训练系列产品 / Atlas A3 推理系列产品支持
Atlas A2 训练系列产品 / Atlas A2 推理系列产品支持
Atlas 200I/500 A2 推理产品不支持
Atlas 推理系列产品不支持
Atlas 训练系列产品不支持

从源码看,math/log/op_api/aclnn_log2.cpp 中通过GetCurrentPlatformInfo().GetSocVersion()判断芯片类型,并分别维护了 910 与 910B(即 A2 系列)两套输入/输出数据类型支持列表(DTYPE_SUPPORT_LIST_910/DTYPE_SUPPORT_LIST_910B),用于接口第一段入参校验,这从实现层面印证了不同产品线在数据类型支持上的差异(例如 BFLOAT16 仅 910B 及以上平台支持)。

2. 两段式接口与函数原型

aclnnLog2aclnnInplaceLog2均遵循 CANN 单算子 API 的两段式接口调用规范:

  • 第一段接口aclnnLog2GetWorkspaceSize/aclnnInplaceLog2GetWorkspaceSize:完成入参校验,计算本次调用所需的 workspace 内存大小,并生成包含算子计算流程的 op 执行器(aclOpExecutor*);
  • 第二段接口aclnnLog2/aclnnInplaceLog2:真正下发计算任务,执行算子。

其中 workspace 是指除输入/输出外,算子在 NPU 上完成计算所需的临时内存。需要特别注意的是,第二段接口不能重复调用,否则会出现异常;正确的调用序列是「第一段 → 按 workspaceSize 申请内存 → 第二段」。

四个接口的原型如下:

aclnnStatus aclnnLog2GetWorkspaceSize( const aclTensor* self, const aclTensor* out, uint64_t* workspaceSize, aclOpExecutor** executor)
aclnnStatus aclnnLog2( void* workspace, uint64_t workspaceSize, aclOpExecutor* executor, aclrtStream stream)
aclnnStatus aclnnInplaceLog2GetWorkspaceSize( const aclTensor* selfRef, uint64_t* workspaceSize, aclOpExecutor** executor)
aclnnStatus aclnnInplaceLog2( void* workspace, uint64_t workspaceSize, aclOpExecutor* executor, aclrtStream stream)

从实现角度可以更直观地看到两者的关系。math/log/op_api/aclnn_log2.cpp 中,aclnnInplaceLog2GetWorkspaceSize在完成 Inplace 入参校验后,将selfRef同时作为输入和输出,直接复用公共实现aclnnLog2Common(selfRef, out, ...)。而aclnnLog2Common内部构造的计算流程为:

self | Contiguous(workspace_0) // 将非连续输入转为连续张量 | Cast(workspace_1) // 类型规整 | Log(workspace_4) // 以 base=2.0、scale=1.0、shift=0.0 执行对数计算 | Cast(workspace_5) // 转换为输出 out 的目标数据类型 | ViewCopy | result

对应代码中l0op::Contiguousl0op::Log(LOG2_BASE=2.0, LOG2_SCALE=1.0, LOG2_SHIFT=0.0)l0op::Castl0op::ViewCopy的调用链,这解释了第一段接口返回的workspaceSize正是中间这些临时张量所需的 NPU 内存总和。同时,源码中l0op::Contiguous的存在说明接口对非连续(non-contiguous)输入张量天然支持(见下节参数表格中"非连续Tensor"一列标记 √)。

3. aclnnLog2GetWorkspaceSize 参数说明

参数说明:

参数名输入/输出描述使用说明数据类型数据格式维度非连续Tensor
self(aclTensor*)输入--BOOL、INT64、INT32、INT16、INT8、UINT8、FLOAT、FLOAT16、DOUBLE、COMPLEX64、COMPLEX128、BFLOAT16ND1-8
out(const aclTensor*)输出-当 self 是复数时,out 必须是复数FLOAT、FLOAT16、DOUBLE、COMPLEX64、COMPLEX128、BFLOAT16ND1-8-
workspaceSize(uint64_t*)输出返回需要在 Device 侧申请的 workspace 大小-----
executor(aclOpExecutor**)输出返回 op 执行器,包含了算子计算流程-----

返回值:

aclnnStatus返回状态码,具体参见 aclnn返回码。第一段接口完成入参校验,出现如下场景时报错:

返回值错误码描述
ACLNN_ERR_PARAM_NULLPTR161001传入的 self、out 是空指针。
ACLNN_ERR_PARAM_INVALID161002self 和 out 的数据类型和数据格式不在支持的范围之内。
ACLNN_ERR_PARAM_INVALID161002计算结果的数据类型无法转换为指定输出 out 的类型。
ACLNN_ERR_PARAM_INVALID161002self 与 out 的 shape 不同。
ACLNN_ERR_PARAM_INVALID161002self 和 out 的维度大于 8。

这些校验逻辑在源码中有严格对应。math/log/op_api/aclnn_log2.cpp 的CheckParams依次执行:CheckNotNull2Tensor(空指针检查,对应 161001)、CheckDtypeValid(数据类型检查,内部按芯片平台选择支持列表)、CheckPromoteType(输出类型可转换性检查)与CheckSameShape1In1Out(shape 一致性检查),后三者均对应 161002。此外CheckFormat会在检测到FORMAT_FRACTAL_NZ格式时输出告警日志,提示 aclnnLog2 不支持 NZ 格式。

4. aclnnLog2 参数说明

参数说明:

参数名输入/输出描述
workspace输入在 Device 侧申请的 workspace 内存地址。
workspaceSize输入在 Device 侧申请的 workspace 大小,由第一段接口 aclnnLog2GetWorkspaceSize 获取。
executor输入op 执行器,包含了算子计算流程。
stream输入指定执行任务的 Stream。

返回值:

aclnnStatus:返回状态码,具体参见 aclnn返回码。

第二段接口的实现非常简洁:在 math/log/op_api/aclnn_log2.cpp 中直接调用框架能力CommonOpExecutorRun(workspace, workspaceSize, executor, stream)完成计算下发,并带有L2_DFX_PHASE_2调用跟踪埋点,方便在 DFX(可观测性)场景下追踪执行过程。

5. aclnnInplaceLog2GetWorkspaceSize 参数说明

参数说明:

参数名输入/输出描述使用说明数据类型数据格式维度非连续Tensor
selfRef(aclTensor*)输入/输出公式中的输入 self 与输出 output-FLOAT、FLOAT16、DOUBLE、COMPLEX64、COMPLEX128、BFLOAT16ND1-8
workspaceSize(uint64_t*)输出返回需要在 Device 侧申请的 workspace 大小-----
executor(aclOpExecutor**)输出返回 op 执行器,包含了算子计算流程-----

返回值:

aclnnStatus:返回状态码,具体参见 aclnn返回码。第一段接口完成入参校验,出现如下场景时报错:

返回值错误码描述
ACLNN_ERR_PARAM_NULLPTR161001传入的 selfRef 是空指针时。
ACLNN_ERR_PARAM_INVALID161002selfRef 的数据类型不在支持的范围之内。
ACLNN_ERR_PARAM_INVALID161002selfRef 的维度超过 8 维。

注意与普通版接口的区别:由于 Inplace 版本只有一个selfRef参数(输入输出复用),没有独立的out,因此其数据类型支持列表限定为浮点与复数类型(FLOAT、FLOAT16、DOUBLE、COMPLEX64、COMPLEX128、BFLOAT16),不支持 BOOL 与各类整型——这一点从源码CheckInplaceParams中调用的CheckInplaceDtypeValid(复用输出类型支持列表GetDtypeSupportListV2(ASCEND910B_OUTPUT_DTYPE_SUPPORT_LIST, ASCEND910_OUTPUT_DTYPE_SUPPORT_LIST))可以得到印证。

6. aclnnInplaceLog2 参数说明

参数说明:

参数名输入/输出描述
workspace输入在 Device 侧申请的 workspace 内存地址。
workspaceSize输入在 Device 侧申请的 workspace 大小,由第一段接口 aclnnInplaceLog2GetWorkspaceSize 获取。
executor输入op 执行器,包含了算子计算流程。
stream输入指定执行任务的 Stream。

返回值:

aclnnStatus:返回状态码,具体参见 aclnn返回码。

7. 约束说明

  • 确定性计算aclnnLog2aclnnInplaceLog2默认采用确定性实现,即相同输入在多次运行中结果可复现。
  • 数据类型匹配:普通版接口要求输入self支持整型/BOOL/浮点/复数等宽泛类型,但输出out仅支持浮点与复数类型;整数类输入的计算结果会以浮点形式输出。当self是复数时,out必须是复数。
  • shape 一致性selfout的 shape 必须相同,维度范围 1-8 维。
  • 格式要求:数据格式为 ND,非连续 Tensor 在输入侧受支持(源码通过l0op::Contiguous自动规整),但不支持 NZ 等特殊格式。

关于数值精度可参考 math/log/README.md 中底层 Log 算子的通用约束:base参数必须大于 0;输入值在 (0, 0.01] 或 [0.95, 1.05) 范围内时,输出精度可能不稳定。

8. 调用示例

以下示例代码与仓库 math/log/examples/test_aclnn_log2.cpp 和 math/log/examples/test_aclnn_inplace_log2.cpp 中提供的可运行样例一致,具体编译和执行过程请参考编译与运行样例。

8.1 aclnnLog2 示例代码

#include <iostream> #include <vector> #include "acl/acl.h" #include "aclnnop/aclnn_log2.h" #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) { // 固定写法,资源初始化 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() { // 1.(固定写法)device/stream初始化,参考acl API手册 // 根据自己的实际device填写deviceId int32_t 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); // 2. 构造输入与输出,需要根据API的接口自定义构造 std::vector<int64_t> selfShape = {4, 2}; std::vector<int64_t> outShape = {4, 2}; void* selfDeviceAddr = nullptr; void* outDeviceAddr = nullptr; aclTensor* self = nullptr; aclTensor* out = nullptr; std::vector<double> selfHostData = {0, 1, 2, 3, 4, 5, 6, 7}; std::vector<double> outHostData(8, 0); // 创建self aclTensor ret = CreateAclTensor(selfHostData, selfShape, &selfDeviceAddr, aclDataType::ACL_DOUBLE, &self); CHECK_RET(ret == ACL_SUCCESS, return ret); // 创建out aclTensor ret = CreateAclTensor(outHostData, outShape, &outDeviceAddr, aclDataType::ACL_DOUBLE, &out); CHECK_RET(ret == ACL_SUCCESS, return ret); // 3. 调用CANN算子库API,需要修改为具体的API名称 uint64_t workspaceSize = 0; aclOpExecutor* executor; // 调用aclnnLog2第一段接口 ret = aclnnLog2GetWorkspaceSize(self, out, &workspaceSize, &executor); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnLog2GetWorkspaceSize failed. ERROR: %d\n", ret); return ret); // 根据第一段接口计算出的workspaceSize申请device内存 void* workspaceAddr = nullptr; if (workspaceSize > 0) { ret = aclrtMalloc(&workspaceAddr, workspaceSize, ACL_MEM_MALLOC_HUGE_FIRST); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("allocate workspace failed. ERROR: %d\n", ret); return ret); } // 调用aclnnLog2第二段接口 ret = aclnnLog2(workspaceAddr, workspaceSize, executor, stream); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnLog2 failed. ERROR: %d\n", ret); return ret); // 4.(固定写法)同步等待任务执行结束 ret = aclrtSynchronizeStream(stream); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtSynchronizeStream failed. ERROR: %d\n", ret); return ret); // 5. 获取输出的值,将device侧内存上的结果拷贝至host侧,需要根据具体API的接口定义修改 auto size = GetShapeSize(outShape); std::vector<double> resultData(size, 0); ret = aclrtMemcpy(resultData.data(), resultData.size() * sizeof(resultData[0]), outDeviceAddr, size * sizeof(resultData[0]), 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); for (int64_t i = 0; i < size; i++) { LOG_PRINT("result[%ld] is: %f\n", i, resultData[i]); } // 6. 释放aclTensor和aclScalar,需要根据具体API的接口定义修改 aclDestroyTensor(self); aclDestroyTensor(out); // 7. 释放device资源,需要根据具体API的接口定义修改 aclrtFree(selfDeviceAddr); aclrtFree(outDeviceAddr); if (workspaceSize > 0) { aclrtFree(workspaceAddr); } aclrtDestroyStream(stream); aclrtResetDevice(deviceId); aclFinalize(); return 0; }

代码中的关键点说明:

  • CreateAclTensor 辅助函数:先aclrtMalloc申请 Device 侧内存,再aclrtMemcpy将 Host 数据拷入,随后根据 shape 计算连续张量的 strides,最后aclCreateTensor以 ND 格式创建aclTensor
  • 两段式调用aclnnLog2GetWorkspaceSize计算 workspaceSize 与 executor,随后aclrtMalloc申请 workspace(注意workspaceSize == 0时无需申请),再aclnnLog2真正执行;
  • 异步语义:第二段接口是异步下发,必须aclrtSynchronizeStream同步等待,之后才能将结果从 Device 拷回 Host 打印;
  • 资源回收:结束时依次aclDestroyTensoraclrtFreeaclrtDestroyStreamaclrtResetDeviceaclFinalize,避免内存泄漏。

仓库 math/log/examples/test_aclnn_log2.cpp 中的版本还演示了使用std::unique_ptr自定义 deleter 实现 RAII 资源管理的写法(StreamPtrDeviceMemPtrTensorPtr),并利用std::shared_ptraclGuard保证异常路径下也能完成aclrtResetDeviceaclFinalize,可作为工程化代码的参考。

8.2 aclnnInplaceLog2 示例代码

#include <iostream> #include <vector> #include "acl/acl.h" #include "aclnnop/aclnn_log2.h" #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) { // 固定写法,资源初始化 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() { // 1.(固定写法)device/stream初始化,参考acl API手册 // 根据自己的实际device填写deviceId int32_t 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); // 2. 构造输入与输出,需要根据API的接口自定义构造 std::vector<int64_t> selfShape = {4, 2}; void* selfDeviceAddr = nullptr; aclTensor* selfRef = nullptr; std::vector<double> selfHostData = {0, 1, 2, 3, 4, 5, 6, 8}; // 创建self aclTensor ret = CreateAclTensor(selfHostData, selfShape, &selfDeviceAddr, aclDataType::ACL_DOUBLE, &selfRef); CHECK_RET(ret == ACL_SUCCESS, return ret); // 3. 调用CANN算子库API,需要修改为具体的API名称 uint64_t workspaceSize = 0; aclOpExecutor* executor; // 调用aclnnInplaceLog2第一段接口 ret = aclnnInplaceLog2GetWorkspaceSize(selfRef, &workspaceSize, &executor); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnInplaceLog2GetWorkspaceSize failed. ERROR: %d\n", ret); return ret); // 根据第一段接口计算出的workspaceSize申请device内存 void* workspaceAddr = nullptr; if (workspaceSize > 0) { ret = aclrtMalloc(&workspaceAddr, workspaceSize, ACL_MEM_MALLOC_HUGE_FIRST); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("allocate workspace failed. ERROR: %d\n", ret); return ret); } // 调用aclnnInplaceLog2第二段接口 ret = aclnnInplaceLog2(workspaceAddr, workspaceSize, executor, stream); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnInplaceLog2 failed. ERROR: %d\n", ret); return ret); // 4.(固定写法)同步等待任务执行结束 ret = aclrtSynchronizeStream(stream); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtSynchronizeStream failed. ERROR: %d\n", ret); return ret); // 5. 获取输出的值,将device侧内存上的结果拷贝至host侧,需要根据具体API的接口定义修改 auto size = GetShapeSize(selfShape); std::vector<double> resultData(size, 0); ret = aclrtMemcpy(resultData.data(), resultData.size() * sizeof(resultData[0]), selfDeviceAddr, size * sizeof(resultData[0]), 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); for (int64_t i = 0; i < size; i++) { LOG_PRINT("result[%ld] is: %f\n", i, resultData[i]); } // 6. 释放aclTensor和aclScalar,需要根据具体API的接口定义修改 aclDestroyTensor(selfRef); // 7. 释放device资源,需要根据具体API的接口定义修改 aclrtFree(selfDeviceAddr); if (workspaceSize > 0) { aclrtFree(workspaceAddr); } aclrtDestroyStream(stream); aclrtResetDevice(deviceId); aclFinalize(); return 0; }

与普通版对比,Inplace 版本的核心差异集中在第 2、3、5 步:

  • 只创建一个张量:仅申请selfRef一份 Device 内存,不创建out,输入输出共用同一内存;
  • 第一段接口只传一个张量aclnnInplaceLog2GetWorkspaceSize(selfRef, &workspaceSize, &executor)
  • 结果回拷自输入内存:计算完成后直接从selfDeviceAddr将结果拷回 Host,即原输入数据已被 log2 结果原地覆盖。

当内存紧张或无需保留原始输入时,优先选择 Inplace 版本可显著降低显存占用;反之若需要保留原始输入,应使用普通版aclnnLog2

9. 编译与运行

9.1 前提条件

  • 已搭建基础运行环境:驱动、固件、CANN 软件包、ops 包等;
  • 本文示例为开发和运行环境合设场景(带 AI 处理器的机器既作开发环境又作运行环境),并已配置环境变量:
source ${INSTALL_DIR}/set_env.sh

其中${INSTALL_DIR}为 CANN 软件安装后的文件存储路径。

9.2 CMakeLists.txt 编写

参考编译与运行样例中的通用 CMake 模板,将源文件替换为test_aclnn_log2.cpptest_aclnn_inplace_log2.cpp,并链接 CANN 提供的库文件:

cmake_minimum_required(VERSION 3.14) project(ACLNN_EXAMPLE) add_compile_options(-std=c++11) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "./bin") add_executable(opapi_test test_aclnn_log2.cpp) if(NOT "$ENV{ASCEND_CUSTOM_PATH}" STREQUAL "") set(ASCEND_PATH $ENV{ASCEND_CUSTOM_PATH}) else() set(ASCEND_PATH "/usr/local/Ascend/cann") endif() set(INCLUDE_BASE_DIR "${ASCEND_PATH}/include") include_directories( ${INCLUDE_BASE_DIR} ${INCLUDE_BASE_DIR}/aclnn ) target_link_libraries(opapi_test PRIVATE ${ASCEND_PATH}/lib64/libascendcl.so ${ASCEND_PATH}/lib64/libnnopbase.so ${ASCEND_PATH}/lib64/libopapi_math.so) install(TARGETS opapi_test DESTINATION ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})

注意:Log2 属于 ops-math 仓库math模块的数学类算子,其 aclnn 接口实现位于 math/log/op_api,对应运行时库为libopapi_math.so。头文件aclnn_log2.h由仓库 math/log/op_api/aclnn_log2.h 提供,安装到 CANN 环境后位于${ASCEND_PATH}/include/aclnnop/aclnn_log2.h

9.3 编译与运行

mkdir -p build cd build cmake ../ -DCMAKE_CXX_COMPILER=g++ -DCMAKE_SKIP_RPATH=TRUE make cd bin ./opapi_test

编译成功后,bin目录下生成opapi_test可执行文件,运行后将打印每个元素的 log2 计算结果:

result[0] is: -inf result[1] is: 0.000000 result[2] is: 1.000000 result[3] is: 1.584963 result[4] is: 2.000000 result[5] is: 2.321928 result[6] is: 2.584963 result[7] is: 2.807355

(注:输入0的 log2 结果为负无穷,1的结果为 0,2的结果为 1,以此类推,可根据log2(n)的数学性质核对输出。)

9.4 异常排查

若执行结果报错,可以使用aclGetRecentErrMsg接口获取报错具体信息。例如将self构造为空指针时:

ret = aclnnLog2GetWorkspaceSize(self, out, &workspaceSize, &executor); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnLog2GetWorkspaceSize failed. ERROR: %d.\n[ERROR msg]%s", ret, aclGetRecentErrMsg()); return ret);

典型输出为错误码161001(ACLNN_ERR_PARAM_NULLPTR),并伴随AclNN_Parameter_Error的详细定位信息。完整的返回码含义可参考 aclnn返回码:0表示成功,161001/161002为参数类错误,361001为 Runtime 内部异常,561xxx为 API 内部异常(如算子二进制包未安装时会报ACLNN_ERR_INNER_FIND_KERNEL_ERROR561003,可用于快速判断 ops 包是否缺失)。

10. 相关资源索引

  • 接口文档:math/log/docs/aclnnLog2&aclnnInplaceLog2.md、aclnnLog&aclnnInplaceLog、aclnnLog10&aclnnInplaceLog10
  • 算子说明:math/log/README.md
  • aclnn 接口实现:math/log/op_api/aclnn_log2.cpp、math/log/op_api/aclnn_log2.h
  • 可运行示例:math/log/examples/test_aclnn_log2.cpp、math/log/examples/test_aclnn_inplace_log2.cpp
  • 单元测试:math/log/tests/ut/op_api/test_aclnn_log2.cpp、math/log/tests/ut/op_api/test_inplace_log.cpp、math/log/tests/ut/op_host/test_log_infershape.cpp
  • 通用参考:两段式接口、aclnn返回码、编译与运行样例

说明:以上文档与源码均位于当前 CANN ops-math 仓库内,本文为只读性质的讲解,不涉及对仓库的任何修改。实际使用前请确认目标 NPU 产品型号在第一节支持列表中。

【免费下载链接】ops-math本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。项目地址: https://gitcode.com/cann/ops-math

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询