news 2026/9/19 17:07:40

CANN ops-math 算子实战:aclnnFmodTensor 与 aclnnInplaceFmodTensor 张量取余接口全解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
CANN ops-math 算子实战:aclnnFmodTensor 与 aclnnInplaceFmodTensor 张量取余接口全解析

CANN ops-math 算子实战:aclnnFmodTensor 与 aclnnInplaceFmodTensor 张量取余接口全解析

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

导读

aclnnFmodTensor/aclnnInplaceFmodTensor是 CANN ops-math 仓库中 Mod(Fmod,截断取余)算子在 aclnn(Ascend CANN 轻量算子接口)层的张量形态对外接口,用于对self与可广播的other执行out = self - other * trunc(self / other)逐元素取余。本文以 关联文档 为核心骨架,结合 op_api 实现、tiling 实现 与 UT 用例 等仓库源码,完整讲解两段式接口的调用流程、数据类型与产品约束、A2/A3 平台的 INT16 增强与大商数值稳定性算法,并给出可直接编译运行的完整示例,帮助读者在 NPU 上正确、高效地完成张量取余计算。

功能说明与数学语义

Mod 算子返回self除以other的余数,采用截断取余(trunc-mod,结果符号跟随被除数)语义:

$$ out_{i} = self_{i} - other \times trunc(self_{i} / other) $$

核心要点(见 关联文档 与 README 功能说明):

  • other需要能广播(broadcast)到self,广播后逐元素计算;
  • out的 shape 必须与self完全一致(余数形状跟随被除数);
  • selfotherout均支持 ND 格式,维度不超过 8 维。

接口原型:两段式(GetWorkspaceSize + Execute)异步调用

aclnnFmodTensoraclnnInplaceFmodTensor均遵循 aclnn 两级接口约定:先调用GetWorkspaceSize完成参数校验与图构建,查询所需 workspace 大小并得到executor;再调用执行接口在指定stream上异步下发算子。两个接口的原型如下(见 关联文档 与 接口头文件):

aclnnStatus aclnnFmodTensorGetWorkspaceSize( const aclTensor* self, const aclTensor* other, aclTensor* out, uint64_t* workspaceSize, aclOpExecutor** executor); aclnnStatus aclnnFmodTensor( void* workspace, uint64_t workspaceSize, aclOpExecutor* executor, aclrtStream stream); aclnnStatus aclnnInplaceFmodTensorGetWorkspaceSize( aclTensor* selfRef, const aclTensor* other, uint64_t* workspaceSize, aclOpExecutor** executor); aclnnStatus aclnnInplaceFmodTensor( void* workspace, uint64_t workspaceSize, aclOpExecutor* executor, aclrtStream stream);

两者区别仅在于:

  • aclnnFmodTensor:非原地版本,selfotherout三个张量独立传入,结果写入out
  • aclnnInplaceFmodTensor:原地版本,只有selfRef(同时充当被除数与输出)和other,结果直接写回selfRef。从 源码 可以看到,原地版本内部通过auto out = const_cast<aclTensor*>(selfRef);out指向selfRef,并复用CheckParamsInplaceTensorTensor做参数校验后,与普通版本共享同一条ExecFmodTensorGetWorkspaceSize执行链路。

参数说明

参数名输入/输出/属性描述数据类型数据格式
self输入待进行 mod 计算的入参,即公式中的 self_i(被除数)BFLOAT16、FLOAT16、FLOAT32、INT32、INT16*ND
other输入待进行 mod 计算的入参,即公式中的 other(除数)BFLOAT16、FLOAT16、FLOAT32、INT32、INT16*ND
out输出待进行 mod 计算的出参,即公式中的 out_i(余数)BFLOAT16、FLOAT16、FLOAT32、INT32、INT16*ND

