新闻详情

新闻详情

首页 / 资讯中心 / 详情

CANN ops-math 算子 aclnnHistc 接口详解:张量直方图统计的完整实践指南

发布时间:2026/9/20 16:56:30来源:尧图网络
CANN ops-math 算子 aclnnHistc 接口详解:张量直方图统计的完整实践指南
CANN ops-math 算子 aclnnHistc 接口详解张量直方图统计的完整实践指南【免费下载链接】ops-math本项目是CANN提供的数学类基础计算算子库实现网络在NPU上加速计算。项目地址: https://gitcode.com/cann/ops-math导读aclnnHistc是 CANN ops-math 算子库中HistogramV2算子的上层调用接口用于计算张量元素在等宽区间内的频数分布即直方图是实现数值分布统计、梯度裁剪分析、量化校准等场景的基础能力。本文以 math/histogram_v2/docs/aclnnHistc.md 为骨架结合 aclnn_histc.cpp、histogram.cpp、histogram_v2.cpp 等源码完整讲解接口原型、参数约束、错误码语义、确定性计算行为与端到端调用示例帮助读者在 Ascend 系列硬件上快速、正确地完成直方图统计计算。功能说明直方图统计的数学语义aclnnHistc完成的核心计算是张量直方图Histogram统计以min和max作为统计的上下限在二者之间划出等宽的、数量为bins的区间统计输入张量self中所有元素落入各个区间的数量写入输出张量out如果min与max相等则使用张量中所有元素的最小值和最大值作为统计上下限该逻辑在 aclnn_histc.cpp 的NeedComputeMinMax中体现在 Atlas A2 训练/推理系列与 Ascend 950 上当min max浮点场景差值绝对值小于1e-6或min max inf / -inf时会通过AllMinMax调用l0op::ReduceMin/l0op::ReduceMax重新求取张量内的真实极值小于min或大于max的元素不会被统计直接丢弃。以文档调用示例的数据为例self {1,2,3,4,5,6,7,8,9}、bins 3、min 1、max 9区间被等分为[1, 3.667)、[3.667, 6.333)、[6.333, 9]各区间恰好落入 3 个元素因此输出为{3, 3, 3}。该行为与 PyTorchtorch.histc对齐仓库中的 golden 测试 aclnnHistc.py 正是以torch.histc(input0_t, binsbins, minminVal, maxmaxVal)作为参照生成期望结果。产品支持情况aclnnHistc的硬件支持情况如下以 aclnnHistc.md 为准产品是否支持Ascend 950PR / Ascend 950DT支持Atlas A3 训练系列产品 / Atlas A3 推理系列产品支持Atlas A2 训练系列产品 / Atlas A2 推理系列产品支持Atlas 200I/500 A2 推理产品不支持Atlas 推理系列产品支持Atlas 训练系列产品支持从算子定义看histogram_v2_def.cpp 为HistogramV2注册了ascend910b、ascend910_93对应 A3、ascend310p、ascend950四套 AICore 配置与上述支持矩阵一致其中 Ascend 950 配置额外指定了opFile.value histogram_v2_apt对应 histogram_v2_apt.cpp 专属实现。两段式接口先规划、后执行aclnnHistc遵循 CANN 算子的两段式接口设计调用流程分为两步先调用aclnnHistcGetWorkspaceSize完成入参校验、构建算子计算图executor并返回执行所需的 workspace 大小再调用aclnnHistc在指定的 Stream 上真正执行计算。在 aclnn_histc.cpp 的第一段接口实现中可以看到接口内部依次完成参数校验、Contiguous连续性转换、min/max标量转张量、必要时重算极值、l0op::Histogram计算图构建、Cast类型转换与ViewCopy结果回写最后通过GetWorkspaceSize()汇总出整个算子链的 workspace 需求。函数原型aclnnStatus aclnnHistcGetWorkspaceSize( const aclTensor* self, int64_t bins, const aclScalar* min, const aclScalar* max, aclTensor* out, uint64_t* workspaceSize, aclOpExecutor** executor)aclnnStatus aclnnHistc( void* workspace, uint64_t workspaceSize, aclOpExecutor* executor, aclrtStream stream)aclnnHistcGetWorkspaceSize 参数说明第一段接口共 7 个参数其含义、类型与使用约束如下表参数名输入/输出描述使用说明数据类型数据格式维度(shape)非连续TensorselfaclTensor*输入待被统计元素在各个 bins 的数量的张量-FLOAT16、FLOAT32、INT32、INT64、INT16、INT8、UINT8ND0-8√binsint64_t输入直方图 bins 的数量取值范围需大于 0----minaclScalar*输入直方图的统计下限包括数据类型需要是可转换成 FLOAT 的类型取值范围不能大于 max 的值----maxaclScalar*输入直方图的统计上限包括数据类型需要是可转换成 FLOAT 的类型取值范围不能小于 min 的值----outaclTensor*输出直方图统计结果out 数据类型需要可转换为 self 的数据类型参考互转换关系元素个数等于 binsFLOAT16、FLOAT32、INT32、INT64、INT16、INT8、UINT8ND1√workspaceSizeuint64_t*输出返回需要在 Device 侧申请的 workspace 大小-----executoraclOpExecutor**输出返回 op 执行器包含了算子计算流程-----补充说明数据类型限制与实现一致aclnn_histc.cpp 中的DTYPE_SUPPORT_LIST_910B定义了与文档完全一致的支持列表FLOAT、FLOAT16、INT64、INT32、INT16、INT8、UINT8。维度上限self支持 0-8 维对应源码中的MAX_DIM 8aclnn_histc.cppout必须是 1 维且元素个数等于bins。非连续 Tensorself与out均支持非连续存储第一段接口内部会先通过l0op::Contiguous完成连续性转换后再送入内核因此调用方无需手动预做contiguous。标量转换min/max为aclScalar内部通过ConvertToTensor按self的数据类型转换为单元素张量参与构图aclnn_histc.cpp。空张量场景当self-IsEmpty()时接口会直接走EmptyTensor分支通过ZerosLikeViewCopy将out清零后返回不进入真正的直方图计算aclnn_histc.cpp。返回值状态码与错误码语义aclnnHistcGetWorkspaceSize与aclnnHistc均返回aclnnStatus状态码通用返回码定义见 aclnn 返回码。第一段接口aclnnHistcGetWorkspaceSize会完成全部入参校验以下场景将返回错误返回码错误码描述ACLNN_ERR_PARAM_NULLPTR161001传入的 self、out、min、max 是空指针ACLNN_ERR_PARAM_INVALID161002self 和 out 的数据类型和数据格式不在支持的范围之内ACLNN_ERR_PARAM_INVALID161002self 与 out 的数据类型不满足互转换关系ACLNN_ERR_PARAM_INVALID161002传入的 bins 小于等于 0ACLNN_ERR_PARAM_INVALID161002传入的 min 大于 maxACLNN_ERR_PARAM_INVALID161002out 的 shape 维度不为 1ACLNN_ERR_PARAM_INVALID161002self 的 shape 维度大于 8ACLNN_ERR_PARAM_INVALID161002out 的 size 不等于 bins这些校验逻辑在源码中有严格对应空指针检查CheckNotNull对应错误码 161001aclnn_histc.cpp数据类型合法性CheckDtypeValid、类型互转CheckPromoteType、取值范围CheckValueRange含bins 0、min max、shape 约束CheckShape含维度上限与 size 校验均返回 161002最终由CheckHistcParams按顺序串起完整的校验链aclnn_histc.cpp额外的合法性保护CheckMinMaxIsInfNan会拒绝非法inf/nan作为min/max仅允许min max inf或min max -inf的退化场景此时交由自动极值重算处理aclnn_histc.cpp。aclnnHistc 参数说明第二段接口负责在 Device 上真正执行计算共 4 个参数参数名输入/输出描述workspace输入在 Device 侧申请的 workspace 内存地址workspaceSize输入在 Device 侧申请的 workspace 大小由第一段接口 aclnnHistcGetWorkspaceSize 获取executor输入op 执行器包含了算子计算流程stream输入指定执行任务的 Stream从实现上看aclnn_histc.cpp 的第二段接口会记录 DFX 日志后直接调用CommonOpExecutorRun(workspace, workspaceSize, executor, stream)完成执行调用方需保证 workspace 已按第一段接口返回的workspaceSize通过aclrtMalloc在 Device 侧申请。约束说明确定性计算aclnnHistc的确定性行为与硬件平台相关直方图统计本质上是多核并行的原子累加操作不同平台默认策略不同Atlas A2 训练系列产品 / Atlas A2 推理系列产品、Atlas A3 训练系列产品 / Atlas A3 推理系列产品、Atlas 推理系列产品、Atlas 训练系列产品默认确定性实现相同输入必然得到逐位一致的输出Ascend 950PR / Ascend 950DT默认非确定性实现支持通过aclrtCtxSetSysParamOpt开启确定性计算。该差异在 tiling 数据结构中也能观察到histogram_v2_tiling.h 针对确定性 / 非确定性、UB 是否整块加载、输出为 fp32 等组合注册了大量专用 tiling key如HistogramV2_101~_107为默认 int32 输出确定性版本HistogramV2_1111/1117等为 fp32 输出的确定性版本HistogramV2_2111/2117为 SIMD 确定性 fp32 输出版本kernel 入口 histogram_v2.cpp 则按TILING_KEY将不同的数据类型组合分派到对应的HistogramV2Scalar模板实例。若业务对多次运行结果的一致性有强要求请在 Ascend 950 上显式开启确定性开关。端到端调用示例下面代码来自 examples/test_aclnn_histc.cpp与文档示例一致演示了从环境初始化、构造 tensor/scalar、两段式调用到结果回拷与资源释放的完整流程。具体编译与执行方式可参考编译与运行样例。#include iostream #include vector #include acl/acl.h #include aclnnop/aclnn_histc.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 {3, 3}; std::vectorint64_t outShape {3}; void* selfDeviceAddr nullptr; void* outDeviceAddr nullptr; aclTensor* self nullptr; aclScalar* min nullptr; aclScalar* max nullptr; aclTensor* out nullptr; std::vectorfloat selfHostData {1, 2, 3, 4, 5, 6, 7, 8, 9}; std::vectorfloat outHostData {0, 0, 0}; int64_t bins 3; float minValue 1.0f; float maxValue 9.0f; // 创建self aclTensor ret CreateAclTensor(selfHostData, selfShape, selfDeviceAddr, aclDataType::ACL_FLOAT, self); CHECK_RET(ret ACL_SUCCESS, return ret); // 创建min aclScalar min aclCreateScalar(minValue, aclDataType::ACL_FLOAT); CHECK_RET(min ! nullptr, return ret); // 创建max aclScalar max aclCreateScalar(maxValue, aclDataType::ACL_FLOAT); CHECK_RET(max ! 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; // 调用aclnnHistc第一段接口 ret aclnnHistcGetWorkspaceSize(self, bins, min, max, out, workspaceSize, executor); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclnnHistcGetWorkspaceSize 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); } // 调用aclnnHistc第二段接口 ret aclnnHistc(workspaceAddr, workspaceSize, executor, stream); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclnnHistc 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]); } // 6. 释放aclTensor和aclScalar需要根据具体API的接口定义修改 aclDestroyTensor(self); aclDestroyScalar(min); aclDestroyScalar(max); aclDestroyTensor(out); // 7. 释放Device资源需要根据具体API的接口定义修改 aclrtFree(selfDeviceAddr); aclrtFree(outDeviceAddr); if (workspaceSize 0) { aclrtFree(workspaceAddr); } aclrtDestroyStream(stream); aclrtResetDevice(deviceId); aclFinalize(); return 0; }示例中几个关键点需要留意workspace 申请以第一段接口返回值为准即使workspaceSize为 0也应保留条件分支判断避免对 0 大小内存执行aclrtMalloc/aclrtFree输出结果需显式回拷aclnnHistc是异步任务必须先用aclrtSynchronizeStream同步再通过aclrtMemcpy将 Device 侧结果拷回 Host示例运行结果self为 3x3 的{1,...,9}bins3、min1、max9时三个等宽区间各落入 3 个元素打印结果为result[0] is: 3.000000、result[1] is: 3.000000、result[2] is: 3.000000。底层实现路径从 aclnn 到 Kernel从源码结构看aclnnHistc的完整执行链路可分为四层理解这条链路有助于排查问题与评估性能接口校验与构图层op_api/aclnn_histc.cpp完成全部入参校验将self转连续张量、min/max标量转张量并把Histogram算子注册进 executor 的启动列表L0 算子分发层op_api/histogram.cppl0op::Histogram根据当前 NPU 架构与数据类型做双路分发AiCore 路径IsAiCoreSupport返回 true覆盖 Atlas A2、Ascend 950、Atlas 310P 且类型在支持列表内走ADD_TO_LAUNCHER_LIST_AICORE启动 Vector/SIMT 内核。值得注意的是输出中间类型为INT32950 上可支持 FLOAT 输出最终再统一Cast到out声明的类型histogram.cppAiCPU 路径兜底通过ADD_TO_LAUNCHER_LIST_AICPU启动 AiCPU 实现FLOAT16 输入会先提升为 FLOAT 计算histogram.cppTiling 计算层op_host/histogram_v2_tiling.cpp 及 histogram_v2_tiling.h在 Host 侧读取平台 AIV 核数、UB 内存大小、libapi workspace 大小等编译期信息依据bins、输入数据长度与平台约束计算出 former/tail 分块、UB 循环次数、参与计算的核数等切分参数Kernel 执行层op_kernel/histogram_v2.cpp 与 histogram_v2_scalar.h按TILING_KEY将 7 种输入数据类型FLOAT、INT32、INT8、UINT8、INT16、INT64、FLOAT16实例化为对应的标量累加内核arch35 目录下的 SIMD/SIMT 模板如histogram_v2_simd_full_load_det_fp32out.h、histogram_v2_simt_full_load.h则承载 950 的确定性/非确定性、fp32 输出等变体。对于需要更高层集成的场景仓库还提供了两条补充路径图模式GEIR调用通过 test_geir_histogram_v2.cpp 与 histogram_v2_proto.h 中的算子 IR 构图方式调用图模式下的算子定义输入x/min/max、输出y、可选属性bins与y_dtype见 histogram_v2_def.cpp算子属性默认值图模式下bins为可选属性默认值为 100y_dtype默认 INT32输出y初始化为 0histogram_v2_def.cpp。而 aclnn 接口中bins为必传参数调用时需显式指定。测试与验证参考仓库为aclnnHistc提供了完整的测试资产可作为自测与二次开发的参考OP API 单元测试tests/ut/op_api/test_aclnn_histc.cpp 与 Python golden 脚本 aclnnHistc.py后者以torch.histc为参照生成期望值OP Host 单元测试tests/ut/op_host/test_histogram_v2_infershape.cpp 验证 shape 推导arch22/arch35 下各有 test_histogram_v2_tiling.cpp 验证 tiling 计算OP Kernel 单元测试tests/ut/op_kernel/test_histogram_v2.cpp 配合 gen_data.py 构造多类型、多 shape 的输入数据ST 测试tests/st/aclnnHistc/atk_aclnnHistc.json 与 executor_aclnnHistc.py 覆盖端到端场景arch35 下还有 ttk_e2e_histogram_v2_145.csv 等批量用例矩阵。小结aclnnHistc以简洁的两段式接口封装了直方图统计的完整实现第一段接口负责参数校验与 workspace 规划第二段接口完成流式执行内部通过 AiCore/AiCPU 双路径分发适配不同硬件并针对min max、空张量、非连续存储、inf/nan 边界等场景做了精细处理。使用时可重点把握三类约束——bins 0、min max、out一维且元素个数等于bins同时结合目标平台的确定性策略与输出类型支持950 支持 FLOAT 输出其余平台为 INT32进行合理设计。算子完整定义、示例与测试代码均位于 math/histogram_v2 目录下可供进一步查阅。【免费下载链接】ops-math本项目是CANN提供的数学类基础计算算子库实现网络在NPU上加速计算。项目地址: https://gitcode.com/cann/ops-math创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

