CANN ops-math StridedSlice 算子实战解析:begin/end/strides 子张量提取与掩码机制、aclnn 调用与源码实现
CANN ops-math StridedSlice 算子实战解析begin/end/strides 子张量提取与掩码机制、aclnn 调用与源码实现【免费下载链接】ops-math本项目是CANN提供的数学类基础计算算子库实现网络在NPU上加速计算。项目地址: https://gitcode.com/cann/ops-mathStridedSlice 是 CANN ops-math 数学算子库项目主页conversion目录下提供的切片算子核心功能是按照指定的起始位置 begin、结束位置 end 与步长 strides从输入张量中提取一个子张量。本文以 conversion/strided_slice/README.md 为骨架结合仓库内的算子定义、形状推导、Tiling 与 Kernel 源码、aclnn 接口文档及完整示例系统讲解该算子的参数语义、五种掩码的位运算规则、两段式 aclnn 调用与图模式调用方式并深入剖析其在 NPU 上的落地实现帮助读者在 Ascend 平台上正确、高效地使用 StridedSlice。一、功能说明一次切片背后的完整语义按 conversion/strided_slice/README.md 的功能说明StridedSlice 算子按照指定的起始、结束位置和步长从输入张量中提取一个子张量。它与 NumPy / TensorFlow 中形如x[begin:end:stride]的切片操作语义一致是网络模型中x[:, 1:3, ...]这类高频切片表达式在 NPU 上的底层算子。配套接口文档 conversion/strided_slice/docs/aclnnStridedSlice.md 给出了更精确的数学描述在指定维度dim上按照起始位置begin、结束位置end和步长strides从输入张量self中提取子张量out。begin和end可以取[0, self.shape[dim]]以外的值包括负值取值后按以下规则规范化为合法值设self.shape[dim] Nbegin 0 若 begin -N begin N 若 begin N begin (beginN)%N 否则 end N 若 end N end begin 否则若 (endN)%N begin end (endN)%N 否则输出张量out的形状与self仅在dim轴上不一致其余轴保持一致out.shape[dim] floor((end - begin strides - 1) / strides)可以看到begin 支持负索引负值按(beginN)%N换算为正索引等价于 Python 中x[-1]表示倒数第一个元素end 支持越界值end N时按N处理等价于切片到末尾输出维度的计算采用向上取整(end - begin strides - 1) / strides的取整方式保证了步长不能整除区间长度时仍能取到最后一个元素。上述数学公式与 strided_slice_util.h 中BuildProcessingData的canonical逻辑相互印证源码在stride_i 0时把越界 begin/end 裁剪进[0, dim_i]并把负索引x 0 ? dim_i x : x换算为正索引。二、产品支持情况按 README 的“产品支持情况”表StridedSlice 算子在当前仓库中支持以下产品产品是否支持Ascend 950PR/Ascend 950DT√Atlas A3 训练系列产品/Atlas A3 推理系列产品√Atlas A2 训练系列产品/Atlas A2 推理系列产品√Atlas 200I/500 A2 推理产品√Atlas 推理系列产品√Atlas 训练系列产品√需要特别区分两层含义算子本身含 AICore Kernel、AICPU Kernel支持上表全部产品aclnn 单算子接口aclnnStridedSlice在产品支持上更严格按 aclnnStridedSlice.md 的说明当前仅 Ascend 950PR/Ascend 950DT 支持该接口其余产品“不支持”是指 aclnn 接口不可用。这一区别也体现在算子定义的 AICore 配置中见 strided_slice_def.cppOpAICoreConfig只向ascend950与ascend350两个平台注册了配置并启用了动态编译DynamicCompileStaticFlag(true)、动态 RankDynamicRankSupportFlag(true)与动态 ShapeDynamicShapeSupportFlag(true)支持。三、参数说明READMEA 给出了完整参数表整理如下数据类型与数据格式均以仓库实际声明为准参数名输入/输出/属性描述数据类型数据格式x输入输入的张量INT8、UINT8、INT16、UINT16、INT32、UINT32、INT64、UINT64、FLOAT、FLOAT16、BF16、BOOL、COMPLEX32、COMPLEX64、HIFLOAT8、FLOAT8_E5M2、FLOAT8_E4M3FNNDbegin输入每个维度的起始值同上注aclnn 接口中为 INT32、INT64NDend输入每个维度的结束值同上NDstrides输入每个维度上每个点取值的跨度同上NDbegin_mask属性指定哪些维度的 begin 被忽略INT32、INT64NDend_mask属性指定哪些维度的 end 被忽略INT32、INT64NDellipsis_mask属性从指定的维度开始全选直到遇到用户指定 begin 才退出INT32、INT64NDnew_axis_mask属性在指定位置增加维度为 1 的 shapeINT32、INT64NDshrink_axis_mask属性把对应索引处维度强制降为 1INT32、INT64NDy输出输出张量与 x 一致ND需要补充的几个关键实现细节begin/end/strides 的索引数据类型图模式GE IR下三者使用IndexNumberType即 int32/int64aclnn 接口中同样为 INT32/INT64且要求三者数组长度一致strides中不能出现 0。这些约束在 strided_slice_proto.h 与 aclnn 接口文档的参数表中均有体现。输入张量 x 的维度上限aclnn 接口要求self的 shape 为0-8 维Tiling 侧也通过MAX_AXIS_NUM_FOR_STRIDESLICE 8常量限制最大轴数见 strided_slice_tiling_arch35.h。掩码默认值五个掩码属性默认均为 0见 strided_slice_def.cpp 中的Attr(begin_mask).AttrType(OPTIONAL).Int(0)等声明。确定性计算aclnnStridedSlice默认采用确定性实现见 aclnnStridedSlice.md 约束说明。四、五种掩码的位语义与组合规则StridedSlice 的核心难点在于五个掩码属性。它们都是按位bit生效的整数bit i 为 1 表示对第 i 个索引位置生效掩码位语义典型用途begin_maskbit i 1 时第 i 维的 begin 被忽略改用该维度的最大合法区间起点即 0表达x[:3]这类省略 begin 的切片end_maskbit i 1 时第 i 维的 end 被忽略改用该维度的最大合法区间终点即维度大小表达x[1:]这类省略 end 的切片ellipsis_maskbit i 1 时从第 i 个索引位置开始全选后续维度直到遇到用户显式指定的 begin 才退出表达x[..., 3:]中的...new_axis_maskbit i 1 时在第 i 个索引位置增加一个大小为 1 的新维度表达x[None, ...]的扩维操作shrink_axis_maskbit i 1 时第 i 维强制降为 1该维度从最终 shape 中收缩掉表达x[3]这类取单个元素并降维的操作组合规则与约束ellipsis_mask 只能有一个 bit 为 1源码 strided_slice_util.h 中通过(ellipsis_mask (ellipsis_mask - 1)) ! 0判定“多个省略号”若出现多个 ellipsis 会直接报错 “Multiple ellipses in slice spec not allowed.”。shrink_axis_mask 对应的 strides 必须为正bit 位为 1 的索引对应维度只允许stride 1的正向取值源码 strided_slice_util.h 中if (shrink_i stride_i 0)会报错 “only stride 1 allowed on non-range indexing.”。掩码是稀疏索引五个掩码作用于“稀疏规范”sparse spec的索引位置源码内部会先将其展开为与输入维度一一对应的“稠密规范”dense spec再参与形状计算。以 strided_slice_util.h 中的注释为例对foo.shape(2,2,3)执行foo[..., 3:]时稀疏掩码begin_mask0, end_mask2会被展开为稠密掩码begin_mask6, end_mask7即为缺失的前两个维度自动补上“全选”语义。掩码参与形状推导的完整链路从 strided_slice_util.h 的InferShape实现可以看出掩码的作用被组织为清晰的四步流水BuildSparseSpec统计 dims 数量、ellipsis 出现位置、ellipsis 之后新增轴的数量若全程没有 ellipsis则在末尾隐式补一个等价于 Python 中省略号可出现在任意位置。BuildDenseSpec把稀疏规范展开为与输入秩一致的稠密规范同时生成final_shape_gather_indices向量记录最终 shape 的每个维度取自哪里普通维取区间长度、new_axis产生 1、shrink维被跳过。BuildProcessingData把掩码隐式区间显式化对每个维度做 begin/end 规范化与越界裁剪并对stride 0报错同时统计is_identity全维取整段与is_simple_slicestride 全为 1等优化标记供后续 Kernel 路径选择。BuildFinalShape依据final_shape_gather_indices组装最终输出 shape并回填规范化后的 begin/end/strides 与收缩维信息。这套逻辑与 TensorFlow 的 StridedSlice 实现同源strided_slice_util.h 文件头声明其代码片段来自 TensorFlow 项目因此迁移到 NPU 的算子在切片语义上能与 TensorFlow 保持严格一致。五、调用方式一aclnn 两段式接口调用5.1 两段式接口CANN 算子库的 aclnn 接口采用两段式设计详见接口文档 aclnnStridedSlice.md必须先调用aclnnStridedSliceGetWorkspaceSize获取计算所需 workspace 大小以及包含算子计算流程的执行器executor再调用aclnnStridedSlice执行计算。// 第一段获取 workspace 大小与执行器 aclnnStatus aclnnStridedSliceGetWorkspaceSize( const aclTensor *self, const aclIntArray *begin, const aclIntArray *end, const aclIntArray *strides, int64_t beginMask, int64_t endMask, int64_t ellipsisMask, int64_t newAxisMask, int64_t shrinkAxisMask, aclTensor *out, uint64_t *workspaceSize, aclOpExecutor **executor); // 第二段执行计算 aclnnStatus aclnnStridedSlice( void *workspace, uint64_t workspaceSize, aclOpExecutor *executor, aclrtStream stream);5.2 第一段接口参数说明GetWorkspaceSize参数名输入/输出描述使用说明数据类型数据格式维度(shape)非连续Tensorself输入输入的张量self 与 out 数据类型一致INT8、UINT8、INT16、UINT16、INT32、UINT32、INT64、UINT64、FLOAT、FLOAT16、BF16、BOOL、COMPLEX32、COMPLEX64、HIFLOAT8、FLOAT8_E5M2、FLOAT8_E4M3FNND0-8√begin输入每个维度的起始值begin/end/strides 数组长度需一致INT32、INT64---end输入每个维度的结束值begin/end/strides 数组长度需一致INT32、INT64---strides输入每个维度上每个点取值的跨度数组长度需一致strides 值不能有 0INT32、INT64---beginMask输入指定 bit 位为 1 对应的索引维度的 begin 被忽略-INT64---endMask输入指定 bit 位为 1 对应的索引维度的 end 被忽略-INT64---ellipsisMask输入从 bit 位为 1 对应的索引维度开始全选直到遇到指定 begin 才退出ellipsisMask 只能有一个 bit 位为 1INT64---newAxisMask输入把 bit 位为 1 对应的索引维度增加维度为 1 的 shape-INT64---shrinkAxisMask输入把 bit 位为 1 对应的索引维度强制降为 1shrinkAxisMask 中 bit 位为 1 的索引对应的 strides 需要大于 0即正数INT64---out输出输出张量self 与 out 数据类型一致与 self 一致ND-√workspaceSize输出返回需要在 Device 侧申请的 workspace 大小-----executor输出返回 op 执行器包含了算子计算流程-----5.3 返回值与错误码aclnnStridedSliceGetWorkspaceSize第一段接口完成入参校验出现以下场景时报错返回值错误码描述ACLNN_ERR_PARAM_NULLPTR161001传入的 self、begin、end、strides 或 out 是空指针ACLNN_ERR_PARAM_INVALID161002self 或 out 的数据类型不在支持范围之内ACLNN_ERR_PARAM_INVALID161002self 或 out 的数据类型不一致ACLNN_ERR_PARAM_INVALID161002self 的维度大于 8 维ACLNN_ERR_PARAM_INVALID161002begin、end、strides 的长度不一致ACLNN_ERR_PARAM_INVALID161002strides 存在等于 0 的元素ACLNN_ERR_PARAM_INVALID161002out 的数据维度与 inferShape 的维度不相同ACLNN_ERR_PARAM_INVALID161002产品型号不在支持的范围之内ACLNN_ERR_PARAM_INVALID161002ellipsisMask 不止有一个 bit 位为 1ACLNN_ERR_PARAM_INVALID161002shrinkAxisMask 中 bit 位为 1 的索引对应的 strides 小于 05.4 完整调用示例以下示例来自 conversion/strided_slice/examples/arch35/test_aclnn_strided_slice.cpp接口文档 aclnnStridedSlice.md 中亦附有同样代码。场景为对 shape 为{4, 3}的 FLOAT 张量执行x[1:3, 1:3]切片输出 shape 为{2, 2}即取中间 2×2 子块#include iostream #include vector #include acl/acl.h #include aclnnop/aclnn_strided_slice.h #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 shapeSize 1; for (auto i : shape) { shapeSize * i; } return shapeSize; } int Init(int32_t deviceId, aclrtStream* stream) { // 固定写法初始化 acl 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); // 申请 device 侧内存并拷贝 host 数据 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); 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]; } // 创建 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 初始化 int32_t deviceId 0; aclrtStream stream; auto ret Init(deviceId, stream); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(Init acl failed. ERROR: %d\n, ret); return ret); // 2. 构造输入与输出self shape {4,3}切片 x[1:3, 1:3]out shape {2,2} std::vectorint64_t selfShape {4, 3}; std::vectorint64_t outShape {2, 2}; void* selfDeviceAddr nullptr; void* outDeviceAddr nullptr; aclTensor* self nullptr; aclIntArray* begin nullptr; aclIntArray* end nullptr; aclIntArray* strides nullptr; aclTensor* out nullptr; std::vectorfloat selfHostData {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; std::vectorint64_t beginData {1, 1}; // 每维起始 std::vectorint64_t endData {3, 3}; // 每维结束不含 std::vectorint64_t stridesData {1, 1}; // 每维步长 std::vectorfloat outHostData(4, 0); int64_t beginMask 0; int64_t endMask 0; int64_t ellipsisMask 0; int64_t newAxisMask 0; int64_t shrinkAxisMask 0; ret CreateAclTensor(selfHostData, selfShape, selfDeviceAddr, aclDataType::ACL_FLOAT, self); CHECK_RET(ret ACL_SUCCESS, return ret); ret CreateAclTensor(outHostData, outShape, outDeviceAddr, aclDataType::ACL_FLOAT, out); CHECK_RET(ret ACL_SUCCESS, return ret); begin aclCreateIntArray(beginData.data(), 2); CHECK_RET(begin ! nullptr, return ret); end aclCreateIntArray(endData.data(), 2); CHECK_RET(end ! nullptr, return ret); strides aclCreateIntArray(stridesData.data(), 2); CHECK_RET(strides ! nullptr, return ret); // 3. 两段式调用 uint64_t workspaceSize 0; aclOpExecutor* executor; // 第一段获取 workspace 大小与执行器 ret aclnnStridedSliceGetWorkspaceSize(self, begin, end, strides, beginMask, endMask, ellipsisMask, newAxisMask, shrinkAxisMask, out, workspaceSize, executor); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclnnStridedSliceGetWorkspaceSize failed. ERROR: %d\n, ret); return ret); // 按需申请 device 侧 workspace 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 aclnnStridedSlice(workspaceAddr, workspaceSize, executor, stream); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclnnStridedSlice 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. 拷贝结果回 host 并打印 auto size GetShapeSize(outShape); std::vectorfloat resultData(size, 0); ret aclrtMemcpy(resultData.data(), resultData.size() * sizeof(resultData[0]), outDeviceAddr, size * sizeof(resultData[0]), 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 / aclIntArray aclDestroyTensor(self); aclDestroyIntArray(begin); aclDestroyIntArray(end); aclDestroyIntArray(strides); aclDestroyTensor(out); // 7. 释放 device 资源 aclrtFree(selfDeviceAddr); aclrtFree(outDeviceAddr); if (workspaceSize 0) { aclrtFree(workspaceAddr); } aclrtDestroyStream(stream); aclrtResetDevice(deviceId); aclFinalize(); return 0; }以self [1..12]shape{4,3}为例x[1:3, 1:3]的输出应为[5, 6, 8, 9]第 1、2 行 × 第 1、2 列。运行前需在设备上配置好 CANN 环境按仓库 docs/zh 编译运行指南 与 examples 说明 编译并链接libaclnn相关库该示例对应 arch35Ascend 950 系列平台。六、调用方式二图模式GE IR调用除 aclnn 接口外StridedSlice 还支持通过图模式GE 构图调用示例见 examples/test_geir_strided_slice.cpp。图模式基于算子 IR 定义构图IR 定义位于 strided_slice_proto.hREG_OP(StridedSlice) .INPUT(x, TensorType({BasicType(), DT_HIFLOAT8, DT_FLOAT8_E5M2, DT_FLOAT8_E4M3FN})) .INPUT(begin, TensorType::IndexNumberType()) .INPUT(end, TensorType::IndexNumberType()) .INPUT(strides, TensorType::IndexNumberType()) .ATTR(begin_mask, Int, 0) .ATTR(end_mask, Int, 0) .ATTR(ellipsis_mask, Int, 0) .ATTR(new_axis_mask, Int, 0) .ATTR(shrink_axis_mask, Int, 0) .OUTPUT(y, TensorType({BasicType(), DT_HIFLOAT8, DT_FLOAT8_E5M2, DT_FLOAT8_E4M3FN})) .OP_END_FACTORY_REG(StridedSlice)该 IR 声明了四个输入x、begin、end、strides、五个Int类型属性默认值均为 0与一个输出y其中begin/end/strides使用IndexNumberTypeint32/int64。IR 注释明确说明该算子兼容 TensorFlow 的 StridedSlice 算子“Compatible with the TensorFlow operator StridedSlice.”这也解释了为什么图模式可以作为 TF 模型迁移到 NPU 时的直接映射目标。七、源码级实现剖析从定义到 NPU Kernel7.1 算子定义OpDefstrided_slice_def.cpp 通过OpDef注册算子x/y 数据类型覆盖 INT8FLOAT8_E4M3FN 共 17 种类型含 BOOL、COMPLEX32/64、HIFLOAT8、FLOAT8_E5M2、FLOAT8_E4M3FN与 README 参数表完全一致begin/end/strides 数据类型限定为 INT32/INT64并标记ValueDepend(OPTIONAL)表示这三个输入以常量值参与形状推导数据格式输入输出统一为FORMAT_NDAICore 配置向ascend950、ascend350注册配置开启动态编译、动态 Rank、动态 Shape并指定opFile.value strided_slice_apt指向 Kernel 实现。7.2 形状推导InferShapestrided_slice_infershape.cpp 实现了形状推导入口InferShape4StridedSlice读取x/begin/end/strides的 shape 与常量 tensor取三者 shape 的最大长度shape_maxCalcMaxShapeSize通过GetValueList按 INT32/INT64 读出 begin/end/strides 的常量值校验 strides 有效性begin/end缺失时按 strided_slice_util.h 的MakeParamSameLen规则补齐begin 补 0end 补输入各维大小读取五个掩码属性填充StridedSliceParams后调用InferShape即第四节的四步流水得到输出 shape通过.InputsDataDependency({IDX_BEGIN, IDX_END, IDX_STRIDES})声明三个输入参与推导从而在动态 Shape 场景下也能得到精确的输出维度。形状推导细节值得注意的边界处理BuildProcessingData中针对stride 为负的场景倒序切片valid_range取[-1, dim_i-1]mask 生效时 begin 取-1、end 取dim_i-1从而支持x[::-1]这类逆序切片。7.3 Tiling 与多路径 Kernel切片在 NPU 上的性能高度依赖数据搬移方式因此 Tiling 阶段会根据切片形态选择不同实现路径。从 strided_slice_tiling_arch35.h 可以看到 Tiling Key 的设计Tiling Key路径适用形态100MoveAlign搬移对齐常规跨步切片101MoveAlignLastDim末维对齐末维步长 1 的高效搬移102/103NDDMA多维 DMA多维连续搬移、末维搬移150MoveAlignTwoDim两维搬移倒数两个维度连续搬移200/201SIMT / SIMT 大 Shape步长不规则、gather 类切片300/301MoveAlignGather / UB2UB负步长等需 gather 或 UB 中转的场景302/303NDDMAGather / NDDMA UB2UB负步长 NDDMA 场景Tiling 侧还针对负 strides倒序切片设置了独立的拆分逻辑SetTilingModeNeg、CalMaxSplitDimNeg等私有方法并引入DATA_SPARSITY_THRESHOLD等阈值来决定走搬移路径还是 SIMT 逐元素路径见 strided_slice_tiling_arch35.h。Kernel 侧对应地提供了 strided_slice_base.h、strided_slice_move_align.h、strided_slice_nddma.h、strided_slice_simt.h 等实现文件。7.4 AICPU 实现与编译配置AICPU 兜底仓库还提供了 AICPU 实现 strided_slice_aicpu.cpp 及配套定义 strided_slice_aicpu_def.cpp覆盖 CPU 侧执行路径二进制生成配置ascend950/strided_slice_binary.json 描述了算子二进制生成规则其中x/y的dtype_match_mode为DtypeByte按字节宽分组生成二进制begin/end/strides按 int32/int64 两个变体展开shape为-2表示动态 Shape支持任意形状TTK Kernel 测试tests/st/arch35/ttk_kernel_strided_slice_st.csv 提供了 Kernel 级测试用例矩阵。八、框架兼容TensorFlow 与 ONNX 接入StridedSlice 在 framework 目录下提供了两个框架插件strided_slice_tf_plugin.cpp通过REGISTER_CUSTOM_OP(StridedSlice).FrameworkType(TENSORFLOW).OriginOpType(StridedSlice)注册将 TensorFlow 的StridedSlice算子自动映射AutoMappingByOpFn到本算子实现 TF 模型到 NPU 的无缝迁移npu_indexing_onnx_plugin.cpp面向 ONNX 框架的索引类算子转换适配。这两份插件的存在印证了 IR 注释中“兼容 TensorFlow StridedSlice”的设计目标也说明该算子在框架迁移链路上同时覆盖 TF 与 ONNX 两大生态。九、约束说明与使用建议按 conversion/strided_slice/README.md算子自身的“约束说明”为无但从接口文档与源码中可以提炼出以下实际约束供调用方规避常见错误strides 不能为 0aclnn 接口报 161002图模式在BuildProcessingData中显式报 “strides[i] must be non-zero”begin/end/strides 长度必须一致ellipsis 最多一个ellipsis_mask只能有一个 bit 为 1多省略号报错shrink 维的 strides 必须为正shrink_axis_mask对应索引的 strides 需大于 0维度上限aclnn 接口要求 self 为 0-8 维Tiling 侧MAX_AXIS_NUM_FOR_STRIDESLICE 8负步长可用支持x[::-1]倒序切片但走 gather/UB2UB 等专用路径aclnn 接口产品范围aclnnStridedSlice当前仅 Ascend 950 系列产品支持算子本身支持全部列出的产品系列。十、总结StridedSlice 算子以begin/end/strides三元组完成任意维度的步进取样并通过五个位掩码覆盖了 Python 切片语法中的省略ellipsis、扩维new axis、降维shrink等全部语义。在 CANN ops-math 仓库中它由算子 IR 定义、OpDef 注册、四步形状推导、多路径 Tiling/Kernel 与 AICPU 兜底共同构成完整闭环同时通过 TF/ONNX 插件与上游框架生态打通。读者可通过 conversion/strided_slice/README.md、aclnnStridedSlice.md 与 examples 直接上手验证也可以深入 strided_slice_util.h 与 strided_slice_tiling_arch35.h 了解其与 TensorFlow 同源的实现细节。【免费下载链接】ops-math本项目是CANN提供的数学类基础计算算子库实现网络在NPU上加速计算。项目地址: https://gitcode.com/cann/ops-math创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考