*INT16 同数据类型计算,以及self/other分别为 INT16 与 BFLOAT16/FLOAT16/FLOAT32 的混合数据类型计算,仅由 Atlas A2 训练系列产品/Atlas A2 推理系列产品、Atlas A3 训练系列产品/Atlas A3 推理系列产品的 AICore 支持;其余产品上该增强不适用,BFLOAT16/FLOAT16/FLOAT32/INT32 的既有支持不受影响。

约束与数据类型支持

形状约束

  • selfotherout支持 ND,维度不超过 8 维(源码中通过CheckTensorDimSize校验,见 aclnn_fmod_tensor.cpp);
  • other必须可广播到self,且广播结果 shape 必须等于selfshape;
  • outshape 必须等于selfshape。这两条校验实现在 CheckBroadcastShape:先计算广播 shape,再与self->GetViewShape()out->GetViewShape()逐一比对,不满足即返回ACLNN_ERR_PARAM_INVALID

数据类型与产品差异化

aclnn 层支持 DOUBLE、BFLOAT16、FLOAT16、FLOAT32、INT32、INT64、INT8、UINT8、INT16 的类型推导;AICore kernel 直接覆盖 BFLOAT16、FLOAT16、FLOAT32、INT32,其余类型(DOUBLE/INT64/INT8/UINT8)走 AICPU fallback。其中:

  • INT16 同数据类型计算,以及INT16 与 BFLOAT16/FLOAT16/FLOAT32 的混合数据类型计算,是 Atlas A2 / Atlas A3 平台(AICore)的专属增强;
  • 其余产品上的 INT16 增强不适用,但已有的 BFLOAT16/FLOAT16/FLOAT32/INT32 同数据类型计算与 DOUBLE/INT64/INT8/UINT8 的 AICPU 回退行为保持不变。

从 op_api 源码 可以看到不同 NPU 架构的 dtype 支持列表是分架构维护的:ASCEND910B_DTYPE_DTYPE_SUPPORT_LIST(对应DAV_2201及 RegBase,即 Atlas A2/A3 平台)在原有 DOUBLE/BF16/FP16/FP32/INT32/INT64/INT8/UINT8 基础上新增了 INT16(源码注释明确标注 "int16 同 dtype lane 的 L2 门控 (A2)");而ASCEND910_DTYPE_DTYPE_SUPPORT_LISTDAV_2002)与ASCEND310P_DTYPE_DTYPE_SUPPORT_LISTDAV_1001)均不含 INT16。

精度说明:大商场景数值稳定性增强

针对self/other商值较大(大 |self/other|)的场景,Atlas A2/A3 上的 AICore 计算路径引入了数值稳定性增强算法,相比朴素截断取余(trunc-mod)实现,降低了大商场景下的精度损失风险。该增强与 INT16/混合数据类型能力一并限定于 Atlas A2/A3,其余产品的既有算法与精度行为不变(见 README 精度说明)。

其工程实现在 mod_tiling.cpp 中:

  • 定义了自适应路由阈值FMOD_NAIVE_THRESH_DEFAULT = 256.0f,当 |商| 超过该阈值时路由到增强算法(源码注释为 "AlgoA 大商精度路");
  • 该阈值可由环境变量FMOD_NAIVE_THRESH覆盖,供真机精度 sweep 使用(生产环境默认不设置);FmodNaiveThresh 对非法输入(无法解析、溢出、非有限值、非正值)会安全回退到默认阈值;
  • FP32/FP16/INT16/INT32 各 dtype 按 kernel 缓冲区的实际占用分别使用不同的 UB 切分因子(UB_DIVIDER_FP32=69UB_DIVIDER_FP16=65UB_DIVIDER_INT16=45UB_DIVIDER_INT32=69),其中 INT16 同 dtype 走整数域 naive 路径、不分配 A1..A5 工作块,因此每元素占用更低;连续派发(isInput2Scalar || isInput2SameShape)的同 dtype FP32/FP16/BF16 场景还会走 kernel 精简核(USE_LEAN_CONTIG),UB 切分因子进一步下调到 48,以获得更宽的 tile。

产品支持情况

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

(来源:README 产品支持情况)

