news 2026/9/19 20:49:02

CANN ops-math AssignSub 算子深度解析:从算子定义到 aclnn 调用实践

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
CANN ops-math AssignSub 算子深度解析:从算子定义到 aclnn 调用实践

CANN ops-math AssignSub 算子深度解析:从算子定义到 aclnn 调用实践

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

导读

AssignSub 是 CANN ops-math 数学算子库中用于计算var - value并将结果写入输出的基础算子,在权重更新(如 SGD 类优化器)等场景中扮演减法赋值的关键角色。本文以 experimental/math/assign_sub/README.md 为骨架,结合算子定义、Shape 推导、Tiling、AscendC Kernel 与单测用例,系统讲解其功能规格、参数约束、两段式 aclnn 调用方式与底层实现原理,帮助你快速上手在 NPU 上调用该算子并理解其性能优化设计。

一、产品支持情况

AssignSub 算子当前支持以下产品形态(详见 README 及 docs/aclnnAssignSub.md):

产品是否支持
Atlas A2 训练系列产品/Atlas A2 推理系列产品
Atlas A3 训练系列产品/Atlas A3 推理系列产品

产品形态详细说明请参见昇腾产品形态说明(可查阅 CANN 官方文档的"昇腾产品形态说明"章节)。

从源码实现看,该算子通过 op_host/assign_sub_def.cpp 中的AICore().AddConfig("ascend910b").AddConfig("ascend910_93")注册了对应的 AI Core 编译配置,分别对应 Atlas A2(Ascend 910B)与 Atlas A3(Ascend 910_93)系列芯片,与文档中的产品支持情况一一对应。

二、功能说明

AssignSub 的算子功能为:计算var - value,并将结果写入输出张量var_out

计算公式如下:

$$ var_out = var - value $$

从语义上看,该算子等价于"原地减法赋值"(类似var -= value),但输入输出通过独立张量传递,适合在反向传播后的参数更新阶段以显式算子形式插入计算图。它的计算模式是**逐元素(element-wise)**运算,两个输入张量在对应位置逐个做减法。

三、参数说明

3.1 算子参数

参数名输入/输出说明
var输入被减数张量,数据类型支持 FLOAT16、INT8、FLOAT、INT32、UINT8、BF16、INT64,数据格式支持 ND。
value输入减数张量,数据类型与 var 一致,shape 与 var 一致,数据格式支持 ND。
var_out输出输出张量,shape 与 var 一致,数据类型与 var 一致,数据格式支持 ND。

上述数据类型与格式约束在 op_host/assign_sub_def.cpp 的算子定义中有完整体现:varvaluevar_out三个端口均声明为REQUIRED参数,依次支持DT_FLOAT16、DT_INT8、DT_FLOAT、DT_INT32、DT_UINT8、DT_BF16、DT_INT64七种数据类型,Format 与 UnknownShapeFormat 均限定为FORMAT_ND,并统一调用.AutoContiguous()保证输入在计算前被处理为连续内存布局。

3.2 aclnn 两段式接口参数

在 aclnn(AscendCL NN 算子)调用方式下,每个算子分为两段式接口,必须先调用aclnnAssignSubGetWorkspaceSize获取工作空间大小并完成执行器创建,再调用aclnnAssignSub执行计算:

aclnnStatus aclnnAssignSubGetWorkspaceSize( const aclTensor* var, const aclTensor* value, const aclTensor* varOut, uint64_t* workspaceSize, aclOpExecutor** executor);
aclnnStatus aclnnAssignSub( void* workspace, uint64_t workspaceSize, aclOpExecutor* executor, const aclrtStream stream);

aclnnAssignSubGetWorkspaceSize的参数说明如下:

参数名输入/输出说明
var输入被减数张量,数据类型支持 FLOAT16、INT8、FLOAT、INT32、UINT8、BF16、INT64。
value输入减数张量,数据类型与 var 一致,shape 与 var 一致。
varOut输出输出张量,shape 与 var 一致。
workspaceSize输出返回需要在 Device 侧申请的 workspace 大小。
executor输出返回 op 执行器。

需要说明的是,从 op_host/assign_sub_tiling.cpp 的GetWorkspaceSize实现看,AssignSub 当前将 workspace 大小置为 0(WS_SYS_SIZE),即正常调用时无需额外申请 workspace 空间;示例代码中workspaceSize > 0才分配内存的写法是 aclnn 调用的通用防御性范式,可覆盖算子内部未来可能引入 workspace 需求的情况。

