CANN opbase 算子开发指南:aclTensor::SetStorageFormat 接口详解与 StorageFormat 内存排布设置
【免费下载链接】opbase本项目是CANN算子库的基础框架库,为算子提供公共依赖文件和基础调度能力。项目地址: https://gitcode.com/cann/opbase
导读
本文聚焦 CANN/opbase 算子库中aclTensor对象的核心元数据接口之一 ——SetStorageFormat,讲解如何显式设置张量在设备内存中的排布格式(StorageFormat),并深入源码说明该接口与op::Tensor底层存储格式属性之间的调用关系。阅读本文后,你将掌握op::Format(即ge::Format)枚举的取值约定、aclTensor的 storage/origin/view 三层格式视图的区别,以及如何在算子开发与单测中正确设置和校验张量存储格式。
一、StorageFormat:张量在内存中的排布格式
在算子开发过程中,张量具备逻辑维度(如 NCHW 四维语义)与物理存储排布(数据在内存中的实际存放顺序)两层信息。StorageFormat描述的正是后者 ——aclTensor 在内存中的排布格式,例如NCHW、ND、NHWC、NC1HWC0、FRACTAL_NZ等。它决定了算子读取输入、写入输出时如何根据多维索引计算物理内存偏移。
在 common_types.h 中可以看到,op命名空间对底层类型做了别名统一:
namespace op { using Format = ge::Format; using Tensor = gert::TensorV2; using StorageFormat = gert::StorageFormat; using StorageShape = gert::StorageShape; }其中using Format = ge::Format;明确说明SetStorageFormat的入参类型op::Format本质就是ge::Format枚举。
1.1 aclTensor 的三层格式视图
从 common_types.h 的接口声明可以看到,aclTensor对格式的管理分为三个维度,分别对应三对 Get/Set 接口:
| 格式视图 | Get 接口 | Set 接口 | 语义 |
|---|---|---|---|
| StorageFormat | GetStorageFormat() | SetStorageFormat() | 张量在内存中的实际排布格式 |
| OriginalFormat | GetOriginalFormat() | SetOriginalFormat() | 张量原始的逻辑排布格式(用户视角) |
| ViewFormat | GetViewFormat() | SetViewFormat() | 当前视图(view)下的排布格式 |
StorageFormat是其中最贴近硬件与底层框架的一层。原始格式(Origin)通常与存储格式一致,但当发生格式转换(如插入 format transform 算子)时,二者就会出现差异;而视图格式(View)则用于描述Reshape、切片等视图操作后张量呈现的格式。
1.2 op::Format 枚举的常见取值
op::Format枚举包含多种格式。结合 format_utils.h 中ToAclFormat的可转换列表,常见取值包括:
| 枚举值 | 说明 |
|---|---|
FORMAT_ND | N 维通用格式,无特定排布要求 |
FORMAT_NCHW | 通道在前(C 维按序排布)的四维格式 |
FORMAT_NHWC | 通道在最后(C 维不连续)的四维格式 |
FORMAT_NC1HWC0 | Ascend 硬件典型的 5 维分形格式,C 维拆分为 C1/C0 |
FORMAT_FRACTAL_Z/FORMAT_FRACTAL_NZ | 面向特定硬件算子(如矩阵运算)的分形排布格式 |
FORMAT_HWCN | 与 NCHW 相反的排布 |
FORMAT_NDHWC/FORMAT_NCDHW | 5 维数据的 NHWC / NCDHW 排布 |
FORMAT_NC、FORMAT_NCL | 二维 / 三维的通道格式 |
此外,format_utils.h 还提供了GetPrimaryFormat、GetSubFormat、HasSubFormat、GetFormatFromSub等工具函数,用于解析带子格式(sub format)与 C0 信息的复合格式编码,说明 Format 枚举值在底层可能是主格式与子格式的位组合。
二、函数原型与参数说明
2.1 函数原型
接口声明位于 common_types.h:
op::Format GetStorageFormat() const; void SetStorageFormat(op::Format format);2.2 参数说明
| 参数 | 输入/输出 | 说明 |
|---|---|---|
format | 输入 | 数据类型为op::Format(即ge::Format),是一个枚举,包含多种不同的 Format,例如NCHW、ND、NHWC、NC1HWC0、FRACTAL_NZ等。 |
2.3 返回值说明
无返回值(void)。
2.4 约束说明
接口本身无额外约束。但从实现看,SetStorageFormat只更新元数据中记录的格式信息,不会触发实际的数据重排。若将 StorageFormat 设置为与物理内存实际排布不一致的值,会导致后续算子按错误的索引计算读取数据,因此调用方必须保证设置值与该张量真实的内存排布一致。
三、调用示例:将 StorageFormat 置为 ND 格式
原文档给出的核心用法是将aclTensor的存储格式显式置为ND格式:
// 将input的Storage Format置为ND格式 void Func(const aclTensor *input) { input->SetStorageFormat(ge::FORMAT_ND); }ge::FORMAT_ND与op::Format::FORMAT_ND指向同一枚举值(因op::Format即ge::Format),因此在算子实现中也可写作:
void SetToFractalNz(const aclTensor *input) { input->SetStorageFormat(ge::FORMAT_FRACTAL_NZ); }典型的应用场景包括:
- 自定义算子 Tiling 前,将输入张量标记为硬件友好的分形格式(如
FORMAT_FRACTAL_NZ)以匹配算子 kernel 的读取方式; - 在
Reshape、Transpose等算子中,显式修正输出张量的存储格式元数据; - 构造用于单测的张量时,模拟指定内存排布,验证格式相关逻辑分支。
四、源码级实现解析:SetStorageFormat 的调用链
4.1 接口实现
SetStorageFormat的实现位于 common_types.cpp:
void aclTensor::SetStorageFormat(op::Format format) { tensor_->SetStorageFormat(format); }可以看到,aclTensor内部持有一个op::Tensor(即gert::TensorV2)指针tensor_,SetStorageFormat是薄封装,将调用直接转发给底层张量的SetStorageFormat方法,写入其StorageFormat属性。与之对应的读取接口在 common_types.cpp:
op::Format aclTensor::GetStorageFormat() const { return tensor_->GetFormat().GetStorageFormat(); }Get/Set一一对应:写入通过tensor_->SetStorageFormat(),读取通过tensor_->GetFormat().GetStorageFormat(),二者操作的是同一个存储格式字段。
4.2 构造阶段对 StorageFormat 的初始化
从源码看,aclTensor在多种构造路径中都会把传入的storageFormat写入底层gert::StorageFormat对象。例如 common_types.cpp:
aclTensor::aclTensor(const op::Shape& storageShape, const op::Shape& originShape, op::DataType dataType, op::Format storageFormat, op::Format originFormat, void* tensorDataAddr) { ... op::StorageFormat gertFormat; gertFormat.SetStorageFormat(storageFormat); gertFormat.SetOriginFormat(originFormat); ... tensor_ = new (base) op::Tensor(gertShape, gertFormat, op::TensorPlacement::kOnDeviceHbm, dataType, tensorDataAddr); }类似的初始化逻辑还出现在其他构造函数中(见 common_types.cpp 与 common_types.cpp)。这从侧面印证:aclTensor创建时即可同时指定存储格式与原始格式,SetStorageFormat则用于在张量创建后按需修改存储格式元数据。
4.3 私有格式校验
在 common_types.cpp 中可以看到,aclTensor的视图构造路径会对私有格式(private format)做警告检查:
OP_CHECK(!op::IsPrivateFormat(viewFormat_), OP_LOGW("the private format[%s], should not use this constructor.", op::ToString(viewFormat_).GetString()), ;);这提示开发者:并非所有 Format 枚举值都适合直接用于公开构造或设置路径,私有格式(如带特定 C0 拆分的内部格式)应通过专门的框架机制处理。
五、配套接口与格式视图协同使用
SetStorageFormat通常与aclTensor的其他格式接口配合使用,以完整刻画张量的格式信息(声明见 common_types.h):
op::Format GetStorageFormat() const; void SetStorageFormat(op::Format format); op::Format GetOriginalFormat() const; void SetOriginalFormat(op::Format format); op::Format GetViewFormat() const; void SetViewFormat(op::Format format);对应的实现(common_types.cpp):
op::Format aclTensor::GetStorageFormat() const { return tensor_->GetFormat().GetStorageFormat(); } op::Format aclTensor::GetOriginalFormat() const { return tensor_->GetFormat().GetOriginFormat(); } op::Format aclTensor::GetViewFormat() const { return viewFormat_; } void aclTensor::SetStorageFormat(op::Format format) { tensor_->SetStorageFormat(format); } void aclTensor::SetOriginalFormat(op::Format format) { tensor_->SetOriginFormat(format); } void aclTensor::SetViewFormat(op::Format format) { viewFormat_ = format; }从实现差异可以看出设计意图:
- StorageFormat / OriginalFormat存储在底层
gert::TensorV2的格式对象中,随张量持久化,是算子框架真正消费的元数据; - ViewFormat存储在
aclTensor自身的viewFormat_成员中,服务于视图层逻辑,viewFormat_的初值在构造时取自originFormat(见 common_types.cpp)。
因此,如果目标是影响算子调度与内存访问,应设置的是SetStorageFormat;而仅表达视图语义的格式偏好,则使用SetViewFormat。
六、测试验证:仓库中对该接口的实际使用
仓库的测试代码直接印证了SetStorageFormat的典型用法:
- 在 composite_op 单测 test_common_types.cpp 中,将张量存储格式设置为分形格式:
a.SetStorageFormat(Format::FORMAT_FRACTAL_NZ);- 同样用法出现在 composite_op 系统测试 test_common_types.cpp;
- 在 individual_op 的 executor 单测中,以
ge::FORMAT_ND作为入参调用该接口,例如 executor_utest.cpp 与 indv_args_utest.cpp:
tensor.rt2Tensor.SetStorageFormat(ge::FORMAT_ND);这些测试一方面验证了op::Format(ge::Format)与op::Format::FORMAT_*/ge::FORMAT_*两种写法等价,另一方面也说明SetStorageFormat被广泛用于构造待测张量,是算子级单测中模拟内存排布的标准手段。
七、使用注意事项小结
- 只改元数据,不重排数据:
SetStorageFormat仅更新张量格式元数据,真实数据需由 format transform 算子或显式拷贝完成重排,二者必须保持一致; - 三视图勿混淆:物理排布用
SetStorageFormat,逻辑排布用SetOriginalFormat,视图语义用SetViewFormat;底层框架真正消费的是前两者; - 枚举取值:
op::Format即ge::Format,常用值包括FORMAT_ND、FORMAT_NCHW、FORMAT_NHWC、FORMAT_NC1HWC0、FORMAT_FRACTAL_NZ等,私有格式应避免在普通构造路径中使用; - 配套读取:设置后可通过
GetStorageFormat()校验结果,二者在 common_types.cpp 中操作同一个底层格式字段。
关于aclTensor格式接口的完整集合(含 Shape 视图的 Get/Set 接口、SetData系列数据写入接口、Numel、IsEmpty、CheckOverflows等),可继续阅读 common_types 接口总览,其中列出了 GetStorageFormat、SetOriginalFormat、SetViewFormat 等全部配套接口文档。
【免费下载链接】opbase本项目是CANN算子库的基础框架库,为算子提供公共依赖文件和基础调度能力。项目地址: https://gitcode.com/cann/opbase
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考