完整调用示例:两段式接口实战

仓库在 examples/test_aclnn_fmod_tensor.cpp 提供了可直接参考的完整示例。整个调用流程分为初始化设备、构造 aclTensor、两段式执行、读取结果、释放资源五个阶段,核心执行逻辑如下:

int Compute(aclrtStream stream, aclTensor* self, aclTensor* other, aclTensor* out) { uint64_t workspaceSize = 0; aclOpExecutor* executor; // 第一阶段:查询 workspace 大小并构建执行器 auto ret = aclnnFmodTensorGetWorkspaceSize(self, other, out, &workspaceSize, &executor); if (ret != ACL_SUCCESS) { LOG_PRINT("aclnnFmodTensorGetWorkspaceSize failed. ERROR: %d\n", ret); return ret; } void* workspaceAddr = nullptr; if (workspaceSize > 0) { ret = aclrtMalloc(&workspaceAddr, workspaceSize, ACL_MEM_MALLOC_HUGE_FIRST); if (ret != ACL_SUCCESS) { LOG_PRINT("allocate workspace failed. ERROR: %d\n", ret); return ret; } } // 第二阶段:在指定 stream 上异步执行 ret = aclnnFmodTensor(workspaceAddr, workspaceSize, executor, stream); if (ret != ACL_SUCCESS) { LOG_PRINT("aclnnFmodTensor failed. ERROR: %d\n", ret); return ret; } ret = aclrtSynchronizeStream(stream); if (ret != ACL_SUCCESS) { LOG_PRINT("aclrtSynchronizeStream failed. ERROR: %d\n", ret); return ret; } if (workspaceSize > 0) { aclrtFree(workspaceAddr); } return 0; }

示例主函数中构造了{4, 4}的 FLOAT 类型输入:

std::vector<int64_t> selfShape = {4, 4}; std::vector<int64_t> otherShape = {4, 4}; std::vector<int64_t> outShape = {4, 4}; std::vector<float> selfData = {5.5, -11.51, 36.23, 7, -10, -8, -15, -7, 10, 8, 15, 7, -10, -8, -15, -7}; std::vector<float> otherData = {2, 3, -24.1, 2, 3, 5, 4, 2, -3, -5, -4, -2, -3, -5, -4, -2};

使用要点总结:

  1. 初始化:先aclInit(nullptr)aclrtSetDevice(deviceId)aclrtCreateStream(&stream)
  2. 张量构造:通过aclrtMalloc+aclrtMemcpy(HOST_TO_DEVICE)准备设备侧数据,再用aclCreateTensor创建 ND 格式、带连续 strides 的aclTensor
  3. workspace 分配:仅当workspaceSize > 0时才需要aclrtMalloc分配 workspace,执行完毕记得aclrtFree
  4. 异步语义aclnnFmodTensor为异步下发,必须aclrtSynchronizeStream(stream)后再读取结果;
  5. 结果回读aclrtMemcpy(DEVICE_TO_HOST)将结果拷回 host 打印;
  6. 资源释放:依次aclDestroyTensoraclrtFreeaclrtDestroyStreamaclrtResetDeviceaclFinalize

源码级实现剖析:从 aclnn 到 AICore 的完整链路

op_api 层:参数校验与 dtype 归一化

aclnn_fmod_tensor.cpp 是 aclnn 接口的核心实现,非原地入口aclnnFmodTensorGetWorkspaceSize(L694-L702)依次完成:

  1. 空指针检查CheckNotNullTensorTensor校验self/other/out非空;
  2. 类型推导检查CheckPromoteType计算selfother的 promote 类型,要求其能 cast 成out,且属于当前 NPU 架构的 dtype 支持列表;当out为 INT16 时,要求 promote 类型必须是{FLOAT32, FLOAT16, BFLOAT16, INT16}之一(源码 L234-L273);
  3. 广播与形状检查CheckBroadcastShape校验广播合法性及outshape 与self一致;
  4. 维度检查CheckTensorDimSize限制不超过 8 维。

随后ExecFmodTensorGetWorkspaceSize(L567-L598)按计算 dtype 选择执行路径:

  • AICore 路径:当 promote 类型或计算类型属于{BF16, FP16, FP32, INT32, INT16}时(IsAiCoreComputeDtype,L145-L150),对self/otherContiguous归一,再以计算 dtype 做 Mod,最后把结果 cast 到out的 dtype 并ViewCopy落盘。这一设计保证窄输出(尤其是out=int16)不会改变运算精度域——先按 promote 类型算,最后才收窄
  • 混合 dtype 的 FP32 桥接CastWithFp32Bridge(L169-L188)针对INT16 ↔ BF16之间的互转做了特殊处理——DAV_2201 架构没有 int16 与 bf16 之间的直接 Cast lane,因此统一经 FP32 中转(int16/bf16 -> fp32是精确的,最终 Cast 的收窄与直接转换等价);
  • 通用路径(AICPU fallback):对于 DOUBLE/INT64/INT8/UINT8 等非 AICore dtype,InitializeTensor先将张量转连续、0 维转 1 维、cast 到 promote 类型,BroadcastTensor广播到outshape 后交给底层l0op::Mod完成。

host 层:shape 推导与 tiling

  • shape 推导:mod_infershape.cpp 中InferShape4Mod校验other能右对齐广播到selfCanBroadcastOtherToSelf逐维判断otherDim == 1 || otherDim == selfDim),然后令输出 shape 直接继承selfshape(*yShape = *xShape),印证了"余数形状跟随被除数"的语义;
  • tiling:mod_tiling.cpp 的ModTilingForGe读取 x1/x2/y 三个 dtype 并映射为编译期 tiling key(MOD_TPL_FP32/FP16/BF16/INT32/INT16),按每核最小 1024 元素、UB 容量与 dtype 对应的ubDivider计算needCoreNumperCoreDataCount等切分参数;此外还会对通用广播场景尝试"融合广播"tiling(SetFusedBroadcastTiling),将右对齐后恰好为 OUTER(行)广播或 INNER(列)广播的二维折叠形状命中为融合路径,降低广播场景的访存开销;最终SetBlockDim(tilingData->needCoreNum)并设置 32MB 的 workspace(WORK_SPACE_SIZE);
  • 算子定义:mod_def.cpp 注册Mod算子的 x1/x2/y 输入输出,dtype 三元组为{BF16, FP16, FP32, INT32, INT16}(kernel 只暴露同 dtype 原型,跨 dtype 由 aclnn 层先 promote + cast),并声明AICore().AddConfig("ascend910b")AICore().AddConfig("ascend910_93")——即该 AICore kernel 仅在 Atlas A2/A3 平台(DAV_2201)注册,这与文档"INT16 增强仅 A2/A3"的产品边界完全一致。