四、约束与限制

  • shape 与数据类型必须完全一致:var 和 value 的 shape 及数据类型必须完全一致,不支持 broadcast。这一点在 op_host/assign_sub_infershape.cpp 的 InferShape 中有强校验:varShapevalueShape的维度数(GetDimNum)必须相等,逐维(GetDim(i))也必须相等,任一不一致即通过OP_LOGE记录错误日志并返回GRAPH_FAILED
  • 数据格式仅支持 ND
  • int8/uint8 的减法溢出按模 256 环绕处理:即结果按 8 位无符号环绕语义计算,这与 tests/ut/op_kernel/assign_sub_data/gen_data.py 中 golden 数据的生成逻辑一致——int8 结果先以int16做减法,再按((result + 128) % 256 - 128)回绕到 int8 范围;uint8 则按result % 256回绕。
  • int64 类型输入值范围限制在 int32 可表示范围内([-2^31+1, 2^31-1]):这是因为 Kernel 内部使用 int32 作为 int64 的中间计算类型(详见下文 Kernel 实现分析),超出范围会产生截断导致的错误结果。

五、调用说明与完整示例

README 中给出了测试命令的调用方式,可参考 docs/zh/invocation/quick_op_invocation.md 中描述的 build.sh 流程进行算子调用测试。

目录描述
examples/test_aclnn_assign_sub.cpp通过 aclnn 调用的方式调用 AssignSub 算子。

5.1 完整可运行的 aclnn 调用示例

仓库提供的 test_aclnn_assign_sub.cpp 是一个完整的最小可运行示例,其主流程如下:

  1. 初始化环境aclInit(nullptr)初始化 ACL,aclrtSetDevice(deviceId)设置设备(示例中使用设备 0),aclrtCreateStream(&stream)创建流。
  2. 构造张量:示例构造{4, 5}形状的 FLOAT 张量,var全部填充 10.0f,value全部填充 3.0f,预期输出为全 7.0f。CreateAclTensor模板函数完成 host 数据到 device 内存的搬运(aclrtMalloc+aclrtMemcpy),并按 ND 格式计算连续 strides 后通过aclCreateTensor创建aclTensor
  3. 两段式调用
uint64_t workspaceSize = 0; aclOpExecutor* executor = nullptr; ret = aclnnAssignSubGetWorkspaceSize(var, value, varOut, &workspaceSize, &executor); CHECK_RET(ret == ACLNN_SUCCESS, LOG_PRINT("aclnnAssignSubGetWorkspaceSize failed. ERROR: %d\n", ret); return ret); 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); } ret = aclnnAssignSub(workspaceAddr, workspaceSize, executor, stream); CHECK_RET(ret == ACLNN_SUCCESS, LOG_PRINT("aclnnAssignSub failed. ERROR: %d\n", ret); return ret); ret = aclrtSynchronizeStream(stream);
  1. 资源释放:依次aclDestroyTensor销毁张量、aclrtFree释放 device 内存、aclDestroyOpExecutor销毁执行器、aclrtDestroyStream销毁流、aclrtResetDevice复位设备、aclFinalize结束 ACL。

需要包含的头文件为:

#include "acl/acl.h" #include "aclnnop/aclnn_assign_sub.h"

5.2 编译与测试

  • 算子的 CMake 构建入口位于 experimental/math/assign_sub/CMakeLists.txt,并挂接在 experimental/math/CMakeLists.txt 的算子集合中。
  • Tiling 单测位于 tests/ut/op_host/test_assign_sub_tiling.cpp,测试用例assign_sub_0使用{3, 5, 7, 9}的 INT8 ND 张量、64 核、262144 字节 UB 的模拟环境,断言 Tiling 结果为945 32 32(依次对应 totalNum=945、blockFactor=32、ubFactor=32)且 workspace 为 0。
  • Kernel 侧数据生成脚本 tests/ut/op_kernel/assign_sub_data/gen_data.py 使用{3, 5, 7, 9}形状的全 1 张量,按上述环绕规则生成 golden 数据,用于与 NPU 计算结果对比验证。

六、源码级实现原理

6.1 算子注册(OpDef)

op_host/assign_sub_def.cpp 通过继承OpDef并在构造函数中声明输入输出端口完成算子注册,最后由OP_ADD(AssignSub)注册到算子库。三个端口均声明为REQUIRED,说明 var、value、var_out 在调用时缺一不可。

6.2 Shape 推导(InferShape / InferDataType)

op_host/assign_sub_infershape.cpp 中:

  • InferShapeAssignSub:校验 var 与 value 的维度数和各维大小完全一致,然后把varShape整体赋给输出,即*outShape = *varShape。这也从图编译层面保证了"输出 shape 与 var 一致"的规格。
  • InferDataTypeAssignSubcontext->SetOutputDataType(0, context->GetInputDataType(0)),将输出的数据类型直接继承自输入 var。

6.3 Tiling 策略

