)
CANN ops-math 算子解析aclnnDivV3 带舍入模式的三元除法接口RealDiv / TruncDiv / FloorDiv【免费下载链接】ops-math本项目是CANN提供的数学类基础计算算子库实现网络在NPU上加速计算。项目地址: https://gitcode.com/cann/ops-math导读aclnnDivV3是 CANN ops-math 数学算子库中实现带舍入模式除法的 L2aclnn接口它根据mode属性在同一算子内完成直接浮点除法RealDiv、向零取整除法TruncDiv与向下取整除法FloorDiv三种语义。本文以 experimental/math/div_v3/docs/aclnnDivV3.md 为骨架结合算子源码逐层拆解接口原型、参数约束、数据类型与广播策略并给出完整的调用示例与底层 kernel 实现原理帮助读者掌握在 NPU 上通过 aclnn 两段式接口调用该算子并理解其内部流水线实现。一、功能与语义aclnnDivV3对两个张量self被除数与other除数执行逐元素除法并通过整数属性mode选择舍入模式mode语义公式0RealDiv真实除法out_i self_i / other_i1TruncDiv向零取整out_i trunc(self_i / other_i)2FloorDiv向下取整out_i floor(self_i / other_i)该语义与官方aclnnDivMod对齐覆盖了除法、截断除法与向下取整除法三种常用计算路径对应 experimental/math/div_v3/README.md 中DivV3带模式的除法 / DivMod的定位。产品支持情况为 Atlas A2 / A3 训练与推理系列产品。二、接口原型与调用流程aclnnDivV3遵循 CANN aclnn 两段式接口规范分为查询 workspace 大小与执行计算两个阶段aclnnStatus aclnnDivV3GetWorkspaceSize( const aclTensor* self, const aclTensor* other, int64_t mode, aclTensor* out, uint64_t* workspaceSize, aclOpExecutor** executor); aclnnStatus aclnnDivV3( void* workspace, uint64_t workspaceSize, aclOpExecutor* executor, aclrtStream stream);典型调用流程为调用aclnnDivV3GetWorkspaceSize完成参数校验并构建执行器executor同时返回所需 workspace 大小若workspaceSize 0通过aclrtMalloc分配设备侧内存调用aclnnDivV3将任务提交到指定stream执行调用aclrtSynchronizeStream同步流随后读取out结果释放 workspace、张量与流资源。参数说明参数名输入/输出说明self输入被除数device 侧 aclTensorother输入除数device 侧 aclTensormode输入舍入模式取值 0 / 1 / 2out输出结果张量device 侧 aclTensorworkspaceSize输出所需 workspace 大小字节executor输出op 执行器由 GetWorkspaceSize 阶段生成workspace输入workspace 内存起始地址可为空指针若 workspaceSize 为 0stream输入aclrtStream 流三、支持的数据类型与格式约束数据类型selfotheroutFLOAT32FLOAT32FLOAT32FLOAT16FLOAT16FLOAT16BFLOAT16BFLOAT16BFLOAT16INT32INT32INT32INT16INT16INT16该列表在源码中有三处一致性声明可以互相印证L2 接口层DTYPE_SUPPORT_LIST定义了{DT_FLOAT, DT_FLOAT16, DT_BF16, DT_INT32, DT_INT16}见 aclnn_div_v3.cpp算子定义层div_v3_def.cpp中Input(x1)、Input(x2)与Output(y)的DataType均为同一列表见 div_v3_def.cppTiling 层supportedDtype集合再次校验见 div_v3_tiling.cpp。约束条件self与other的数据类型必须一致out的数据类型必须与self一致源码在CheckDtypeValid中逐项比对见 aclnn_div_v3.cpp数据格式仅支持 ND维度数不超过 8 维ACLNN_MAX_SHAPE_RANK宏定义见 aclnn_div_v3.cppmode只能取 0、1、2CheckModeValid校验mode 0 || mode 2报错见 aclnn_div_v3.cpp空张量输入self-IsEmpty()或other-IsEmpty()时直接返回ACLNN_SUCCESSworkspace 大小为 0不触发实际计算。四、广播规则self与other的 shape 需满足 NumPy 广播规则out的 shape 必须等于广播后的 shape。这一约束在接口层通过BroadcastInferShape显式计算广播结果并与out比对见 aclnn_div_v3.cpp。从源码结构看广播在 aclnn 接口层op_api完成kernel 层只执行逐元素计算l0op::Contiguous将输入转为连续内存布局见 aclnn_div_v3.cpp当输入 shape 与out的 shape 不一致时通过l0op::BroadcastTo将self、other广播到输出 shape见 aclnn_div_v3.cppl0op::DivV3对同形状输入执行逐元素除法l0op::ViewCopy将结果复制到out见 aclnn_div_v3.cpp。对应地div_v3_infershape.cpp 中的 shape 推导直接将输入 shape 赋给输出——这正是因为广播已在接口层完成kernel 侧的 shape 推导只需透传。五、完整调用示例仓库提供了可直接参考的完整可运行示例 examples/test_aclnn_div_v3.cpp。核心流程如下#include acl/acl.h #include aclnn_div_v3.h // 1. 初始化aclInit → aclrtSetDevice → aclrtCreateStream int Init(int32_t deviceId, aclrtStream* stream) { ... } // 2. 构造输入通过 aclrtMalloc aclrtMemcpy 将 host 数据搬到 device // 并用 aclCreateTensor 以 ACL_FORMAT_ND 格式创建 aclTensor int CreateAclTensor(/* hostData, shape, dataType, deviceAddr, tensor */) { ... } int main() { aclrtStream stream; Init(0, stream); // 输入 shape {4, 256}被除数 7.0、除数 2.0 std::vectorint64_t shape {4, 256}; std::vectorDataType selfHostData(totalSize, 7.0f); std::vectorDataType otherHostData(totalSize, 2.0f); // 创建 selfTensor / otherTensor / outTensor ... // 3. 依次测试三种 mode for (int64_t mode 0; mode 2; mode) { uint64_t workspaceSize 0; aclOpExecutor* executor nullptr; ret aclnnDivV3GetWorkspaceSize(selfTensor, otherTensor, mode, outTensor, workspaceSize, executor); void* workspaceAddr nullptr; if (workspaceSize 0) { ret aclrtMalloc(workspaceAddr, workspaceSize, ACL_MEM_MALLOC_HUGE_FIRST); } ret aclnnDivV3(workspaceAddr, workspaceSize, executor, stream); ret aclrtSynchronizeStream(stream); PrintOutResult(shape, outDeviceAddr); // 回拷 host 并打印 if (workspaceSize 0) { aclrtFree(workspaceAddr); } } // 4. 清理aclDestroyTensor / aclrtFree / aclrtDestroyStream / aclrtResetDevice / aclFinalize return 0; }示例中PrintOutResult通过aclrtMemcpy(ACL_MEMCPY_DEVICE_TO_HOST)将结果回拷到 host 侧并打印前 32 个元素方便直接观察三种 mode 的输出差异对 7.0/2.0 的输入mode0 得 3.5mode1 得 3mode2 得 3。编译时需包含头文件路径并链接 CANN 的libascendcl等相关库具体构建方式可参考仓库 scripts/build_example.sh 与 scripts/build_cmake.sh。六、算子实现架构从 aclnn 到 kernel整个调用链的架构在 div_v3/README.md 中有清晰描述aclnnDivV3 (op_api 层) ├── 参数检查 (dtype / shape / format) ├── Contiguous 转换 ├── BroadcastTo 广播对齐 ├── l0op::DivV3 → kernel 调度 │ └── DivV3 kernel (手写流水线) │ ├── CopyIn: GM → UB (DataCopyPad) │ ├── Compute: 根据 mode 分支计算 │ └── CopyOut: UB → GM (DataCopyPad) └── ViewCopy 输出6.1 op_api 层的组装逻辑aclnn_div_v3.cpp 是 L2 接口的核心实现aclnnDivV3GetWorkspaceSize按顺序完成CheckParams非空检查、dtype 一致性检查、mode 范围检查、广播 shape 检查空输入短路处理l0op::Contiguous→l0op::BroadcastTo→l0op::DivV3→l0op::ViewCopy的计算图组装通过executor-GetWorkspaceSize()汇总 workspace 大小。其中l0op::DivV3L1 接口在 op_api/div_v3.cpp 中实现它基于self的 view shape 与 dtype 分配输出张量再通过ADD_TO_LAUNCHER_LIST_AICORE将OP_INPUT(self)、OP_INPUT(other)、OP_OUTPUT(out)与OP_ATTR(mode, mode)注册到执行器。6.2 算子定义与注册div_v3_def.cpp 定义了算子元信息输入x1、x2REQUIRED输出yREQUIRED均支持{DT_FLOAT, DT_INT32, DT_INT16, DT_FLOAT16, DT_BF16}且仅支持 ND 格式属性mode为 REQUIRED 的整数属性默认值 0AICore 配置DynamicCompileStaticFlag(true)、DynamicRankSupportFlag(true)、DynamicShapeSupportFlag(true)、PrecisionReduceFlag(true)平台注册ascend910b与ascend910_93对应 Atlas A2/A3 系列。6.3 Tiling 与多核切分div_v3_tiling.cpp 中的DivV3TilingFunc完成宿主侧 tiling 计算获取平台信息AIV core 数量与 UB 大小GetCoreNumAiv/GetCoreMemSize(UB)读取输入 shape 的总元素数、dtype 与mode属性依据 dtype 与 mode 估算 UB 内队列数基础 3 个队列x/y/z非 float 类型额外 2tmpBuf0/tmpBuf1mode2 再 1tmpBufFloor见 div_v3_tiling.cpp计算单 tile 的 block 数与数据数再按小核/大核进行多核负载均衡切分ComputeCoreDistribution最终通过SetBlockDim设置 block 维度。空输入时 tiling 直接清零并SetBlockDim(1)返回避免无效调度。6.4 Kernel 层手写三级流水线Kernel 实现采用原生 Ascend C LocalTensor 手写 CopyIn / Compute / CopyOut 三级流水线而非高级 DAG/BroadcastSch 模板Kernel 入口位于 op_kernel/div_v3.cpp类实现在 op_kernel/div_v3.h。计算路径依据 dtype 分为两条float32 直接计算ComputeFloat见 div_v3.hDiv(z, x, y)后mode1 执行Truncmode2 执行FloorFloor 需要额外的tmpBufFloor缓冲其余类型先升精度再回落ComputeNeedCast见 div_v3.hfloat16 / bfloat16 / int32 / int16 先Cast到 float32 完成除法与舍入再按 mode 以不同的RoundMode转回原类型mode0 用CAST_NONEmode2 用CAST_TRUNCmode1 的浮点回落用CAST_RINT。这既避免了低精度除法误差累积也统一了整数除法的舍入语义。尾块搬运使用DataCopyPad避免非对齐数据在搬运中丢失所有基础类型成员变量在声明时初始化为 0完善了边界处理。七、与 DivV2 的差异与设计要点根据 div_v3/README.md 的说明DivV3 相比 DivV2 的改进集中在新增mode属性一个算子覆盖 RealDiv / TruncDiv / FloorDiv 三种语义对齐 DivMod 完整语义DataCopyPad 尾块处理避免非对齐数据丢失成员变量初始化声明时置 0规避未初始化风险op_api 层广播广播上移到 aclnn 接口层kernel 只做纯逐元素计算kernel 实现更简洁Floor 模式 tmpBuf 支持Floor 指令需要额外缓冲区tiling 层据此动态增减 UB 队列数。这些设计使 DivV3 同时具备接口层语义完整、kernel 层实现精简、流水线清晰可读的特点是学习 Ascend C 低级手写流水线算子实现的良好样例。八、总结aclnnDivV3是 CANN ops-math 中实现带舍入模式除法的标准 L2 接口通过mode属性在一个算子内覆盖浮点除法、向零取整与向下取整三种语义广播在 aclnn 接口层通过l0op::BroadcastTo完成kernel 层仅做逐元素计算内部采用手写 CopyIn/Compute/CopyOut 三级流水线并对非 float32 类型执行升 float32 计算再回落的策略以保证精度与舍入语义一致。开发者可按本文的两段式调用流程与示例代码在 Atlas A2/A3 系列产品上快速接入该算子进一步深入可阅读 experimental/math/div_v3/op_kernel/div_v3.h、op_host/div_v3_tiling.cpp 与 op_api/aclnn_div_v3.cpp 对照理解完整调用链。【免费下载链接】ops-math本项目是CANN提供的数学类基础计算算子库实现网络在NPU上加速计算。项目地址: https://gitcode.com/cann/ops-math创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考