news 2026/9/18 18:34:44

CANN opbase 算子开发指南:aclGetFormat 接口详解与 aclTensor 数据格式获取实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
CANN opbase 算子开发指南:aclGetFormat 接口详解与 aclTensor 数据格式获取实战

CANN opbase 算子开发指南:aclGetFormat 接口详解与 aclTensor 数据格式获取实战

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

导读

aclGetFormat是 CANN opbase 基础框架库中用于查询aclTensor数据排布格式(format)的元数据访问接口。在单算子(ACLNN)开发中,张量以aclTensor结构承载,数据在 Device 内存上的排布方式(ND、NCHW、NC1HWC0、FRACTAL_NZ 等)直接影响算子的地址计算与 kernel 实现。本文以 docs/zh/api/nnopbase/aclnn/aclGetFormat.md 为骨架,结合仓库内头文件声明、源码实现与单元测试,完整讲解aclGetFormat的函数原型、参数与返回码、底层实现原理、与aclCreateTensor的配套用法,并给出可复用的属性读取与张量重建实战示例,帮助读者安全、正确地查询与传递张量格式信息。

一、接口定位:aclTensor 元数据访问家族中的一员

aclGetFormat属于 ACLNN 元数据(meta)接口族,负责从aclTensor中取出数据排布格式。aclTensor由 aclCreateTensor 接口创建,是框架定义的一种用来管理和存储张量数据的结构,开发者无需关注其内部实现,直接使用即可。一个aclTensor内部记录了一组完整的张量描述信息,包括:

  • ViewShape(逻辑 shape)与 StorageShape(物理排布 shape);
  • 各维度的访问步长(stride)与首元素偏移(offset);
  • 数据类型(dataType)与数据排布格式(format);
  • Device 侧存储地址。

围绕这些属性,opbase 提供了一组一一对应的查询接口,aclGetFormat是其中专门负责 format 的一个:

接口查询内容声明位置
aclGetViewShape逻辑 shape(ViewShape)include/nnopbase/aclnn/acl_meta.h
aclGetViewStrides各维度访问步长include/nnopbase/aclnn/acl_meta.h
aclGetViewOffset首元素相对 storage 的偏移include/nnopbase/aclnn/acl_meta.h
aclGetStorageShape实际物理排布 shape(StorageShape)include/nnopbase/aclnn/acl_meta.h
aclGetFormat数据排布格式(format)include/nnopbase/aclnn/acl_meta.h
aclGetDataType数据类型(dataType)include/nnopbase/aclnn/acl_meta.h

这组接口共同支撑了"读取既有 aclTensor 的完整属性 → 基于这些属性重建新的 aclTensor"的典型开发模式(详见第四节实战示例)。

二、函数原型与参数说明

aclGetFormat的接口声明位于 include/nnopbase/aclnn/acl_meta.h,以ACL_FUNC_VISIBILITY修饰对外导出:

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

2.1 参数解析

参数名输入/输出说明
tensor输入输入的 aclTensor,即待查询格式的张量对象。
format输出返回的 aclTensor 的数据格式,类型为aclFormat枚举。

其中aclFormataclnnStatus等基础类型定义在acl/acl_base.hacl_meta.h通过#include "acl/acl_base.h"引入),常见的 format 取值包括:

  • ACL_FORMAT_UNDEFINED:未定义格式,通常作为输出变量的初始化值;
  • ACL_FORMAT_ND:按维度顺序连续存储,是最常用的通用格式;
  • ACL_FORMAT_NCHWACL_FORMAT_NHWCACL_FORMAT_HWCN:经典多维排布;
  • ACL_FORMAT_NC1HWC0ACL_FORMAT_FRACTAL_ZACL_FORMAT_FRACTAL_NZ:昇腾硬件算子常用的分形/分块格式。

2.2 返回值说明

