资讯详情

CANN ops-math 算子 aclnnLogicalXor 接口使用指南:逻辑异或两段式调用与源码解析

📅 2026/9/20 4:03:11 | 华诺云谱 👁 阅读
CANN ops-math 算子 aclnnLogicalXor 接口使用指南:逻辑异或两段式调用与源码解析
CANN ops-math 算子 aclnnLogicalXor 接口使用指南逻辑异或两段式调用与源码解析【免费下载链接】ops-math本项目是CANN提供的数学类基础计算算子库实现网络在NPU上加速计算。项目地址: https://gitcode.com/cann/ops-math本文是 CANN/ops-math 开源算子库中LogicalXor逻辑异或算子的 aclnn 接口实战指南。文章围绕 experimental/math/not_equal/docs/aclnnLogicalXor.md 展开完整讲解 aclnnLogicalXor 两段式接口的参数、数据类型支持、返回码语义与完整可运行示例并结合本仓库 NotEqual 算子的算子定义、Tiling、AscendC Kernel 与单测源码说明该接口在 NPU 上的底层实现原理。读完本文你将掌握 aclnnLogicalXor 的完整调用流程并能独立完成算子样例的编译、运行与结果验证。一、接口定位与产品支持情况aclnnLogicalXor 是 CANN 数学基础算子库中逻辑异或算子的 aclnnAscend CANN 单算子调用接口。算子功能为逐元素对两个输入张量执行逻辑异或运算。当self和other为非 bool 类型时0 被视为 False非 0 被视为 True因此该接口天然具备对数值张量做“非零即真”语义转换后再异或的能力。当前仓库中该接口绑定的是 NotEqual 算子的实现在 experimental/math/not_equal/README.md 中明确列出test_aclnn_logical_xor 即通过 aclnnLogicalXor 接口方式调用 NotEqual 算子。产品支持情况以接口文档为准汇总如下产品是否支持Ascend 950PR/Ascend 950DT×Atlas A3 训练系列产品/Atlas A3 推理系列产品×Atlas A2 训练系列产品/Atlas A2 推理系列产品√Atlas 200I/500 A2 推理产品×Atlas 推理系列产品×Atlas 训练系列产品×需要注意的是底层算子定义 not_equal_def.cpp 中注册的 AICore 配置为ascend910b与文档表格中Atlas A2 训练系列产品910B 平台支持 √ 的结论一致说明该接口当前主要面向 A2 系列 NPU 平台提供逻辑异或能力。实际部署前请以对应 CANN 版本的兼容性矩阵为准。二、两段式接口与函数原型与其他 aclnn 算子一致aclnnLogicalXor 采用两段式接口设计背景知识参见 两段式接口说明必须先调用第一段接口aclnnLogicalXorGetWorkspaceSize获取计算所需的 workspace 大小以及包含算子计算流程的执行器再调用第二段接口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)这种先规划、再执行的拆分设计有两方面好处一是 host 侧可以在不实际分配大块内存的情况下提前获知 workspace 需求便于用户统一管理 Device 侧内存二是执行器aclOpExecutor封装了算子计算流程包含已经解析好的图、算子上下文与 tiling 信息第二段接口只需在指定 Stream 上直接执行避免了重复的解析开销。三、aclnnLogicalXorGetWorkspaceSize 参数详解第一段接口完成入参校验与 workspace 规划各参数说明如下。3.1 输入参数 self / other类型aclTensor*计算输入。shape 约束self与other需要满足 broadcast 关系逐元素二元运算的通用约束。内存布局支持 非连续 Tensor即允许以任意 stride 视图传入底层 Kernel 会按对应 strides 访问数据格式 支持ND。数据类型Atlas 推理系列产品、Atlas 训练系列产品FLOAT、FLOAT16、DOUBLE、INT32、INT64、INT16、INT8、UINT8、BOOL、COMPLEX64、COMPLEX128。数据类型Atlas A2 训练系列产品 / Atlas 800I A2 推理产品 / A200I A2 Box 异构组件、Atlas A3 训练系列产品 / Atlas A3 推理系列产品、昇腾 910_95 AI 处理器在上述基础上额外支持BFLOAT16即 FLOAT、FLOAT16、BFLOAT16、DOUBLE、INT32、INT64、INT16、INT8、UINT8、BOOL、COMPLEX64、COMPLEX128。3.2 输出参数 out类型aclTensor*计算输出。shape 约束out的 shape 需要是self与otherbroadcast 之后的 shape。这一点与底层算子实现一致在 not_equal_infershape.cpp 中InferShape 直接把输出 shape 置为输入 x1 的 shape*y_shape *x1_shape而 aclnn 层LogicalXor 语义进一步做了 broadcast 归一化因此调用方需显式准备广播后 shape 的输出张量。内存布局支持 非连续 Tensor数据格式 支持 ND。数据类型与 self/other 相同同上两组产品分别对应两种支持集合。3.3 出参 workspaceSize 与 executorworkspaceSizeuint64_t*出参返回需要在 Device 侧申请的 workspace 大小单位字节。底层由 Tiling 逻辑计算在 not_equal_tiling.cpp 中workspace_sizes[0] plaform.GetLibApiWorkSpaceSize()即通过platform_ascendc平台信息接口获取向量指令库 API 所需的工作空间大小。当返回 0 时第二段接口无需申请 workspace。executoraclOpExecutor**出参返回 op 执行器封装了算子计算流程第二段接口直接使用。3.4 返回值与错误码返回aclnnStatus状态码完整语义参见 aclnn 返回码说明。第一段接口完成入参校验出现以下场景时报错161001 (ACLNN_ERR_PARAM_NULLPTR): 1. 传入的 self、other 或 out 是空指针。 161002 (ACLNN_ERR_PARAM_INVALID): 1. self 和 other 的数据类型不在支持的范围之内。 2. self 和 other 的 shape 无法做 broadcast。 3. self 或 other 的 shape 大于 8 维complex64/complex128 大于 7 维度。其中维度限制普通类型不超过 8 维、复数类型不超过 7 维与 ND 格式下的存储索引能力直接相关是编写 shape 时最容易踩到的边界建议在构造张量前先做维度自检。四、aclnnLogicalXor 参数详解第二段接口负责真正下发计算参数均为入参参数类型说明workspacevoid*在 Device 侧申请的 workspace 内存地址由用户在调用前通过aclrtMalloc申请若第一段返回的 workspaceSize 为 0可传空指针workspaceSizeuint64_t在 Device 侧申请的 workspace 大小必须与第一段接口返回的值一致executoraclOpExecutor*第一段接口返回的 op 执行器包含算子计算流程streamaclrtStream指定执行任务的 Stream任务在该 Stream 上异步执行返回值同样为aclnnStatus具体参见 aclnn 返回码说明。第二段接口为异步下发调用成功后需要通过aclrtSynchronizeStream同步等待任务执行结束再读取输出结果。五、约束说明接口文档明确无额外约束。需要强调的是无约束是针对 aclnn 接口层而言的——broadcast、数据类型、维度等限制均已通过第一段接口的参数校验体现见上文错误码 161001/161002。但底层 NotEqual 算子实现层面仍有限制可作参考在 experimental/math/not_equal/README.md 中注明不支持广播、不支持 int64这是算子 Kernel 直接实现非 aclnn 适配层的能力边界当通过 aclnnLogicalXor 调用时broadcast 与 INT64 的扩展能力由 aclnn 适配层在前置处理中补齐因此接口文档不将其列为约束。六、调用示例从 Host 数据到 NPU 执行接口文档提供了完整可运行的示例代码仓库中的同名样例见 examples/test_aclnn_logical_xor.cpp。下面按执行流程拆解讲解完整代码可直接复制编译具体编译与运行环境准备参见 编译与运行样例。#include iostream #include vector #include acl/acl.h #include aclnn_logical_xor.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; } templatetypename 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. 构造输入与输出需要根据API的接口自定义构造 std::vectorint64_t selfShape {4, 2}; std::vectorint64_t otherShape {4, 2}; std::vectorint64_t outShape {4, 2}; void *selfDeviceAddr nullptr; void *otherDeviceAddr nullptr; void *outDeviceAddr nullptr; aclTensor *self nullptr; aclTensor *other nullptr; aclTensor *out nullptr; std::vectorfloat selfHostData {0, 1, 2, 3, 4, 5, 6, 7}; std::vectorfloat otherHostData {1, 1, 1, 0, 4, 2, 3, 7}; std::vectorfloat outHostData(8, 0); // 创建self aclTensor ret CreateAclTensor(selfHostData, selfShape, selfDeviceAddr, aclDataType::ACL_FLOAT, self); CHECK_RET(ret ACL_SUCCESS, return ret); // 创建other aclTensor ret CreateAclTensor(otherHostData, otherShape, otherDeviceAddr, aclDataType::ACL_FLOAT, other); 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 LOG_PRINT(test aclnnLogicalXor\n); uint64_t workspaceSize 0; aclOpExecutor *executor; // 调用aclnnLogicalXor第一段接口 ret aclnnLogicalXorGetWorkspaceSize(self, other, out, workspaceSize, executor); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclnnLogicalXorGetWorkspaceSize 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); } // 调用aclnnLogicalXor第二段接口 ret aclnnLogicalXor(workspaceAddr, workspaceSize, executor, stream); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclnnLogicalXor 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和aclScalar aclDestroyTensor(self); aclDestroyTensor(other); aclDestroyTensor(out); // 7.释放device资源 aclrtFree(selfDeviceAddr); aclrtFree(otherDeviceAddr); aclrtFree(outDeviceAddr); if (workspaceSize 0) { aclrtFree(workspaceAddr); } aclrtDestroyStream(stream); aclrtResetDevice(deviceId); aclFinalize(); return 0; }6.1 执行流程七步拆解资源初始化固定写法aclInit初始化 ACL 运行环境aclrtSetDevice绑定设备aclrtCreateStream创建执行 Stream。构造输入与输出根据接口原型自定义。示例使用 shape 为{4, 2}的 FLOAT 张量共 8 个元素。CreateAclTensor内部依次完成计算字节数 →aclrtMalloc申请 Device 侧内存 →aclrtMemcpy将 Host 数据拷入 Device → 按连续布局计算 strides →aclCreateTensor创建aclTensor格式固定为ACL_FORMAT_ND。两段式调用先调用第一段接口拿到workspaceSize与executor若workspaceSize 0则aclrtMalloc申请 workspace再调用第二段接口把任务提交到指定 Stream。同步等待aclrtSynchronizeStream阻塞等待 Stream 上任务执行完成异步语义下必须执行。取回结果aclrtMemcpy以ACL_MEMCPY_DEVICE_TO_HOST方式把输出拷回 Host 并打印。释放张量aclDestroyTensor释放 self、other、out。释放资源依次释放各 Device 内存含 workspace、销毁 Stream、aclrtResetDevice复位设备、aclFinalize反初始化。6.2 预期计算结果示例输入非零即真语义与逐元素异或结果如下索引selfother逻辑值 (self XOR other)数值输出00(False)1(True)True111(True)1(True)False022(True)1(True)False033(True)0(False)True144(True)4(True)False055(True)2(True)False066(True)3(True)False077(True)7(True)False0即输出应为{1, 0, 0, 1, 0, 0, 0, 0}可作为运行结果的正确性判据。注意示例以 FLOAT 承载输出便于打印在 BOOL 输入场景下输出张量应为 BOOL 类型数值上等价。七、源码级实现原理从 aclnn 接口到 NPU Kernel为便于深入理解该接口在 NPU 上如何落地本仓库提供了完整的算子侧实现接口绑定的是 NotEqual 算子aclnnLogicalXor 在适配层将逻辑异或映射到该 Kernel 上。7.1 算子定义op_host/not_equal_def.cppnot_equal_def.cpp 使用OpDef注册机制声明算子原型输入x1、x2对应接口中的 self、otherParamType(REQUIRED)支持 FLOAT16、FLOAT、INT32、INT8、UINT8、BOOL、BF16格式与 UnknownShapeFormat 均为 ND。输出yParamType(REQUIRED)全部为 BOOL格式 ND。通过this-AICore().AddConfig(ascend910b)声明 AICore 配置即面向 910BAtlas A2 系列平台编译。可以看到算子定义层的数据类型集合含 BF16、不含 INT64/DOUBLE/COMPLEX是 Kernel 直接支持的子集接口文档中更宽的数据类型支持范围由 aclnn 适配层通过类型转换等前置逻辑补齐这正是接口支持面大于裸算子支持面的原因。7.2 Tiling 逻辑op_host/not_equal_tiling.cppnot_equal_tiling.cpp 实现了 host 侧 Tiling读取输入 x1 的存储 shape计算总元素数size写入NotEqualTilingData结构体仅含int size一个字段见 not_equal_tiling_data.h。通过platform_ascendc::PlatformAscendC获取 AIV 核数作为初始block_dim并按数据规模动态缩减FLOAT/BF16 场景下size 2^13时只用 1 核、size 2^21时核数减半其他类型按字节数data_size以2^16、2^24为阈值做同样的核数缩减。设置 TilingKey 为ELEMENTWISE_TPL_SCH_MODE_0逐元素模板调度模式见 not_equal_tiling_key.h。通过context-GetWorkspaceSizes(1)申请 1 块 workspace大小为plaform.GetLibApiWorkSpaceSize()——这正是第一段接口返回的workspaceSize的来源。这种小张量用少核、大张量多核并行的策略可以在小规模计算时降低调度开销在大规模计算时充分利用多核并行。7.3 AscendC Kernelop_kernel/not_equal.cpp、not_equal.hnot_equal.cpp 声明 Kernel 入口not_equal通过REGISTER_TILING_DEFAULT读取 TilingData并按 TilingKey 分发到 not_equal.h 中的核心实现。Kernel 采用典型的三段流水结构数据搬运MTE以MAX_TILE_SIZE 30KB / sizeof(U)为单次搬运上限将 Global Memory 数据分块DataCopy到 Vector 侧缓冲区TQueTPosition::VECIN不足一块时按32 / sizeof(T)字节对齐取整。向量计算V按输入类型选择不同实现路径half直接Compare(x1, x2, CMPMODE::EQ)得相等掩码再Duplicate构造 0/1 向量Select按掩码挑选后Cast输出——即相等为 0、不等为 1的布尔取反技巧等价实现 XOR。float/bfloat16_t以 256 位为粒度循环CompareCMPMODE::EQ配合Select完成掩码到 0/1 的映射。int先转 half 复用 0/1 选择逻辑再Cast回输出。int8_t/uint8_t通过Cast→Sub→Abs→Mins→ 两次Muls利用0x1p-24与0x1p12的数值技巧将相等为 0、不等非 0归一化到 0/1。bool以uint8_t路径承载输出归一化后的布尔值。结果回写MTE将 Vector 侧结果按块DataCopy写回 Global Memory 的y缓冲区。从 Kernel 实现可以确认逻辑异或等价于逐元素比较是否不等out (self ! other)与接口文档当 self 和 other 为非 bool 类型时0 视为 False非 0 视为 True的描述完全对应。7.4 单测验证tests/ut仓库为 Kernel 提供了单元测试 test_not_equal.cpp其数据由 gen_data.py 与 compare_data.py 生成与校验覆盖多种输入类型的逐元素比较正确性。读者在验证自己的调用示例时可参考测试数据生成方式构造覆盖边界值0、负数、极大值的输入以提高验证可信度。八、常见问题与排错建议第一段接口返回 161001检查 self、other、out 是否为空指针尤其是 out 必须预先通过aclCreateTensor创建不能传 nullptr。第一段接口返回 161002优先检查 ① 数据类型是否落在支持集合内特别注意非 A2/A3 平台上不支持 BFLOAT16② self 与 other 能否 broadcast且 out 是否为广播后的 shape③ 维度是否超过 8 维复数类型 7 维。结果全 0 或全 1检查是否误把输出张量初始化为非 0 常量且未正确拷贝确认示例中的“非零即真”语义例如数值 2 与 3 都视为 TrueXOR 结果为 0。workspace 相关崩溃workspaceSize 必须严格使用第一段接口的返回值且 workspace 内存必须通过aclrtMalloc在 Device 侧申请第二段接口的 stream 必须与同步等待使用同一 Stream。编译链接务必在编译选项中链接 aclnn 算子库并包含aclnn_logical_xor.h头文件路径具体工具链与链接参数参见 编译与运行样例 及仓库根目录的构建脚本如 scripts/build_example.sh。九、总结aclnnLogicalXor 是 CANN/ops-math 中实现逐元素逻辑异或的标准 aclnn 接口通过aclnnLogicalXorGetWorkspaceSizeaclnnLogicalXor两段式调用完成参数校验、workspace 规划与异步执行支持 FLOAT、FLOAT16、BFLOAT16、DOUBLE、各类整型、BOOL 与复数类型ND 格式并支持广播与非连续 Tensor当前产品支持面以 Atlas A2 训练/推理系列为主。在底层它由 NotEqual 算子的 AscendC Kernel 承载Tiling 层按数据规模动态决定并行核数Kernel 层针对不同数据类型分别实现相等掩码 → 0/1 归一化的等价异或计算。结合本文的完整示例、结果判据与源码走读你可以快速完成从 Host 侧数据准备、NPU 计算到结果回收的完整闭环并具备定位常见调用错误的能力。【免费下载链接】ops-math本项目是CANN提供的数学类基础计算算子库实现网络在NPU上加速计算。项目地址: https://gitcode.com/cann/ops-math创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
📝

华诺云谱内容团队

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

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

你可能需要的服务

订阅华诺云谱资讯周报

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