kernel 层:同 dtype 分发与 AlgoA 数值稳定性

mod_dispatch_impl.h 展示了ModKernelDispatchSameDtype的五个同 dtype 分发 lane:

  • INT32 -> Mod<int>
  • FP16 -> Mod<half>
  • FP32 -> Mod<float>
  • BF16 -> Mod<bfloat16_t>(在__NPU_ARCH__ == 3003上不编译);
  • INT16 -> Mod<int16_t>(受MOD_ENH_ARCH22宏保护,即 A2/A3 增强)。

结合 op_kernel 目录 下的mod_compute_impl.hmod_algoa_impl.hmod_bcast_impl.hmod_flat_impl.hmod_leancontig_impl.hmod_int32_impl.h等实现文件,可以推断 kernel 层按输入几何形态(标量/同形/广播/扁平/连续精简)与大商判定分别走不同的计算内核,其中mod_algoa_impl.h即大商数值稳定性增强算法(AlgoA)的实现载体。

测试与验证

仓库为 Tensor 形态接口提供了完整的 UT 覆盖:

  • test_aclnn_fmod_tensor.cpp:覆盖float_same_shapefp16_broadcast{2,3,5}{1,3,1}广播)、invalid_broadcast(非法广播返回ACLNN_ERR_PARAM_INVALID)、int16_same_dtype(INT16 同 dtype 新 lane)、mixed_int16_fp32_promote_to_fp32(INT16 self × FP32 other 归一化到 FP32 计算)、mixed_int16_fp32_to_int16(FP32 计算后 cast 回 INT16 输出)等关键场景;
  • test_aclnn_inplace_fmod_tensor.cpp:覆盖原地版本的float_same_shapeinvalid_broadcast
  • host 侧另有 test_mod_infershape.cpp 与 test_mod_tiling.cpp 验证 shape 推导与 tiling 切分。

