CANN ops-math 张量标量加法算子 aclnnAdds 与 aclnnInplaceAdds 完整实战指南

发布时间:2026/9/20 9:05:13

CANN ops-math 张量标量加法算子 aclnnAdds 与 aclnnInplaceAdds 完整实战指南 CANN ops-math 张量标量加法算子 aclnnAdds 与 aclnnInplaceAdds 完整实战指南【免费下载链接】ops-math本项目是CANN提供的数学类基础计算算子库实现网络在NPU上加速计算。项目地址: https://gitcode.com/cann/ops-mathaclnnAdds / aclnnInplaceAdds 是 CANN ops-math 算子库中 Add 系列面向张量 标量场景的 aclnn 单算子 API实现了out self alpha × other的带缩放标量加法语义。本文以 math/add/docs/aclnnAddsaclnnInplaceAdds.md 为骨架结合 math/add/op_api/aclnn_add.cpp 的源码实现与 math/add/tests/st/aclnnAdds/atk_aclnnAdds.json 的测试用例完整讲解接口原型、参数约束、两段式调用流程、底层计算图构建原理与可直接编译运行的调用示例帮助开发者在 Ascend NPU 上快速、正确地完成张量加标量的加速计算。产品支持情况根据官方文档与 math/add/README.md 的算子说明aclnnAdds 与 aclnnInplaceAdds 在以下产品上支持情况如下产品支持情况Ascend 950PR / Ascend 950DT支持Atlas A3 训练系列产品 / Atlas A3 推理系列产品支持Atlas A2 训练系列产品 / Atlas A2 推理系列产品支持Atlas 200I/500 A2 推理产品不支持Atlas 推理系列产品支持Atlas 训练系列产品支持注意aclnnAdds与aclnnInplaceAdds在 Atlas 推理系列产品310p上支持而双张量版本的aclnnAdd/aclnnInplaceAdd在该产品上不支持选用接口时请按目标硬件核对支持矩阵。功能说明接口功能完成张量 标量的加法计算其中标量可以乘以缩放系数alpha。计算公式$$ out_i self_i alpha \times other $$其中self为张量other与alpha均为标量aclScalar。当alpha 1时公式退化为out self other此时接口内部会走专门的快速路径见下文源码分析。该接口语义与 PyTorch 的torch.add(input, other, alpha1)张量加标量形式一致从 atk_aclnnAdds.json 的测试用例可以看出其基准对标函数即torch.add。函数原型与两段式接口aclnnAdds与aclnnInplaceAdds实现相同的计算功能二者的区别仅在于输出结果的存储方式aclnnAdds需要调用方新建一个输出张量对象out来存储计算结果输入张量在计算前后保持不变。aclnnInplaceAdds无需新建输出张量对象直接在输入张量selfRef的内存中就地写入计算结果可减少一次输出张量的内存申请与搬运。与 CANN 单算子 API 的通用规范一致每个算子都遵循两段式接口设计必须先调用第一段...GetWorkspaceSize接口获取本次计算所需的 workspace 大小以及封装了算子计算流程的执行器aclOpExecutor再调用第二段接口执行真正的计算。两段式接口的完整形态如下// aclnnAdds 第一段参数校验 获取 workspace 大小与执行器 aclnnStatus aclnnAddsGetWorkspaceSize( const aclTensor* self, // 输入张量 const aclScalar* other, // 输入标量 const aclScalar* alpha, // 缩放标量 aclTensor* out, // 输出张量 uint64_t* workspaceSize, // 输出workspace 大小 aclOpExecutor** executor) // 输出算子执行器 // aclnnAdds 第二段执行计算 aclnnStatus aclnnAdds( void* workspace, // Device 侧申请的 workspace 内存地址 uint64_t workspaceSize, // 由第一段接口获取的大小 aclOpExecutor* executor, // 第一段接口返回的执行器 aclrtStream stream) // 指定执行任务的 Stream // aclnnInplaceAdds 第一段 aclnnStatus aclnnInplaceAddsGetWorkspaceSize( const aclTensor *selfRef, // 输入/输出张量结果写回该张量 const aclScalar *other, const aclScalar *alpha, uint64_t *workspaceSize, aclOpExecutor **executor) // aclnnInplaceAdds 第二段 aclnnStatus aclnnInplaceAdds( void *workspace, uint64_t workspaceSize, aclOpExecutor *executor, aclrtStream stream)关于两段式接口需要特别注意的是workspace 是指除输入/输出外算子在 NPU 上完成计算所需的临时内存workspaceSize表示该临时内存的大小具体数值由第一段接口根据计算图动态推算第一段接口仅做入参校验与计算图构建不会真正执行计算第二段接口aclnnAdds(...)不能重复调用同一个 executor 调用两次属于异常用法若workspaceSize为 0则无需申请 workspace第二段接口可直接传入nullptr。aclnnAddsGetWorkspaceSize 参数说明参数名输入/输出描述使用说明数据类型数据格式维度(shape)非连续Tensorself输入公式中的输入 self-FLOAT、FLOAT16、DOUBLE、INT32、INT64、INT16、INT8、UINT8、BOOL、COMPLEX128、COMPLEX64、BFLOAT16ND不高于8维√other输入公式中的 other标量-FLOAT、FLOAT16、DOUBLE、INT32、INT64、INT16、INT8、UINT8、BOOL、COMPLEX128、COMPLEX64、BFLOAT16---alpha输入公式中的 alpha标量数据类型需要可转换成 self 与 other 推导后的数据类型FLOAT、FLOAT16、DOUBLE、INT32、INT64、INT16、INT8、UINT8、BOOL、COMPLEX128、COMPLEX64、BFLOAT16---out输出公式中的 out数据类型需要是 self 与 other 推导之后可转换的数据类型参见互转换关系FLOAT、FLOAT16、DOUBLE、INT32、INT64、INT16、INT8、UINT8、BOOL、COMPLEX128、COMPLEX64、BFLOAT16ND与 self 一致√workspaceSize输出返回需要在 Device 侧申请的 workspace 大小-----executor输出返回 op 执行器包含了算子计算流程-----不同产品上的类型约束差异Atlas 推理系列产品、Atlas 训练系列产品不支持 BFLOAT16 数据类型self 与 other 数据类型需满足互推导关系。Atlas A2 训练系列产品 / Atlas A2 推理系列产品、Atlas A3 训练系列产品 / Atlas A3 推理系列产品self 与 other 数据类型需满足互推导关系。Ascend 950PR / Ascend 950DTother 数据类型与 self 需满足 TensorScalar 互推导关系。返回值aclnnStatus返回状态码具体参见 aclnn返回码。第一段接口完成入参校验出现以下场景时报错返回码错误码描述ACLNN_ERR_PARAM_NULLPTR161001传入的 self、other、out 或 alpha 是空指针。ACLNN_ERR_PARAM_INVALID161002self 的数据类型不在支持的范围之内。ACLNN_ERR_PARAM_INVALID161002self 和 other 无法做数据类型推导。ACLNN_ERR_PARAM_INVALID161002推导出的数据类型无法转换为 out 的类型。ACLNN_ERR_PARAM_INVALID161002alpha 无法转换为 self 和 other 推导后的数据类型。ACLNN_ERR_PARAM_INVALID161002self 与 out 的 shape 不一致。ACLNN_ERR_PARAM_INVALID161002self 与 out 的维度大于 8。说明ACLNN_ERR_PARAM_NULLPTR161001与ACLNN_ERR_PARAM_INVALID161002均为参数校验类错误运行时类错误以361xxx开头API 内部异常以561xxx开头完整的错误码语义可查阅 docs/zh/context/aclnn_return_code.md。aclnnAdds 参数说明参数名输入/输出描述workspace输入在 Device 侧申请的 workspace 内存地址。workspaceSize输入在 Device 侧申请的 workspace 大小由第一段接口 aclnnAddsGetWorkspaceSize 获取。executor输入op 执行器包含了算子计算流程。stream输入指定执行任务的 Stream。返回值aclnnStatus具体参见 aclnn返回码。aclnnInplaceAddsGetWorkspaceSize 参数说明参数名输入/输出描述使用说明数据类型数据格式维度(shape)非连续TensorselfRef输入/输出公式中的输入 selfRef计算完成后结果直接写回该张量-FLOAT、FLOAT16、DOUBLE、INT32、INT64、INT16、INT8、UINT8、BOOL、BFLOAT16ND不超过8维√other输入公式中的 other标量-FLOAT、FLOAT16、DOUBLE、INT32、INT64、INT16、INT8、UINT8、BOOL、BFLOAT16---alpha输入公式中的 alpha标量数据类型需要可转换成 selfRef 与 other 推导后的数据类型FLOAT、FLOAT16、DOUBLE、INT32、INT64、INT16、INT8、UINT8、BOOL、BFLOAT16---workspaceSize输出返回需要在 Device 侧申请的 workspace 大小-----executor输出返回 op 执行器包含了算子计算流程-----不同产品上的类型约束差异Atlas 训练系列产品不支持 BFLOAT16 数据类型selfRef 与 other 需满足互推导关系且需要是推导之后可转换的数据类型参见互转换关系。Atlas A2 训练系列产品 / Atlas A2 推理系列产品、Atlas A3 训练系列产品 / Atlas A3 推理系列产品selfRef 与 other 需满足互推导关系且需要是推导之后可转换的数据类型。Ascend 950PR / Ascend 950DTselfRef 与 other 需满足 TensorScalar 互推导关系且需要是推导之后可转换的数据类型。返回值aclnnStatus返回码错误码描述ACLNN_ERR_PARAM_NULLPTR161001传入的 selfRef、other 或 alpha 是空指针。ACLNN_ERR_PARAM_INVALID161002selfRef 的数据类型不在支持的范围之内。ACLNN_ERR_PARAM_INVALID161002selfRef 和 other 无法做数据类型推导。ACLNN_ERR_PARAM_INVALID161002推导出的数据类型无法转换为 selfRef 的类型。ACLNN_ERR_PARAM_INVALID161002alpha 无法转换为 selfRef 和 other 推导后的数据类型。aclnnInplaceAdds 参数说明参数名输入/输出描述workspace输入在 Device 侧申请的 workspace 内存地址。workspaceSize输入在 Device 侧申请的 workspace 大小由第一段接口 aclnnInplaceAddsGetWorkspaceSize 获取。executor输入op 执行器包含了算子计算流程。stream输入指定执行任务的 Stream。返回值aclnnStatus具体参见 aclnn返回码。约束说明确定性计算aclnnAdds 与 aclnnInplaceAdds 默认采用确定性实现同一输入下多次执行结果可复现。精度约束other参数对于 float 无精度损失对于 int32、int64 数据类型当other参数大于 2^24 时可能存在精度损失。此时推荐改用双张量版本的 aclnnAdd / aclnnInplaceAdd避免标量精度损失问题。源码级实现解析理解了接口与约束后我们再深入 math/add/op_api/aclnn_add.cpp看第一段接口内部到底做了什么。这有助于理解参数校验规则和 workspace 的来源。第一段接口的完整流程aclnnAddsGetWorkspaceSize的源码实现aclnn_add.cpp大致分为以下几个步骤创建执行器调用框架宏CREATE_EXECUTOR()创建aclOpExecutor失败返回ACLNN_ERR_INNER_CREATE_EXECUTOR。入参校验调用CheckParamsScalar完成空指针、数据类型、推导关系、shape 一致性等全部校验该校验逻辑与文档中的错误码表格一一对应。空张量短路若self-IsEmpty()直接返回workspaceSize 0并释放执行器kernel 层支持空张量。类型推导与计算图构建根据PromoteTypeScalar推导出的计算类型promoteType将标量other通过ConvertToTensor转换为张量然后按分支构建计算图见下文。结果类型转换与写回将计算结果Cast为out的数据类型再通过ViewCopy写入输出张量out 可能是非连续张量。返回 workspace通过uniqueExecutor-GetWorkspaceSize()汇总整张计算图所需临时内存并通过ReleaseTo(executor)将执行器所有权转移给调用方。标量类型推导规则 PromoteTypeScalarPromoteTypeScalaraclnn_add.cpp负责确定self张量与other标量参与运算的最终计算类型若self为浮点类型FLOAT推导结果直接取self的类型若推导结果为 FLOAT16 或 BFLOAT16则进一步检查other与alpha是否能无损转成该半精度类型若不能则提升为 FLOAT避免精度损失若推导出 COMPLEX32则统一提升为 COMPLEX64若self为 BOOL 或other为浮点类型则按op::PromoteType通用推导规则计算。这也解释了文档中alpha 的数据类型需要可转换成 self 与 other 推导后的数据类型这一约束的来历——CheckPromoteType会调用CanCast校验 alpha 能否无损转换到推导类型。计算图构建的四种分支aclnnAddsGetWorkspaceSize在构建计算图时会针对不同情况选择不同的底层算子组合aclnn_add.cpp分支条件底层计算图说明alpha 等于 1Add(self, other)免去标量乘法直接调用 L0 层 Add支持 AxpyAxpy(self, other, alpha)将 乘加 融合为单算子性能最优支持 AxpyV2仅 RegBase 架构AxpyV2(self, other, alphaTensor)alpha 以张量形式传入的融合乘加其他情况Mul(other, alphaTensor)后再Add(self, otherRes)通用拆解路径其中IsEqualToOneaclnn_add.cpp用于判断 alpha 是否等于 1IsSupportAxpy/IsSupportAxpyV2依据当前 NPU 架构与推导类型决定能否走融合算子。此外在进入上述分支前源码还会视需要插入Contiguous转连续张量与Cast隐式类型转换节点在输出侧统一追加Cast转回 out 类型与ViewCopy写回输出节点。整个流程可以概括为self / other(标量) ── Contiguous ── Cast(promoteType) └─ Add / Axpy / AxpyV2 / MulAdd │ v Cast(out类型) ── ViewCopy ── out一个值得注意的细节是 BOOL 类型的特殊处理aclnn_add.cpp当 self、other、alpha 均为 BOOL 且 out 非 BOOL、other alpha为真时会额外插入一次Cast 到 BOOL 再 Cast 回来的中间节点防止 BOOL 相加出现值为 2 的错误结果。Inplace 版本的实现方式aclnnInplaceAddsGetWorkspaceSize的实现非常简洁aclnn_add.cpp它将selfRef通过const_cast直接作为out传入aclnnAddsGetWorkspaceSize复用同一套校验与构图逻辑最终计算结果通过ViewCopy写回selfRef所在的内存。这就是就地计算、无需新建输出张量的实现本质。从更底层的 L0 视角看l0op::Add会先对self与other做 broadcast shape 推导若输入为混合数据类型如 FP16FP32、BF16FP32则自动输出 FLOAT 类型中间结果见 math/add/op_api/add.cpp而AddInplace则额外校验 broadcast 后的 shape 必须与输出张量一致add.cpp。测试用例佐证atk_aclnnAdds.json 中的 ATKAscend Test Kit用例以torch.add为基准函数覆盖了 fp32、fp16、bf16、fp64、int8、int16、int32、int64、bool 等数据类型shape 覆盖 1~4 维与[1,1,1,1]等边界形态以及nan、inf、-inf、[-inf, inf]等特殊输入值。其中绝大部分用例将alpha固定为 1对应alpha 等于 1 走快速路径的分支同时也有alpha为一般值的用例覆盖通用 MulAdd 路径可作为回归验证的参考。调用示例以下示例代码来自仓库 math/add/examples/test_aclnn_adds.cpp在同一程序中依次演示aclnnAdds输出到新建张量 out与aclnnInplaceAdds结果写回 self 内存的完整调用流程。具体编译和执行过程请参考编译与运行样例。#include iostream #include vector #include acl/acl.h #include aclnnop/aclnn_add.h #define CHECK_RET(cond, return_expr) \ do { \ if (!(cond)) { \ return_expr; \ } \ } while (0) #define LOG_PRINT(message, ...) \ do { \ printf(message, ##__VA_ARGS__); \ } while (0) int64_t GetShapeSize(const std::vectorint64_t shape) { int64_t shapeSize 1; for (auto i : shape) { shapeSize * i; } return shapeSize; } int Init(int32_t deviceId, aclrtStream* stream) { // 固定写法资源初始化 auto ret aclInit(nullptr); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclInit failed. ERROR: %d\n, ret); return ret); ret aclrtSetDevice(deviceId); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclrtSetDevice failed. ERROR: %d\n, ret); aclFinalize(); return ret); ret aclrtCreateStream(stream); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclrtCreateStream failed. ERROR: %d\n, ret); aclrtResetDevice(deviceId); aclFinalize(); return ret); return 0; } template typename T int CreateAclTensor(const std::vectorT hostData, const std::vectorint64_t shape, void** deviceAddr, aclDataType dataType, aclTensor** tensor) { auto size GetShapeSize(shape) * sizeof(T); // 调用aclrtMalloc申请device侧内存 auto ret aclrtMalloc(deviceAddr, size, ACL_MEM_MALLOC_HUGE_FIRST); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclrtMalloc failed. ERROR: %d\n, ret); return ret); // 调用aclrtMemcpy将host侧数据拷贝到device侧内存上 ret aclrtMemcpy(*deviceAddr, size, hostData.data(), size, ACL_MEMCPY_HOST_TO_DEVICE); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclrtMemcpy failed. ERROR: %d\n, ret); return ret); // 计算连续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]; } // 调用aclCreateTensor接口创建aclTensor *tensor aclCreateTensor(shape.data(), shape.size(), dataType, strides.data(), 0, aclFormat::ACL_FORMAT_ND, shape.data(), shape.size(), *deviceAddr); return 0; } int main() { // 1.固定写法device/stream初始化参考acl API手册 // 根据自己的实际device填写deviceId int32_t deviceId 0; aclrtStream stream; auto ret Init(deviceId, stream); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(Init acl failed. ERROR: %d\n, ret); return ret); // 2. 构造输入与输出需要根据API的接口自定义构造 std::vectorint64_t selfShape {4, 2}; std::vectorint64_t outShape {4, 2}; void* selfDeviceAddr nullptr; void* outDeviceAddr nullptr; aclTensor* self nullptr; aclScalar* other nullptr; aclScalar* alpha nullptr; aclTensor* out nullptr; std::vectorfloat selfHostData {0, 1, 2, 3, 4, 5, 6, 7}; std::vectorfloat outHostData(8, 0); float otherValue 2.0f; float alphaValue 1.2f; // 创建self aclTensor ret CreateAclTensor(selfHostData, selfShape, selfDeviceAddr, aclDataType::ACL_FLOAT, self); CHECK_RET(ret ACL_SUCCESS, return ret); // 创建other aclScalar other aclCreateScalar(otherValue, aclDataType::ACL_FLOAT); CHECK_RET(other ! nullptr, return ret); // 创建alpha aclScalar alpha aclCreateScalar(alphaValue, aclDataType::ACL_FLOAT); CHECK_RET(alpha ! nullptr, return ret); // 创建out aclTensor ret CreateAclTensor(outHostData, outShape, outDeviceAddr, aclDataType::ACL_FLOAT, out); CHECK_RET(ret ACL_SUCCESS, return ret); // 3. 调用CANN算子库API需要修改为具体的API名称 uint64_t workspaceSize 0; aclOpExecutor* executor; // 调用aclnnAdds第一段接口 ret aclnnAddsGetWorkspaceSize(self, other, alpha, out, workspaceSize, executor); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclnnAddsGetWorkspaceSize failed. ERROR: %d\n, ret); return ret); // 根据第一段接口计算出的workspaceSize申请device内存 void* workspaceAddr nullptr; if (workspaceSize 0) { ret aclrtMalloc(workspaceAddr, workspaceSize, ACL_MEM_MALLOC_HUGE_FIRST); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(allocate workspace failed. ERROR: %d\n, ret); return ret); } // 调用aclnnAdds第二段接口 ret aclnnAdds(workspaceAddr, workspaceSize, executor, stream); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclnnAdds failed. ERROR: %d\n, ret); return ret); // 4.固定写法同步等待任务执行结束 ret aclrtSynchronizeStream(stream); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclrtSynchronizeStream failed. ERROR: %d\n, ret); return ret); // 5. 获取输出的值将device侧内存上的结果拷贝至host侧需要根据具体API的接口定义修改 auto size GetShapeSize(outShape); std::vectorfloat resultData(size, 0); ret aclrtMemcpy(resultData.data(), resultData.size() * sizeof(resultData[0]), outDeviceAddr, size * sizeof(resultData[0]), ACL_MEMCPY_DEVICE_TO_HOST); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(copy result from device to host failed. ERROR: %d\n, ret); return ret); for (int64_t i 0; i size; i) { LOG_PRINT(result[%ld] is: %f\n, i, resultData[i]); } // aclnnInplaceAdds接口调用示例 // 3. 调用CANN算子库API LOG_PRINT(\ntest aclnnInplaceAdds\n); // 调用aclnnInplaceAdds第一段接口 ret aclnnInplaceAddsGetWorkspaceSize(self, other, alpha, workspaceSize, executor); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclnnInplaceAddsGetWorkspaceSize failed. ERROR: %d\n, ret); return ret); // 根据第一段接口计算出的workspaceSize申请device内存 if (workspaceSize 0) { ret aclrtMalloc(workspaceAddr, workspaceSize, ACL_MEM_MALLOC_HUGE_FIRST); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(allocate workspace failed. ERROR: %d\n, ret); return ret); } // 调用aclnnInplaceAdds第二段接口 ret aclnnInplaceAdds(workspaceAddr, workspaceSize, executor, stream); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclnnInplaceAdds failed. ERROR: %d\n, ret); return ret); // 4.固定写法同步等待任务执行结束 ret aclrtSynchronizeStream(stream); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclrtSynchronizeStream failed. ERROR: %d\n, ret); return ret); // 5. 获取输出的值将device侧内存上的结果拷贝至host侧需要根据具体API的接口定义修改 ret aclrtMemcpy(resultData.data(), resultData.size() * sizeof(resultData[0]), selfDeviceAddr, size * sizeof(resultData[0]), ACL_MEMCPY_DEVICE_TO_HOST); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(copy result from device to host failed. ERROR: %d\n, ret); return ret); for (int64_t i 0; i size; i) { LOG_PRINT(result[%ld] is: %f\n, i, resultData[i]); } // 6. 释放aclTensor和aclScalar需要根据具体API的接口定义修改 aclDestroyTensor(self); aclDestroyScalar(other); aclDestroyScalar(alpha); aclDestroyTensor(out); // 7. 释放device资源 aclrtFree(selfDeviceAddr); aclrtFree(outDeviceAddr); if (workspaceSize 0) { aclrtFree(workspaceAddr); } aclrtDestroyStream(stream); aclrtResetDevice(deviceId); aclFinalize(); return 0; }示例运行结果说明输入self {0,1,2,3,4,5,6,7}、other 2.0、alpha 1.2则out_i self_i 1.2 × 2.0 self_i 2.4aclnnAdds输出{2.4, 3.4, 4.4, 5.4, 6.4, 7.4, 8.4, 9.4}随后aclnnInplaceAdds基于已被修改的 self 再执行一次同样的运算结果{4.8, 5.8, 6.8, 7.8, 8.8, 9.8, 10.8, 11.8}写回 self 内存。该示例同时演示了两段式接口的固定套路初始化 → 构造张量/标量 → 第一段取 workspace → 按需申请 Device 内存 → 第二段执行 → 同步 Stream → 拷贝结果 → 释放资源。示例关键点提示头文件#include aclnnop/aclnn_add.h是 aclnnAdds / aclnnInplaceAdds 等 Add 系列接口的声明头文件。标量创建other与alpha都通过aclCreateScalar(value, aclDataType::ACL_FLOAT)创建需要传入值的指针与数据类型示例中将其声明为aclScalar*并通过aclDestroyScalar释放。workspace 申请只有workspaceSize 0时才需要aclrtMalloc申请 Device 内存否则workspaceAddr保持nullptr即可。Inplace 语义aclnnInplaceAddsGetWorkspaceSize只接受selfRef、other、alpha三个参数没有 out计算完成后从selfDeviceAddr对应的内存读取结果。总结aclnnAdds / aclnnInplaceAdds 是 ops-math 中张量 标量场景的标准解法二者语义相同、仅输出方式不同前者需要独立输出张量后者直接原地写回输入。掌握其两段式接口调用范式GetWorkspaceSize → 申请 workspace → 执行 → 同步理解第一段接口内部的类型推导、alpha1 快速路径、Axpy 融合与 Inplace 复用机制即可在目标 NPU 产品上高效、正确地完成带缩放系数的标量加法计算。若需对 Add 系列算子有更全面的了解可继续阅读双张量版本的 aclnnAdd / aclnnInplaceAdd 文档或查看底层 kernel 实现 math/add/op_kernel、tiling 配置 math/add/op_host/config 与单元测试 math/add/tests。【免费下载链接】ops-math本项目是CANN提供的数学类基础计算算子库实现网络在NPU上加速计算。项目地址: https://gitcode.com/cann/ops-math创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
延伸阅读

