ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

CANN opbase aclCreateScalar 接口详解:创建 aclScalar 标量作为单算子 API 入参的完整指南

2026/9/18 13:49:33 拓冰建站 浏览量
CANN opbase aclCreateScalar 接口详解:创建 aclScalar 标量作为单算子 API 入参的完整指南 CANN opbase aclCreateScalar 接口详解创建 aclScalar 标量作为单算子 API 入参的完整指南【免费下载链接】opbase本项目是CANN算子库的基础框架库为算子提供公共依赖文件和基础调度能力。项目地址: https://gitcode.com/cann/opbase本指南围绕 CANN 算子库基础框架库cann/opbase中aclnn公共接口aclCreateScalar展开讲解 aclScalar 标量对象的用途、函数原型、参数语义、底层实现原理及其配套的生命周期管理接口。读完本文你将掌握如何正确创建、使用与销毁 aclScalar 标量并将其作为单算子 APIaclnn 系列接口的入参传入同时了解框架内部的数据拷贝与数据类型转换机制避免空指针、内存泄漏与双重释放等常见问题。aclScalar 是什么单算子 API 中的标量入参载体在 CANN 单算子 APIaclnn 接口的执行模型中算子入参通常分为张量aclTensor与标量scalar两类。标量用于表达算子定义中那些以单个数值形式存在的属性或参数例如缩放系数 alpha、偏移量 beta、归一化中的 epsilon 等。为了统一管理和传递这类数据opbase 框架在aclnn公共接口层定义了aclScalar这一数据结构用于管理和存储标量数据。从设计定位上看aclScalar与aclTensor、aclTensorList、aclIntArray、aclFloatArray、aclBoolArray等接口对象同属一层——它们是框架定义的数据封装对象开发者无需关注其内部实现直接使用即可。aclScalar的类定义位于 include/nnopbase/opdev/common_types.h继承自op::Object内部通过联合体union按数据类型存储标量值并提供丰富的类型读取方法。函数原型与参数说明aclCreateScalar的完整函数原型如下aclScalar *aclCreateScalar(void *value, aclDataType dataType)该接口的详细参数语义如下表所示参数名输入/输出说明value输入Host 侧的 scalar 类型的指针其指向的值会作为 scalar 被拷贝进框架内部。dataType输入scalar 的数据类型取值为aclDataType枚举如ACL_FLOAT、ACL_INT32、ACL_DOUBLE等。返回值说明成功则返回创建好的aclScalar对象指针否则返回nullptr。两个参数需要配合使用dataType决定了框架按何种类型解读value指向的内存两者必须保持一致。例如当dataType为ACL_FLOAT时value应指向一个float类型的变量当dataType为ACL_INT32时value应指向一个int32_t类型的变量。底层实现解析从 Host 值到框架内部存储aclCreateScalar的实现位于 src/nnopbase/common/api/acl_op_api.cpp源码如下aclScalar* aclCreateScalar(void* value, aclDataType dataType) { if (value nullptr) { return nullptr; } aclScalar* scalar nullptr; ADD_TRY_CATCH(scalar new aclScalar(value, op::ToOpDataType(dataType)); return scalar; , OP_LOGE(ACLNN_ERR_INNER, aclCreateScalar error.); delete scalar; return nullptr;); }从源码可以梳理出三条关键实现事实1. 空指针保护是显式的第一道防线。函数入口处首先检查value nullptr一旦传入空指针直接返回nullptr而不会进入后续的构造逻辑。这是调用方最容易踩的坑之一——忘记给value传有效地址。2. 数据类型发生了一次从 acl 域到 op 域的转换。构造aclScalar时调用op::ToOpDataType(dataType)将aclDataType转换为框架 op 层使用的op::DataType。该转换函数定义于 include/nnopbase/opdev/data_type_utils.hconstexpr inline DataType ToOpDataType(aclDataType type) { if (type ! aclDataType::ACL_DT_UNDEFINED) { return static_castDataType(type); } return DataType::DT_UNDEFINED; }可见aclDataType与op::DataType的枚举值在数值上是对齐的除ACL_DT_UNDEFINED特殊处理外直接做静态转换即可完成映射。3. 异常安全由ADD_TRY_CATCH宏保障。对象的创建new及构造函数被包在框架的ADD_TRY_CATCH异常捕获宏中一旦构造过程中抛出异常会记录ACLNN_ERR_INNER内部错误日志并释放已分配的内存最终返回nullptr。aclScalar 内部构造按数据类型拷贝标量值aclScalar的核心构造函数实现在 src/nnopbase/common/utils/common_types.cpp它根据dataType将value指向的 Host 内存按对应类型解引用并写入内部的联合体存储aclScalar::aclScalar(const void* data, op::DataType dataType) { dataType_ dataType; switch (dataType_) { case op::DataType::DT_FLOAT: v.f *static_castconst float*(data); break; case op::DataType::DT_FLOAT16: v.ui16 *static_castconst uint16_t*(data); break; case op::DataType::DT_BF16: v.ui16 *static_castconst uint16_t*(data); break; case op::DataType::DT_INT8: v.i8 *static_castconst int8_t*(data); break; case op::DataType::DT_INT16: v.i16 *static_castconst int16_t*(data); break; case op::DataType::DT_UINT16: v.ui16 *static_castconst uint16_t*(data); break; case op::DataType::DT_UINT8: v.ui8 *static_castconst uint8_t*(data); break; case op::DataType::DT_INT32: v.i32 *static_castconst int32_t*(data); break; case op::DataType::DT_INT64: v.i64 *static_castconst int64_t*(data); break; case op::DataType::DT_UINT32: // ... break; // 其余类型依此类推 } }这里有一个值得注意的语义要点aclCreateScalar传递的是 Host 指针但aclScalar构造函数内部是按值拷贝——将标量值从 Host 内存读入框架内部存储而不是保存该指针。因此创建完成后调用方栈上的临时变量如float alphaValue即可安全销毁标量值不会随栈变量失效。aclScalar 支持的类型读取接口aclScalar在 include/nnopbase/opdev/common_types.h 中暴露了完整的数据访问接口除基础的GetData()、Size()、GetDataType()外还提供了一系列类型化读取方法方法返回值类型ToFloat()/ToDouble()/ToBool()float / double / boolToInt8()/ToInt16()/ToInt32()/ToInt64()有符号整型ToUint8()/ToUint16()/ToUint32()/ToUint64()无符号整型ToFp16()/ToBf16()fp16 / bf16ToFloat8E5M2()/ToFloat8E4M3FN()/ToFloat8E8M0()float8 系列ToFloat6E3M2()/ToFloat6E2M3()/ToFloat4E2M1()/ToFloat4E1M2()float6 / float4 系列ToHiFloat4()/ToHiFloat8()hi float 系列ToComplex64()/ToComplex128()复数ToString()字符串表示此外还提供模板方法CheckOverflowsto()用于检查 scalar 转换为目标数据类型时是否溢出。这套接口表明aclScalar的覆盖面非常广——从基础整型浮点到 fp16/bf16、float4/float6/float8 低比特浮点再到复数均可作为单算子 API 的标量入参。生命周期管理与 aclDestroyScalar 配套使用aclCreateScalar创建的aclScalar对象由框架new分配必须与销毁接口配套使用分别完成 aclScalar 的创建与销毁。aclDestroyScalar的原型与语义如下完整文档见 aclDestroyScalaraclnnStatus aclDestroyScalar(const aclScalar *scalar)参数名输入/输出说明scalar输入需要销毁的 scalar 指针。返回 0 表示成功返回其他值表示失败返回码列表参见公共接口返回码。其实现位于 src/nnopbase/common/api/acl_op_api.cppaclnnStatus aclDestroyScalar(const aclScalar* scalar) { if (scalar nullptr) { return OK; } if (unlikely(op::internal::IsAclnnDebugEnabled()) op::internal::CheckDoubleFree(const_castaclScalar*(scalar))) { OP_LOGW(Possible double-free at addr %p., static_castconst void*(scalar)); } delete scalar; return OK; }从实现可以看出空指针安全传入nullptr时直接返回成功OK因此销毁一个未创建成功的对象不会出错双重释放检测当开启 aclnn 调试IsAclnnDebugEnabled()时框架会对同一地址的重复销毁做CheckDoubleFree检测并打印Possible double-free告警日志。从 tests/nnopbase/ut/composite_op/test_check_double_free.cpp 等测试可以确认这一调试能力是框架主动提供的防护手段但在生产环境中仍需开发者自行保证一次创建对应一次销毁。标量列表当算子需要多个标量入参时当单算子 API 需要多个标量参数时逐个创建aclScalar并分别传参并不现实opbase 提供了aclScalarList标量列表结构来存储一组标量。配套接口的完整文档见 aclCreateScalarList。aclScalarList *aclCreateScalarList(const aclScalar *const *value, uint64_t size)参数名输入/输出说明value输入指向 aclScalar 指针数组首地址的指针数组内 aclScalar 指针会依次拷贝给 aclScalarList。size输入标量列表的长度取值为正整数。使用aclCreateScalarList前需提前通过aclCreateScalar创建好列表中的每个 aclScalar列表本身需与aclDestroyScalarList配套使用。其实现位于 src/nnopbase/common/api/acl_op_api.cpp同样是new aclScalarList(value, size)的封装。销毁逻辑acl_op_api.cpp值得关注aclDestroyScalarList会先遍历列表逐个销毁内部的 aclScalar再销毁外层列表对象本身aclnnStatus aclDestroyScalarList(const aclScalarList* array) { if (array nullptr) { return OK; } // 仅对外层 list 指针本身做一次预检元素由循环内 aclDestroyScalar 各自覆盖 if (unlikely(op::internal::IsAclnnDebugEnabled()) op::internal::CheckDoubleFree(const_castaclScalarList*(array))) { OP_LOGW(Possible double-free at addr %p., static_castconst void*(array)); } for (uint64_t i 0; i array-Size(); i) { aclDestroyScalar((*array)[i]); } delete array; return OK; }这带来一个重要的所有权语义一旦通过aclDestroyScalarList销毁列表其内部元素也会被框架统一销毁。因此不要再单独调用aclDestroyScalar销毁已加入列表的标量否则会造成双重释放即使有调试检测也属于使用错误。创建列表时传入的size必须是元素个数的真实值框架会按此长度遍历。如果需要查询aclScalarList的长度可使用 aclGetScalarListSize 接口aclnnStatus aclGetScalarListSize(const aclScalarList *scalarList, uint64_t *size)该接口在参数scalarList或size为空指针时返回错误码161001成功时通过size输出列表长度。调用示例与单算子 API 的完整配合流程以下代码整合了aclCreateScalar、aclDestroyScalar以及单算子 API 的两阶段调用模式先GetWorkspaceSize再执行示例仅供参考不支持直接拷贝运行// 创建aclScalar float alphaValue 1.2f; aclScalar* alpha aclCreateScalar(alphaValue, aclDataType::ACL_FLOAT); if (alpha nullptr) { // 创建失败通常为 value 为空指针或构造异常 return; } ... // aclScalar作为单算子API执行接口的入参 auto ret aclxxXxxGetWorkspaceSize(srcTensor, alpha, ..., outTensor, ..., workspaceSize, executor); ret aclxxXxx(...); ... // 销毁aclScalar ret aclDestroyScalar(alpha);当需要多个标量入参时使用标量列表模式// 创建alpha1、alpha2两个aclScalar float alpha1Value 1.2f; aclScalar *alpha1 aclCreateScalar(alpha1Value, aclDataType::ACL_FLOAT); float alpha2Value 2.2f; aclScalar *alpha2 aclCreateScalar(alpha2Value, aclDataType::ACL_FLOAT); // 创建aclScalarList std::vectoraclScalar * tempscalar{alpha1, alpha2}; aclScalarList *scalarlist aclCreateScalarList(tempscalar.data(), tempscalar.size()); // 查询列表大小 uint64_t size 0; ret aclGetScalarListSize(scalarlist, size); // size为2 ... // aclScalarList作为单算子API执行接口的入参 ret aclxxXxxGetWorkspaceSize(srcTensor, scalarlist, ..., outTensor, ..., workspaceSize, executor); ret aclxxXxx(...); ... // 销毁aclScalarList内部元素会一并销毁无需再逐个销毁alpha1/alpha2 ret aclDestroyScalarList(scalarlist);测试佐证多种数据类型的创建验证仓库单元测试 tests/nnopbase/ut/individual_op/api_utest.cpp 中对aclCreateScalar/aclCreateScalarList覆盖了多种数据类型场景包括ACL_INT32aclCreateScalar(scalarValue, aclDataType::ACL_INT32)后创建长度为 1 的标量列表ACL_FLOATaclCreateScalar(scalar_value, aclDataType::ACL_FLOAT)ACL_DOUBLEaclCreateScalar(scalar_value, aclDataType::ACL_DOUBLE)。同时 tests/nnopbase/ut/composite_op/test_acl_op_api.cpp 也在 composite op 层面覆盖了标量参数的组装与传递。这些测试印证了本文所述的调用方式也说明aclCreateScalar的实际使用场景贯穿 individual op 与 composite op 两条执行链路。使用约束与注意事项汇总综合接口文档aclCreateScalar与源码实现使用aclCreateScalar时需遵守以下约束必须配套销毁本接口需与aclDestroyScalar配套使用分别完成 aclScalar 的创建与销毁防止内存泄漏value 不能为空传入空指针会直接返回nullptr建议对返回值做判空处理dataType 必须与 value 实际类型一致dataType决定了框架按何种类型解引用value指向的内存两者不一致会导致数据被错误解释值按拷贝语义存储创建后 Host 侧变量可安全销毁标量值已拷贝至框架内部多个标量优先使用列表如需创建多个 aclScalar 对象可调用aclCreateScalarList存储标量列表且列表销毁后其内部元素由框架统一释放不要再单独销毁元素避免双重释放对同一aclScalar指针重复调用aclDestroyScalar属于使用错误虽然开启调试模式时框架会打印告警但不应依赖该机制。通过本文的介绍你已经掌握了aclCreateScalar从创建、使用到销毁的完整闭环以及标量列表的扩展用法。该接口是 CANN 单算子 API 入参组装的公共基础能力之一与其配套的aclCreateTensor、aclCreateIntArray等公共接口共同构成了完整的 aclnn 入参构造体系完整接口列表见 0_aclnn_meta_api.md可在实际算子调用中组合使用。【免费下载链接】opbase本项目是CANN算子库的基础框架库为算子提供公共依赖文件和基础调度能力。项目地址: https://gitcode.com/cann/opbase创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考