op_host/assign_sub_tiling.cpp 实现了 Tiling 函数AssignSubTilingFunc,核心思路如下:

  1. 获取平台信息:通过GetPlatformInfo读取 AIV 核数(GetCoreNumAiv)与 UB 内存大小(GetCoreMemSize),用于后续并行度与片上空间的计算。
  2. 按数据类型选择调度模式GetDtypeInfo为七种数据类型分别生成 TilingKey(ASSIGNSUB_TPL_SCH_MODE_0~MODE_6),并给出对应的dtypeSizeperElemBytes。其中 INT8/UINT8 因需在计算中使用 half 中间类型并做模 256 处理,额外计入2 * sizeof(uint16_t)的临时缓冲区开销;BF16 计入2 * sizeof(float);INT64 计入2 * sizeof(int32_t)
  3. 计算三级因子(写入 op_kernel/assign_sub_tiling_data.h 定义的AssignSubTilingData结构):
    • totalNum:输入总元素数(由存储 shape 的元素个数得到);
    • blockFactor:每个核承担的元素块大小,由CeilDiv(totalNum, coreNum)向上对齐到alignNum(由 UB 块大小GetUbBlockSize除以 dtypeSize 得到)算出,并做 512 字节的粗粒度对齐尝试(COARSE_ALIGN_BYTES)以提升 GM 访问效率;
    • ubFactor:单次流水(一次 CopyIn/Compute/CopyOut 循环)处理的元素数,由ubSize / perElemBytes向下对齐到alignNum得到,且不超过 blockFactor。
  4. 设置执行参数SetBlockDim(usedCoreNum)设置实际使用的核数,SetTilingKey(tilingKey)让 Kernel 侧按数据类型模板展开。

从单测 tests/ut/op_host/test_assign_sub_tiling.cpp 的期望值945 32 32可以看到:945 个元素被划分为每个核 32 个元素的块,每个核内再按 32 个元素为一片(ubFactor)进行多轮流水处理。

6.4 AscendC Kernel 实现

op_kernel/assign_sub.cpp 使用if constexpr按 TilingKey(即数据类型)展开模板,实例化NsAssignSub::AssignSub<half/int8_t/float/int32_t/uint8_t/bfloat16_t/int64_t>,并统一调用op.Init(...)op.Process()

op_kernel/assign_sub.h 中的核心设计:

  • 流水结构TPipe配合三个TQue(输入 var、输入 value、输出,各 2 个 buffer)构成双缓冲流水;Process()ubFactor分片循环,每轮依次执行CopyIn(GM→UB,非对齐场景使用DataCopyPad)、ComputeCopyOut(UB→GM),通过EnQue/DeQue实现生产者-消费者同步。
  • DtypeTrait 特化:每种类型定义中间计算类型ComputeT与策略标志:
    • FLOAT16/FLOAT/INT32:直接Sub,无需类型转换;
    • INT8/UINT8:先Cast到 half 做减法,再经ShiftLeft/ShiftRight的 8 位算术移位实现模 256 环绕(对应 README 中的溢出约束),最后转回原类型;
    • BF16:提升到 float 做减法,结果用CAST_RINT舍入回 BF16;
    • INT64:使用 int32 作为中间计算类型(源码注释明确说明该设计),因此输入值必须限制在 int32 可表示范围内,超出会产生截断错误——这与 README 的 int64 范围约束完全对应。

6.5 调度模式的模板参数化

op_kernel/assign_sub_tiling_key.h 通过ASCENDC_TPL_ARGS_DECLASCENDC_TPL_SEL宏将schMode(0~6,对应七种数据类型)声明为编译期模板参数,使 Kernel 在编译时即可针对每种类型展开出最合适的指令序列,避免了运行时的类型分支开销。

七、贡献说明

贡献者贡献方贡献算子贡献时间贡献内容
Xzz西工大智能感知交互实验室AssignSub2026/07/12新增AssignSub算子

八、小结

AssignSub 是 ops-math 中结构清晰、规格精简的逐元素减法算子:功能上实现var - value并输出到独立张量;实现上由 assign_sub_def.cpp 完成算子注册、assign_sub_infershape.cpp 完成 shape/数据类型推导、assign_sub_tiling.cpp 完成多核与 UB 双缓冲调度、assign_sub.h 完成按类型的流水计算;使用上通过aclnnAssignSubGetWorkspaceSize+aclnnAssignSub两段式接口即可在 Atlas A2/A3 系列产品上完成调用。需要注意三点规格限制:不支持 broadcast、仅支持 ND 格式、int8/uint8 按模 256 环绕以及 int64 值域受 int32 中间类型限制。掌握该算子的调用范式后,可以类推到 ops-math 中其他结构相近的 element-wise 算子。

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

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

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

子代理结果没回传?TaoToken 这样改 LangChain model 配置

/* 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 20:48:33

把 Cursor 的模型通道改到 TaoToken,再对照 FastMCP 的 stdio 通信

/* 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 20:45:46

2026上海紧固件展:产业链创新与数字化转型

1. 展会定位与行业背景2026上海紧固件专业展作为紧固件产业链的年度盛会&#xff0c;其核心价值在于构建覆盖原材料、生产设备、成品件到应用解决方案的全产业链展示平台。当前全球紧固件市场规模已突破1000亿美元&#xff0c;中国作为全球最大的紧固件生产国和消费国&#xff…

作者头像 李华