
CANN ops-math 算子库 aclnnReplicationPad1d 接口详解基于 PadV3 的边缘复制填充实战【免费下载链接】ops-math本项目是CANN提供的数学类基础计算算子库实现网络在NPU上加速计算。项目地址: https://gitcode.com/cann/ops-math本篇技术指南以 CANN ops-math 开源仓库中aclnnReplicationPad1d算子的官方接口文档为骨架完整讲解该接口的产品支持范围、两段式调用流程、参数约束与错误码语义并结合仓库源码、单元测试与 Python 参考实现说明其在 NPU 上以复制边缘值方式填充输入张量最后一维的实现原理与调用方式。读完本文你将能够独立完成aclnnReplicationPad1d的 workspace 申请、执行器创建、计算调用与结果回读的完整编码并理解其与底层 PadV3 算子、torch.nn.ReplicationPad1d之间的对应关系。功能概述用输入边界填充最后一维aclnnReplicationPad1d是 CANN ops-math 数学算子库中用于一维复制填充Replication Padding的接口其功能是使用输入张量的边界值edge value填充输入张量的最后一维即把每个切片首尾的元素值向外复制padding指定的次数。这与 PyTorch 的torch.nn.ReplicationPad1d行为一致仓库中的 Python 参考实现正是调用该模块生成期望结果见 executor_aclnnReplicationPad1d.py。文档给出的示例如下输入tensor([[0,1,2]]) padding([2,2]) 输出为([[0,0,0,1,2,2,2]])即在最后一维宽度方向上左侧复制边界值0共 2 个右侧复制边界值2共 2 个。该算子本质上对应底层 PadV3 算子的edge填充模式REPLICATION_MODE这一点在实现源码中有直接体现见下文源码实现解析。关于 PadV3 四种填充模式constant、edge、REFLECT、SYMMETRIC的差异可参考 pad_v3/README.md。产品支持情况aclnnReplicationPad1d在以下产品上可用产品是否支持Ascend 950PR / Ascend 950DT支持Atlas A3 训练系列产品 / Atlas A3 推理系列产品支持Atlas A2 训练系列产品 / Atlas A2 推理系列产品支持Atlas 200I / 500 A2 推理产品不支持Atlas 推理系列产品支持Atlas 训练系列产品支持说明Atlas 200I/500 A2 推理产品为该接口的明确不支持项其余主流训练/推理产品线均可用。需要指出其底层的 PadV3 算子本身在各产品上都支持而 1d 复制填充这一上层 aclnn 接口对 200I/500 A2 做了裁剪。函数原型两段式接口aclnnReplicationPad1d遵循 CANN 单算子 API 的两段式接口规范详见 两段式接口说明第一段aclnnReplicationPad1dGetWorkspaceSize完成入参校验、构图与执行器创建返回计算所需的 workspace 大小及包含算子计算流程的执行器第二段aclnnReplicationPad1d根据第一段返回的 workspace 与执行器在指定 Stream 上执行实际计算。aclnnStatus aclnnReplicationPad1dGetWorkspaceSize( const aclTensor* self, const aclIntArray* padding, aclTensor* out, uint64_t* workspaceSize, aclOpExecutor** executor)aclnnStatus aclnnReplicationPad1d( void* workspace, uint64_t workspaceSize, aclOpExecutor* executor, aclrtStream stream)其中aclnn为算子接口前缀ReplicationPad1d为算子类型名。第二段接口不可重复调用每次计算流程应重新走一遍两段式调用。aclnnReplicationPad1dGetWorkspaceSize 参数详解第一段接口的参数如下表参数名输入/输出描述使用说明数据类型数据格式维度shape非连续TensorselfaclTensor*输入待填充的原输入数据。-BOOL、INT8、UINT8、INT16、UINT16、FLOAT16、BFLOAT16、INT32、UINT32、FLOAT32、INT64、UINT64、DOUBLE、COMPLEX64、COMPLEX128、HIFLOAT8、FLOAT8_E5M2、FLOAT8_E4M3FN、FLOAT8_E8M0ND2-3√paddingaclIntArray*输入输入中需要填充的大小。长度为2两个数值依次代表左右两边需要填充的值。INT64---outaclTensor*输出填充后的输出结果。输出shape中除被填充的最后一维外需要与self一致out最后一维度的数值等于self最后一维度的数值加padding前两个值。与self一致ND维度与self保持一致√workspaceSizeuint64_t*输出返回需要在Device侧申请的workspace大小。-----executoraclOpExecutor**输出返回op执行器包含了算子计算流程。-----关键约束要点self支持 2 维或 3 维张量仅在最后一维做填充数据格式限定为 ND且支持非连续 Tensor内部会自动转连续见下文源码解析。padding为长度为 2 的aclIntArray元素类型 INT64padding[0]表示左侧行首填充量padding[1]表示右侧行尾填充量。out的 shape 必须满足除最后一维外与 self 完全相同且out.shape[last] self.shape[last] padding[0] padding[1]其数据类型、数据格式、维度均与 self 保持一致。不同产品线的数据类型裁剪在Atlas A3 / A2 训练与推理系列产品、Atlas 推理系列产品、Atlas 训练系列产品上以下数据类型不支持BOOL、UINT16、UINT32、UINT64、HIFLOAT8、FLOAT8_E5M2、FLOAT8_E4M3FN、FLOAT8_E8M0即在这些产品上self/out 的数据类型实际可取范围为 INT8、INT16、INT32、INT64、UINT8、FLOAT16、BFLOAT16、FLOAT32、DOUBLE、COMPLEX64、COMPLEX128。头文件注释中给出的基础支持类型为 FLOAT16、FLOAT32、DOUBLE、INT8、INT16、INT32、INT64、UINT8、COMPLEX64、COMPLEX128见 aclnn_replication_pad1d.h与产品裁剪后范围一致。返回值与错误码语义第一段接口的返回值为aclnnStatus状态码具体可参考 aclnn 返回码说明。第一段接口完成入参校验出现以下场景时按表报错返回值错误码描述ACLNN_ERR_PARAM_NULLPTR161001传入的 self、padding、out 是空指针。ACLNN_ERR_PARAM_INVALID161002self、out 的数据类型或数据格式不在支持的范围之内。ACLNN_ERR_PARAM_INVALID161002self、out 的数据类型不一致。ACLNN_ERR_PARAM_INVALID161002self、padding 和 out 的输入 shape 在支持范围之外。ACLNN_ERR_PARAM_INVALID161002self 或 out 为空 tensor且 self 最后两个维度的值存在 0。ACLNN_ERR_PARAM_INVALID161002padding 的数值大于等于 self 最后一维度的值。ACLNN_ERR_PARAM_INVALID161002out 最后一维的值不等于 self 最后一维的值加 padding 的两个值。上述错误场景在仓库的单元测试中都有对应的覆盖用例例如padding长度不为 2、self 维度超过 3、self 与 out 维度不一致、数据格式非 ND、self/out 数据类型不一致、out 最后一维尺寸错误等均断言返回ACLNN_ERR_PARAM_INVALID空指针场景断言返回ACLNN_ERR_PARAM_NULLPTR见 test_aclnn_replication_pad1d.cpp。aclnnReplicationPad1d 执行接口参数详解第二段接口参数如下参数名输入/输出描述workspace输入在 Device 侧申请的 workspace 内存地址。workspaceSize输入在 Device 侧申请的 workspace 大小由第一段接口 aclnnReplicationPad1dGetWorkspaceSize 获取。executor输入op 执行器包含了算子计算流程。stream输入指定执行任务的 Stream。workspace 是指除输入/输出外算子在 NPU 上完成计算所需的临时内存第二段接口依据第一段计算出的workspaceSize使用aclrtMalloc申请内存后传入任务执行完成后需释放。约束说明确定性计算aclnnReplicationPad1d默认为确定性实现即相同输入在多次运行下结果可复现。关于确定性计算的更多背景可参考 确定性计算说明。源码实现解析两段式调用的内部流转aclnnReplicationPad1d的实现位于 aclnn_replication_pad1d.cpp其内部流转可以拆解为以下关键环节入参校验CheckParams依次检查 self/padding/out 是否为空指针、数据类型是否在支持范围内CheckDtypeValid、数据格式是否 NDCheckFormat、shape 是否满足约束CheckShape。其中CheckShape具体校验self 维度必须在 2~3 之间、self 与 out 维度数一致、padding 长度必须为 2、out 除最后一维外与 self 一致、且out.shape[last] self.shape[last] padding[0] padding[1]源码CheckShape中使用expectShape.SetDim(selfDimnum - 1, ... (*padding)[0] (*padding)[1])构造期望 shape 并逐元素比对。空 tensor 处理若 self/out 为空 tensor则直接返回workspaceSize 0并结束但对 2 维空 tensor以及 3 维中第 1、2 维为 0 的情况会返回ACLNN_ERR_PARAM_INVALID即仅允许 batch 维为 0 的空 tensor。测试用例case_2shape{0,3,10}batch 维为 0通过校验而case_13shape{3,0,10}、case_14shape{0,10}均报错与文档错误码表self 或 out 为空 tensor且 self 最后两个维度的值存在 0的语义一致。输入预处理InputPreprocess若 self 非连续先通过l0op::Contiguous转成连续张量若为 2 维输入则在维度 0 上做UnsqueezeNd扩展为 3 维以便复用 PadV3 的 3 维计算路径。构图与执行器构建通过GetPaddingTensor(dim, padding, executor)将用户传入的aclIntArray转换为基础算子的 paddings 张量然后调用l0op::PadV3(self, paddingsTensor, ..., REPLICATION_MODE, true, executor)执行边缘复制填充。这里传入的REPLICATION_MODE正是 PadV3 四种填充模式中的edge模式。在非 RegBase 分支中还会额外分配一个值为 0 的常量 scalar 作为 constant_values 参数传入edge 模式下该值不生效。计算完成后对 2 维输入再通过SqueezeNd恢复维度最后通过l0op::ViewCopy(pad1dResult, out, executor)将结果写入 out——这一步保证 out 为非连续 tensor 时也能正确写入。workspace 计算与返回*workspaceSize uniqueExecutor-GetWorkspaceSize()汇总整个计算图所需的临时内存随后把执行器通过ReleaseTo(executor)交还给用户。第二段接口aclnnReplicationPad1d则调用框架统一的CommonOpExecutorRun(workspace, workspaceSize, executor, stream)完成在 NPU 上的实际执行。从上述实现可以看出aclnnReplicationPad1d并非独立实现一套填充内核而是对底层 PadV3 算子在最后一维 edge 模式 2/3 维场景下的便捷封装这也是它和aclnnReplicationPad2d、aclnnReplicationPad3d在同一目录 pad_v3/op_api 下并列存放的原因。调用示例完整可运行代码下面给出完整示例代码其运行前提是已正确配置 CANN 环境并完成编译链接编译与运行样例的通用流程请参考 编译与运行样例同一示例也以独立文件形式存在于 test_aclnn_replication_pad_1d.cpp可直接参照。#include acl/acl.h #include aclnnop/aclnn_replication_pad1d.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手册 // 根据自己的实际device填写deviceId int32_t deviceId 0; aclrtStream stream; auto ret Init(deviceId, stream); // check根据自己的需要处理 CHECK_RET(ret 0, LOG_PRINT(Init acl failed. ERROR: %d\n, ret); return ret); // 2.构造输入与输出需要根据API的接口定义构造 std::vectorint64_t selfShape {1, 3}; std::vectorint64_t outShape {1, 7}; void* selfDeviceAddr nullptr; void* outDeviceAddr nullptr; aclTensor* self nullptr; aclIntArray* padding nullptr; aclTensor* out nullptr; std::vectorfloat selfHostData {1, 2, 3}; std::vectorint64_t paddingData {2, 2}; std::vectorfloat outHostData {0, 0, 0, 0, 0, 0, 0}; // 创建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); // 创建out aclTensor ret CreateAclTensor(outHostData, outShape, outDeviceAddr, aclDataType::ACL_FLOAT, out); CHECK_RET(ret ACL_SUCCESS, return ret); // 3.调用CANN算子库API需要修改为具体的API uint64_t workspaceSize 0; aclOpExecutor* executor; // 调用aclnnReplicationPad1d第一段接口 ret aclnnReplicationPad1dGetWorkspaceSize(self, padding, out, workspaceSize, executor); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclnnReplicationPad1dGetWorkspaceSize 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;); } // 调用aclnnReplicationPad1d第二段接口 ret aclnnReplicationPad1d(workspaceAddr, workspaceSize, executor, stream); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclnnReplicationPad1d 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侧需要根据具体API的接口定义修改 auto size GetShapeSize(outShape); std::vectorfloat resultData(size, 0); ret aclrtMemcpy(resultData.data(), resultData.size() * sizeof(resultData[0]), outDeviceAddr, 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和aclScalar需要根据具体API的接口定义修改 aclDestroyTensor(self); aclDestroyIntArray(padding); aclDestroyTensor(out); // 7.释放device资源需要根据具体API的接口定义修改 aclrtFree(selfDeviceAddr); aclrtFree(outDeviceAddr); if (workspaceSize 0) { aclrtFree(workspaceAddr); } aclrtDestroyStream(stream); aclrtResetDevice(deviceId); aclFinalize(); return 0; }示例运行结果分析对上述示例输入self [[1, 2, 3]]、padding [2, 2]预期输出out [[1, 1, 1, 2, 3, 3, 3]]左侧补 2 个边界值1右侧补 2 个边界值3。代码运行后会逐元素打印result[i]的值可与预期结果比对验证正确性。若需要 3 维输入只需将selfShape/outShape调整为 3 维如{2, 3, 10}与{2, 3, 13}padding 取[1, 2]其余流程完全一致——该形态同样被单元测试case_float32_3d覆盖。示例代码各步骤解读资源初始化aclInit→aclrtSetDevice→aclrtCreateStream是 CANN 应用的固定前置步骤构造输入输出通过aclrtMalloc申请 device 内存、aclrtMemcpy将 host 数据拷入 device再用aclCreateTensorND 格式、连续 strides创建aclTensor用aclCreateIntArray创建padding两段式调用先调aclnnReplicationPad1dGetWorkspaceSize得到workspaceSize与executor按需aclrtMallocworkspace 后调用aclnnReplicationPad1d执行同步与回读aclrtSynchronizeStream等待计算完成再用aclrtMemcpy将 device 结果拷回 host 打印资源释放依次aclDestroyTensor/aclDestroyIntArray销毁 tensoraclrtFree释放 device 内存与 workspace最后销毁 stream、reset device 并aclFinalize。测试与验证单元测试与 Python 参考实现仓库为该接口提供了完整的多层级测试支撑可作为功能验证与二次开发的参照单元测试test_aclnn_replication_pad1d.cpp覆盖正常调用2 维/3 维、FLOAT16/FLOAT32/INT32/INT64/INT8/UINT8/DOUBLE 等类型、空 tensorbatch 维为 0 通过其余维度为 0 报错、空指针、padding 长度错误、self 维度超限、self/out 维度不一致、格式非 ND、数据类型不一致、输出 shape 错误等全部校验分支是理解参数约束的最直接材料。Python 参考实现executor_aclnnReplicationPad1d.py通过torch.nn.ReplicationPad1d(padding)生成期望输出padding 传入 int 时会自动扩展为(padding, padding)float16 输入会先转 float32 计算再转回配合 atk_aclnnReplicationPad1d.json 中的用例数据用于算子精度比对可以据此确认接口与 PyTorch 语义的对齐程度。常见问题与排错建议返回 ACLNN_ERR_PARAM_NULLPTR161001检查 self、padding、out 是否已正确创建且非空尤其是aclCreateTensor或aclCreateIntArray失败返回 nullptr 的情况。返回 ACLNN_ERR_PARAM_INVALID161002按错误码表的六类场景逐一排查——数据类型是否在支持范围且 self/out 一致、数据格式是否 ND、self 维度是否为 2~3、padding 长度是否为 2、self 最后两维是否含 0 的空 tensor、out 最后一维是否严格等于self 最后一维 padding[0] padding[1]。结果与预期不符确认填充语义是复制边界值而非常量 0 或镜像反射若需常量填充可使用aclnnConstantPadNd若需反射/对称填充可参考 pad_v3/docs 下的 2d/3d 复制填充接口或直接使用 PadV3 的 REFLECT/SYMMETRIC 模式。产品适配若目标产品为 Atlas 200I/500 A2 推理产品该接口不可用建议评估底层 PadV3 算子或其他填充接口。延伸阅读两段式接口说明理解 GetWorkspaceSize 执行接口的通用范式与 workspace 含义aclnn 返回码说明完整的状态码与错误码定义编译与运行样例示例代码的编译链接与运行方法非连续 Tensor 说明了解非连续输入在接口内部被自动处理为连续的机制pad_v3/README.mdPadV3 四种填充模式constant/edge/REFLECT/SYMMETRIC的语义与 paddings 行/列主序规则aclnnReplicationPad2d 文档、aclnnReplicationPad3d 文档同系列接口的对照参考。【免费下载链接】ops-math本项目是CANN提供的数学类基础计算算子库实现网络在NPU上加速计算。项目地址: https://gitcode.com/cann/ops-math创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考