更多相关文章

2026/9/20 9:05:13

GitHub热榜上的迷你小模型:本地部署与量化实践指南

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

2026/9/20 9:00:12

企业级MultiAgent落地方案:从Plan模式到主子Agent协作的工程实践

做 MultiAgent 的人很多,真正敢把它放到生产环境里的团队没几个。这不是模型能力跟不上,而是工程化难度被严重低估了。去年我们在得物内部启动了一个 MultiAgent 项目,目标是让一组不同职责的 Agent 协作完成一条完整的业务链路。跑通 Demo 只…

2026/9/20 10:15:23

小学生学C++几年级开始合适

小学生学C的黄金启动窗口是四年级到五年级,完全适配你家孩子当前的四年级节奏,不同年级的适配性差异非常明确: ❌ 1-3年级:绝对不建议系统学C 这个阶段孩子以具象思维为主,完全无法理解变量、循环嵌套等抽象C语法&…

2026/9/20 10:15:23

cnPuTTY CAC 0.83中文版发布与安全使用指南

1. cnPuTTY CAC 0.83 Update 1中文版本发布说明 作为一名长期从事服务器运维工作的技术人员,我深知PuTTY在Windows平台SSH连接中的重要性。今天要介绍的是cnPuTTY CAC 0.83 Update 1中文版本,这是一个基于PuTTY CAC的本地化版本。 需要特别说明的是&am…