这些用例从三个维度印证了文档约束:广播合法性会被拒绝、FP32 计算 + 收窄输出的混合 dtype 语义正确、INT16 增强仅在 A2/A3 dtype 支持列表内放行。

常见问题与使用建议

  1. 返回ACLNN_ERR_PARAM_INVALID:优先检查other是否可广播到selfoutshape 是否严格等于selfshape、维度是否超过 8 维、promote 类型是否在架构 dtype 支持列表内;
  2. INT16 报不支持:INT16 同/混合 dtype 计算仅限 Atlas A2/A3 训练/推理系列产品(AICore),其他产品请改用 INT32 等既有支持类型;
  3. 原地版本别忘自引用aclnnInplaceFmodTensor没有独立的out,结果覆盖selfRef,调用前应确认原始self数据不再需要;
  4. workspace 不要无条件分配:遵循示例代码,仅当workspaceSize > 0时再aclrtMalloc,避免不必要的显存开销;
  5. 大商精度敏感场景:Atlas A2/A3 上 AICore 已内置 AlgoA 大商数值稳定性增强(默认阈值 256,可用FMOD_NAIVE_THRESH环境变量覆盖以做真机 sweep),建议在精度敏感应用中使用该路径;非 A2/A3 平台仍为朴素 trunc-mod 语义,精度行为保持不变。

延伸阅读

  • Tensor 形态配套接口:aclnnFmodScalar & aclnnInplaceFmodScalar(self为张量、other为标量的取余接口);
  • Mod 算子总览(参数表、贡献说明、调用方式汇总):experimental/math/mod/README.md;
  • 完整示例:examples/test_aclnn_fmod_tensor.cpp。

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

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/19 17:07:39

ChatGPT Plus iOS订阅充值失败与限购排查指南

1. 订阅这件事&#xff0c;为什么在 iOS 上格外容易翻车先说结论&#xff1a;ChatGPT Plus 在 iOS 端充值失败&#xff0c;绝大多数情况不是你的卡没钱&#xff0c;也不是账号被封&#xff0c;而是苹果内购体系、Apple ID 地区、支付方式风控、以及 OpenAI 侧订阅状态不同步这四…

作者头像 李华
网站建设 2026/9/19 17:07:36

Keil MDK安装激活与配置全攻略:从下载到避坑一次搞定

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/19 17:06:42

告别Node版本混乱:Windows下nvm安装切换与避坑全指南

以前我在Windows上装Node.js&#xff0c;第一反应都是去官网下载msi安装包&#xff0c;下一步下一步装完收工。后来项目一多&#xff0c;问题就来了&#xff1a;老项目要用Node 14&#xff0c;新项目要用Node 20&#xff0c;某个工程的lock文件版本对不上&#xff0c;每次build…

作者头像 李华
网站建设 2026/9/19 17:01:56

管理学试卷PDF的结构化解析与知识图谱构建

简介&#xff1a;本资源为湖南商学院管理学课程期末考试真题汇编&#xff0c;面向高校管理类专业本科生及备考学生&#xff0c;助力系统复习核心知识点、熟悉题型结构与应试节奏。试卷共两套&#xff08;A卷为主&#xff09;&#xff0c;涵盖选择题、名词解释、简答、论述及案例…

作者头像 李华