返回0(即OK)表示成功,返回其他值表示失败。公共返回码列表参见 公共接口返回码,可能失败的原因:

  • 返回161001(ACLNN_ERR_PARAM_NULLPTR:参数tensorformat为空指针,即"参数校验错误,参数中存在非法的 nullptr"。

2.3 约束说明

该接口无额外约束。但需要注意一个使用前提:待查询的tensor必须是通过aclCreateTensor等接口合法创建的 aclTensor 对象,且format必须指向有效的可写内存,否则会触发参数空指针校验错误。

三、源码级实现原理:两次转换与一份白名单

3.1 接口实现位置与调用链

aclGetFormat的实现位于 src/nnopbase/common/api/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任一为nullptr时立即返回ACLNN_ERR_PARAM_NULLPTR(对应公共返回码 161001),与文档"可能失败的原因"完全一致;
  2. 读取内部格式:调用tensor->GetViewFormat()取出 aclTensor 内部记录的格式。从源码结构看,该内部格式以op::Format viewFormat_成员存储,由SetViewFormat写入、GetViewFormat读取(见 src/nnopbase/common/utils/common_types.cpp 与 L478);
  3. 格式转换:通过op::ToAclFormat将内部op::Format转换为对外暴露的aclFormat枚举并写入输出参数。

3.2 ToAclFormat 的可转换白名单

op::ToAclFormat定义于 include/nnopbase/opdev/format_utils.h。该函数维护了一份"可安全转换为 aclFormat 的格式白名单",包括FORMAT_NCHWFORMAT_NHWCFORMAT_NDFORMAT_NC1HWC0FORMAT_FRACTAL_ZFORMAT_FRACTAL_NZFORMAT_NDHWCFORMAT_NC等 19 种常见格式;若内部格式不在白名单中,则返回aclFormat::ACL_FORMAT_UNDEFINED。这意味着:

  • 常规张量(ND、NCHW、NC1HWC0 等)通过aclGetFormat可以无损取回原格式;
  • 极少数算子内部使用的私有/特殊格式无法直接映射为公开的aclFormat枚举,此时返回ACL_FORMAT_UNDEFINED,开发者应将其视为"内部格式,不可直接用于外部枚举语义"而非数据异常。

3.3 与 aclCreateTensor 的格式写入路径对称

在创建方向,aclCreateTensor会把入参formatop::ToOpFormat转换后写入张量(见 src/nnopbase/common/utils/common_types.cpp)。ToOpFormatToAclFormat互为反向转换,一写一读形成闭环:创建时ACL_FORMAT_ND → FORMAT_ND存储,查询时FORMAT_ND → ACL_FORMAT_ND返回,保证开发者传入的格式在属性回读时保持一致。

四、实战示例:读取属性并重建 aclTensor

原文档给出了一段完整的关键代码示例(仅供参考,不支持直接拷贝运行),演示了"先创建 xTensor → 读取其全部属性(含 format)→ 用属性重建 yTensor"的标准流程,本节在保留原示例全部细节的基础上补充逐段注释。

4.1 创建 xTensor

// 1.创建xTensor int64_t xViewDims[] = {2, 4}; int64_t xStridesValue[] = {4, 1}; // 第1维步长4,第2维步长1 int64_t xStorageDims[] = {2, 4}; aclTensor *xTensor = aclCreateTensor(xViewDims, 2, ACL_FLOAT16, xStridesValue, 0, ACL_FORMAT_ND, xStorageDims, 2, nullptr);

aclCreateTensor的完整原型为(见 aclCreateTensor):

aclTensor *aclCreateTensor(const int64_t *viewDims, uint64_t viewDimsNum, aclDataType dataType, const int64_t *stride, int64_t offset, aclFormat format, const int64_t *storageDims, uint64_t storageDimsNum, void *tensorData);

其中viewDims/viewDimsNum是逻辑 shape,storageDims/storageDimsNum是物理排布 shape,stride是各维度访问步长,offset是首元素相对 storage 的偏移,format是数据排布格式,tensorData是 Device 侧存储地址(需 32 字节对齐,否则可能出现未定义错误)。示例中 xTensor 为2×4的 FP16 张量,连续存储、偏移为 0、格式为 ND。

4.2 读取 xTensor 的全部属性

// 2. 获取xTensor的各种属性值 // 获取xTensor的逻辑shape,viewDims为{2, 4}, viewDimsNum为2 int64_t *viewDims = nullptr; uint64_t viewDimsNum = 0; auto ret = aclGetViewShape(xTensor, &viewDims, &viewDimsNum); // 获取xTensor的数据类型为ACL_FLOAT16 aclDataType dataType = aclDataType::ACL_DT_UNDEFINED; ret = aclGetDataType(xTensor, &dataType); // 获取xTensor的步长信息,stridesValue为{4, 1}, stridesNum为2 int64_t *stridesValue = nullptr; uint64_t stridesNum = 0; ret = aclGetViewStrides(xTensor, &stridesValue, &stridesNum); // 获取xTensor的首元素对于storage的偏移值,offset为0 int64_t offset = 0; ret = aclGetViewOffset(xTensor, &offset); // 获取xTensor的数据排布格式为ACL_FORMAT_ND aclFormat format = aclFormat::ACL_FORMAT_UNDEFINED; ret = aclGetFormat(xTensor, &format); // 获取xTensor的实际物理排布shape,storageDims为{2, 4}, storageDimsNum为2 int64_t *storageDims = nullptr; uint64_t storageDimsNum = 0; ret = aclGetStorageShape(xTensor, &storageDims, &storageDimsNum); // device侧地址 void *deviceAddr;

需要特别说明的是:formatdataType这两个输出参数在调用前必须先初始化为ACL_FORMAT_UNDEFINED/ACL_DT_UNDEFINED,即使调用失败也不会读到未初始化内存;而viewDimsstridesValuestorageDims三个指针型输出由接口内部new分配,使用完毕后必须手动delete[],否则会造成内存泄漏。

4.3 根据属性重建 yTensor 并释放内存

// 3.根据xTensor的属性创建新的tensor aclTensor *yTensor = aclCreateTensor(viewDims, viewDimsNum, dataType, stridesValue, offset, format, storageDims, storageDimsNum, deviceAddr); // 4.手动释放内存 delete[] viewDims; delete[] stridesValue; delete[] storageDims;

由于 xTensor 是连续存储的 ND 张量,重建出的 yTensor 与 xTensor 属性完全一致。对于非连续张量(如转置、切片产生的视图),只需通过aclGetViewStrides/aclGetViewOffset读取到的步长与偏移,配合aclCreateTensorstride/offset参数即可无损重建同样的视图语义——这正是aclTensor以 shape、stride、offset、format 等描述信息刻画张量的价值所在。aclTensor的逻辑结构与 torch.Tensor 类似,由一块连续或非连续的内存地址和一系列描述信息组成,详见 aclCreateTensor 文档中的 tensor 逻辑结构图。

五、单元测试佐证:行为与边界可验证

仓库在单元测试与系统测试中均对aclGetFormat的边界行为做了验证,例如 tests/nnopbase/ut/composite_op/test_acl_op_api.cpp 中的用例:

TEST_F(AclOpApiTest, aclGetFormat) { EXPECT_NE(aclGetFormat(nullptr, nullptr), OK); // 空指针入参必须返回非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); // 正常查询返回OK EXPECT_EQ(formatRes, aclFormat::ACL_FORMAT_ND); // 与创建时传入的格式一致 }

该用例验证了两个关键事实:

  1. 空指针校验aclGetFormat(nullptr, nullptr)必然返回非OK,与文档中"返回 161001:参数 tensor 或 format 为空指针"的描述一致;
  2. 格式回读一致性:创建时以ACL_FORMAT_ND创建的张量,查询后返回ACL_FORMAT_ND,印证了 3.3 节所述的ToOpFormat/ToAclFormat双向转换闭环。

同样的测试用例也存在于系统测试 tests/nnopbase/st/composite_op/test_acl_op_api.cpp 中,开发者可对照阅读,了解该接口在测试环境下的完整行为预期。

六、常见问题与使用建议

  • 调用后 format 仍为ACL_FORMAT_UNDEFINED:先检查返回值,若返回非 0 则是入参为空指针或非法对象;若返回 0 但格式为UNDEFINED,从源码结构看是内部格式不在ToAclFormat白名单中(见 3.2 节),属于框架内部格式,不建议直接依赖其枚举值。
  • 忘记释放指针型输出aclGetViewShapeaclGetViewStridesaclGetStorageShape的输出数组由接口内部分配,调用方需delete[]formatoffsetdataType是值型输出,无需释放。
  • format 与 shape 的配套使用aclGetFormat仅返回格式枚举,实际内存排布还需结合aclGetStorageShape理解——例如 NC1HWC0 格式下物理 shape 与逻辑 shape 并不相同,二者配套查询才能正确计算地址偏移。
  • 与创建接口的配套:format 在创建时通过aclCreateTensorformat参数写入,读取时通过aclGetFormat取回;若需修改既有张量的属性,可使用 aclInitTensor 初始化给定 tensor 的参数。

七、小结

aclGetFormat虽是一个参数极简的查询接口,却是 aclTensor 元数据读取链路中承上启下的一环:向下它映射aclTensor内部以op::Format存储的格式字段,向上它把格式转换为开发者可直接使用的aclFormat枚举。配合aclGetViewShapeaclGetViewStridesaclGetViewOffsetaclGetStorageShapeaclGetDataType,即可完整读取一个 aclTensor 的全部属性,再经aclCreateTensor重建出新张量,实现视图克隆、格式校验、算子入参构造等常见场景。理解其 161001 空指针返回码语义与ToAclFormat白名单行为,有助于在实际算子开发中快速定位参数问题,写出健壮的 ACLNN 单算子调用代码。

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

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/18 18:34:23

死锁活锁饥饿阻塞无锁:高并发故障诊断与预防实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/18 18:33:00

Win10此电脑默认文件夹隐藏教程:注册表与reg文件实操指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/18 18:30:17

Cadence CIS连不上数据库?32位ODBC驱动与DSN配置全解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/18 18:30:00

从干等到在听:qwen-audio-agent 接入 TaoToken 的 LLM Key

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/18 18:27:49

宠物电推剪升压芯片选型:输入电流、堵转余量与热设计实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华