2026/9/20 10:15:23

OpenResearch:一套可回溯可复现的研究工作流实践

开头部分我打算这样写:直接以一位长期在研究一线、被传统研究流程折磨过的从业者口吻切入,说明OpenResearch是什么——不是某个具体软件,而是一套把研究过程系统化、开放化、可追踪的完整工作流。然后点出它解决的核心痛点(回溯困…

2026/9/20 10:15:23

四年级孩子学信奥需要报班还是自学

四年级孩子学信奥,没有绝对的“必须报班”或“完全自学”,核心看孩子的自律性、家长的时间精力和升学目标,适配你家情况的最优方案是「入门阶段轻量自学关键节点针对性报班」的组合模式。 📚 自学能走通的前提(缺一不可…

2026/9/20 10:15:23

用Git Worktree和Rust工具管理并行Claude Code会话

同时开 10 个 Claude Code 这事儿,听起来很爽,真干起来很崩溃。我最开始就是简单地开 10 个终端窗口,往同一个项目目录里一戳,然后给每个会话派不同的任务。结果半小时之后整个工作区彻底失控:A 会话改的文件被 B 会话…

2026/9/20 0:04:49

GAMP 5 基于风险的计算机化系统验证:软件分类与审计追踪实践

简介:《A Risk-Based Approach to Compliant GxP Computerized Systems》即业内熟知的GAMP 5指南,面向制药企业质量与IT合规人员、验证工程师及计算机化系统管理者,用于解决GxP法规环境下系统合规性难以科学落地的问题。文档以风险管理为主线…

2026/9/20 0:04:49

安全托管MSSP实战:从静态防御到人机协同的攻防运营与应急响应

简介:这份PPT围绕互联网业务安全托管服务展开,面向企业安全负责人、IT运维人员及关注MSSP/MSS选型的读者,重点回应传统安全过度依赖人工、碎片化静态防御难以对抗产业化攻击等痛点。资源共1个pptx文件,包体约30.63MB,以…

2026/9/20 0:04:49

GAMP 5 基于风险的计算机化系统验证:软件分类与审计追踪实践

简介:《A Risk-Based Approach to Compliant GxP Computerized Systems》即业内熟知的GAMP 5指南,面向制药企业质量与IT合规人员、验证工程师及计算机化系统管理者,用于解决GxP法规环境下系统合规性难以科学落地的问题。文档以风险管理为主线…

2026/9/20 0:04:49

安全托管MSSP实战:从静态防御到人机协同的攻防运营与应急响应

简介:这份PPT围绕互联网业务安全托管服务展开,面向企业安全负责人、IT运维人员及关注MSSP/MSS选型的读者,重点回应传统安全过度依赖人工、碎片化静态防御难以对抗产业化攻击等痛点。资源共1个pptx文件,包体约30.63MB,以…

2026/9/20 4:54:47

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/20 5:01:23

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/20 5:09:33

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
咨询二维码