ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

CANN opbase 算子开发指南:GetViewStrides 接口详解与视图步长获取实战

CANN opbase 算子开发指南:GetViewStrides 接口详解与视图步长获取实战 CANN opbase 算子开发指南GetViewStrides 接口详解与视图步长获取实战【免费下载链接】opbase本项目是CANN算子库的基础框架库为算子提供公共依赖文件和基础调度能力。项目地址: https://gitcode.com/cann/opbase导读GetViewStrides()是 CANN opbase 算子库中aclTensor的核心视图元数据访问接口用于获取张量视图View在每一维度上的步长stride信息。在算子开发过程中步长信息直接决定了算子如何从物理存储中正确索引逻辑视图的元素是切片、转置、广播等视图类算子正确实现的基础。阅读本文后你将掌握GetViewStrides()的函数签名、返回值语义、底层实现原理、与SetViewStrides()/aclGetViewStrides()等接口的配合方式以及它在算子调度、缓存和视图校验等真实场景中的使用方式。接口概览GetViewStrides()定义于 opbase 仓库的公共头文件 common_types.h 中是aclTensor类的公开成员方法。该接口的功能是获取aclTensor的 ViewStrides视图步长即该张量逻辑视图各维度上相邻元素在存储空间中的间距。函数原型FVectorint64_t GetViewStrides()从源码中的实际声明可见common_types.h其精确签名为const op::Strides GetViewStrides() const;其中op::Strides是FVectorint64_t, MAX_DIM_NUM的别名MAX_DIM_NUM在 common_types.h 中被定义为常量 25即张量最大支持 25 维。参数与返回值项目说明参数无返回值一个FVectorint64_t即op::Strides对象依次存储aclTensor视图每个维度的步长值限制无由于返回的是const引用调用方只能读取步长信息若需要修改步长应使用配套的 SetViewStrides 接口。步长Stride与视图View的基本概念在理解GetViewStrides()之前需要先厘清 opbase 中张量的两个关键概念存储Storage与视图View。StorageShape / StorageFormat描述张量在物理存储中的真实形状、内存布局格式如FORMAT_ND、FORMAT_NCHW等私有格式ViewShape / ViewFormat / ViewStrides描述张量的逻辑视图。同一个底层存储可以派生出多个不同的逻辑视图例如通过Reshape、切片、转置等方式视图通过 shape、stride、offset 三元组来刻画访问方式。aclTensor内部将两者分开维护common_types.hmutable int64_t viewOffset_{0}; // 视图首元素相对存储起始的偏移 op::Strides viewStrides_{}; // 视图各维度步长 op::Shape viewShape_{0}; // 视图形状 op::Format viewFormat_; // 视图格式 bool isView_{false}; // 是否为视图GetViewStrides()返回的正是viewStrides_成员。步长的物理含义是在某一维度上移动 1 个逻辑索引时在底层存储中需要跳过的元素个数。底层实现原理GetViewStrides()的实现非常直接位于 common_types.cppconst op::Strides aclTensor::GetViewStrides() const { return viewStrides_; }真正有技术含量的是viewStrides_的初始化时机与来源。从源码结构看viewStrides_在多个构造函数中被赋值主要分两种情况情况一显式传入 stride当通过带stride参数的构造路径创建aclTensor时例如 common_types.cppaclTensor::aclTensor(const int64_t* viewDims, uint64_t viewDimsNum, aclDataType dataType, const int64_t* stride, int64_t offset, const aclFormat format, const int64_t* storageDims, uint64_t storageDimsNum, void* tensorDataAddr) : viewOffset_(offset), viewStrides_(), viewShape_(), viewFormat_(static_castop::Format(format)) { op::ToShape(viewDims, viewDimsNum, viewShape_); if (stride ! nullptr) { viewStrides_.assign(stride, stride viewDimsNum); // 直接拷贝调用方传入的步长数组 } else { op::ToContiguousStrides(viewShape_, viewStrides_); // 否则按连续布局推导 } ... }可见当调用方显式提供 stride 时viewStrides_直接拷贝该数组当 stride 为空指针时系统自动调用op::ToContiguousStrides()按视图形状推导连续步长。情况二自动推导连续步长在大多数不涉及特殊视图的构造函数中如 common_types.cpp、common_types.cppviewStrides_通过op::ToContiguousStrides(viewShape_, viewStrides_)生成。连续步长的推导规则是标准的内存布局计算对形状为[d0, d1, ..., dn-1]的张量其连续步长为[d1*d2*...*dn-1, d2*...*dn-1, ..., 1]即最后一个维度的步长为 1往前依次累乘。该辅助函数同样声明于 common_types.h实现在 common_types.cpp 中。此外InitTensor()common_types.cpp与视图派生构造函数common_types.cpp也遵循同样的两条赋值路径前者用于无参构造后的延迟初始化后者在创建视图时直接沿用源张量的步长viewStrides_(oriStride)。与 C 接口 aclGetViewStrides 的关系GetViewStrides()是 C 层aclTensor的成员方法而对外暴露的 C 风格 API 是aclGetViewStrides()两者的对应关系体现在 acl_op_api.cppaclnnStatus aclGetViewStrides(const aclTensor* tensor, int64_t** stridesValue, uint64_t* stridesNum) { if (tensor nullptr || stridesValue nullptr || stridesNum nullptr) { return ACLNN_ERR_PARAM_NULLPTR; } auto stridesCount tensor-GetViewStrides().size(); *stridesNum stridesCount; *stridesValue new (std::nothrow) int64_t[stridesCount]; CHECK_RET(*stridesValue ! nullptr, ACLNN_ERR_INNER_NULLPTR); for (size_t i 0; i stridesCount; i) { (*stridesValue)[i] tensor-GetViewStrides()[i]; } return OK; }从源码可以确认以下事实aclGetViewStrides()内部直接调用GetViewStrides()将FVector中的步长值逐维拷贝到动态分配的int64_t*数组中入参为空指针时返回ACLNN_ERR_PARAM_NULLPTR对应错误码 161001 场景stridesValue指向的内存由 API 内部new分配调用方使用完毕后必须手动delete[]释放。aclGetViewStrides的完整使用示例含创建张量、读取各属性、基于属性重建张量、手动释放内存的完整流程参见 aclGetViewStrides.md。官方示例与运行说明示例一遍历打印各维度步长原文档示例完整保留如下其作用是获取输入张量的视图步长并逐维打印// Obtain the view stride of the input and print the stride of each dimension in sequence. void Func(const aclTensor *input) { auto strides input-GetViewStrides(); for (int64_t stride : strides) { std::cout stride std::endl; } }这里strides的类型为op::Strides即FVectorint64_t, MAX_DIM_NUM支持基于范围的 for 循环直接遍历。以形状[2, 4]的连续张量为例打印结果为4 1即第一维步长为 4第一维移动 1 个单位存储中跨越 4 个元素第二维步长为 1相邻元素在存储中紧邻。示例二读取并修改步长配合SetViewStrides()即可实现步长的读改写官方SetViewStrides文档中的示例为将最后一维步长放大 8 倍// Multiply the inputs last dimension stride by 8. void Func(const aclTensor *input) { auto strides input-GetViewStrides(); strides[strides.size() - 1] * 8; input-SetViewStrides(strides); }SetViewStrides()提供了两种重载左值引用与右值引用后者在传入临时对象时避免拷贝common_types.cppvoid aclTensor::SetViewStrides(const op::Strides strides) { viewStrides_ strides; } void aclTensor::SetViewStrides(op::Strides strides) { viewStrides_ std::move(strides); }需要特别说明的是SetViewStrides文档指出步长值不能为负数从Validate()的实现见下文可以推断步长取 0 通常意味着该维度发生了广播需要谨慎处理。源码中的真实应用场景GetViewStrides()在 opbase 框架内部被广泛使用是视图语义得以落地的关键。以下场景均有对应源码佐证。场景一连续性判断IsContiguoustensor_view_utils.cpp 中IsContiguous(const aclTensor*)通过比对视图形状与步长判断张量是否内存连续bool IsContiguous(const aclTensor* tensor) { CHECK_RET(tensor ! nullptr, true); if (IsPrivateFormat(tensor-GetStorageFormat())) { return true; } const auto viewShape tensor-GetViewShape(); if (viewShape.GetShapeSize() 0 || viewShape.GetShapeSize() 1) { return true; } return IsContiguous(viewShape, tensor-GetViewStrides()); }核心算法tensor_view_utils.cpp从最后一维向前校验期望步长从 1 开始逐维乘以形状若某维步长与期望不符且该维大小不为 1则判定为非连续inline bool IsContiguous(const Shape shape, const Strides strides) { int64_t validStride 1; for (int64_t i static_castint64_t(strides.size()) - 1; i 0; --i) { if (shape[i] 1) { continue; } if (validStride ! strides[i]) { return false; } validStride * shape[i]; } return true; }场景二视图合法性校验Validatetensor_view_utils.cpp 中Validate()利用 ViewShape、ViewStrides 与 ViewOffset 三者共同校验视图是否越界要求viewShape的维度数与viewStrides的长度一致否则报ACLNN_ERR_PARAM_INVALID并提示 ViewShape and ViewStride mismatch.计算视图在存储中覆盖的最大/最小偏移范围若超出存储大小或出现负偏移则判定为 ViewShape overlap.。这意味着步长信息的正确性直接影响算子能否安全执行错误的步长可能导致越界访问。场景三视图优化决策CanPickViewAsContiguoustensor_view_utils.cpp 中CanPickViewAsContiguous()分析视图步长模式是否含转置、是否含广播判断能否将视图按连续张量处理以走优化路径。步长为 0 且维度大小不为 1 时判定为广播mayBroadcast true此时不能按连续处理同时它还提供了多张量版本要求多个输入张量的 ViewShape 与 ViewStrides 完全一致。场景四算子执行与缓存在 indv_executor_tensor.cpp 中单个算子执行时通过in_tensor-GetViewStrides()获取输入步长用于构造算子参数与内核下发在 op_cache.cpp 与 indv_cache_key_builder.cpp 中步长信息参与算子缓存键cache key的构建与比较——步长不同的张量视图不能复用同一份缓存因为其数据访问模式已发生变化。场景五视图工具辅助函数common_types.cpp 的ToString()输出中会携带view_strides ...字段便于调试时打印张量视图信息。测试验证opbase 的单元测试 test_common_types.cpp 对视图步长行为进行了系统性验证覆盖以下关键断言EXPECT_EQ(a.GetViewStrides(), op::Strides({1})); // 标量/单元素视图步长为 {1} EXPECT_EQ(a.GetViewStrides(), op::Strides({2, 1})); // 二维连续视图步长 EXPECT_EQ(a.GetViewStrides(), op::Strides({8, 2, 1})); // 三维连续视图步长 a.SetViewStrides(strides1); EXPECT_EQ(a.GetViewStrides(), strides1); // 显式设置后读取一致 // 视图派生dst 沿用 src 的步长 aclTensor* dst exe.CreateView(src, Shape({16, 16, 16}), Shape({17, 17, 17}), src.GetViewStrides(), 0); EXPECT_EQ(dst-GetViewStrides(), src.GetViewStrides());从测试可以确认默认构造的连续张量其步长严格遵循连续布局规则末维为 1向前累乘SetViewStrides设置的步长可通过GetViewStrides原样读回派生视图默认继承源张量的步长。使用注意事项只读语义GetViewStrides()返回 const 引用用于读取。需要修改步长时必须显式调用 SetViewStrides且步长值不能为负数维度一致性步长数组的长度必须与GetViewShape()返回的视图维度数一致否则在Validate()校验时会报错可通过GetViewShape()接口参见 GetViewShape获取视图形状进行对照步长 0 的语义在视图场景中步长为 0 通常表示该维度进行了广播相关处理逻辑可参考 tensor_view_utils.cpp与连续步长的差异只有连续布局如FORMAT_ND下未切片、未转置的张量的步长才严格等于连续推导值Reshape、转置、切片等操作产生的视图步长可能不连续这正是需要通过GetViewStrides()显式获取的原因C 接口内存管理若通过 aclGetViewStrides 获取步长返回的int64_t*数组由 API 内部分配使用后必须手动delete[]否则会造成内存泄漏维度上限op::Strides的容量上限为MAX_DIM_NUM 25维超出该上限的视图无法用此类型表示参见 common_types.h。总结GetViewStrides()虽是一个轻量级的访问器但它是 opbase 视图View机制的数据出口之一张量在创建时通过显式 stride 或ToContiguousStrides()推导完成步长初始化算子开发与框架内部则通过该接口读取步长完成连续性判断、越界校验、优化路径决策、缓存键构建与内核参数构造。掌握该接口及其配套的SetViewStrides()、GetViewShape()、aclGetViewStrides()等接口是编写正确处理视图类算子的前提。【免费下载链接】opbase本项目是CANN算子库的基础框架库为算子提供公共依赖文件和基础调度能力。项目地址: https://gitcode.com/cann/opbase创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表