opbase aclnn 张量元数据接口解析:aclGetFormat 获取 aclTensor 数据布局格式的原理与实践
2026/9/18 9:09:10 网站建设 项目流程

opbase aclnn 张量元数据接口解析:aclGetFormat 获取 aclTensor 数据布局格式的原理与实践

【免费下载链接】opbase本项目是CANN算子库的基础框架库,为算子提供公共依赖文件和基础调度能力。项目地址: https://gitcode.com/cann/opbase

本篇围绕 CANN opbase 仓库中 aclnn 公共 APIaclGetFormat展开:它用于读取通过aclCreateTensor创建的aclTensor对象所携带的数据布局格式(aclFormat)。读完本文,你将掌握该接口的原型、参数与错误码语义、在“复制张量属性创建新张量”场景下的完整用法,以及从源码层面理解格式转换(内部FormataclFormat)的实现细节和单元测试验证方式。

接口功能与定位

aclGetFormat是 aclnn 元数据(Meta)API 族中的一员,作用是获取 aclTensor 创建时指定的数据布局格式。与aclGetDataTypeaclGetViewShapeaclGetViewStridesaclGetViewOffsetaclGetStorageShape等接口并列,它共同构成了一组“只读访问张量属性”的查询接口,其典型用途是:先创建一张张量,再逐项读出它的属性,据此构造另一张属性相同的张量(例如在动态 shape/动态地址场景下重建张量描述)。

接口原型定义如下:

aclnnStatus aclGetFormat(const aclTensor *tensor, aclFormat *format)

该原型在公开头文件 acl_meta.h 中声明,aclnnStatusint32_t类型,返回 0 表示成功(见 acl_meta.h)。

参数说明

参数输入/输出说明
tensor输入输入 aclTensor,即需要查询格式的对象,通常由 aclCreateTensor 创建。
format输出返回该 aclTensor 的数据布局格式(aclFormat),由调用方提供可写指针。

需要注意两点语义:

  1. 查询的是“视图格式”而非存储格式。从源码实现看,接口内部读取的是张量的视图格式字段GetViewFormat(),与aclCreateTensorformat参数对应的是同一概念(张量逻辑视图的布局),而不是物理存储侧的属性;
  2. format指针不可为 null。两个参数任一为nullptr都会导致调用失败并返回错误码 161001(下文详述)。

返回值与错误码

  • 成功返回0ACLNN_SUCCESS);
  • 失败返回非 0 错误码,具体含义可参考 Common API Return Codes。

该接口最可能出现的失败原因是空指针参数

  • 错误码161001ACLNN_ERR_PARAM_NULLPTR):tensorformat为 null 指针时返回。该错误码在仓库的错误码文档中定义为“Parameter verification error. Parameters contain invalidnullptr”,完整错误码表见 common_api_return_codes.md(同表还包含161002参数校验错误、361001NPU Runtime 异常、561xxx内部异常等,可一并了解 aclnn 公共 API 的返回码体系)。

文档标注该接口无使用限制(Restrictions: None)。

源码实现解析:空指针校验与格式转换

aclGetFormat的实现位于 acl_op_api.cpp,核心逻辑非常简洁:

aclnnStatus aclGetFormat(const aclTensor* tensor, aclFormat* format) { if (tensor == nullptr || format == nullptr) { return ACLNN_ERR_PARAM_NULLPTR; } *format = op::ToAclFormat(tensor->GetViewFormat()); return OK; }

从源码结构看,该实现包含两层关键信息:

  1. 参数校验:对tensorformat做空指针检查,任一为空直接返回ACLNN_ERR_PARAM_NULLPTR(即 161001),与文档描述完全一致;
  2. 内部格式到 aclFormat 的转换:真正的取值动作是op::ToAclFormat(tensor->GetViewFormat())aclTensor内部以 opbase 自己的Format枚举保存格式,而 aclnn 对外暴露的是aclFormat枚举,二者需要一个显式映射函数衔接。

这个映射函数ToAclFormat定义在 format_utils.h,它是一个带“白名单”的转换:

