
CANN ops-math PadV4Grad 算子深度解析reflection_pad1d/reflection_pad2d 反向传播的 aclnn 调用与 AICore 内核实现【免费下载链接】ops-math本项目是CANN提供的数学类基础计算算子库实现网络在NPU上加速计算。项目地址: https://gitcode.com/cann/ops-math本指南以 CANN ops-math 仓库中 conversion/pad_v4_grad/README.md 为骨架系统讲解 PadV4Grad 算子的产品支持情况、功能语义、参数与约束并结合 aclnnReflectionPad1dBackward 接口文档、算子定义、tiling 与内核源码深入剖析其两段式 aclnn 调用流程、workspace 机制、tiling 策略选择与多形态内核实现。读完本文你将掌握如何在 Atlas A2/A3 系列产品上通过aclnnReflectionPad1dBackward完成反射填充反向求导并能理解其底层 Kernel 的分支调度原理。一、算子定位与产品支持情况PadV4Grad 是 CANN ops-math 中负责反射填充reflect padding反向传播的算子承接reflection_pad1d/reflection_pad2d两个正向算子的梯度计算。其对外暴露的 aclnn 接口为aclnnReflectionPad1dBackward内部注册的算子名为PadV4Grad。根据 README 产品支持表产品是否支持Ascend 950PR/Ascend 950DT×Atlas A3 训练系列产品/Atlas A3 推理系列产品√Atlas A2 训练系列产品/Atlas A2 推理系列产品√Atlas 200I/500 A2 推理产品×Atlas 推理系列产品×Atlas 训练系列产品×从算子注册代码 pad_v4_grad_def.cpp 可以看到内核侧仅注册了ascend910b对应 Atlas A2与ascend910_93对应 Atlas A3两份 AICore 配置与上表仅 A2/A3 系列支持的结论一致。需要说明的是aclnnReflectionPad1dBackward.md 中的产品支持列表将 Atlas 推理系列、Atlas 训练系列标注为支持与 README 存在差异实际支持情况请以目标环境的编译与运行结果为准。二、功能说明反射填充反向传播的数学语义算子功能reflection_pad1d/reflection_pad2d的反向传播即给定正向输出的梯度gradOutput还原出正向输入self的梯度gradInput。反射填充reflect padding的特点是填充值来源于输入边缘的镜像反射且不包含边缘本身与 replicate 填充不同。因此反向传播时填充区域产生的梯度需要按照反射映射累加回对应的输入位置——这正是 PadV4Grad 的核心计算模式多个gradOutput位置可能映射到同一个gradInput位置需做加法归并。aclnnReflectionPad1dBackward.md 给出了一个直观示例输入 gradOutput ([[1, 1, 1, 1, 1]]) self ([[0, 1, 2]]) padding ([1, 1]) 输出 gradInput ([[1, 3, 1]])正向时[0,1,2]左右各反射填充 1 位得到[1,0,1,2,1]反向时梯度张量的第 0、2 个元素来自左右填充区分别映射回self的第 0、1 个位置与中间元素累加后得到[1, 3, 1]。该算子只实现反射模式mode reflect这一点在 pad_v4_grad_def.cpp 的属性定义中写死为this-Attr(mode).AttrType(REQUIRED).String(reflect)。三、参数说明READMEE 中给出了四个参数的完整定义整理如下参数名输入/输出/属性描述数据类型数据格式gradOutput输入reflection_pad1d/reflection_pad2d的正向输出梯度shape 支持 2~4 维且维度需与 self 和 gradInput 保持一致reflection_pad1d反向对应 2/3 维reflection_pad2d反向对应 3/4 维FLOAT16、FLOAT32、DOUBLE、COMPLEX64、COMPLEX128NDself输入正向传播的输入张量shape 与 gradInput 一致FLOAT16、FLOAT32、DOUBLE、COMPLEX64、COMPLEX128NDpadding输入向外填充的大小长度为 2 或 4。长度 2 表示 1d 左右填充值前两个值需小于 self 最后一维数值长度 4 表示 2d 左右上下填充值前两个值需小于最后一维数值后两个值需小于倒数第二维数值INT64-gradInput输出计算得到的 self 的梯度数据类型与 self 保持一致FLOAT16、FLOAT32、DOUBLE、COMPLEX64、COMPLEX128ND补充说明在 aclnn 接口层面aclnnReflectionPad1dBackward.md 对self的数据类型列出的支持范围是 BFLOAT16、FLOAT16、FLOAT32、DOUBLE、COMPLEX64、COMPLEX128且四个 aclTensor 均支持非连续non-contiguous输入。而从 pad_v4_grad_tiling.cpp 的 tiling 校验代码看底层 Kernel 实际校验的输入类型为 bfloat16/float16/floatpadding 类型为 int32/int64与 README/接口文档所列类型存在差异实际可用类型请以目标环境实测为准。在算子图Graph层面pad_v4_grad_def.cpp 将输入命名为x、paddings输出命名为y并声明了paddings_contiguous布尔属性默认true表示 padding 数组连续排列。pad_v4_grad_binary.json 中为x与paddings的每种组合fp16/bf16/fp32 × int32/int64都生成了对应的算子二进制。四、约束说明依据 READMEPadV4Grad 的核心约束如下输入 shape 限制gradOutput、self、gradInput的维度需一致支持三/四维且它们的形状需与reflection_pad1d/reflection_pad2d正向传播的输出形状相互一致。输入值域限制padding长度为 2 时前两个值都需小于self最后一维的数值长度为 4 时前两个值需小于最后一维的数值后两个值需小于倒数第二维的数值反射填充不允许 padding 超过输入维度本身。确定性计算aclnnReflectionPad1dBackward默认采用确定性实现。性能风险当gradOutput中元素个数大于300*1024*1024时有运行超时风险。此外从接口文档的返回值错误码表可以归纳出第一段接口的入参校验规则gradOutput、self、padding、gradInput任一为空指针时报ACLNN_ERR_PARAM_NULLPTR错误码 161001数据类型/数据格式越界、shape 越界、self为空 tensor 且存在非 batch 维度值为 0、padding的 size 不等于 2、padding数值大于等于self对应维度、gradOutputshape 与正向输出不一致时均报ACLNN_ERR_PARAM_INVALID错误码 161002。这些校验行为在 aclnn_reflection_pad1d_backward.cpp 中通过复用的Pad1dBackwardCompute公共逻辑完成。五、aclnn 两段式接口调用方式PadV4Grad 遵循 CANN 算子的两段式接口模式必须先调用GetWorkspaceSize接口获取 workspace 大小与执行器再调用执行接口完成计算aclnnStatus aclnnReflectionPad1dBackwardGetWorkspaceSize( const aclTensor *gradOutput, const aclTensor *self, const aclIntArray *padding, aclTensor *gradInput, uint64_t *workspaceSize, aclOpExecutor **executor) aclnnStatus aclnnReflectionPad1dBackward( void *workspace, uint64_t workspaceSize, aclOpExecutor *executor, aclrtStream stream)5.1 第一段接口参数参数名输入/输出描述使用说明数据类型数据格式维度shape非连续 TensorgradOutputaclTensor*输入输入的梯度shape 支持 2-3 维且与 self、gradInput 一致与 self 保持一致ND与 self 一致√selfaclTensor*输入需要进行填充的 tensorshape 支持 2-3 维与 gradInput 一致BFLOAT16、FLOAT16、FLOAT32、DOUBLE、COMPLEX64、COMPLEX128ND2-3√paddingaclIntArray*输入填充范围长度为 2两个数值都需小于 self 最后一维的数值INT64---gradInputaclTensor*输出计算得到的 self 的梯度shape 与 self 一致与 self 保持一致ND与 self 一致√workspaceSizeuint64_t*输出需要在 Device 侧申请的 workspace 大小-----executoraclOpExecutor**输出op 执行器包含算子计算流程-----5.2 第二段接口参数参数名输入/输出描述workspace输入在 Device 侧申请的 workspace 内存地址workspaceSize输入由第一段接口aclnnReflectionPad1dBackwardGetWorkspaceSize获取的 workspace 大小executor输入op 执行器包含算子计算流程stream输入指定执行任务的 Stream5.3 workspace 的由来workspace并非凭空分配其大小由 tiling 阶段精确计算。在 pad_v4_grad_tiling.cpp 中uint64_t workspacePerCore tilingData.get_workspacePerCore(); uint32_t tilingKey tilingData.get_tilingKey(); uint32_t blockNum tilingData.get_blockNum(); size_t usrWorkspace workspacePerCore * blockNum; tilingContext-SetTilingKey(tilingKey); tilingContext-SetBlockDim(blockNum); size_t* workspaces tilingContext-GetWorkspaceSizes(1); workspaces[0] usrWorkspace sysWorkspaceSize;即usrWorkspace workspacePerCore × blockNum每核所需 workspace 乘以实际使用的 AIV 核数再加上系统库要求的sysWorkspaceSize。workspacePerCore的具体值又取决于 tiling 分支仅 H 维填充NO_W_PAD或 mini shape 分支workspacePerCore 0即无需用户 workspace仅 W 维填充NO_H_PAD分支workspacePerCore 2 × partWs × dataTypeSizeH/W 双维填充H_W_PAD等其余分支workspacePerCore alignWidth × WORK_SPACE_PART × dataTypeSize见 pad_v4_grad_tiling.cpp。内核入口 pad_v4_grad.cpp 也显式校验了 workspaceif (workspace nullptr || GetUserWorkspace(workspace) nullptr) { return; }说明该算子在多数形态下依赖 workspace 完成中间结果的暂存与归并。六、完整调用示例仓库在 examples/test_aclnn_reflection_pad1d_backward.cpp 中提供了可直接参考的完整样例接口文档中的示例代码与之等价。以下为核心流程固定写法与资源管理部分完整保留#include acl/acl.h #include aclnnop/aclnn_reflection_pad1d_backward.h #include iostream #include vector #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 shape_size 1; for (auto i : shape) { shape_size * i; } return shape_size; } 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手册 int32_t deviceId 0; aclrtStream stream; auto ret Init(deviceId, stream); CHECK_RET(ret 0, LOG_PRINT(Init acl failed. ERROR: %d\n, ret); return ret); // 2.构造输入与输出需要根据API的接口定义构造 std::vectorint64_t gradOutputShape {1, 4}; std::vectorint64_t selfShape {1, 2}; std::vectorint64_t gradInputShape {1, 2}; void* gradOutputDeviceAddr nullptr; void* selfDeviceAddr nullptr; void* gradInputDeviceAddr nullptr; aclTensor* gradOutput nullptr; aclTensor* self nullptr; aclIntArray* padding nullptr; aclTensor* gradInput nullptr; std::vectorfloat gradOutputHostData {1, 1, 1, 1}; std::vectorfloat selfHostData {1, 2}; std::vectorint64_t paddingData {1, 1}; std::vectorfloat gradInputHostData {0, 0}; // 创建gradOutput aclTensor ret CreateAclTensor(gradOutputHostData, gradOutputShape, gradOutputDeviceAddr, aclDataType::ACL_FLOAT, gradOutput); CHECK_RET(ret ACL_SUCCESS, return ret); // 创建self aclTensor ret CreateAclTensor(selfHostData, selfShape, selfDeviceAddr, aclDataType::ACL_FLOAT, self); CHECK_RET(ret ACL_SUCCESS, return ret); // 创建padding aclIntArray padding aclCreateIntArray(paddingData.data(), 2); CHECK_RET(padding ! nullptr, return ret); // 创建gradInput aclTensor ret CreateAclTensor(gradInputHostData, gradInputShape, gradInputDeviceAddr, aclDataType::ACL_FLOAT, gradInput); CHECK_RET(ret ACL_SUCCESS, return ret); // 3.调用CANN算子库API uint64_t workspaceSize 0; aclOpExecutor* executor; // 调用aclnnReflectionPad1dBackward第一段接口 ret aclnnReflectionPad1dBackwardGetWorkspaceSize(gradOutput, self, padding, gradInput, workspaceSize, executor); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclnnReflectionPad1dBackwardGetWorkspaceSize 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;); } // 调用aclnnReflectionPad1dBackward第二段接口 ret aclnnReflectionPad1dBackward(workspaceAddr, workspaceSize, executor, stream); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclnnReflectionPad1dBackward 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侧 auto size GetShapeSize(gradInputShape); std::vectorfloat resultData(size, 0); ret aclrtMemcpy(resultData.data(), resultData.size() * sizeof(resultData[0]), gradInputDeviceAddr, size * sizeof(float), 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 aclDestroyTensor(gradOutput); aclDestroyTensor(self); aclDestroyIntArray(padding); aclDestroyTensor(gradInput); // 7.释放device资源 aclrtFree(gradOutputDeviceAddr); aclrtFree(selfDeviceAddr); aclrtFree(gradInputDeviceAddr); if (workspaceSize 0) { aclrtFree(workspaceAddr); } aclrtDestroyStream(stream); aclrtResetDevice(deviceId); aclFinalize(); return 0; }示例的编译与运行流程参考 编译与运行样例返回码定义参考 aclnn 返回码。需要特别提醒示例中gradOutputshape 为{1,4}、self为{1,2}、padding为[1,1]即正向[1,2]左右各反射填充 1 位后得到 4 元素输出反向计算出gradInput [2, 2]左右填充位的梯度各为 1分别累加到 self 的两个位置上读者可据此自测。七、源码级实现剖析7.1 算子定义与二进制配置pad_v4_grad_def.cpp 通过OP_ADD(PadV4Grad)完成注册输入xFLOAT/BF16/FLOAT16、输入paddingsINT32/INT64、输出y并固定mode reflect、paddings_contiguous true。对应的 pad_v4_grad_binary.json 在ascend910_93与ascend910b两套平台配置下为 fp16/int32、fp16/int64、fp32/int32、fp32/int64、bf16/int32、bf16/int64 六种组合各生成一份内核二进制如PadV4Grad_fp16_int32_true。7.2 tiling 策略按 shape 与数据类型选择 6 类计算分支tiling 的核心是 pad_v4_grad_tiling.cpp 中的PadV3GradV2Tiling::GetTilingKey()。它对 float/float16/bfloat16 三种数据类型分别计算 tilingKey以 float 反射模式为例分支条件依次为height ≤ 128 width ≤ 112→mini shapeFLOAT_MINI_SHAPE_TILING_KEYUB 分为 3 份padLeft 0 padRight 0 (padTop ! 0 || padBottom ! 0) height 64→仅 H 维填充NO_W_PADUB 分 8 份无需 workspacepadTop 0 padBottom 0 (padLeft ! 0 || padRight ! 0)→仅 W 维填充NO_H_PADUB 分 4 份height ≤ 64 width 112→小 H 大 WSMALL_H_LARGE_WUB 分 3 份height 128 width ≤ 128→大 H 小 WLARGE_H_SMALL_WUB 分 3 份其余 →H/W 双维填充H_W_PADUB 分 5 份。SplitUb()再依据 tilingKey 将可用 UBubSize - 11KB 保留区对齐划分为ubFactorElement元素块用于行/列分片搬运。tiling 数据pad_v4_grad_tiling.h共 20 个字段包括 batch/channel/height/width、对齐后的 height/width、padTop/padBottom/padLeft/padRight、blockNum、ubFactorElement、ncPerCore/tailNC、tilingKey、wPadCopyCount、workspacePerCore 等。tiling 通过TilingInputsDataDependency({1})声明对第 1 个输入paddings有数据依赖因此 padding 值会被先搬运到 Host 侧参与 shape 推导。7.3 Kernel 入口按 tilingKey 分发到 17 个模板实例内核入口 pad_v4_grad.cpp 使用GET_TILING_DATA读取 tiling并通过TILING_KEY_IS(...)宏分发。tilingKey 的三位数字分别编码百位表示数据类型1float322float163bfloat16后两位表示形态分支00mini、10小H大W、01大H小W、11仅W/H或H/W填充的细分。全部 17 个实例包括tilingKey实例类数据类型1000/2000/3000PadV4GradPadMiniHWfloat/half/bf161100/2100/3100PadV4GradPadSamllHLargeW(Bf16)float/half/bf161010/2010/3010PadV4GradLargeHSmallW(Bf16)float/half/bf161101/2101/3101PadV4GradPadWfloat/half/bf161110/2110/3110PadV4GradPadHfloat/half/bf161111/2111/3111PadV4GradPadHW(Bf16)float/half/bf16所有形态类都继承自 pad_v4_grad_base.h 中的PadV4GradBaseT, DerivedT采用 CRTP 手法共享Initbatch/NCHW/padding 等参数的装载、Process*LoopBody主循环与各类 Copy/Compute 公共方法派生类只需实现各自的缓冲初始化、队列与转置细节。Init中还根据blockIdx与tailNC计算了每个核承担的 NC 分片ncOffset实现多核并行。7.4 反射累加的计算本质以仅 H 维填充的 pad_v4_grad_h_pad.h 为例其ComputeHGrad计算 H 方向镜像位置的梯度累加// bf16 需要先 Cast 到 float 再累加最后转回 bf16 Cast(floatTenosr, xLocal, RoundMode::CAST_NONE, 2 * this-ubFactorElement); Add(floatTenosr, floatTenosr[0], floatTenosr[this-ubFactorElement], calCount); Cast(yLocal, floatTenosr, RoundMode::CAST_RINT, calCount); // 其余类型直接双行相加 Add(yLocal, xLocal[0], xLocal[this-ubFactorElement], calCount);Process()中对顶部padTop行依次搬运第i行与第2*padTop - i行镜像行相加后写回gradInput的(padTop - i)行对底部padBottom行同理。对 H/W 双维填充等复杂形态pad_v4_grad_h_w_pad.h、pad_v4_grad_large_h_small_w_pad.h 等则先做行方向的梯度归并写入 workspace再通过TransDataTo5HD转置后用Add完成 W 方向的反射累加见 pad_v4_grad_base.h 的ImplTransposeAndComputeCommon最后转置写回。整个过程使用TQue/TBuf双缓冲、SetFlag/WaitFlag硬事件同步MTE3→MTE2与PipeBarrierPIPE_MTE2保证流水并行。bfloat16 由于精度需要先经Cast提升为 float 计算再Cast回落这是 pad_v4_grad_h_w_bf16_pad.h 与 pad_v4_grad_small_h_large_w_bf16_pad.h 存在的根本原因。八、测试验证仓库为 PadV4Grad 提供了完整的 UT 与 ST 覆盖接口层 UTtest_aclnn_reflection_pad1d_backward_l2.cpp 基于 gtest 验证参数校验逻辑例如传入空指针gradOutput/self/padding/gradInput时第一段接口必须返回ACLNN_ERR_PARAM_NULLPTR输入与 gradInput shape 不一致时返回ACLNN_ERR_PARAM_INVALID与接口文档错误码表一一对应。tiling UTtest_pad_v4_grad_tiling.cpp 结合 tiling_data_def.h 校验各 shape 组合下的 tilingKey、blockNum 与 workspacePerCore 计算。Kernel UTtest_pad_v4_grad.cpp 直接驱动内核在模拟环境上比对输出。STexecutor_aclnnReflectionPad1dBackward.py 与 atk_aclnnReflectionPad1dBackward.json 组成 ATK 用例可在真实设备上端到端验证接口行为。九、总结PadV4Grad 是 ops-math 中面向反射填充reflect mode的反向梯度算子对外提供aclnnReflectionPad1dBackward两段式接口支持 Atlas A2/A3 系列产品上的 float16/float32/bf16以及接口文档列出的 double/complex 等ND 数据。其实现要点可归纳为三点反射映射的梯度累加语义、tiling 阶段按 shape 特征选出 6 类计算形态并精确计算每核 workspace、内核按 tilingKey 分发到 17 个 CRTP 模板实例并借助转置、双缓冲与硬事件同步完成 H/W 双维归并。开发者可直接复用 examples/test_aclnn_reflection_pad1d_backward.cpp 作为接入模板并参照 UT/ST 用例构造自己的验证场景。【免费下载链接】ops-math本项目是CANN提供的数学类基础计算算子库实现网络在NPU上加速计算。项目地址: https://gitcode.com/cann/ops-math创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考