资讯详情

CANN ops-math 中的 Neg 算子:aclnnNeg 与 aclnnInplaceNeg 取负运算完全指南

📅 2026/9/19 17:56:34 | 华诺云谱 👁 阅读
CANN ops-math 中的 Neg 算子:aclnnNeg 与 aclnnInplaceNeg 取负运算完全指南
算子库人工智能CANN【免费下载链接】ops-math本项目是CANN提供的数学类基础计算算子库实现网络在NPU上加速计算。项目地址https://gitcode.com/cann/ops-math点击查看免费下载Neg 是 CANN ops-math 数学算子库中实现逐元素取负相反数运算的基础算子通过aclnnNeg/aclnnInplaceNeg两段式接口对外提供能力是神经网络中符号翻转、梯度计算等场景的常用构件。本文以 Neg 算子 README 与其配套的 aclnnNegaclnnInplaceNeg 接口文档 为主体结合算子 API、Host 侧 tiling、Kernel 侧实现与可运行样例完整讲解该算子的规格、接口、源码原理与调用方式。读完本文你将能够在 Atlas A2 系列产品上独立编译运行 Neg 算子样例并理解其底层取负 类型转换 视图拷贝的完整计算流水。Neg 算子概述Neg算子对输入的数值型数据执行逐元素取负操作计算公式为y -x即对输入张量x中的每一个元素计算其相反数并写入输出张量y。该算子在 neg_def.cpp 中通过算子原型注册OP_ADD(Neg)定义其 shape 推导规则非常简单——输出与输入形状完全一致见 neg_infershape.cpp 中的InferShapeNeg直接将输入 shape 拷贝给输出。算子规格描述根据 READMENeg 算子的规格如下项名称Typedata typeformat算子输入xtensorint64, int32, int16, int8, uint8, float16, bfloat16, float32ND算子输出ytensor与输入相同ND核函数名--neg-从源码结构看aclnn_neg接口层实际支持的 dtype 范围更宽。在 aclnn_neg.cpp 中定义了两份 dtype 支持列表dtype_support_listint8/uint8/int16/int32/int64/float16/float32/double/complex64/complex128与dtype_support_list_afterV200在上述基础上追加 bfloat16。是否启用后者由GetCurNpuArch()判断——当芯片架构为DAV_2201或DAV_3510即昇腾 910B 及后续架构时bfloat16 才会被放行。算子类型与贡献信息该算子位于experimental/math/neg目录属于实验性数学算子的增量贡献。按 README 中的贡献表Neg 算子由个人开发者新增并持续演进最初贡献 Neg 算子主体随后贡献了 int64/int16/uint8 三种整数类型的支持。目录内的完整代码结构如下op_api/aclnn 对外 API 层aclnn_neg.h/.cpp、neg.h/.cppop_host/算子原型、shape 推导与 tilingneg_def.cpp、neg_infershape.cpp、neg_tiling.cppop_kernel/昇腾 AI Core 上的 kernel 实现neg.cpp、neg.h、neg_tiling_data.h、neg_tiling_key.hexamples/可直接编译运行的调用样例test_aclnn_neg.cpp、test_aclnn_inplace_neg.cppdocs/接口详细说明 aclnnNegaclnnInplaceNeg.md支持的产品型号与环境要求按 README 说明本算子样例支持如下产品型号Atlas A2 训练系列产品Atlas 800I A2 推理产品配套的 接口文档 亦确认 Atlas A2 训练系列产品/Atlas A2 推理系列产品均支持本接口。这与源码中的 AICore 支持列表相互印证neg.cpp中的AICORE_DTYPE_SUPPORT_LIST_AFTER_910B面向DAV_2201、DAV_3510架构也就是 910B 之后的昇腾芯片。编译运行此样例前需要先按 CANN 软件安装指南完成开发运行环境的部署包括 CANN 工具链、昇腾驱动与固件并准备好编译工具链。环境就绪后可通过 quick_op_invocation.md 描述的build.sh脚本方式快速编译运行样例。两段式接口aclnnNeg 与 aclnnInplaceNegCANN 的算子 API 采用标准的两段式调用模型Neg 算子同样遵循该模型。两段式接口的通用背景可参考 two_phase_api.md第一段接口负责参数校验、构建算子计算流程并返回所需的 workspace 大小与执行器第二段接口才真正把任务提交到指定 Stream 上执行。aclnnNeg与aclnnInplaceNeg实现完全相同的数学功能区别仅在于输出的承载方式aclnnNeg需要调用方新建一个输出张量对象out来存储计算结果原输入保持不变aclnnInplaceNeg无需新建输出张量直接在输入张量selfRef的内存中就地写入结果原地取负适合需要省内存、允许破坏原始数据的场景。四个接口的函数原型如下声明见 aclnn_neg.haclnnStatus aclnnNegGetWorkspaceSize(const aclTensor *self, aclTensor *out, uint64_t *workspaceSize, aclOpExecutor **executor); aclnnStatus aclnnNeg(void *workspace, uint64_t workspaceSize, aclOpExecutor *executor, const aclrtStream stream); aclnnStatus aclnnInplaceNegGetWorkspaceSize(aclTensor *selfRef, uint64_t *workspaceSize, aclOpExecutor **executor); aclnnStatus aclnnInplaceNeg(void *workspace, uint64_t workspaceSize, aclOpExecutor *executor, aclrtStream stream);aclnnNegGetWorkspaceSize 参数说明参数方向说明self计算输入Device 侧的aclTensor*支持非连续 Tensor见 non_contiguous_tensor.md数据格式支持 ND见 data_format.mdout计算输出Device 侧的aclTensor*数据类型需是 self 可转换的类型类型转换关系见 conversion_relationship.md同样支持非连续 Tensor 与 ND 格式workspaceSize计算输出返回需要在 Device 侧申请的 workspace 大小executor计算输出返回 op 执行器封装了算子计算流程返回值aclnnStatus为统一状态码具体定义参见 aclnn_return_code.md。第一段接口会完成入参校验主要错误场景161001(ACLNN_ERR_PARAM_NULLPTR): 1. 传入的 self、out 是空指针。 161002(ACLNN_ERR_PARAM_INVALID): 1. self 和 out 的数据类型不在支持的范围之内。 2. self 与 out 的 dtype 不同。 3. self 与 out 的 shape 不同。这些校验逻辑在 aclnn_neg.cpp 的CheckParams中一一对应CheckNotNull检查空指针、CheckDtypeValid检查 dtype 是否在支持列表内、CheckPromoteType校验 self 的 dtype 能否转换为 out 的 dtype、CheckShape校验维度不超过 8 且 shape 一致。aclnnNeg 参数说明参数方向说明workspace计算输入在 Device 侧申请的 workspace 内存起始地址workspaceSize计算输入在 Device 侧申请的 workspace 大小由第一段接口获取executor计算输入op 执行器包含算子计算流程stream计算输入指定执行任务的 acl 流aclnnInplaceNegGetWorkspaceSize 参数说明参数方向说明selfRef计算输入/计算输出Device 侧的aclTensor*既是输入也是输出就地计算支持非连续 TensorND 格式workspaceSize计算输出返回需要在 Device 侧申请的 workspace 大小executor计算输出返回 op 执行器第一段接口的错误场景161001(ACLNN_ERR_PARAM_NULLPTR): 1. 传入的 selfRef 是空指针。 161002(ACLNN_ERR_PARAM_INVALID): 1. selfRef 的数据类型不在支持的范围之内。 2. selfRef 的维度范围超过 8。从实现上看aclnnInplaceNegGetWorkspaceSize并没有单独写一套流程而是直接复用普通接口——将selfRef同时作为 self 和 out 传入aclnnNegGetWorkspaceSize见 aclnn_neg.cpp因此它天然继承了所有 dtype、shape 一致性校验只是 out 与 self 指向同一块内存。aclnnInplaceNeg 参数说明与aclnnNeg完全相同workspaceDevice 侧 workspace 内存地址、workspaceSize由第一段接口获取、executorop 执行器、stream任务执行的 acl 流。源码原理取负的完整计算流水理解了接口我们再深入源码看这条计算流水到底做了什么。aclnnNegGetWorkspaceSize的构建逻辑aclnn_neg.cpp清晰展示了 API 的完整计算路径空 Tensor 快速返回若self-IsEmpty()为真直接返回 workspace 大小为 0空 Tensor 在 kernel 中天然支持Contiguous调用l0op::Contiguous把非连续输入规整为连续 Tensor这就是支持非连续 Tensor的实现基础Neg 计算调用l0op::Neg(selfContiguous, ...)执行真正的取负 kernelCast 类型转换调用l0op::Cast把计算结果转换为 out 的数据类型支撑out 是 self 可转换类型的能力ViewCopy 落盘调用l0op::ViewCopy将结果写入 out兼容 out 可能是非连续 Tensor 的情况最后通过uniqueExecutor-GetWorkspaceSize()汇总整条流水所需 workspace 大小。第二段接口aclnnNeg/aclnnInplaceNeg的实现则非常简洁直接调用框架的CommonOpExecutorRun完成提交执行。AiCore 与 AiCpu 的分发策略l0op::Neg本身neg.cpp会根据输入 dtype 和当前芯片架构决定走哪条计算路径AiCore 路径当 dtype 命中AICORE_DTYPE_SUPPORT_LISTfloat/float16/int32/int8/int64/int16/uint8/bf16时通过ADD_TO_LAUNCHER_LIST_AICORE宏将 Neg 算子加入 AiCore 任务队列AiCpu 路径其余情况通过ADD_TO_LAUNCHER_LIST_AICPU宏回退到 AiCpu 执行保证接口的通用性。Kernel 层的逐类型实现AiCore 上的 kernel 实现neg.cpp 与 neg.h对每种 dtype 都做了针对性处理这是取负看似简单、实则有坑的地方float / half直接调用向量指令Muls(dst, src, -1)完成乘 -1int32 / int16先Duplicate(-1)再用Mul逐元素相乘int8由于向量乘单元不支持直接对 int8 做乘法先Cast到 half 计算-1倍再通过左移/右移 8 位模拟 int8 的溢出截断行为最后转回 int8uint8与 PyTorch 的torch.neg(uint8)语义对齐——保持 uint8 类型并按 256 取模回绕。实现上先转 half 乘 -1、加 256再与 255 做按位与mask实现模 256 效果int6464 位取负最为复杂采用按位取反 加 1的补码思路Not逐位取反后通过掩码实现对低 32 位 1 的进位传播含低位进位向高位传递的判断详细流程见 neg.h 中 int64 分支的实现bfloat16因Muls指令不支持 bfloat16先Cast到 float 乘 -1再Cast回 bf16。Tiling 策略按核心均分 尾部块处理Host 侧的 neg_tiling.cpp 负责把输入数据切分到多个 AI Core 上并行计算。核心思路是按 32BBLOCK_SIZE对齐切块每个核分得大块big core或小块small core数据余数作为尾部块tail block交给前tailBlockNum个核多算一份并通过SetTilingKey(1/0)区分是否有尾部块两种切分形态Kernel 侧根据TILING_KEY_IS选择对应的初始化分支。tiling 还针对不同 dtype 调整 UB 缓冲区的划分份数ubPartNum默认 4 份int8 加 4 份、uint8 加 6 份、bf16 加 2 份这些类型计算时需要额外临时缓冲如 int8/uint8 的 half 与 int16 中间缓冲区int64 则固定为 7 份并做 32 元素对齐与最大 repeat 次数255限制。Kernel 侧使用双缓冲BUFFER_NUM 2流水线按CopyIn → Compute → CopyOut循环处理每个分片尾部片单独处理剩余数据。约束说明确定性计算aclnnNeg与aclnnInplaceNeg默认即为确定性实现多次运行结果一致可满足调试与复现需求。确定性计算相关的通用约定可参考 determinism_compute.md。输入输出数据格式限定为NDshape 必须一致输出 dtype 必须是输入可转换到的类型。调用示例aclnnNeg示例代码位于 examples/test_aclnn_neg.cpp完整编译与运行流程参考 compile_and_run_sample.md。核心代码如下#include iostream #include vector #include acl/acl.h #include aclnnop/aclnn_neg.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); return ret); ret aclrtCreateStream(stream); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclrtCreateStream failed. ERROR: %d\n, ret); 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; aclTensor* out nullptr; std::vectorfloat selfHostData {0, 1, 2, 3, 4, 5, 6, 7}; std::vectorfloat outHostData {0, 0, 0, 0, 0, 0, 0, 0}; // 创建self aclTensor ret CreateAclTensor(selfHostData, selfShape, selfDeviceAddr, aclDataType::ACL_FLOAT, self); CHECK_RET(ret ACL_SUCCESS, return ret); // 创建out aclTensor ret CreateAclTensor(outHostData, outShape, outDeviceAddr, aclDataType::ACL_FLOAT, out); CHECK_RET(ret ACL_SUCCESS, return ret); // 3. 调用CANN算子库API uint64_t workspaceSize 0; aclOpExecutor* executor; // 调用aclnnNeg第一段接口 ret aclnnNegGetWorkspaceSize(self, out, workspaceSize, executor); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclnnNegGetWorkspaceSize 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); } // 调用aclnnNeg第二段接口 ret aclnnNeg(workspaceAddr, workspaceSize, executor, stream); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclnnNeg 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侧 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]); } // 6. 释放aclTensor aclDestroyTensor(self); aclDestroyTensor(out); // 7. 释放device资源 aclrtFree(selfDeviceAddr); aclrtFree(outDeviceAddr); if (workspaceSize 0) { aclrtFree(workspaceAddr); } aclrtDestroyStream(stream); aclrtResetDevice(deviceId); aclFinalize(); return 0; }这段代码完整覆盖了 CANN 算子调用的七个标准步骤初始化 → 构造 aclTensor → 两段式调用 → 同步等待 → 取回结果 → 释放张量 → 释放资源。对于 shape 为{4, 2}、输入为{0,1,2,3,4,5,6,7}的 float32 数据期望输出为{-0,-1,-2,-3,-4,-5,-6,-7}-0即浮点负零是取负运算的正常结果。调用示例aclnnInplaceNeg原地版本examples/test_aclnn_inplace_neg.cpp与普通版本的结构几乎一致核心差异有两点不需要创建 out 张量以及结果直接回写到 selfRef 的内存。核心代码如下#include iostream #include vector #include acl/acl.h #include aclnnop/aclnn_neg.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); return ret); ret aclrtCreateStream(stream); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclrtCreateStream failed. ERROR: %d\n, ret); 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手册 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. 构造输入与输出原地版本只需要selfRef无需新建out std::vectorint64_t selfRefShape {4, 2}; void* selfRefDeviceAddr nullptr; aclTensor* selfRef nullptr; std::vectorfloat selfRefHostData {0, 1, 2, 3, 4, 5, 6, 7}; // 创建selfRef aclTensor ret CreateAclTensor(selfRefHostData, selfRefShape, selfRefDeviceAddr, aclDataType::ACL_FLOAT, selfRef); CHECK_RET(ret ACL_SUCCESS, return ret); // 3. 调用CANN算子库API uint64_t workspaceSize 0; aclOpExecutor* executor; // 调用aclnnInplaceNeg第一段接口 ret aclnnInplaceNegGetWorkspaceSize(selfRef, workspaceSize, executor); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclnnInplaceNegGetWorkspaceSize 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); } // 调用aclnnInplaceNeg第二段接口 ret aclnnInplaceNeg(workspaceAddr, workspaceSize, executor, stream); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclnnInplaceNeg 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. 获取输出的值结果已写回selfRef的device内存 auto size GetShapeSize(selfRefShape); std::vectorfloat resultData(size, 0); ret aclrtMemcpy(resultData.data(), resultData.size() * sizeof(resultData[0]), selfRefDeviceAddr, size * sizeof(resultData[0]), ACL_MEMCPY_DEVICE_TO_HOST); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(copy resultData from device to host failed. ERROR: %d\n, ret); return ret); for (int64_t i 0; i size; i) { LOG_PRINT(resultData[%ld] is: %f\n, i, resultData[i]); } // 6. 释放aclTensor原地版本只需要释放selfRef aclDestroyTensor(selfRef); // 7. 释放device资源 aclrtFree(selfRefDeviceAddr); if (workspaceSize 0) { aclrtFree(workspaceAddr); } aclrtDestroyStream(stream); aclrtResetDevice(deviceId); aclFinalize(); return 0; }对比两个示例可以看到原地版本少了out张量的创建、释放与aclrtFree整体内存占用更低。使用原地接口前务必确认原始输入不再被需要否则会因数据被覆盖而产生错误。如何选择与注意事项普通版 vs 原地版的选择若计算结果要写入独立的输出张量、原始输入还需要继续参与后续计算使用aclnnNeg若希望省掉一块输出内存、且原始数据允许被覆盖例如作为某个链式运算的中间结果就地翻转符号使用aclnnInplaceNeg。实践要点workspace 的申请必须在第一段接口之后、第二段接口之前且只有当workspaceSize 0时才需要申请否则直接传空指针即可两段式调用之间executor是连接两者的关键句柄不可提前释放计算完成后务必调用aclrtSynchronizeStream同步再执行 Device 到 Host 的结果拷贝否则可能读到未完成计算的数据输出out的 dtype 与 shape 必须与self保持一致或可转换否则第一段接口会返回161002(ACLNN_ERR_PARAM_INVALID)整数类型尤其 uint8 取负遵循模 256 回绕语义与 PyTorch 行为对齐数值上neg(1) 255这是由 Kernel 层实现决定的预期行为而非错误。小结Neg 算子虽然语义简单y -x但其工程实现完整覆盖了 CANN 算子开发的各个层面算子原型注册与 shape 推导neg_def.cpp、neg_infershape.cpp、dtype 感知的 tiling 切分neg_tiling.cpp、AiCore/AiCpu 双路径分发neg.cpp、以及针对 int8/uint8/int64/bf16 等类型的精细向量化 kernelneg.h。通过aclnnNeg与aclnnInplaceNeg两段式接口开发者既可以在独立输出上完成取负也可以原地翻转数据配合 examples 中的可直接运行样例即可在 Atlas A2 系列产品上快速验证算子行为为理解 CANN 数学算子的标准开发范式提供了极佳的参照。赞分享算子库人工智能CANN【免费下载链接】ops-math本项目是CANN提供的数学类基础计算算子库实现网络在NPU上加速计算。项目地址https://gitcode.com/cann/ops-math点击查看免费下载相关推荐CANN ops-math 算子 aclnnAcos 与 aclnnInplaceAcos 两段式接口完全指南CANN ops math 算子 aclnnAcos 与 aclnnInplaceAcos 两段式接口完全指南 导读 本文以 CANN 数学算子库 ops ma算子库人工智能CANNAIMNet2-rxn在有机合成中的应用5个真实案例带你掌握核心技巧AIMNet2 rxn在有机合成中的应用5个真实案例带你掌握核心技巧 AIMNet2 rxn是一款专为广义有机反应建模设计的神经网络原子间势函数能够以接近ω算子库人工智能CANNCANN ops-math 算子解析IsNegInf 负无穷判断算子的 ACLNN 接口与 AiCore 实现CANN ops math 算子解析IsNegInf 负无穷判断算子的 ACLNN 接口与 AiCore 实现 导读 本文聚焦 CANN ops math 仓算子库人工智能CANN创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。