ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

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

CANN ops-math 中 aclnnLog2 与 aclnnInplaceLog2 算子接口使用指南 CANN ops-math 中 aclnnLog2 与 aclnnInplaceLog2 算子接口使用指南【免费下载链接】ops-math本项目是CANN提供的数学类基础计算算子库实现网络在NPU上加速计算。项目地址: https://gitcode.com/cann/ops-math本文基于 CANN ops-math 开源仓库中 math/log/docs/aclnnLog2aclnnInplaceLog2.md 编写结合 math/log 目录下的 op_api 实现源码与可运行示例系统讲解以 2 为底的对数算子在 CANN 单算子 API 场景下的两种调用方式普通输出与 Inplace 原地计算、两段式接口的完整参数语义、返回码约束并给出可直接编译运行的完整示例代码。读完本文你将能够独立完成 aclnnLog2 / aclnnInplaceLog2 的接口选型、入参构造、workspace 申请、算子执行与结果回拷的全流程编码。1. 算子功能与产品支持情况aclnnLog2与aclnnInplaceLog2完成以 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等价于固定底数 base2、scale1、shift0 的 Log 特化接口。仓库中同族的接口还包括 aclnnLogaclnnInplaceLog 与 aclnnLog10aclnnInplaceLog10。产品支持情况如下产品是否支持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. 两段式接口与函数原型aclnnLog2与aclnnInplaceLog2均遵循 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) // 以 base2.0、scale1.0、shift0.0 执行对数计算 | Cast(workspace_5) // 转换为输出 out 的目标数据类型 | ViewCopy | result对应代码中l0op::Contiguous→l0op::Log(LOG2_BASE2.0, LOG2_SCALE1.0, LOG2_SHIFT0.0)→l0op::Cast→l0op::ViewCopy的调用链这解释了第一段接口返回的workspaceSize正是中间这些临时张量所需的 NPU 内存总和。同时源码中l0op::Contiguous的存在说明接口对非连续non-contiguous输入张量天然支持见下节参数表格中非连续Tensor一列标记 √。3. aclnnLog2GetWorkspaceSize 参数说明参数说明参数名输入/输出描述使用说明数据类型数据格式维度非连续TensorselfaclTensor*输入--BOOL、INT64、INT32、INT16、INT8、UINT8、FLOAT、FLOAT16、DOUBLE、COMPLEX64、COMPLEX128、BFLOAT16ND1-8√outconst aclTensor*输出-当 self 是复数时out 必须是复数FLOAT、FLOAT16、DOUBLE、COMPLEX64、COMPLEX128、BFLOAT16ND1-8-workspaceSizeuint64_t*输出返回需要在 Device 侧申请的 workspace 大小-----executoraclOpExecutor**输出返回 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输出类型可转换性检查与CheckSameShape1In1Outshape 一致性检查后三者均对应 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 参数说明参数说明参数名输入/输出描述使用说明数据类型数据格式维度非连续TensorselfRefaclTensor*输入/输出公式中的输入 self 与输出 output-FLOAT、FLOAT16、DOUBLE、COMPLEX64、COMPLEX128、BFLOAT16ND1-8√workspaceSizeuint64_t*输出返回需要在 Device 侧申请的 workspace 大小-----executoraclOpExecutor**输出返回 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. 约束说明确定性计算aclnnLog2与aclnnInplaceLog2默认采用确定性实现即相同输入在多次运行中结果可复现。数据类型匹配普通版接口要求输入self支持整型/BOOL/浮点/复数等宽泛类型但输出out仅支持浮点与复数类型整数类输入的计算结果会以浮点形式输出。当self是复数时out必须是复数。shape 一致性self与out的 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::vectorint64_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::vectorT hostData, const std::vectorint64_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::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; } 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::vectorint64_t selfShape {4, 2}; std::vectorint64_t outShape {4, 2}; void* selfDeviceAddr nullptr; void* outDeviceAddr nullptr; aclTensor* self nullptr; aclTensor* out nullptr; std::vectordouble selfHostData {0, 1, 2, 3, 4, 5, 6, 7}; std::vectordouble 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::vectordouble 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 打印资源回收结束时依次aclDestroyTensor、aclrtFree、aclrtDestroyStream、aclrtResetDevice、aclFinalize避免内存泄漏。仓库 math/log/examples/test_aclnn_log2.cpp 中的版本还演示了使用std::unique_ptr自定义 deleter 实现 RAII 资源管理的写法StreamPtr、DeviceMemPtr、TensorPtr并利用std::shared_ptr作aclGuard保证异常路径下也能完成aclrtResetDevice与aclFinalize可作为工程化代码的参考。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::vectorint64_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::vectorT hostData, const std::vectorint64_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::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; } 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::vectorint64_t selfShape {4, 2}; void* selfDeviceAddr nullptr; aclTensor* selfRef nullptr; std::vectordouble 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::vectordouble 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.cpp或test_aclnn_inplace_log2.cpp并链接 CANN 提供的库文件cmake_minimum_required(VERSION 3.14) project(ACLNN_EXAMPLE) add_compile_options(-stdc11) 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_COMPILERg -DCMAKE_SKIP_RPATHTRUE 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的结果为 02的结果为 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);典型输出为错误码161001ACLNN_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/aclnnLog2aclnnInplaceLog2.md、aclnnLogaclnnInplaceLog、aclnnLog10aclnnInplaceLog10算子说明math/log/README.mdaclnn 接口实现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),仅供参考
返回列表