ops-transformer 中 CANN 算子 API 的基础数据结构:aclTensor、aclIntArray 与 aclOpExecutor 全解析 ops-transformer 中 CANN 算子 API 的基础数据结构aclTensor、aclIntArray 与 aclOpExecutor 全解析【免费下载链接】ops-transformer本项目是CANN提供的transformer类大模型算子库实现网络在NPU上加速计算。项目地址: https://gitcode.com/cann/ops-transformer本篇指南基于 CANN 算子库 ops-transformer 的《数据结构》文档系统讲解调用 transformer 类大模型算子 API 时必须掌握的 9 类基础数据结构张量aclTensor、标量aclScalar、整型/浮点/布尔数组aclIntArray/aclFloatArray/aclBoolArray、张量与标量列表aclTensorList/aclScalarList、执行器aclOpExecutor以及流aclrtStream。读完本文你将能够理解每种数据结构的创建接口与适用场景并结合仓库中 flash_attention_score、add_example 等真实示例写出完整可运行的两段式算子调用代码。一、数据结构总览与定位在 ops-transformer 中所有算子 APIaclnn前缀的两段式接口的参数都通过一套统一的 C 风格数据结构传递。这套数据结构由 CANN 运行时AscendCL定义开发者无需关注其内部实现直接使用即可。这些基础数据结构可通过 CANN《算子库》的“公共接口”创建如aclCreateTensor等。仓库中的 基本概念导航文档 将 数据结构 列为调用算子 API 前的必备前置知识与 两段式接口、数据类型、数据格式、非连续 Tensor 等章节共同构成完整的调用上下文。9 种数据结构及其创建接口如下数据结构用途创建接口销毁接口对应aclTensor管理和存储张量数据向量、矩阵等多维数据aclCreateTensoraclDestroyTensoraclScalar管理和存储标量数据单一数值aclCreateScalaraclDestroyScalaraclIntArray管理和存储整型数据的数组aclCreateIntArrayaclDestroyIntArrayaclFloatArray管理和存储 float32 型数据的数组aclCreateFloatArrayaclDestroyFloatArrayaclBoolArray管理和存储布尔型数据的数组aclCreateBoolArrayaclDestroyBoolArrayaclTensorList管理和存储多个张量的数组aclCreateTensorListaclDestroyTensorListaclScalarList管理和存储标量数据的数组aclCreateScalarListaclDestroyScalarListaclOpExecutor执行算子计算的容器执行器框架自动创建框架自动释放aclrtStream管理异步操作的执行顺序流aclrtCreateStreamaclrtDestroyStream各结构的 C 类型定义均为不透明指针opaque pointer例如typedef struct aclTensor aclTensor; // 张量 typedef struct aclScalar aclScalar; // 标量 typedef struct aclIntArray aclIntArray; // 整型数组 typedef struct aclFloatArray aclFloatArray; // float32 数组 typedef struct aclBoolArray aclBoolArray; // 布尔数组 typedef struct aclTensorList aclTensorList; // 张量列表 typedef struct aclScalarList aclScalarList; // 标量列表 typedef struct aclOpExecutor aclOpExecutor; // 执行器 typedef void *aclrtStream; // 流二、aclTensor算子调用的核心数据载体aclTensor是整套数据结构中出镜率最高的类型——几乎所有算子的输入张量、输出张量、mask 张量都以aclTensor*形式传入。2.1 创建参数详解从 flash_attention_score 示例 的CreateAclTensor模板函数可以完整还原aclCreateTensor的调用方式// 1. 调用 aclrtMalloc 申请 device 侧内存 auto ret aclrtMalloc(deviceAddr, size, ACL_MEM_MALLOC_HUGE_FIRST); // 2. 调用 aclrtMemcpy 将 host 侧数据拷贝到 device 侧 ret aclrtMemcpy(*deviceAddr, size, hostData.data(), size, ACL_MEMCPY_HOST_TO_DEVICE); // 3. 计算连续 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]; } // 4. 调用 aclCreateTensor 接口创建 aclTensor *tensor aclCreateTensor(shape.data(), shape.size(), // shape 指针与维度数 dataType, // aclDataType如 ACL_FLOAT strides.data(), 0, // strides 与偏移 aclFormat::ACL_FORMAT_ND, // 数据格式 shape.data(), shape.size(), // logicalShape 与维度数 *deviceAddr); // device 内存地址关键参数说明shape张量各维大小int64_t*元素个数为各维乘积决定aclrtMalloc申请的字节数乘以元素字节数dataType元素类型示例中分别使用aclDataType::ACL_FLOATfloat32 张量与aclDataType::ACL_UINT8attention maskstrides行主序下各维的步长末维为 1向前依次累乘后续维大小。连续内存的 strides 可按上述循环自动计算这是仓库中多个示例的统一写法format数据排布格式简单连续张量传ACL_FORMAT_NDdeviceAddr必须指向已通过aclrtMalloc申请的 device 内存aclTensor本身只持有描述信息与指针不拥有数据内存。2.2 真实算子调用中的典型用法flash_attention_score 示例 中一次调用创建了 9 个aclTensorq、k、v、pse、dropMask、padding、attenmask、attentionOut、softmaxMax、softmaxSum等其中q/k/v为float32形状{S1,B,H1}/{S2,B,H2}attenmask为uint8形状{S1,S2}输出张量attentionOut在调用前同样需要先分配 device 内存并包装成aclTensor。随后它们作为参数整体传入两段式接口uint64_t workspaceSize 0; aclOpExecutor* executor; ret aclnnFlashAttentionScoreV2GetWorkspaceSize( q, k, v, pse, dropMask, padding, attenmask, prefix, qStartIdx, kvStartIdx, scaleValue, keepProb, preTokens, nextTokens, headNum, layOut, innerPrecise, sparseMode, pseType, softmaxMax, softmaxSum, softmaxOut, attentionOut, workspaceSize, executor); // ... 申请 workspace 后 ret aclnnFlashAttentionScoreV2(workspaceAddr, workspaceSize, executor, stream);这里可以看到数据结构与两段式接口的直接耦合第一阶段接口aclnnXxxGetWorkspaceSize的所有张量/数组参数都会登记到框架自动创建的aclOpExecutor中第二阶段接口aclnnXxx只需传入 workspace 和 executor。三、aclIntArray / aclFloatArray / aclBoolArray标量数组参数算子接口中大量整型标量参数如prefix、qStartIdx、kvStartIdx或动态维度的形状数组需要通过aclIntArray等数组结构传递。flash_attention_score 示例 展示了标准写法std::vectorint64_t prefixOp {0}; std::vectorint64_t qStartIdxOp {0}; std::vectorint64_t kvStartIdxOp {0}; aclIntArray *prefix aclCreateIntArray(prefixOp.data(), 1); aclIntArray *qStartIdx aclCreateIntArray(qStartIdxOp.data(), 1); aclIntArray *kvStartIdx aclCreateIntArray(kvStartIdxOp.data(), 1);要点aclCreateIntArray(const int64_t* data, int64_t len)的入参是host 侧的 int64_t 数组和长度框架内部完成拷贝封装aclFloatArray对应 float32 数组、aclBoolArray对应布尔数组签名风格一致aclCreateFloatArray/aclCreateBoolArray与aclTensor不同数组结构不要求数据位于 device 内存host 侧数组即可。生命周期方面使用结束后必须显式释放示例中对应aclDestroyIntArray(prefix); aclDestroyIntArray(qStartIdx); aclDestroyIntArray(kvStartIdx);四、aclScalar / aclScalarList标量及其列表aclScalar用于管理和存储标量数据单一数值通过aclCreateScalar创建。它适用于算子接口中以“单个数值张量”形式出现的参数当某个算子需要成组接收多个标量例如一组偏移量或一组超参数时则使用aclScalarList通过aclCreateScalarList创建。仓库的单元测试框架中封装了这两类结构的构造逻辑例如 aclnn_tensor_list.cpp 与 tensor_desc.cpp 展示了框架在批量 UT 中如何程序化地批量创建aclTensorList/aclScalar可参考其封装方式理解各创建接口的参数组织。五、aclTensorList批量张量参数aclTensorList用于管理和存储多个张量的数组结构通过aclCreateTensorList创建。从命名与结构对应关系看它适用于那些“张量个数不固定、需以列表形式传入”的算子接口如支持动态个数的输入通道/分支场景。与aclIntArray等标量数组不同aclTensorList的每个元素本身是一个完整的aclTensor含 shape、dataType、strides、device 地址。这意味着构造一个 tensor list 前需要先按 aclTensor 创建流程 逐个创建底层张量。仓库测试框架中 aclnn_tensor_list.cpp 的工具类即承担“host 数据 → 一组 aclTensor → aclTensorList”的封装工作可作为批量张量构造的参考实现。六、aclOpExecutor两段式接口的状态容器aclOpExecutor是执行算子计算的容器它的生命周期由框架托管开发者不需要也不应该手动创建或释放调用一阶段接口aclxxXxxGetWorkspaceSize时框架自动创建aclOpExecutor并把本次调用的全部输入/输出数据结构张量、数组、标量与中间配置登记其中调用二阶段接口aclxxXxx时框架基于 executor 完成实际计算并在返回后自动释放该对象。两段式接口文档 对此有明确约束aclnnStatus aclxxXxxGetWorkspaceSize(const aclTensor *src, ..., aclTensor *out, ..., uint64_t *workspaceSize, aclOpExecutor **executor); aclnnStatus aclxxXxx(void *workspace, uint64_t workspaceSize, aclOpExecutor *executor, aclrtStream stream);其中“aclxx”表示算子接口前缀如 aclnn“Xxx”表示算子类型。workspace 是指除输入/输出外算子在 NPU 上完成计算所需的临时内存。两条易踩的坑在源码示例中同样得到印证第二段接口不能重复调用two_phase_api.md 指出同一 executor 连续两次调用aclxxXxx(...)会出现异常workspace 需按返回值申请add_example 示例 演示了标准处理——先判断workspaceSize 0才调用aclrtMalloc避免 0 长度分配uint64_t workspaceSize 0; aclOpExecutor *executor; ret aclnnAddExampleGetWorkspaceSize(selfX, selfY, out, workspaceSize, executor); void *workspaceAddr nullptr; if (workspaceSize static_castuint64_t(0)) { ret aclrtMalloc(workspaceAddr, workspaceSize, ACL_MEM_MALLOC_HUGE_FIRST); } ret aclnnAddExample(workspaceAddr, workspaceSize, executor, stream);七、aclrtStream异步执行的顺序保障aclrtStream定义为typedef void *aclrtStream用于管理和维护一些异步操作的执行顺序。它是二阶段接口的第四个参数决定算子被提交到哪条流上执行。完整的流生命周期在 add_example 示例 中体现为固定三件套// 初始化阶段固定写法 aclrtCreateStream(stream); // 创建流 // ... 提交算子任务 ... aclrtSynchronizeStream(stream); // 同步等待任务执行结束 aclrtDestroyStream(stream); // 销毁流注意aclrtSynchronizeStream是取结果前的必要步骤算子提交后 NPU 异步执行必须在拷贝输出前同步否则 PrintOutResult 中的aclrtMemcpy会读到未完成的内存。八、完整生命周期串联从创建到释放以 add_example 完整示例 为骨架一次标准算子调用中各数据结构的创建与释放顺序为初始化aclInit→aclrtSetDevice→aclrtCreateStream获得aclrtStream构造数据aclrtMallocaclrtMemcpy准备 device 数据 →aclCreateTensor包装为aclTensor标量数组参数用aclCreateIntArray等创建两段式调用aclnnXxxGetWorkspaceSize框架自动创建aclOpExecutor→ 按需aclrtMallocworkspace →aclnnXxx框架自动释放 executor同步取结果aclrtSynchronizeStream→aclrtMemcpy拷回 host释放aclDestroyTensor逐个销毁张量、aclDestroyIntArray等销毁数组 →aclrtFree释放 device 内存含 workspace→aclrtDestroyStream→aclrtResetDevice→aclFinalize。flash_attention_score 示例 的清理段印证了每个结构各有对应销毁接口的配对关系aclDestroyTensor(q); aclDestroyTensor(k); aclDestroyTensor(v); aclDestroyTensor(attenmask); // ... aclDestroyIntArray(prefix); aclDestroyIntArray(qStartIdx); aclDestroyIntArray(kvStartIdx);九、选型建议该用哪种结构结合文档定义与仓库示例的实际用法给出如下选型对照均可从 数据结构文档 及上文示例验证多维设备数据特征、激活、KV cache→aclTensor需要 shape/dataType/strides/device 地址完整描述是算子输入输出的绝对主力单个数值→aclScalar如某些算子以标量张量形式传入的超参数一组 int64 参数prefix、startIdx、动态 shape→aclIntArrayhost 侧数组直接创建无需 device 内存一组 float32 参数→aclFloatArray一组 bool 参数→aclBoolArray可变数量的张量集合→aclTensorList可变数量的标量集合→aclScalarListexecutor 与 stream 不用自己管理生命周期executor 由两段式接口自动创建/释放stream 通过 AscendCL 标准接口创建/销毁。小结aclTensor、aclScalar、aclIntArray、aclFloatArray、aclBoolArray、aclTensorList、aclScalarList、aclOpExecutor、aclrtStream这 9 种结构构成了 ops-transformer 中所有aclnn算子 API 的参数基础。理解谁负责描述数据aclTensor 等、谁负责传递数据到 NPUaclrtMalloc/aclrtMemcpy aclrtStream、谁负责承载一次调用aclOpExecutor 自动托管三层职责并严格遵循创建 → 两段式调用 → 同步 → 配对销毁的生命周期是正确调用本仓库任意 transformer 算子flash_attention_score、sparse_flash_attention、grouped_matmul 等的前提。更多上下文可延伸阅读 基本概念导航 下的两段式接口、数据类型、数据格式与非连续 Tensor 章节以及 编译运行示例指南。【免费下载链接】ops-transformer本项目是CANN提供的transformer类大模型算子库实现网络在NPU上加速计算。项目地址: https://gitcode.com/cann/ops-transformer创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考