Jetson eFuse 安全启动烧录实战:从设计到量产的避坑指南 2026/9/20 17:44:41

Jetson eFuse 安全启动烧录实战:从设计到量产的避坑指南

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

阅读更多 →
LabVIEW分布式轧机监测系统架构与实战 2026/9/20 17:44:41

LabVIEW分布式轧机监测系统架构与实战

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

阅读更多 →
MicroPython 与 RP2040 入门:从 GPIO 到 PWM 的硬件开发实战 2026/9/20 17:44:41

MicroPython 与 RP2040 入门:从 GPIO 到 PWM 的硬件开发实战

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

阅读更多 →
VS Code + LaTeX 论文写作环境搭建与实战指南 2026/9/20 17:44:41

VS Code + LaTeX 论文写作环境搭建与实战指南

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

阅读更多 →
C语言题库精讲:从main函数到运算符优先级,夯实基础 2026/9/20 17:44:41

C语言题库精讲:从main函数到运算符优先级,夯实基础

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

阅读更多 →
Vitess v11.0.2 补丁发布解析:Log4j 安全漏洞修复、VReplication 已知问题与生成列 Bug 修复 2026/9/20 17:41:41

Vitess v11.0.2 补丁发布解析:Log4j 安全漏洞修复、VReplication 已知问题与生成列 Bug 修复

Vitess v11.0.2 补丁发布解析:Log4j 安全漏洞修复、VReplication 已知问题与生成列 Bug 修复 【免费下载链接】vitess Vitess is a database clustering system for horizontal scaling of MySQL. 项目地址: https://gitcode.com/gh_mirrors/vi/vitess 本篇文…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