inline aclFormat ToAclFormat(Format format) { static const std::vector<Format> CAN_CONVERT_TO_ACL_FORMAT_LIST = {Format::FORMAT_NCHW, Format::FORMAT_NHWC, Format::FORMAT_ND, Format::FORMAT_NC1HWC0, Format::FORMAT_FRACTAL_Z, Format::FORMAT_NC1HWC0_C04, Format::FORMAT_HWCN, Format::FORMAT_NDHWC, Format::FORMAT_FRACTAL_NZ, Format::FORMAT_NCDHW, Format::FORMAT_NDC1HWC0, Format::FORMAT_FRACTAL_Z_3D, Format::FORMAT_NC, Format::FORMAT_NCL, Format::FORMAT_FRACTAL_NZ_C0_16, Format::FORMAT_FRACTAL_NZ_C0_32, Format::FORMAT_FRACTAL_NZ_C0_2, Format::FORMAT_FRACTAL_NZ_C0_4, Format::FORMAT_FRACTAL_NZ_C0_8}; auto iter = std::find(CAN_CONVERT_TO_ACL_FORMAT_LIST.begin(), CAN_CONVERT_TO_ACL_FORMAT_LIST.end(), format); if (iter == CAN_CONVERT_TO_ACL_FORMAT_LIST.end()) { return aclFormat::ACL_FORMAT_UNDEFINED; } return static_cast<aclFormat>(format); }

由此可以推断出对使用者有意义的行为细节:

  • 内部FormataclFormat在白名单内的 19 种格式上数值一一对应(直接static_cast),例如FORMAT_ND映射为ACL_FORMAT_NDFORMAT_NC1HWC0映射为ACL_FORMAT_NC1HWC0
  • 若张量内部保存的格式不在白名单内(例如某些仅内部使用、未对外暴露的格式),aclGetFormat不会报错,而是返回ACL_FORMAT_UNDEFINED。因此在业务代码中,用ACL_FORMAT_UNDEFINED初始化出参并在使用前判断是否被正确填充,是稳妥的防御式写法;
  • 同文件中的逆映射ToOpFormat(format_utils.h)则是“非ACL_FORMAT_UNDEFINED直接强转为内部格式、否则返回FORMAT_MAX”,两者共同保证 aclTensor 创建(aclCreateTensor传入aclFormat)与查询(aclGetFormat返回aclFormat)之间的格式语义闭环。

实战示例:读取属性并据此创建新张量

官方文档给出的典型场景是:假设已有一个aclTensor对象xTensor,需要读取它的数据类型、数据布局格式、维度、stride、offset 等属性,并基于这些属性创建一个新的aclTensor对象yTensor。完整示例代码如下(该示例仅供参考,不可直接复制运行,实际工程中需结合设备地址申请等上下文):

// 1. Create an xTensor. int64_t xViewDims = {2, 4}; int64_t xStridesValue = {4, 1}; // The stride of the first dimension is 4, and that of the second dimension is 1. int64_t xStorageDims = {2, 4}; xTensor = aclCreateTensor(xViewDims, 2, ACL_FLOAT16, xStridesValue, 0, ACL_FORMAT_ND, xStorageDims, 2, nullptr); // 2. Obtain the attribute values of xTensor. // Obtain the logical shape of xTensor. viewDims is {2, 4}, and viewDimsNum is 2. int64_t *viewDims = nullptr; uint64_t viewDimsNum = 0; auto ret = aclGetViewShape(xTensor, &viewDims, &viewDimsNum); // Obtain the data type (ACL_FLOAT16) of xTensor. aclDataType dataType = aclDataType::ACL_DT_UNDEFINED; ret = aclGetDataType(xTensor, &dataType); // Obtain the stride information about xTensor. stridesValue is {4, 1}, and stridesNum is 2. int64_t *stridesValue = nullptr; uint64_t stridesNum = 0; ret = aclGetViewStrides(xTensor, &stridesValue, &stridesNum); // Obtain the offset of the first element of xTensor relative to storage. The offset is 0. int64_t offset = 0; ret = aclGetViewOffset(xTensor, &offset); // Obtain the data layout format (ACL_FORMAT_ND) of xTensor. aclFormat format = aclFormat::ACL_FORMAT_UNDEFINED; ret = aclGetFormat(xTensor, &format); // Obtain the actual physical shape of xTensor. storageDims is {2, 4}, and storageDimsNum is 2. int64_t *storageDims = nullptr; uint64_t storageDimsNum = 0; ret = aclGetStorageShape(xTensor, &storageDims, &storageDimsNum); // Device address void *deviceAddr; // 3. Create a tensor based on the xTensor attributes. aclTensor *yTensor = aclCreateTensor(viewDims, viewDimsNum, dataType, stridesValue, offset, format, storageDims, storageDimsNum, deviceAddr); // 4. Manually free memory. delete[] viewDims; delete[] stridesValue; delete[] storageDims;

