CANN ops-math 算子 aclnnFloorDivide 使用指南两段式调用、计算原理与源码级解析【免费下载链接】ops-math本项目是CANN提供的数学类基础计算算子库实现网络在NPU上加速计算。项目地址: https://gitcode.com/cann/ops-math导读本文围绕 CANN ops-math 开源算子库中的 FloorDiv向下取整除法算子展开完整讲解其aclnnFloorDivide单算子 API 的功能语义、两段式调用流程、参数约束与返回码含义并结合仓库中 op_api、op_host、op_kernel 三层的真实源码与单元测试深入剖析其在 NPU 上的计算路径与 tiling 切分机制。读完本文你将掌握如何在 Atlas 训练/推理产品上正确编写并编译运行一段可用的 FloorDiv 单算子调用程序并理解该算子从 aclnn 接口到底层 AI Core kernel 的完整执行链路。一、算子概览与产品支持情况FloorDivaclnnFloorDivide是 CANN ops-math 数学类基础算子库中面向逐元素运算的算子位于仓库目录 experimental/math/floor_div。它不对外暴露图模式下的自定义算子原型该目录无 op_graph 实现只有op_api、op_host、op_kernel三层通过 aclnn 单算子 API 方式供上层框架如 PyTorch或用户程序直接调用。在产品支持方面当前仓库 READMEexperimental/math/floor_div/README.md明确列出的支持产品为产品是否支持Atlas A2 训练系列产品 / Atlas 800I A2 推理产品√从实现细节看算子注册op_host/floor_div_def.cpp为 AICore 添加了ascend910b的配置项对应 Atlas A2 系列所采用的昇腾 SoC并在 op_api 层通过GetCurrentPlatformInfo().GetCurNpuArch()区分架构DAV_2201或寄存器基座RegBase架构走ASCEND910B_DTYPE_SUPPORT_LIST其余走ASCEND910_DTYPE_SUPPORT_LIST两套列表差异仅在于 bfloat16DT_BF16是否受支持见 op_api/aclnn_floor_divide.cpp。因此若需在非上述 SoC 上使用应首先确认平台支持列表。二、功能与计算公式算子功能对输入的两个张量self与other的每一个元素执行除法后将结果向下取整floor即数学上的下取整除法对于 float16、float32原文写作 float16、float32源码注释与实现同时覆盖两浮点类型 $$out \lfloor \frac{self}{other} \rfloor$$对于 int32、int8、uint8、bfloat16 等整型/低精度类型为避免直接整型除法丢失语义先转换为 float32 完成除法与向下取整再转回原类型 $$ dtype self.dtype\ out cast(\lfloor \frac{cast(self, float32)}{cast(other, float32)} \rfloor, dtype) $$这一“先提精度、再计算、最后回写”的语义在源码中有清晰印证op_api 层aclnn_floor_divide.cpp中CalcFloorDivide先将两个输入经l0op::Contiguous连续化、再按op::PromoteType推导出的类型l0op::Cast提升随后调用l0op::FloorDiv完成下取整除法最后l0op::Cast回输出类型并经l0op::ViewCopy写入out。中间计算结果会因 Promotion 机制临时提升为 float32 等更宽类型避免整型运算截断。op_kernel 层op_kernel/floor_div.h的Compute中float32 分支直接AscendC::DivAscendC::Floorint8/uint8 分支则依次执行Cast整型→half→float→Div→Floor→Castfloat→half→整型使用RoundMode::CAST_FLOOR其余类型half、int32、bfloat16 等则执行Cast原类型→half/float→Div→Floor→Cast回原类型与公式完全一致。算子对空 tensor 有短路处理CalcFloorDivide中若self-IsEmpty() || other-IsEmpty()直接返回成功aclnn_floor_divide.cpptiling 侧对totalIdx 0的空输入也会直接设置SetBlockDim(1)后返回op_host/floor_div_tiling.cpp。三、两段式接口原型aclnnFloorDivide遵循 CANN 单算子调用的两段式接口规范详见 docs/zh/context/two_phase_api.md第一段aclnnFloorDivideGetWorkspaceSize完成入参校验、算子计算流程编排并返回 workspace 大小与执行器第二段aclnnFloorDivide真正在指定 Stream 上执行计算。第一段接口原型aclnnStatus aclnnFloorDivideGetWorkspaceSize( const aclTensor *self_x, const aclTensor *self_y, aclTensor *out, uint64_t *workspaceSize, aclOpExecutor **executor)第二段接口原型aclnnStatus aclnnFloorDivide( void *workspace, uint64_t workspaceSize, aclOpExecutor *executor, const aclrtStream stream)其中“aclnn”是接口前缀“FloorDivide”即算子类型名。两段式接口的核心是 workspace它是除输入/输出外算子在 NPU 上完成计算所需的临时内存。必须严格按“先 GetWorkspaceSize → 按 size 申请内存 → 再执行”的顺序调用且第二段接口不能重复调用重复调用同一 executor 会出现异常。从源码看第一段接口内部通过CREATE_EXECUTOR()创建执行器、调用CalcFloorDivide完成校验与 L0 算子图编排Contiguous/Cast/FloorDiv/ViewCopy的链接随后*workspaceSize uniqueExecutor-GetWorkspaceSize()返回所需临时内存大小并将执行器通过uniqueExecutor.ReleaseTo(executor)交给用户aclnn_floor_divide.cpp第二段接口则直接调用CommonOpExecutorRun(workspace, workspaceSize, executor, stream)执行aclnn_floor_divide.cpp。四、参数说明4.1 aclnnFloorDivideGetWorkspaceSize 参数参数名输入/输出描述使用说明数据类型数据格式维度(shape)非连续 Tensorself_x输入待进行 FloorDiv 计算的入参公式中的 self无FLOAT、FLOAT16、BFLOAT16、INT8、INT32、UINT8ND-√self_y输入待进行 FloorDiv 计算的入参公式中的 othershape 与 self 相同FLOAT、FLOAT16、BFLOAT16、INT8、INT32、UINT8ND-√out输出待进行 FloorDiv 计算的出参公式中的 outshape 与 self 相同FLOAT、FLOAT16、BFLOAT16、INT8、INT32、UINT8ND-√workspaceSize输出返回需要在 Device 侧申请的 workspace 大小-----executor输出返回 op 执行器包含了算子计算流程-----要点补充结合源码三个 Tensor 均支持非连续存储表格中“非连续 Tensor”为 √op_api 内部会先l0op::Contiguous做连续化这也是CalcFloorDivide调用链的第一步。文档表格中的数据类型与 floor_div_def.cpp 中Input(x1)、Input(x2)、Output(y)声明一致DT_FLOAT、DT_INT32、DT_INT8、DT_FLOAT16、DT_UINT8、DT_BF16且均限定FORMAT_ND并开启AutoContiguous()。虽然文档中self_y要求“shape 与 self 相同”op_api 层实际实现了 Broadcast 语义CheckShape会对两输入做OP_CHECK_BROADCAST_AND_INFER_SHAPE广播推导再校验输出 shape 是否与广播结果一致aclnn_floor_divide.cpp。若两输入 shape 完全相同则退化为逐元素同 shape 运算。4.2 aclnnFloorDivide 参数参数名输入/输出描述workspace输入在 Device 侧申请的 workspace 内存地址workspaceSize输入在 Device 侧申请的 workspace 大小由第一段接口 aclnnFloorDivideGetWorkspaceSize 获取executor输入op 执行器包含了算子计算流程stream输入指定执行任务的 Stream4.3 返回值与入参校验两段接口的返回值均为aclnnStatus状态码具体返回码定义参见 docs/zh/context/aclnn_return_code.md。第一段接口会完成入参校验以下场景会报错返回码错误码描述ACLNN_ERR_PARAM_NULLPTR161001传入的 tensor 是空指针ACLNN_ERR_PARAM_INVALID161002self 的数据类型和数据格式不在支持的范围之内ACLNN_ERR_PARAM_INVALID161002self 和 out 的数据形状不一致源码中的校验顺序aclnn_floor_divide.cpp 的CheckParams为空指针检查CheckNotNull→ 数据类型支持列表检查CheckDtypeValid→ 类型提升/可转换性检查CheckPromoteType→ shape 检查CheckShape任何一环失败即返回对应错误码。其中CheckPromoteType还负责验证“计算结果类型能否转换到输出类型”并借助OP_CHECK_RESULT_DTYPE_CAST_FAILED排查 Cast 能力问题。此外op_api 层还额外提供了aclnnFloorDividesother 为 aclScalar 标量、aclnnInplaceFloorDivideinplace输出直接写回 selfRef等变体接口见 aclnn_floor_divide.cpp与aclnnFloorDivide共用同一套CalcFloorDivide计算逻辑本文示例仍以主接口为准。五、约束说明原文档给出的约束说明为“无”。结合源码补充两点从实现可观察到的行为输入维度上限受MAX_SUPPORT_DIMS_NUMS约束op_api 层CheckShape使用OP_CHECK_MAX_DIM校验超过该上限会返回参数非法。整数/低精度类型采用“先提升到 float32 计算、再取整回写”的策略因此在 int8/uint8 等窄类型上结果的数值语义以 float32 中间精度为准从 kernel 实现看int8/uint8 分支实际经历 整型→half→float 两级提升后再做 Div/Floor回写时使用CAST_FLOOR舍入模式op_kernel/floor_div.h。这两点并非算子语义缺陷而是需要在精度敏感场景下知晓的实现行为。六、完整调用示例可编译运行以下示例代码来自 experimental/math/floor_div/README.md同内容也维护在 docs/aclnnFloorDiv.md默认以int32_t类型演示编译与运行的整体流程请参考 docs/zh/context/compile_and_run_sample.md。#include iostream #include vector #include acl/acl.h #include aclnn_floor_divide.h // 修改测试数据类型 using DataType int32_t; #define ACL_TYPE aclDataType::ACL_INT32 #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; } void PrintOutResult(std::vectorint64_t shape, void** deviceAddr) { auto size GetShapeSize(shape); std::vectorDataType resultData(size, 0); auto ret aclrtMemcpy( resultData.data(), resultData.size() * sizeof(resultData[0]), *deviceAddr, 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); for (int64_t i 0; i size; i) { LOG_PRINT(result[%ld] is: , i); // int std::cout (int)resultData[i] std::endl; //LOG_PRINT(result[%ld] is: %d\n, i, resultData[i]); // int } } 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); // 2. 申请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); // 3. 调用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. 调用acl进行device/stream初始化 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的接口自定义构造 aclTensor* selfX nullptr; void* selfXDeviceAddr nullptr; std::vectorint64_t selfXShape {2, 1, 16, 32}; // float16: 19328 15 // bf16: 16752 15 int num__ 1; for(int i 0; i selfXShape.size(); i) num__ * selfXShape[i]; std::vectorDataType selfXHostData(num__); for(int i 0; i selfXHostData.size(); i) { selfXHostData[i] (DataType)(i - (int)(100)); } ret CreateAclTensor(selfXHostData, selfXShape, selfXDeviceAddr, ACL_TYPE, selfX); CHECK_RET(ret ACL_SUCCESS, return ret); aclTensor* selfY nullptr; void* selfYDeviceAddr nullptr; std::vectorint64_t selfYShape selfXShape; // float16, bf16: 16384 2 std::vectorDataType selfYHostData(num__, 2); ret CreateAclTensor(selfYHostData, selfYShape, selfYDeviceAddr, ACL_TYPE, selfY); CHECK_RET(ret ACL_SUCCESS, return ret); aclTensor* out nullptr; void* outDeviceAddr nullptr; std::vectorint64_t outShape selfXShape; std::vectorDataType outHostData(num__, 300.0); ret CreateAclTensor(outHostData, outShape, outDeviceAddr, ACL_TYPE, out); CHECK_RET(ret ACL_SUCCESS, return ret); // 3. 调用CANN算子库API需要修改为具体的API名称 uint64_t workspaceSize 0; aclOpExecutor* executor; LOG_PRINT(Before GetWorkspaceSize: selfX%p, selfY%p, out%p\n, (void*)selfX, (void*)selfY, (void*)out); LOG_PRINT(Before GetWorkspaceSize: selfXDeviceAddr%p, selfYDeviceAddr%p, outDeviceAddr%p\n, selfXDeviceAddr, selfYDeviceAddr, outDeviceAddr); // 4. 调用aclnnFloorDivide第一段接口 ret aclnnFloorDivideGetWorkspaceSize(selfX, selfY, out, workspaceSize, executor); LOG_PRINT(aclnnFloorDivideGetWorkspaceSize returned %d, workspaceSize%llu, executor%p\n, ret, (unsigned long long)workspaceSize, (void*)executor); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclnnFloorDivideGetWorkspaceSize failed. ERROR: %d\n, ret); return ret); // 根据第一段接口计算出的workspaceSize申请device内存 void* workspaceAddr nullptr; if (workspaceSize static_castuint64_t(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); } // 5. 调用aclnnFloorDivide第二段接口 ret aclnnFloorDivide(workspaceAddr, workspaceSize, executor, stream); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclnnFloorDivide failed. ERROR: %d\n, ret); return ret); // 6. 固定写法同步等待任务执行结束 ret aclrtSynchronizeStream(stream); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclrtSynchronizeStream failed. ERROR: %d\n, ret); return ret); // 5. 获取输出的值将device侧内存上的结果拷贝至host侧需要根据具体API的接口定义修改 std::vectorint64_t outShape1 outShape; PrintOutResult(outShape1, outDeviceAddr); // 7. 释放aclTensor需要根据具体API的接口定义修改 aclDestroyTensor(selfX); aclDestroyTensor(selfY); aclDestroyTensor(out); // 8. 释放device资源 aclrtFree(selfXDeviceAddr); aclrtFree(selfYDeviceAddr); aclrtFree(outDeviceAddr); if (workspaceSize static_castuint64_t(0)) { aclrtFree(workspaceAddr); } aclrtDestroyStream(stream); aclrtResetDevice(deviceId); // 9. acl去初始化 aclFinalize(); return 0; }6.1 示例要点拆解环境初始化InitaclInit(nullptr)→aclrtSetDevice(deviceId)→aclrtCreateStream(stream)是固定的三步初始化写法。Tensor 构造CreateAclTensor先按元素总数在 Device 侧aclrtMalloc申请内存用aclrtMemcpyACL_MEMCPY_HOST_TO_DEVICE拷入数据随后计算连续 tensor 的 strides从最后一维反向累乘再以aclCreateTensor创建aclTensor格式固定为ACL_FORMAT_ND。两段式核心流程aclnnFloorDivideGetWorkspaceSize返回workspaceSize与executor当workspaceSize 0时按该大小申请 Device 内存示例中值为 0 时跳过申请再调用aclnnFloorDivide(workspaceAddr, workspaceSize, executor, stream)执行。结果回读与同步执行后用aclrtSynchronizeStream同步等待再通过aclrtMemcpyACL_MEMCPY_DEVICE_TO_HOST将结果拷回 host 打印。资源释放顺序依次aclDestroyTensor销毁三个 aclTensor →aclrtFree释放三个 Device 内存与 workspace →aclrtDestroyStream→aclrtResetDevice→aclFinalize。6.2 数据类型切换与更多示例仓库在 experimental/math/floor_div/examples 目录下按数据类型拆分提供了可直接参考的样例样例文件演示的数据类型test_aclnn_floor_div-float32.cppfloat32ACL_FLOATtest_aclnn_floor_div-int32.cppint32ACL_INT32test_aclnn_floor_div-int8.cppint8ACL_INT8test_aclnn_floor_div-uint8.cppuint8ACL_UINT8test_aclnn_floor_div_float16.cppfloat16ACL_FLOAT16test_aclnn_floor_div_bfloat16.cppbfloat16ACL_BF16test_geir_floor_div.cppGEIR 图模式调用示例每个样例主体结构与上文一致仅需修改using DataType、#define ACL_TYPE两处宏即可切换数据类型例如 float32 样例对应using DataType float;与#define ACL_TYPE aclDataType::ACL_FLOAT见 test_aclnn_floor_div-float32.cpp。七、源码级原理从 aclnn 到 AI Core 的完整调用链FloorDiv 算子在仓库中的实现共分三层理解这条链路有助于排查问题与做二次开发用户程序 / 上层框架 │ aclnnFloorDivideGetWorkspaceSize / aclnnFloorDivide ▼ op_api 层aclnn_floor_divide.cpp ← 入参校验 L0 算子图编排Contiguous/Cast/FloorDiv/ViewCopy │ 生成执行器 executor返回 workspaceSize ▼ op_host 层floor_div_def.cpp ← OpDef 算子注册输入/输出/数据类型/format/AICore 配置 floor_div_tiling.cpp ← Tiling切分数据、计算 blockDim、设置 tiling key │ tiling data tiling key ▼ op_kernel 层floor_div.cpp / floor_div.h ← 模板化 AI Core kernelCopyIn/Compute/CopyOut 流水7.1 op_api 层校验与算子图编排支持的数据类型按平台分两套列表见上文核心检查函数为CheckParams按“空指针 → dtype 支持 → promote 可推导 → shape 广播一致”四级校验。CalcFloorDivide通过l0op::Contiguous、l0op::Cast、l0op::FloorDiv、l0op::ViewCopy等 L0 接口组合出计算流程这些调用以executor为载体延迟构建最终由第二段接口CommonOpExecutorRun提交执行。空 tensor 直接成功返回避免空输入触发无效计算。7.2 op_host 层算子注册与 Tiling算子注册floor_div_def.cppFloorDiv继承OpDef声明输入x1、x2均REQUIRED支持DT_FLOAT/DT_INT32/DT_INT8/DT_FLOAT16/DT_UINT8/DT_BF16FORMAT_NDAutoContiguous输出y同类型AICore 配置开启DynamicRankSupportFlag(true)、DynamicShapeSupportFlag(true)、PrecisionReduceFlag(true)等并以ExtendCfgInfo(opFile.value, floor_div)指定 kernel 入口文件名最终通过OP_ADD(FloorDiv)注册进算子信息库。Tiling 计算floor_div_tiling.cppFloorDivTilingFunc依次完成——获取平台信息UB 大小与 AIV 核数→ 获取 shape 并校验输入/输出维度数一致 → 校验 dtype 在支持集合内 → 申请固定大小 workspaceWS_SYS_SIZE 16MB→ 依据 dtype 选择 UB 数据块数量bfloat16/half 用 10、float 用 6、int8/uint8 用 18、int32 用 8→ 计算每 tile 元素数tileDataNum、总 block 数、参与核数coreNum并把数据均衡切分为 big-core前tailBlockNum个核多分一个 block与 small-core最终把smallCoreDataNum/bigCoreDataNum/tileDataNum/smallTailDataNum/bigTailDataNum/finalSmallTileNum/finalBigTileNum/tailBlockNum写入FloorDivTilingData结构体floor_div_tiling_data.h再按 dtype 映射到 0~5 的 tiling key 并SetBlockDim(finalCoreNum)。Tiling 入口通过IMPL_OP_OPTILING(FloorDiv).Tiling(FloorDivTilingFunc)注册floor_div_tiling.cpp。7.3 op_kernel 层AI Core 模板化 kernelkernel 入口 floor_div.cpp 依据编译期 tiling keyschMode0~5 对应 float/int32/int8/half/uint8/bf16用if constexpr实例化NsFloorDiv::FloorDivT, V模板T 为输入/输出类型V 为中间计算类型如FloorDivfloat, float、FloorDivint32_t, float、FloorDivint8_t, half、FloorDivhalf, float、FloorDivbfloat16_t, float。FloorDivT, Vfloor_div.h采用典型 Vector 流水结构TPipe 双缓冲队列BUFFER_NUM 2的TQue输入队列与输出队列TBuf临时缓冲区非 float 分支额外申请中间类型缓冲区half 中间类型还需 float 级联缓冲。执行流程Process以“CopyIn → Compute → CopyOut”循环推进前tileNum-1个 tile 使用tileDataNum元素量最后一个 tile 使用tailDataNum处理尾块Compute中根据 T/V 类型走三条计算分支float 直算、int8/uint8 两级 Cast、其余类型单级 Cast最终结果以CAST_FLOOR舍入回写保证“向下取整”语义。7.4 单元测试佐证仓库提供了 tiling 与 kernel 两侧的单元测试tiling 单测tests/ut/op_host/test_floor_div_tiling.cpp例如ascend9101_test_tiling_fp16_001用例以 shape{1,64,2,64}、DT_FLOAT16构造TilingContextPara断言 tiling key 为 3对应 half 分支、tiling 数据为8192 8208 1 1 13088 8192 8208 0、workspace 为16777216字节即 16MB与WS_SYS_SIZE一致ascend9101_test_tiling_fp32_001则断言 float 分支 tiling key 为 0。这类用例直接验证了 tiling 计算结果与 dtype 分派的正确性。kernel 单测tests/ut/op_kernel/test_floor_div.cpp配合 tests/ut/op_kernel/floor_div_data 下的gen_data.py/compare_data.py数据脚本完成输入数据生成与结果比对。八、总结与使用建议调用范式固定aclnnFloorDivide必须按“GetWorkspaceSize → 申请 workspace → 执行 → 同步 → 释放”的顺序使用workspace 大小以第一段接口返回值为准当返回 0 时无需申请内存。语义确认float16/float32 直接下取整除法整型与低精度类型先提升到 float32 计算再回写因此不存在 C 语言整型除法的截断问题负数的除法结果严格向下取整如-7 / 2 -4。平台确认当前 README 声明支持 Atlas A2 训练系列与 Atlas 800I A2 推理产品op_api 层对不同架构区分支持列表bfloat16 仅在 910B 系列支持跨平台使用时需核对 dtype 支持情况。可观测性第一段接口执行前会打印输入 tensor 与 Device 地址日志LOG_PRINT结合返回码表aclnn_return_code.md可快速定位空指针、dtype 不支持、shape 不一致等常见问题。如需了解两段式接口、返回码、数据类型与数据格式等通用概念的完整说明可继续阅读仓库文档 docs/zh/context 目录下的 two_phase_api.md、aclnn_return_code.md、data_type.md 与 non_contiguous_tensor.md。【免费下载链接】ops-math本项目是CANN提供的数学类基础计算算子库实现网络在NPU上加速计算。项目地址: https://gitcode.com/cann/ops-math创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考