CANN ops-math 中 NotEqual 不相等比较算子实战指南:从 aclnn 接口调用到 NPU 内核实现 📅 发布时间:2026/9/19 21:15:21 👁 浏览次数: CANN ops-math 中 NotEqual 不相等比较算子实战指南从 aclnn 接口调用到 NPU 内核实现【免费下载链接】ops-math本项目是CANN提供的数学类基础计算算子库实现网络在NPU上加速计算。项目地址: https://gitcode.com/cann/ops-mathNotEqual 是 CANN ops-math 仓库experimental/math目录提供的一个逐元素数学比较算子用于判断两个输入 Tensor 对应元素是否不相等并输出 BOOL 型结果。本指南以 NotEqual 算子 README 为主线完整覆盖其产品支持情况、功能语义、参数与约束并基于仓库内三份 aclnn 接口文档与 Host/Kernel 源码深入讲解aclnnLogicalXor、aclnnNeScalar、aclnnNeTensor等两段式调用方式、常见错误码以及算子在 Atlas A2 系列产品上的分核Tiling与向量计算实现原理。读完本文你将能够在 Atlas A2 环境上正确调用 NotEqual 相关 aclnn 接口完成不相等比较并理解算子从图编译到 NPU 执行的全链路。产品支持情况NotEqual 算子本体及其配套 aclnn 接口在仓库中的支持矩阵如下以 README 及 aclnnLogicalXor 文档、aclnnNeScalar 文档、aclnnNeTensor 文档 为准产品是否支持Atlas A2 训练系列产品 / Atlas 800I A2 推理产品 / A200I A2 Box 异构组件√aclnn 系列接口的支持情况更为细分以aclnnNeTensor为例产品是否支持Ascend 950PR / Ascend 950DT×Atlas A3 训练系列产品 / Atlas A3 推理系列产品×Atlas A2 训练系列产品 / Atlas A2 推理系列产品√Atlas 200I/500 A2 推理产品×Atlas 推理系列产品×Atlas 训练系列产品×注意接口文档中term标注的产品名称属于 CANN 官方产品型号术语本文沿用原文档表述。从源码看NotEqual 的内核配置为AICore().AddConfig(ascend910b)见 not_equal_def.cpp与 Atlas A2 系列所对应的昇腾 910B 芯片平台一致从源码结构可以推断该算子面向 A2 系列 NPU 平台设计。功能说明与计算公式NotEqual 执行逐元素比较将输入self与输入other的对应元素逐一比较若两者不相等则输出 1True否则输出 0False最终返回一个 BOOL 型 Tensor。计算公式为$$out_i (self_i \neq other_i)$$其中out_i、self_i、other_i分别表示输出、第一个输入、第二个输入在第 i 个位置上的元素。在仓库的算子注册定义not_equal_def.cpp中该算子被注册为NotEqual声明了两个必需输入x1、x2和一个必需输出y与 README 中的self、other、out一一对应。参数说明NotEqual 算子的输入输出参数如下来自 README 参数表参数名输入/输出/属性描述数据类型数据格式self输入待进行 not_equal 计算的入参公式中的 $self_i$FLOAT16, FLOAT, INT32, INT8, UINT8, BOOL, INT64, BFLOAT16NDother输入待进行 not_equal 计算的入参公式中的 $other_i$FLOAT16, FLOAT, INT32, INT8, UINT8, BOOL, INT64, BFLOAT16NDout输出待进行 not_equal 计算的出参公式中的 $out_i$BOOLND结合算子注册源码可以进一步确认与校准x1与x2在 not_equal_def.cpp 中实际声明的数据类型为ge::DT_FLOAT16, ge::DT_FLOAT, ge::DT_INT32, ge::DT_INT8, ge::DT_UINT8, ge::DT_BOOL, ge::DT_BF16即 FLOAT16、FLOAT、INT32、INT8、UINT8、BOOL、BFLOAT16输出y仅支持ge::DT_BOOL。注意注册定义中并不包含 INT64这与下文不支持 INT64的约束说明一致——README 参数表中的 INT64 应理解为历史遗留写法实际以约束说明与源码为准。所有参数的数据格式均为ND即按自然维度顺序排布的标准格式。约束说明根据 README 约束说明不支持广播self与other的 shape 必须完全一致输出out的 shape 与输入保持一致。不支持 INT64数据类型。这两条约束均可在源码中得到印证形状推导函数 not_equal_infershape.cpp 直接将输出 shape 赋值为第一个输入x1的 shape*y_shape *x1_shape没有进行任何广播扩展逻辑算子注册not_equal_def.cpp的输入数据类型列表中没有ge::DT_INT64。对比之下上层 aclnn 接口如aclnnLogicalXor、aclnnNeTensor本身支持 broadcast 关系与更丰富的数据类型但那属于 aclnn 接口层的语义详见下文各接口小节与 NotEqual 算子内核的约束是两个层面。调用方式总览NotEqual 算子本身不直接导出 aclnn 接口其 CMake 配置中ACLNNTYPE aclnn_exclude见 CMakeLists.txt而是在上层通过三个 aclnn 接口族复用它完成不相等比较这一计算语义。README 给出了三种调用路径调用方式调用样例说明aclnn 调用test_aclnn_logical_xor.cpp通过 aclnnLogicalXor 接口方式调用 NotEqual 算子aclnn 调用test_aclnn_ne_scalar.cpp通过 aclnnNeScalar / aclnnInplaceNeScalar 接口方式调用 NotEqual 算子aclnn 调用test_aclnn_ne_tensor.cpp通过 aclnnNeTensor / aclnnInplaceNeTensor 接口方式调用 NotEqual 算子三类接口的功能对比如下aclnnNeTensor / aclnnInplaceNeTensorTensor 与 Tensor 逐元素比较是最贴合 NotEqual 算子语义self_i ! other_i的接口aclnnNeScalar / aclnnInplaceNeScalarTensor 与标量Host 侧aclScalar比较计算self_i ! otheraclnnLogicalXor逻辑异或语义self_i XOR other_i非 BOOL 输入按0 为 False、非 0 为 True参与运算同样复用 NotEqual 的逐元素比较内核路径。所有 aclnn 接口均采用 CANN 标准的两段式接口先调用xxxGetWorkspaceSize完成入参校验、推导 workspace 大小并创建执行器executor再调用xxx接口在指定 stream 上真正执行计算。aclnnNeTensor 与 aclnnInplaceNeTensorTensor 逐元素不相等比较功能与区别计算self或selfRef中的元素与other中的元素是否不相等$$out_i(self_i \neq other_i)?[1]:[0]$$$$selfRef_i(selfRef_i \neq other_i)?[1]:[0]$$aclnnNeTensor与aclnnInplaceNeTensor功能相同区别在于输出方式详见 接口文档aclnnNeTensor需新建一个输出张量对象out存储计算结果self保持不变aclnnInplaceNeTensor无需新建输出张量直接在输入张量selfRef的内存中覆写计算结果原地计算节省显存。函数原型每个算子都是两段式接口aclnnStatus aclnnNeTensorGetWorkspaceSize(const aclTensor *self, const aclTensor *other, aclTensor *out, uint64_t *workspaceSize, aclOpExecutor **executor); aclnnStatus aclnnNeTensor(void *workspace, uint64_t workspaceSize, aclOpExecutor *executor, aclrtStream stream); aclnnStatus aclnnInplaceNeTensorGetWorkspaceSize(aclTensor *selfRef, const aclTensor *other, uint64_t *workspaceSize, aclOpExecutor **executor); aclnnStatus aclnnInplaceNeTensor(void *workspace, uint64_t workspaceSize, aclOpExecutor *executor, aclrtStream stream);参数说明aclnnNeTensorGetWorkspaceSize参数方向说明self计算输入Device 侧aclTensor。数据类型需与 other 满足互推导关系shape 需与 other 满足 broadcast 关系维度不高于 8 维支持非连续 Tensor格式支持 NDother计算输入Device 侧aclTensor。数据类型需与 self 满足互推导关系shape 需与 self 满足 broadcast 关系维度不高于 8 维支持非连续 Tensor格式支持 NDout计算输出Device 侧aclTensor。数据类型需是 BOOL 可转换的数据类型shape 与 self、other 广播后的 shape 一致维度不高于 8 维支持非连续 Tensor格式支持 NDworkspaceSize出参返回需要在 Device 侧申请的 workspace 大小uint64_t*executor出参返回算子执行器包含算子计算流程aclOpExecutor**各产品线上self/other支持的数据类型A2/A3 产品线DOUBLE, FLOAT16, FLOAT, BFLOAT16, INT64, INT32, INT8, UINT8, BOOL, INT16, COMPLEX64, COMPLEX128昇腾 910_95 额外支持 UINT64Atlas 训练系列产品不支持 BFLOAT16out在上述基础上还支持 UINT32、UINT16。返回值与错误码两段式接口均返回aclnnStatus状态码具体参见 aclnn 返回码文档。第一段接口完成入参校验出现以下场景时报错返回 161001ACLNN_ERR_PARAM_NULLPTR1. 传入的 self、other、out 是空指针时。 返回 161002ACLNN_ERR_PARAM_INVALID 1. self、other 或 out 的数据类型不在支持的范围之内。 2. self、other 或 out 的维度大于 8。 3. self 和 other 的数据类型无法进行推导。 4. self 和 other 的 shape 无法进行 broadcast。 5. out 的 shape 与 broadcast 后的 shape 不一致。aclnnInplaceNeTensorGetWorkspaceSize的校验类似但针对selfRefshape 做 broadcast 后必须等于selfRef的 shape原地写回要求输出形状与输入一致且没有out参数161001 针对 selfRef、other 空指针。调用示例完整可编译示例见 test_aclnn_ne_tensor.cpp 与接口文档其执行流程是 aclnn 调用的标准范式编译与运行过程参考 编译与运行样例#include iostream #include vector #include acl/acl.h #include aclnn_ne_tensor.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 手册按实际环境填写 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. 构造输入与输出 std::vectorint64_t selfShape {4, 2}; std::vectorint64_t otherShape {4, 2}; std::vectorint64_t outShape {4, 2}; void *selfDeviceAddr nullptr, *otherDeviceAddr nullptr, *outDeviceAddr nullptr; aclTensor *self nullptr, *other nullptr, *out nullptr; std::vectorfloat selfHostData {0, 1, 2, 3, 4, 5, 6, 7}; std::vectorfloat otherHostData {1, 1, 1, 2, 2, 2, 3, 3}; std::vectorfloat outHostData {0, 0, 0, 0, 0, 0, 0, 0}; ret CreateAclTensor(selfHostData, selfShape, selfDeviceAddr, aclDataType::ACL_FLOAT, self); CHECK_RET(ret ACL_SUCCESS, return ret); ret CreateAclTensor(otherHostData, otherShape, otherDeviceAddr, aclDataType::ACL_FLOAT, other); CHECK_RET(ret ACL_SUCCESS, return ret); ret CreateAclTensor(outHostData, outShape, outDeviceAddr, aclDataType::ACL_FLOAT, out); CHECK_RET(ret ACL_SUCCESS, return ret); // 3. 调用 acnnNeTensor 第一段接口获取 workspace 大小与执行器 uint64_t workspaceSize 0; aclOpExecutor* executor; ret aclnnNeTensorGetWorkspaceSize(self, other, out, workspaceSize, executor); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclnnNeTensorGetWorkspaceSize failed. ERROR: %d\n, ret); return ret); // 按需为 workspace 申请 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); } // 调用 acnnNeTensor 第二段接口执行计算 ret aclnnNeTensor(workspaceAddr, workspaceSize, executor, stream); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclnnNeTensor 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(other); // 7. 释放 Device 资源 aclrtFree(selfDeviceAddr); aclrtFree(otherDeviceAddr); if (workspaceSize 0) { aclrtFree(workspaceAddr); } aclrtDestroyStream(stream); aclrtResetDevice(deviceId); aclFinalize(); return 0; }示例数据self {0,1,2,3,4,5,6,7}与other {1,1,1,2,2,2,3,3}逐元素比较可预期得到{1,0,1,1,1,1,1,1}。Inplace 变体的差异调用aclnnInplaceNeTensorGetWorkspaceSize(self, other, workspaceSize, executor)时不需要out执行完成后结果直接写入self即selfRef对应的 Device 内存回拷数据时从selfDeviceAddr读取且无需为out申请内存与创建张量示例第 5 步在 Inplace 场景改为从selfDeviceAddr拷贝其余流程与上文完全一致。aclnnNeScalar 与 aclnnInplaceNeScalarTensor 与标量比较功能与区别计算self或selfRef中每个元素与 Host 侧标量other是否不相等$$out_i(self_i \neq other)?[1]:[0]$$$$selfRef_i(selfRef_i \neq other)?[1]:[0]$$aclnnNeScalar需要新建输出张量outaclnnInplaceNeScalar直接覆写输入张量内存详见 接口文档。函数原型aclnnStatus aclnnNeScalarGetWorkspaceSize(const aclTensor *self, const aclScalar *other, aclTensor *out, uint64_t *workspaceSize, aclOpExecutor **executor); aclnnStatus aclnnNeScalar(void* workspace, uint64_t workspaceSize, aclOpExecutor* executor, const aclrtStream stream); aclnnStatus aclnnInplaceNeScalarGetWorkspaceSize(aclTensor *selfRef, const aclScalar *other, uint64_t *workspaceSize, aclOpExecutor **executor); aclnnStatus aclnnInplaceNeScalar(void *workspace, uint64_t workspaceSize, aclOpExecutor *executor, aclrtStream stream);参数说明要点参数方向说明self / selfRef计算输入Inplace 时为输入/输出Device 侧aclTensorshape 维度不高于 8 维支持非连续 Tensor格式 ND数据类型需与 other 满足 TensorScalar 互推导关系other计算输入Host 侧aclScalar这是与 NeTensor 最大的差异数据类型需与 self 满足互推导关系out计算输出Device 侧aclTensor数据类型需为 BOOL 可转换的数据类型shape 与 self 一致workspaceSize / executor出参同前返回 workspace 大小与执行器self/other支持的数据类型A2/A3 产品线为DOUBLE, FLOAT16, FLOAT, BFLOAT16, INT64, INT32, INT8, UINT8, BOOL, INT16, COMPLEX64, COMPLEX128out在此基础上额外支持UINT32, UINT16昇腾 910_95 还支持UINT64。返回值与错误码返回 161001ACLNN_ERR_PARAM_NULLPTR1. 传入的 self、other、out 是空指针。 返回 161002ACLNN_ERR_PARAM_INVALID 1. self、other 或 out 的数据类型不在支持的范围之内。 2. self 和 other 数据类型不满足数据类型推导规则。 3. self 和 out 的 shape 不同。 4. self 和 out 的维度大于 8。Inplace 变体针对selfRef校验无out参数并要求selfRef与other类型满足推导规则、selfRef维度不大于 8。调用示例要点完整示例见 test_aclnn_ne_scalar.cpp与 NeTensor 示例的骨架完全一致关键差异other不再用CreateAclTensor创建而是通过aclCreateScalar在 Host 侧构造标量float otherValue 1.0f; aclScalar *other aclCreateScalar(otherValue, aclDataType::ACL_FLOAT);第一段接口调用变为aclnnNeScalarGetWorkspaceSize(self, other, out, workspaceSize, executor)Inplace 变体调用aclnnInplaceNeScalarGetWorkspaceSize(self, other, workspaceSize, executor)结果回拷自selfDeviceAddr资源释放时使用aclDestroyScalar(other)而非aclDestroyTensor(other)。aclnnLogicalXor逻辑异或方式调用 NotEqual 算子功能说明aclnnLogicalXor完成给定输入张量元素的逻辑异或运算当self和other为非 BOOL 类型时0 被视为 False、非 0 被视为 True详见 aclnnLogicalXor 文档。函数原型aclnnStatus aclnnLogicalXorGetWorkspaceSize(const aclTensor *self, const aclTensor *other, aclTensor *out, uint64_t *workspaceSize, aclOpExecutor **executor); aclnnStatus aclnnLogicalXor(void *workspace, uint64_t workspaceSize, aclOpExecutor *executor, aclrtStream stream);参数说明参数方向说明self / other计算输入Device 侧aclTensorshape 需满足 broadcast 关系支持非连续 Tensor格式 NDout计算输出shape 为 self 与 other 广播之后的 shape支持非连续 Tensor格式 NDworkspaceSize / executor出参返回 workspace 大小与执行器A2/A3 产品线及昇腾 910_95 上self/other/out支持的数据类型为FLOAT, FLOAT16, BFLOAT16, DOUBLE, INT32, INT64, INT16, INT8, UINT8, BOOL, COMPLEX64, COMPLEX128Atlas 推理/训练系列产品不支持 BFLOAT16。返回值与错误码161001ACLNN_ERR_PARAM_NULLPTR1. 传入的 self、other 或 out 是空指针。 161002ACLNN_ERR_PARAM_INVALID 1. self 和 other 的数据类型不在支持的范围之内。 2. self 和 other 的 shape 无法做 broadcast。 3. self 或 other 的 shape 大于 8 维complex64/complex128 大于 7 维度。调用示例要点完整示例见 test_aclnn_logical_xor.cpp流程与 NeTensor 示例一致仅接口名称替换为aclnnLogicalXorGetWorkspaceSize/aclnnLogicalXor头文件为aclnn_logical_xor.h输入数据示例为self {0,1,2,3,4,5,6,7}、other {1,1,1,0,4,2,3,7}。源码级原理Host 侧算子定义、形状推导与 Tiling算子注册与形状推导算子定义not_equal_def.cpp通过OpDef注册NotEqual输入x1、x2支持 7 种数据类型FLOAT16、FLOAT、INT32、INT8、UINT8、BOOL、BF16输出y为 BOOL格式与动态 shape 场景均为 ND并配置 AICore 平台ascend910b形状推导not_equal_infershape.cppInferShape直接把输出 shape 设为输入x1的 shape*y_shape *x1_shape因此该算子天然不支持广播两个输入 shape 必须一致。Tiling 分核策略Tiling 逻辑位于 not_equal_tiling.cpp其策略为计算输入元素总数size各维度乘积写入NotEqualTilingData结构体仅含int size见 not_equal_tiling_data.h获取平台 AIV 核数block_dim plaform.GetCoreNumAiv()然后按数据类型与数据量动态调整核数FLOAT / BF16元素数 2^138192时只用 1 核 2^21约 209 万时核数减半其余使用全部 AIV 核其他类型按总字节数data_size判断 2^1664KB时 1 核 2^2416MB时核数减半设置 Tiling Key 为ELEMENTWISE_TPL_SCH_MODE_0逐元素模板调度模式 0见 not_equal_tiling_key.h通过GetLibApiWorkSpaceSize()申请算子库 API 所需 workspace。可以看出NotEqual 采用按数据量自适应降核的分核策略小张量只启动少量核以减少调度开销大张量才用满 AIV 核属于典型的逐元素算子性能优化手段。源码级原理Kernel 侧的向量化实现Kernel 实现位于 not_equal.cpp 与 not_equal.h要点如下调度入口not_equal(GM_ADDR x1, GM_ADDR x2, GM_ADDR y, GM_ADDR workspace, GM_ADDR tiling)使用KERNEL_TASK_TYPE_DEFAULT(KERNEL_TYPE_AIV_ONLY)声明为仅 AIV 向量核执行的算子通过模板参数DTYPE_X1在编译期实例化具体数据类型主循环not_equal_0()宏中按MTE_BLOCK_SIZE 512/sizeof(uint8_t)将数据划分为 compute blocks再按block_index / block_dim的比例分配给各核每个核内以MAX_TILE_SIZE (3010)/sizeof(U)为 tile 上限进行分段搬运与计算数据经DataCopy进出TQue队列形成 MTE-Vector 流水分类型计算not_equal.hhalf / int先用Compare(..., CMPMODE::EQ, ...)比较相等性再用Duplicate生成 0/1 常量通过Select按比较掩码选出不相等1、相等0最后Cast到输出float / bfloat16_t按 256/sizeof(U) 为步长分块调用CompareU, false配合SELMODE::VSEL_CMPMASK_SPR的Select完成结果写入int8 / uint8 / boolbool按uint8_t处理先将输入Cast到 half 精度中间量再做Sub/Abs/Mins/Muls数值变换利用浮点下溢判定差值为 0 与否最后Cast回int8_t输出输出内存y以int8_t视图写回 Global MemoryBOOL 本质为 1 字节存储。测试与验证仓库为 NotEqual 提供了完整的 CPU 仿真单测test_not_equal.cpp使用 gtest 框架 tikicpulib.h的 ICPU 模式在 Host 上仿真执行 Kernel测试用例test_case_0构造 94,000 个元素的数据tiling_data-size 94000以 20 个 block 分发覆盖中等规模张量的分核路径测试通过模板参数DTYPE_X1覆盖 FLOAT16、FLOAT、INT32、INT8、UINT8、BOOL、BFLOAT16 等数据类型见GetTorchType()的类型映射数据目录 not_equal_data 提供gen_data.py生成输入数据与compare_data.py与 PyTorch 参考结果比对配合GetTorchType的 torch dtype 映射可以推断测试结果以 PyTorch 的ne语义为参照进行数值比对。测试的 CMake 配置CMakeLists.txt随tests/ut构建体系编译运行开发者可在本地通过仓库的 UT 构建脚本build_ut.sh执行验证。总结NotEqual 是 CANN ops-math 在 Atlas A2 系列平台上提供的逐元素不相等比较算子算子内核NotEqual严格限定输入 shape 一致不支持广播且不支持 INT64输出 BOOL 型 ND 格式 Tensor上层可通过aclnnNeTensorTensor-Tensor、aclnnNeScalarTensor-标量含 Inplace 原地写回变体与aclnnLogicalXor逻辑异或语义三类两段式接口完成调用。其实现上采用 AIV-only 向量核 按数据量自适应降核的 Tiling 策略并针对 half、float、int、int8/uint8/bool、bfloat16 分别设计了基于Compare/Select/Duplicate的向量化比较路径配合 ICPU 单测与 PyTorch 参考比对保证正确性。如需进一步阅读可继续查看 NotEqual 算子 README、三份接口文档aclnnLogicalXor、aclnnNeScalar、aclnnNeTensor以及仓库中 equal、not_equal 等其他比较类算子目录了解同类算子的实现模式与差异。【免费下载链接】ops-math本项目是CANN提供的数学类基础计算算子库实现网络在NPU上加速计算。项目地址: https://gitcode.com/cann/ops-math创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考