示例中有几个值得注意的实战要点:

  1. 出参内存由调用方管理aclGetViewShapeaclGetViewStridesaclGetStorageShape这类“形状/步长”查询接口会把内部缓冲new出来并通过指针返回(例如 aclGetStorageShape 中的new (std::nothrow) int64_t[storageCount]),因此示例末尾必须手动delete[]释放viewDimsstridesValuestorageDims三块内存;而aclGetFormataclGetDataTypeaclGetViewOffset只写一个值,不涉及动态分配;
  2. 属性读取与重建的顺序:先取出全部属性(包括format),再用这些属性调用 aclCreateTensor 重建张量,重建时最后传入的是新的设备地址deviceAddr——这正是该接口在“张量对象复用/换址重建”类流程中的价值:aclTensor只是元数据描述,数据本体由deviceAddr指向;
  3. 逐次检查返回值:示例用ret逐个接收各查询接口的aclnnStatus,生产代码中应像单元测试那样对每次调用做断言或错误处理,避免带着失败状态继续重建张量。

单元测试验证

仓库中的单元测试 test_acl_op_api.cpp 直接覆盖了aclGetFormat的两类关键行为,可与上文源码实现相互印证:

TEST_F(AclOpApiTest, aclGetFormat) { EXPECT_NE(aclGetFormat(nullptr, nullptr), OK); std::vector<int64_t> strides = {8, 1}; CHECK_TENSOR(a, std::vector<int64_t>({4, 2}), std::vector<int64_t>({32}), aclDataType::ACL_FLOAT, strides.data(), 0, aclFormat::ACL_FORMAT_ND, nullptr); aclFormat formatRes = aclFormat::ACL_FORMAT_UNDEFINED; EXPECT_EQ(aclGetFormat(a, &formatRes), OK); EXPECT_EQ(formatRes, aclFormat::ACL_FORMAT_ND); }

测试断言了两点:其一,双空指针调用必须失败EXPECT_NE(..., OK)),对应实现中的ACLNN_ERR_PARAM_NULLPTR分支;其二,以ACL_FORMAT_ND创建的张量,查询结果原样返回ACL_FORMAT_ND且状态码为OK,验证了白名单内格式的无损往返。该测试同时出现在集成测试 st/composite_op/test_acl_op_api.cpp 中,说明这一行为契约在单元与系统两级测试中都被持续守护。

小结

aclGetFormat虽是一个几行的轻量接口,但它处在 aclnn 张量元数据体系的枢纽位置:它是aclCreateTensor传入的aclFormat的“回读”通道,也是张量属性复制流程中的必备一环。掌握它的关键有三条——参数任一为空返回161001;内部通过ToAclFormat白名单完成FormataclFormat的映射、白名单外格式得到ACL_FORMAT_UNDEFINED;与aclGetViewShape等兄弟接口配合使用完毕后,注意释放由形状/步长查询接口分配的出参内存。相关实现与测试分别位于 acl_op_api.cpp、format_utils.h 和 test_acl_op_api.cpp,可作为进一步深入源码的入口。

【免费下载链接】opbase本项目是CANN算子库的基础框架库,为算子提供公共依赖文件和基础调度能力。项目地址: https://gitcode.com/cann/opbase

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询