尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

CANN ops-math 算子接口实战:aclnnLtTensor 与 aclnnInplaceLtTensor 全解析

发布时间:2026/9/20 20:00:17

资讯中心
01
ARTICLE

CANN ops-math 算子接口实战:aclnnLtTensor 与 aclnnInplaceLtTensor 全解析

CANN ops-math 算子接口实战:aclnnLtTensor 与 aclnnInplaceLtTensor 全解析
算子库人工智能CANN【免费下载链接】ops-math本项目是CANN提供的数学类基础计算算子库实现网络在NPU上加速计算。项目地址https://gitcode.com/cann/ops-math点击查看免费下载导读本文以 CANN ops-math 仓库中 Less小于比较算子的两个单算子 API 接口为核心系统讲解aclnnLtTensor与aclnnInplaceLtTensor的产品支持情况、功能语义、两段式调用范式、完整参数约束与错误码并结合仓库源码剖析其底层计算流程Contiguous → Cast → Less → ViewCopy、平台差异与 kernel 实现原理。读完本文你将掌握在 NPU 上通过 aclnn 接口完成逐元素小于比较并输出 Bool 张量的完整编码能力并理解普通输出与原地inplace输出的工程取舍。产品支持情况根据 math/less/docs/aclnnLtTensoraclnnInplaceLtTensor.md 与 math/less/README.md 的记载Less 算子及配套的两个 aclnn 接口支持情况如下产品是否支持Ascend 950PR / Ascend 950DT支持Atlas A3 训练系列产品 / Atlas A3 推理系列产品支持Atlas A2 训练系列产品 / Atlas A2 推理系列产品支持Atlas 200I/500 A2 推理产品支持Atlas 推理系列产品不支持Atlas 训练系列产品支持注意在Atlas 200I/500 A2 推理产品与Atlas 训练系列产品上aclnnLtTensor/aclnnInplaceLtTensor不支持BFLOAT16数据类型见原文档约束说明中 npu910,310b 标记段。功能说明aclnnLtTensor与aclnnInplaceLtTensor实现相同的功能判断输入self中的每个元素是否小于输入other中的元素返回一个 Bool 类型的 Tensor。计算公式为$$ out_i (self_i other_i) ? [True] : [False] $$即当self_i严格小于other_i时输出True否则输出False。这一语义与 PyTorch 的torch.lt一致常用于掩码生成、条件筛选、比较统计等场景。两个接口的功能区别在于结果的存放位置aclnnLtTensor需新建一个输出张量对象out存储计算结果输入self保持不变aclnnInplaceLtTensor无需新建输出张量对象直接在输入张量selfRef的内存中原地写入计算结果节省一份输出内存。在仓库中该算子的完整实现分布于 math/less 目录op_api提供 aclnn 接口层、op_host提供算子定义与 tiling、op_kernel提供 AscendC kernel 实现、op_kernel_aicpu提供 AICPU 兜底实现、framework提供 TF 插件适配。两段式接口与函数原型aclnnLtTensor和aclnnInplaceLtTensor均为两段式接口详细范式见 两段式接口必须先调用xxxGetWorkspaceSize第一段接口获取计算所需的workspace 大小与包含算子计算流程的op 执行器executor再依据 workspaceSize 在 Device 侧申请内存后调用第二段接口执行计算。说明workspace 是指除输入/输出外算子在 NPU 上完成计算所需的临时内存。第二段接口如aclnnLtTensor不能重复调用否则会出现异常。四个接口的原型如下aclnnStatus aclnnLtTensorGetWorkspaceSize( const aclTensor* self, const aclTensor* other, aclTensor* out, uint64_t* workspaceSize, aclOpExecutor** executor)aclnnStatus aclnnLtTensor( void* workspace, uint64_t workspaceSize, aclOpExecutor* executor, aclrtStream stream)aclnnStatus aclnnInplaceLtTensorGetWorkspaceSize( const aclTensor* selfRef, const aclTensor* other, uint64_t* workspaceSize, aclOpExecutor** executor)aclnnStatus aclnnInplaceLtTensor( void* workspace, uint64_t workspaceSize, aclOpExecutor* executor, aclrtStream stream)接口声明位于 math/less/op_api/aclnn_lt_tensor.h实现位于 math/less/op_api/aclnn_lt_tensor.cpp调用时需要包含头文件aclnnop/aclnn_lt_tensor.h。aclnnLtTensorGetWorkspaceSize 参数说明参数名输入/输出描述使用说明数据类型数据格式维度(shape)非连续TensorselfaclTensor*输入输入张量数据类型与 other 需满足互推导关系shape 需与 other 满足 broadcast 关系FLOAT、FLOAT16、BFLOAT16、INT32、UINT32、INT64、UINT64、INT16、UINT16、INT8、UINT8、DOUBLE、BOOLND0-8√otheraclTensor*输入输入张量数据类型与 self 需满足互推导关系shape 需与 self 满足 broadcast 关系FLOAT、FLOAT16、BFLOAT16、INT32、UINT32、INT64、UINT64、INT16、UINT16、INT8、UINT8、DOUBLE、BOOL---outaclTensor*输出输出张量shape 与 self、other 广播之后的 shape 一致数据类型要求为 BOOL 可转换的数据类型参见互转换关系FLOAT、FLOAT16、BFLOAT16、INT32、UINT32、INT64、UINT64、INT16、UINT16、INT8、UINT8、DOUBLE、BOOL、COMPLEX64、COMPLEX128ND0-8√workspaceSizeuint64_t*输出返回需要在 Device 侧申请的 workspace 大小-----executoraclOpExecutor**输出返回 op 执行器包含了算子计算流程-----约束Atlas 200I/500 A2 推理产品、Atlas 训练系列产品不支持 BFLOAT16 数据类型。返回值与错误码aclnnLtTensorGetWorkspaceSize返回aclnnStatus状态码完整定义见 aclnn返回码。第一段接口完成入参校验出现以下场景时报错返回值错误码描述ACLNN_ERR_PARAM_NULLPTR161001传入的 self、other 或 out 是空指针ACLNN_ERR_PARAM_INVALID161002self、other 或 out 的数据类型不在支持的范围之内ACLNN_ERR_PARAM_INVALID161002self、other 或 out 的维度大于 8ACLNN_ERR_PARAM_INVALID161002self 和 other 的数据类型无法进行推导ACLNN_ERR_PARAM_INVALID161002self 和 other 的 shape 无法进行 broadcastACLNN_ERR_PARAM_INVALID161002out 的 shape 与 broadcast 后的 shape 不一致在 math/less/op_api/aclnn_lt_tensor.cpp 的CheckParams中可以看到校验顺序与上述错误码一一对应先做空指针检查CheckNotNull返回ACLNN_ERR_PARAM_NULLPTR再检查数据类型是否在支持列表内CheckDtypeValid、类型能否互推导CheckPromoteType、shape 能否 broadcast 且与 out 一致CheckShape其余全部归入ACLNN_ERR_PARAM_INVALID。aclnnLtTensor 参数说明参数名输入/输出描述workspace输入在 Device 侧申请的 workspace 内存地址workspaceSize输入在 Device 侧申请的 workspace 大小由第一段接口 aclnnLtTensorGetWorkspaceSize 获取executor输入op 执行器包含了算子计算流程stream输入指定执行任务的 StreamaclnnInplaceLtTensorGetWorkspaceSize 参数说明参数名输入/输出描述使用说明数据类型数据格式维度(shape)非连续TensorselfRefaclTensor*输入|输出输入|输出张量数据类型与 other 需满足互推导关系shape 需与 other 满足 broadcast 关系broadcast 后的 shape 需与 selfRef 的 shape 一致FLOAT、FLOAT16、BFLOAT16、INT32、UINT32、INT64、UINT64、INT16、UINT16、INT8、UINT8、DOUBLE、BOOLND0-8√otheraclTensor*输入输入张量数据类型与 selfRef 需满足互推导关系shape 需与 self 满足 broadcast 关系broadcast 后的 shape 需与 selfRef 的 shape 一致FLOAT、FLOAT16、BFLOAT16、INT32、UINT32、INT64、UINT64、INT16、UINT16、INT8、UINT8、DOUBLE、BOOL---workspaceSizeuint64_t*输出返回需要在 Device 侧申请的 workspace 大小-----executoraclOpExecutor**输出返回 op 执行器包含了算子计算流程-----约束Atlas 200I/500 A2 推理产品、Atlas 训练系列产品不支持 BFLOAT16 数据类型。返回值与错误码返回值错误码描述ACLNN_ERR_PARAM_NULLPTR161001传入的 selfRef、other 是空指针ACLNN_ERR_PARAM_INVALID161002selfRef 和 other 的数据类型不在支持的范围之内ACLNN_ERR_PARAM_INVALID161002selfRef 和 other 的数据类型无法进行推导ACLNN_ERR_PARAM_INVALID161002selfRef 和 other 的 shape 无法做 broadcastACLNN_ERR_PARAM_INVALID161002selfRef 和 other 做 broadcast 后的 shape 不等于 selfRef 的 shapeACLNN_ERR_PARAM_INVALID161002selfRef、other 的维度大于 8注意原地版本的差异点由于结果写回selfRef因此要求广播后的 shape 必须与 selfRef 的 shape 相等否则无法原地写回这是与普通版本最核心的语义区别。aclnnInplaceLtTensor 参数说明参数名输入/输出描述workspace输入在 Device 侧申请的 workspace 内存地址workspaceSize输入在 Device 侧申请的 workspace 大小由第一段接口 aclnnInplaceLtTensorGetWorkspaceSize 获取executor输入op 执行器包含了算子计算流程stream输入指定执行任务的 Stream约束说明确定性计算aclnnLtTensor与aclnnInplaceLtTensor默认确定性实现。即同一输入在相同硬件与软件环境下多次执行结果完全一致不会引入并行归约顺序导致的非确定误差可放心用于需要复现结果的训练与调试场景更详细的背景可参考 确定性计算说明。调用示例示例代码仅供参考完整编译与执行流程请参考 编译与运行样例。两个完整可运行的示例同样保存在仓库中math/less/examples/test_aclnn_lt_tensor.cpp 与 math/less/examples/test_aclnn_inplace_lt_tensor.cpp。aclnnLtTensor 示例代码#include iostream #include vector #include acl/acl.h #include aclnnop/aclnn_lt_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); 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; } struct LtTensorData { 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::vectordouble selfHostData {0, 1, 2, 3, 4, 5, 6, 7}; std::vectordouble otherHostData {5, 5, 5, 5, 5, 5, 5, 5}; std::vectordouble outHostData {0, 0, 0, 0, 0, 0, 0, 0}; void* workspaceAddr nullptr; uint64_t workspaceSize 0; }; int CreateInputAndOutputTensors(LtTensorData data) { auto ret 0; // 创建self aclTensor ret CreateAclTensor(data.selfHostData, data.selfShape, data.selfDeviceAddr, aclDataType::ACL_DOUBLE, data.self); CHECK_RET(ret ACL_SUCCESS, return ret); // 创建other aclTensor ret CreateAclTensor(data.otherHostData, data.otherShape, data.otherDeviceAddr, aclDataType::ACL_DOUBLE, data.other); CHECK_RET(ret ACL_SUCCESS, return ret); // 创建out aclTensor ret CreateAclTensor(data.outHostData, data.outShape, data.outDeviceAddr, aclDataType::ACL_DOUBLE, data.out); CHECK_RET(ret ACL_SUCCESS, return ret); return ret; } int ExecuteLtTensorComputation(aclrtStream stream, LtTensorData data) { auto ret 0; aclOpExecutor* executor; // 调用aclnnLtTensor第一段接口 ret aclnnLtTensorGetWorkspaceSize(data.self, data.other, data.out, data.workspaceSize, executor); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclnnLtTensorGetWorkspaceSize failed. ERROR: %d\n, ret); return ret); // 根据第一段接口计算出的workspaceSize申请device内存 data.workspaceAddr nullptr; if (data.workspaceSize 0) { ret aclrtMalloc(data.workspaceAddr, data.workspaceSize, ACL_MEM_MALLOC_HUGE_FIRST); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(allocate workspace failed. ERROR: %d\n, ret); return ret); } // 调用aclnnLtTensor第二段接口 ret aclnnLtTensor(data.workspaceAddr, data.workspaceSize, executor, stream); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclnnLtTensor failed. ERROR: %d\n, ret); return ret); // 同步等待任务执行结束 ret aclrtSynchronizeStream(stream); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclrtSynchronizeStream failed. ERROR: %d\n, ret); return ret); return ret; } int ProcessAndPrintResults(const LtTensorData data) { auto ret 0; auto size GetShapeSize(data.outShape); std::vectordouble resultData(size, 0); ret aclrtMemcpy(resultData.data(), resultData.size() * sizeof(resultData[0]), data.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: %lf\n, i, resultData[i]); } return ret; } void ReleaseResources(LtTensorData data) { // 释放aclTensor和aclScalar aclDestroyTensor(data.self); aclDestroyTensor(data.other); aclDestroyTensor(data.out); // 释放device资源 aclrtFree(data.selfDeviceAddr); aclrtFree(data.otherDeviceAddr); aclrtFree(data.outDeviceAddr); if (data.workspaceSize 0) { aclrtFree(data.workspaceAddr); } } int ExecuteLtTensorOperator(aclrtStream stream) { LtTensorData data; // 创建输入和输出张量 auto ret CreateInputAndOutputTensors(data); CHECK_RET(ret ACL_SUCCESS, return ret); // 执行LtTensor算子操作 ret ExecuteLtTensorComputation(stream, data); CHECK_RET(ret ACL_SUCCESS, return ret); // 处理并打印结果 ret ProcessAndPrintResults(data); CHECK_RET(ret ACL_SUCCESS, return ret); // 释放资源 ReleaseResources(data); return 0; } int main() { 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); // 执行LtTensor操作 ret ExecuteLtTensorOperator(stream); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(ExecuteLtTensorOperator failed. ERROR: %d\n, ret); return ret); // 重置设备和终结ACL aclrtDestroyStream(stream); aclrtResetDevice(deviceId); aclFinalize(); return 0; }示例中self {0,1,2,3,4,5,6,7}、other {5,5,5,5,5,5,5,5}逐元素执行比较后期望输出前 5 个元素为True对应数值 1.0后 3 个元素为False对应数值 0.0。结果通过aclrtMemcpy(..., ACL_MEMCPY_DEVICE_TO_HOST)拷回 Host 侧打印验证。aclnnInplaceLtTensor 示例代码#include iostream #include vector #include acl/acl.h #include aclnnop/aclnn_lt_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 shape_size 1; for (auto i : shape) { shape_size * i; } return shape_size; } 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; } 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 ExecuteInplaceLtTensorOperator(aclrtStream stream) { auto ret 0; std::vectorint64_t selfShape {4, 2}; std::vectorint64_t otherShape {4, 2}; void* selfDeviceAddr nullptr; void* otherDeviceAddr nullptr; aclTensor* self nullptr; aclTensor* other nullptr; std::vectordouble selfHostData {0, 1, 2, 3, 4, 5, 6, 7}; std::vectorint otherHostData {1, 1, 1, 1, 0, 0, 0, 0}; ret CreateAclTensor(selfHostData, selfShape, selfDeviceAddr, aclDataType::ACL_DOUBLE, self); CHECK_RET(ret ACL_SUCCESS, return ret); ret CreateAclTensor(otherHostData, otherShape, otherDeviceAddr, aclDataType::ACL_INT32, other); CHECK_RET(ret ACL_SUCCESS, return ret); uint64_t workspaceSize 0; aclOpExecutor* executor; ret aclnnInplaceLtTensorGetWorkspaceSize(self, other, workspaceSize, executor); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclnnInplaceLtTensorGetWorkspaceSize failed. ERROR: %d\n, ret); return ret); 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); } ret aclnnInplaceLtTensor(workspaceAddr, workspaceSize, executor, stream); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclnnInplaceLtTensor failed. ERROR: %d\n, ret); return ret); ret aclrtSynchronizeStream(stream); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclrtSynchronizeStream failed. ERROR: %d\n, ret); return ret); auto size GetShapeSize(selfShape); std::vectordouble resultData(size, 0); 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: %lf\n, i, resultData[i]); } aclDestroyTensor(self); aclDestroyTensor(other); aclrtFree(selfDeviceAddr); aclrtFree(otherDeviceAddr); if (workspaceSize 0) { aclrtFree(workspaceAddr); } return 0; } int main() { 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); // 执行InplaceLtTensor操作 ret ExecuteInplaceLtTensorOperator(stream); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(ExecuteInplaceLtTensorOperator failed. ERROR: %d\n, ret); return ret); // 重置设备和终结ACL aclrtDestroyStream(stream); aclrtResetDevice(deviceId); aclFinalize(); return 0; }原地示例展示了两个工程要点输入可以是混合数据类型self为ACL_DOUBLE、other为ACL_INT32两者满足互推导关系f64 与 s32 推导为 f64由接口内部完成隐式转换无需用户手动 Cast结果从self的 Device 地址读取执行完aclnnInplaceLtTensor后比较结果直接写回self所在内存因此只需从selfDeviceAddr拷回结果即可全程无需分配输出张量。源码级原理剖析接口层的完整计算流水线在 math/less/op_api/aclnn_lt_tensor.cpp 的注释中完整计算流程被描述为self other | | \ / Contiguous(workspace_0) Contiguous(workspace_2) \ / Cast(workspace_1) Cast(workspace_3) \ / Less(workspace_4) | Cast(workspace_5) | ViewCopy | result对应aclnnLtTensorGetWorkspaceSize的实现逻辑aclnn_lt_tensor.cpp空 Tensor 快速路径若self或other为空 Tensor直接返回workspaceSize 0不构造计算图隐式类型提升先通过op::PromoteType或 A2 平台上的op::BinaryOpTypePromote推导统一计算类型特别地若推导结果为DT_BOOL会提升为DT_UINT8aclnn_lt_tensor.cpp因为 BOOL 参与向量比较计算不直接、且 kernel 侧统一按 uint8 语义处理布尔结果Contiguous Cast分别对self、other做l0op::Contiguous支持非连续张量输入与l0op::Cast转换为统一推导类型Less 核心计算调用l0op::Less完成逐元素比较产出 BOOL 结果Cast ViewCopy将 BOOL 结果 Cast 成out的目标数据类型如示例中的 DOUBLE再通过l0op::ViewCopy拷贝到输出张量从而兼容out为非连续张量的情况最后通过executor-GetWorkspaceSize()汇总整条流水线各阶段workspace_0 至 workspace_5所需的临时内存总大小。Inplace 接口的实现本质从 aclnn_lt_tensor.cpp 可以看出aclnnInplaceLtTensorGetWorkspaceSize的实现极为简洁aclnnStatus aclnnInplaceLtTensorGetWorkspaceSize(const aclTensor* selfRef, const aclTensor* other, uint64_t* workspaceSize, aclOpExecutor** executor) { auto out const_castaclTensor*(selfRef); return aclnnLtTensorGetWorkspaceSize(selfRef, other, out, workspaceSize, executor); }即原地版本只是将selfRef同时作为输出out传给普通版本计算完成后结果通过 ViewCopy 写回selfRef的内存。这也解释了为何原地版本要求broadcast 后的 shape 必须与 selfRef 的 shape 一致——输出写回的目标地址容量是固定的。平台相关的数据类型支持在 aclnn_lt_tensor.cpp 中输入与输出的数据类型支持列表按平台区分ASCEND910Atlas 训练系列产品等输入支持 FLOAT、INT32、INT64、FLOAT16、INT16、INT8、UINT8、DOUBLE、UINT32、UINT64、BOOL、UINT16不含 BFLOAT16ASCEND910B 及以上Atlas A2/A3、Ascend 950 等或 RegBase 架构在上述基础上额外支持 BFLOAT16输出侧还额外支持 COMPLEX64、COMPLEX128作为 BOOL 可转换的目标类型。这一差异正是原文档中Atlas 训练系列产品、Atlas 200I/500 A2 推理产品不支持 BFLOAT16约束的代码级来源由GetCurrentPlatformInfo().GetSocVersion()动态判定。算子定义、InferShape 与 Tiling在算子定义层面math/less/op_host/less_def.cpp算子名为Less输入为x1、x2输出为y输入数据类型支持 BF16、FLOAT16、FLOAT、INT8、UINT8、INT32、INT64、UINT64、BOOL、DOUBLE输出固定为 BOOLAICore 配置开启DynamicCompileStaticFlag、DynamicRankSupportFlag、DynamicShapeSupportFlag即支持动态 shape 与动态 rank 编译并指定 kernel 文件为less_apt在 math/less/op_host/less_infershape.cpp 中InferShape 直接复用广播基类Ops::Base::InferShape4Broadcast与接口层shape 需满足 broadcast 关系的校验保持一致在 math/less/op_host/arch35/less_tiling_arch35.cpp 中tiling 按输入数据类型int64、uint64、int32、float、half、uint8、int8/bool、double实例化对应的BroadcastBaseTilingLessComputeT::OpDag模板同一时刻要求x1与x2的数据类型一致该文件在接口层完成类型提升后天然满足。Kernel 层的 DAG 计算图在 math/less/op_kernel/arch35/less_dag.h 中kernel 通过 DAG有向无环图描述计算输入经Vec::CopyInBrcT广播拷贝进 Vector 单元Input0/Input1Vec::Compareuint8_t, T, LESS_CMP_MODELESS_CMP_MODE 0执行小于比较产出 uint8 掩码再经Vec::SelectLESS_SELECT_MODE 2结合常量 1/0 把比较结果映射为布尔值最终Vec::CopyOutuint8_t写回输出内存配置为MemLevel::LEVEL_2。入口 math/less/op_kernel/less_apt.cpp 中lesskernel 使用BroadcastSchschMode, OpDag调度器统一处理广播逻辑当输入类型为 BOOL 时按int8_t模板实例化其余类型按DTYPE_X1模板实例化。此外math/less/op_kernel_aicpu/less_aicpu.cpp 提供 AICPU 侧的兜底实现。二进制配置与测试验证算子的各数据类型二进制配置位于 math/less/op_host/config/ascend950/less_binary.json如Less_BFLOAT16、Less_FLOAT32、Less_INT64等输出统一为bool以及 math/less/op_host/config/ascend350/less_binary.json。仓库同时提供了多层次的测试用例用于验证接口行为OP API 单元测试math/less/tests/ut/op_api/test_lt_tensor.cpp 覆盖 FLOAT、FLOAT16、INT32、INT64 等多种 dtype 与 ND/NHWC/NCHW 等 format 组合并包含广播场景如{4,5}与{2,3,4,5}的广播比较通过OP_API_UT(aclnnLtTensor, ...)框架自动校验GetWorkspaceSize与计算结果InferShape 单元测试math/less/tests/ut/op_host/test_less_infershape.cppAICPU 兜底测试math/less/tests/ut/op_kernel_aicpu/test_less.cppST 测试math/less/tests/st/aclnnLtTensor/atk_aclnnLtTensor.json 与 math/less/tests/st/arch35/ttk_kernel_less_st.csv。工程选型建议需要保留原输入时使用aclnnLtTensor自行创建out张量接收结果希望节省内存、原输入不再需要时使用aclnnInplaceLtTensor直接将selfRef原地改写省去一份输出张量的申请与拷贝注意广播约束普通版本允许out的 shape 等于广播后 shape原地版本要求广播后 shape 与selfRefshape 完全一致平台差异在 Atlas 训练系列产品、Atlas 200I/500 A2 推理产品上避免使用 BFLOAT16 输入否则第一段接口会返回ACLNN_ERR_PARAM_INVALID。如需了解算子本身的完整语义、参数总览与其他调用方式如 aclnnLtScalar、图模式调用可进一步阅读 math/less/README.md 与同目录下的 aclnnLtScalaraclnnInplaceLtScalar 文档。赞分享算子库人工智能CANN【免费下载链接】ops-math本项目是CANN提供的数学类基础计算算子库实现网络在NPU上加速计算。项目地址https://gitcode.com/cann/ops-math点击查看免费下载相关推荐CANN ops-math Less 算子 aclnnLtTensor 与 aclnnInplaceLtTensor 接口调用指南CANN ops math Less 算子 aclnnLtTensor 与 aclnnInplaceLtTensor 接口调用指南 本篇技术指南以 CANN o算子库人工智能CANNCANN ops-math 算子实战aclnnDiv 与 aclnnInplaceDiv 除法算子接口全解析CANN ops math 算子实战aclnnDiv 与 aclnnInplaceDiv 除法算子接口全解析 本篇技术指南围绕 CANN ops math 数算子库人工智能CANNCANN ops-math 除法取模算子 aclnnDivMod / aclnnInplaceDivMod 接口全解析与实战CANN ops math 除法取模算子 aclnnDivMod / aclnnInplaceDivMod 接口全解析与实战 本文是 CANN ops math算子库人工智能CANN创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。