ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

asc-devkit 昇腾算子开发 NumericLimits 工具类完全指南:类型极值查询接口(Max/Min/Infinity/NaN)源码解析与实战

asc-devkit 昇腾算子开发 NumericLimits 工具类完全指南:类型极值查询接口(Max/Min/Infinity/NaN)源码解析与实战
  • 人工智能
  • 深度学习
  • 算子库
  • CANN
  • Ascend

【免费下载链接】asc-devkit

本项目是CANN 推出的昇腾AI处理器专用的算子程序开发语言,原生支持C和C++标准规范,主要由类库和语言扩展层构成,提供多层级API,满足多维场景算子开发诉求。

项目地址:https://gitcode.com/cann/asc-devkit
点击查看免费下载

导读

本文系统讲解 asc-devkit(CANN 昇腾 AI 处理器算子开发语言)中 SIMD API 基础工具类AscendC::NumericLimits:它用于在 Kernel 内查询指定数据类型的最大值、最小值、无穷大、NaN 等极限值,并提供"标量返回 + 矢量填充"两种接口形态,是算子初始化缓冲区、构造哨兵值、做边界判断时最常用的工具类之一。读完本文,你将掌握全部 8 个接口(Max、Lowest、Min、Infinity、NegativeInfinity、QuietNaN、SignalingNaN、DeNormMin)的函数原型、参数约束、各数据类型的精确输出值,并能结合 接口头文件 的源码理解其底层位编码实现与产品支持差异。

NumericLimits 工具类概述

NumericLimits是 asc-devkit 提供的编译期(constexpr)数值极限查询工具类,用于查询指定数据类型的最大值、最小值、正负无穷大、安静 NaN、发信 NaN、最小正非正规值等属性,作用类似 C++ 标准库中的std::numeric_limits,但专为昇腾 AI Core 上的 Kernel 代码设计:接口全部以__aicore__标注、可在设备侧内联执行,并额外提供面向LocalTensor的矢量批量填充重载。

使用前需包含头文件:

#include "basic_api/kernel_operator_limits_intf.h"

头文件的类定义位于AscendC命名空间,通过模板参数T指定待查询的数据类型,类定义原型如下(出自 接口头文件 与 简介文档):

template <typename T> struct NumericLimits { // 标量接口,返回值为标量 constexpr __aicore__ static inline T Max(); constexpr __aicore__ static inline T Lowest(); constexpr __aicore__ static inline T Min(); constexpr __aicore__ static inline T Infinity(); constexpr __aicore__ static inline T NegativeInfinity(); constexpr __aicore__ static inline T QuietNaN(); constexpr __aicore__ static inline T SignalingNaN(); constexpr __aicore__ static inline T DeNormMin(); // 矢量接口,为dstLocal前count个元素赋值 __aicore__ static inline void Max(const LocalTensor<T> &dstLocal, uint32_t count); __aicore__ static inline void Lowest(const LocalTensor<T> &dstLocal, uint32_t count); __aicore__ static inline void Min(const LocalTensor<T> &dstLocal, uint32_t count); __aicore__ static inline void Infinity(const LocalTensor<T> &dstLocal, uint32_t count); __aicore__ static inline void NegativeInfinity(const LocalTensor<T> &dstLocal, uint32_t count); __aicore__ static inline void QuietNaN(const LocalTensor<T> &dstLocal, uint32_t count); __aicore__ static inline void SignalingNaN(const LocalTensor<T> &dstLocal, uint32_t count); __aicore__ static inline void DeNormMin(const LocalTensor<T> &dstLocal, uint32_t count); };

可以看出,每个属性都提供两种调用形态:

  • 标量接口:constexpr修饰、返回类型T的静态内联函数,可在编译期求值,适合作为常量使用;
  • 矢量接口:接受LocalTensor<T>&目的操作数与元素个数count,将对应极限值批量写入张量的前count个元素。

8 个接口功能总览

接口名称功能说明数据类型
Max返回指定数据类型的最大有限值。int8_t、uint8_t、int16_t、uint16_t、half、bfloat16_t、int32_t、uint32_t、float
Lowest返回指定数据类型的最低有限值,即满足无其他有限值 y 符合 y < x 的有限值 x。int8_t、uint8_t、int16_t、uint16_t、half、bfloat16_t、int32_t、uint32_t、float
Min返回指定数据类型的最小有限值(对浮点数为最小规约正数)。int8_t、uint8_t、int16_t、uint16_t、half、bfloat16_t、int32_t、uint32_t、float
Infinity返回指定数据类型的正无穷大值。half、bfloat16_t、float
NegativeInfinity返回指定数据类型的负无穷大值。half、bfloat16_t、float
QuietNaN返回指定数据类型的安静 NaN 值(浮点尾数最高位为 1)。half、bfloat16_t、float
SignalingNaN返回指定数据类型的发信 NaN 值(浮点尾数最高位为 0)。half、bfloat16_t、float
DeNormMin返回指定数据类型的最小正的非正规值。half、bfloat16_t、float

产品支持情况

NumericLimits 全部接口均为Ascend 950PR & 950DT 系列产品专属能力,其余产品线(Atlas A3 系列、Atlas A2 系列、Atlas 200I/500 A2 推理产品、Atlas 推理系列产品 AI Core / Vector Core、Atlas 训练系列产品)均不支持。从源码看,这与头文件中的__NPU_ARCH__编译期分支一一对应:当架构为 3510、5102、5162 时,接口才启用完整类型集(含 bfloat16_t)的支持检查,详见后文"源码实现原理"章节。

标量接口与矢量接口的通用说明

参数说明

矢量接口的公共参数如下(与 LocalTensor 的通用约定一致):

参数名输入/输出描述
dst / dstLocal输出目的操作数。类型为 LocalTensor,支持的 TPosition 为 VECIN / VECCALC / VECOUT。LocalTensor 的起始地址需要 32 字节对齐。
count输入输入数据元素个数。

返回值说明

  • 标量接口:返回值为对应数据类型的极限值(类型为T),且为constexpr,可在编译期参与常量表达式求值;
  • 矢量接口:无返回值(void),通过写目的张量dst前count个元素完成赋值。

约束说明

  • 标量接口无额外约束;
  • 矢量接口内部使用 Memory 矢量计算下的数据填充接口 duplicate 填充前count个元素,因此其地址对齐、count取值等约束需参照 duplicate 接口的约束说明。

接口逐一详解

Max:最大有限值

功能说明:返回指定数据类型的最大有限值(头文件见 Max.md)。

函数原型:

constexpr __aicore__ static inline T Max() // 标量接口 __aicore__ static inline void Max(const LocalTensor<T>& dst, uint32_t count) // 矢量接口

支持数据类型(Ascend 950PR & 950DT):int8_t、uint8_t、int16_t、uint16_t、half、bfloat16_t、int32_t、uint32_t、float。

各数据类型输出:

数据类型十进制数二进制各 bit 位
uint8_t2551111 1111
int8_t1270111 1111
uint16_t655351111 1111 1111 1111
int16_t327670111 1111 1111 1111
half655040 11110 1111111111
bfloat16_t3.4e+380 11111110 1111111
uint32_t42949672951111 1111 1111 1111 1111 1111 1111 1111
int32_t21474836470111 1111 1111 1111 1111 1111 1111 1111
float3.4e+380 11111110 11111111111111111111111

调用示例:

// 标量接口:直接取得 int32_t 的最大值 2147483647 int32_t value = AscendC::NumericLimits<int32_t>::Max(); // 矢量接口:将 dstLocal 前 256 个元素全部填充为 int32_t 最大值 AscendC::NumericLimits<int32_t>::Max(dstLocal, 256);

Lowest:最低有限值

功能说明:返回指定数据类型的最低有限值,即满足"无其他有限值 y 符合 y < x"的有限值 x。对整数类型即其最小可表示值,对浮点类型即其最大的负有限数(约等于负的最大值,区别于Min返回的最小正数)。详见 Lowest.md。

函数原型:

constexpr __aicore__ static inline T Lowest() __aicore__ static inline void Lowest(const LocalTensor<T>& dstLocal, uint32_t count)

支持数据类型:与 Max 相同(int8_t、uint8_t、int16_t、uint16_t、half、bfloat16_t、int32_t、uint32_t、float)。

各数据类型输出:

数据类型十进制数二进制各 bit 位
uint8_t00000 0000
int8_t-1281000 0000
uint16_t00000 0000 0000 0000
int16_t-327681000 0000 0000 0000
half-655041 11110 1111111111
bfloat16_t-3.4e+381 11111110 1111111
uint32_t00000 0000 0000 0000 0000 0000 0000 0000
int32_t-21474836481000 0000 0000 0000 0000 0000 0000 0000
float-3.4e+381 11111110 11111111111111111111111

调用示例:

int32_t value = AscendC::NumericLimits<int32_t>::Lowest(); // 标量:-2147483648 AscendC::NumericLimits<int32_t>::Lowest(dstLocal, 256); // 矢量:填充 256 个最低有限值

Min:最小有限值

功能说明:返回指定数据类型的最小有限值;对浮点数类型来说,返回其最小规约正数(区别于Lowest的负向极值)。详见 Min.md。

函数原型:

constexpr __aicore__ static inline T Min() __aicore__ static inline void Min(const LocalTensor<T>& dst, uint32_t count)

支持数据类型:与 Max 相同。

各数据类型输出:

数据类型十进制数二进制各 bit 位
uint8_t00000 0000
int8_t-1281000 0000
uint16_t00000 0000 0000 0000
int16_t-327681000 0000 0000 0000
half6.1e-50 00001 0000000000
bfloat16_t1.18e-380 00000001 0000000
uint32_t00000 0000 0000 0000 0000 0000 0000 0000
int32_t-21474836481000 0000 0000 0000 0000 0000 0000 0000
float1.18e-380 00000001 00000000000000000000000

调用示例:

int32_t value = AscendC::NumericLimits<int32_t>::Min(); // 标量:-2147483648 AscendC::NumericLimits<int32_t>::Min(dstLocal, 256); // 矢量:填充 256 个最小有限值

注意:整数类型的Min()与Lowest()输出相同(均为类型的最小值),而浮点类型两者语义不同——Lowest()返回负向极限(如 float 的 -3.4e38),Min()返回最小正规正数(如 float 的 1.18e-38)。

Infinity:正无穷大

功能说明:返回指定数据类型的正无穷大值(builtin_data_types)。详见 Infinity.md。

函数原型:

constexpr __aicore__ static inline T Infinity() __aicore__ static inline void Infinity(const LocalTensor<T>& dstLocal, uint32_t count)

支持数据类型:half、bfloat16_t、float(仅浮点类型)。

各数据类型输出:

数据类型十进制数二进制各 bit 位
halfinf0 11111 0000000000
bfloat16_tinf0 11111111 0000000
floatinf0 11111111 00000000000000000000000

调用示例:

float value = AscendC::NumericLimits<float>::Infinity(); // 标量:+inf AscendC::NumericLimits<float>::Infinity(dstLocal, 256); // 矢量:填充 256 个正无穷大值

NegativeInfinity:负无穷大

功能说明:返回指定数据类型的负无穷大值。详见 NegativeInfinity.md。

函数原型:

constexpr __aicore__ static inline T NegativeInfinity() __aicore__ static inline void NegativeInfinity(const LocalTensor<T>& dstLocal, uint32_t count)

支持数据类型:half、bfloat16_t、float。

各数据类型输出:

数据类型十进制数二进制各 bit 位
half-inf1 11111 0000000000
bfloat16_t-inf1 11111111 0000000
float-inf1 11111111 00000000000000000000000

调用示例:

float value = AscendC::NumericLimits<float>::NegativeInfinity(); // 标量:-inf AscendC::NumericLimits<float>::NegativeInfinity(dstLocal, 256); // 矢量:填充 256 个负无穷大值

QuietNaN:安静 NaN

功能说明:返回指定数据类型的**安静 NaN(Quiet NaN)**值,其浮点尾数最高位为 1,在参与运算时不触发浮点异常。详见 QuietNaN.md。

函数原型:

constexpr __aicore__ static inline T QuietNaN() __aicore__ static inline void QuietNaN(const LocalTensor<T>& dstLocal, uint32_t count)

支持数据类型:half、bfloat16_t、float。

各数据类型输出:

数据类型十进制数二进制各 bit 位
halfnan0 11111 1000000000
bfloat16_tnan0 11111111 1000000
floatnan0 11111111 10000000000000000000000

调用示例:

float value = AscendC::NumericLimits<float>::QuietNaN(); // 标量:quiet NaN AscendC::NumericLimits<float>::QuietNaN(dstLocal, 256); // 矢量:填充 256 个 NaN 值

SignalingNaN:发信 NaN

功能说明:返回指定数据类型的**发信 NaN(Signaling NaN)**值,其浮点尾数最高位为 0;当运算操作数包含发信 NaN 时通常会触发浮点无效操作异常(信令)。详见 SignalingNaN.md。

函数原型:

constexpr __aicore__ static inline T SignalingNaN() __aicore__ static inline void SignalingNaN(const LocalTensor<T>& dstLocal, uint32_t count)

支持数据类型:half、bfloat16_t、float。

各数据类型输出:

数据类型十进制数二进制各 bit 位
halfnan0 11111 0100000000
bfloat16_tnan0 11111111 0100000
floatnan0 11111111 01000000000000000000000

调用示例:

float value = AscendC::NumericLimits<float>::SignalingNaN(); // 标量:signaling NaN AscendC::NumericLimits<float>::SignalingNaN(dstLocal, 256); // 矢量:填充 256 个 NaN 值

对比 QuietNaN 与 SignalingNaN 的位编码(以 half 为例):两者指数位均为全 1(11111),区别仅在尾数——QuietNaN 尾数最高位为 1(1000000000),SignalingNaN 尾数最高位为 0(0100000000),与 IEEE 754 标准定义一致。

DeNormMin:最小正非正规值

功能说明:返回指定数据类型的最小正的非正规值(subnormal / denormal 最小值),即浮点尾数最低位为 1、指数位全 0 时的最小可表示正数。详见 DeNormMin.md。

函数原型:

constexpr __aicore__ static inline T DeNormMin() __aicore__ static inline void DeNormMin(const LocalTensor<T>& dstLocal, uint32_t count)

支持数据类型:half、bfloat16_t、float。

各数据类型输出:

数据类型十进制数二进制各 bit 位
half5.96e-80 00000 0000000001
bfloat16_t9.2e-410 00000000 0000001
float1.4e-450 00000000 00000000000000000000001

调用示例:

float value = AscendC::NumericLimits<float>::DeNormMin(); // 标量:1.4e-45 AscendC::NumericLimits<float>::DeNormMin(dstLocal, 256); // 矢量:填充 256 个最小正非正规值

源码实现原理深度解析

1. 编译期类型支持检查:static_assert + SupportType

接口头文件 kernel_operator_limits_intf.h 在实现入口处使用static_assert结合SupportType<T, ...>()做类型合法性检查。SupportType定义于 kernel_utils_ceil_oom_que.h,是一个编译期递归模板,逐一对T与候选类型做IsSameType匹配:

template <typename T, typename U, typename... Args> __aicore__ constexpr bool SupportType() { if constexpr (sizeof...(Args) > 0) { return IsSameType<T, U>::value || SupportType<T, Args...>(); } return IsSameType<T, U>::value; }

因此,当调用NumericLimits<int64_t>::Max()这类不支持的类型时,编译器会在 Kernel 编译阶段直接报错"current data type is not supported on current device!",把错误拦截在编译期而非运行期。

2. 类型集随架构条件编译

从 接口头文件 可以看到,同一接口在不同__NPU_ARCH__下允许的类型集不同:

#if (__NPU_ARCH__ == 3510) || (__NPU_ARCH__ == 5102) || (__NPU_ARCH__ == 5162) static_assert(SupportType<T, uint8_t, int8_t, uint16_t, int16_t, uint32_t, int32_t, half, float, bfloat16_t>(), ...); #else static_assert(SupportType<T, uint8_t, int8_t, uint16_t, int16_t, uint32_t, int32_t, half, float>(), ...); #endif

即仅 Ascend 950PR & 950DT(对应架构 3510 / 5102 / 5162)支持 bfloat16_t 这一类型,其余架构连Infinity等浮点接口也仅支持 half 与 float。这与前文"产品支持情况"中的表格完全一致,也从源码层面解释了文档中 bfloat16_t 仅在 950 系列可用的原因。

3. 极限值的位编码实现:GetScalarBitcodeValue

标量接口并非做运行时计算,而是直接返回预先算好的十六进制位编码(bitcode),通过联合体做位级重解释。辅助函数定义于 kernel_utils_struct_norm_sort.h:

template <typename T> constexpr __aicore__ inline uint64_t GetScalarBitcodeValue(T scalarValue) { union ScalarBitcode { __callee__ ScalarBitcode() {} T input; uint64_t output; } data; data.input = scalarValue; return data.output; }

以Max()为例(接口头文件),每个类型分支用if constexpr匹配后返回对应编码,例如:

  • uint8_t→0xFFu(255,二进制1111 1111);
  • int32_t→0x7FFFFFFFu(2147483647);
  • half→0x7BFFu(65504,二进制0 11110 1111111111);
  • float→0x7F7FFFFFu(3.4e38);
  • bfloat16_t→0x7F7Fu(3.4e38,仅 950 系列分支)。

对照前文输出表可以发现:文档中每个十进制值与二进制位串,都能在源码的十六进制 bitcode 中找到精确对应。这正是该类全部标量接口标记constexpr的原因——位编码在编译期即可内联展开,设备侧零运行时开销。

4. 矢量接口统一委托 Duplicate

矢量接口实现极其简洁,全部委托给 Memory 矢量计算的数据填充接口Duplicate(接口头文件):

__aicore__ static inline void Max(const LocalTensor<T>& dst, uint32_t count) { Duplicate(dst, Max(), count); } __aicore__ static inline void Infinity(const LocalTensor<T>& dst, uint32_t count) { Duplicate(dst, Infinity(), count); } // ... 其余接口同理

即:先由标量接口求出极限值,再由Duplicate把该标量广播填充到dst的前count个元素。这也解释了文档"矢量接口使用 duplicate 填充前 count 个元素"的约束来源——矢量接口的对齐、count 上限等要求均继承自 Duplicate 接口。

5. SIMT 模式编译保护

头文件开头(kernel_operator_limits_intf.h)还有一层编译保护:

#if defined(__NPU_COMPILER_INTERNAL_PURE_SIMT__) #error "kernel_operator_limits_intf.h cannot be used with compile flag --enable-simt enabled." #endif

即该头文件(连同 NumericLimits 类)不能在开启--enable-simt的纯 SIMT 编译模式下使用,编译时会直接报错提示。

测试验证:参数化用例覆盖全部接口与类型组合

仓库在 test_operator_numeric_limits.cpp 中为 NumericLimits 提供了完整的 Kernel 侧用例(同目录下另有ascendc_case_mc62cm12aa版本):

  • Kernel 内先Duplicate(dstLocal, 0, count + 256)将缓冲区清零,再按category分别调用 8 个矢量接口之一,最后DataCopy回写 GM;
  • 通过INSTANTIATE_TEST_CASE_P参数化展开全部组合:Max/Lowest/Min覆盖 9 种类型(uint8_t、int8_t、uint16_t、int16_t、uint32_t、int32_t、half、bfloat16_t、float),Infinity/NegativeInfinity/QuietNaN/SignalingNaN/DeNormMin覆盖 3 种浮点类型(half、bfloat16_t、float),合计 42 组用例;
  • 校验方式是对比设备侧输出与预期位模式(示例中验证写入后的字节),确保接口在不同类型下填充值与文档输出表一致。

该用例可以作为你自行验证 NumericLimits 行为、或编写自定义 Kernel 时的参考骨架。

完整可运行示例

以下组合了标量与矢量两种形态,演示一个典型的"初始化 + 使用"流程:

#include "kernel_operator.h" using namespace AscendC; constexpr int32_t BLOCK_LEN = 256; class KernelNumericLimits { public: __aicore__ inline KernelNumericLimits() {} __aicore__ inline void Init(GM_ADDR dstGm, uint32_t dstSize) { dstGlobal.SetGlobalBuffer(reinterpret_cast<__gm__ float*>(dstGm), dstSize); pipe.InitBuffer(outQueue, 1, dstSize * sizeof(float)); } __aicore__ inline void Process() { LocalTensor<float> dstLocal = outQueue.AllocTensor<float>(); // 标量接口:取得各极限值 float maxVal = NumericLimits<float>::Max(); float minVal = NumericLimits<float>::Min(); float infVal = NumericLimits<float>::Infinity(); float nanVal = NumericLimits<float>::QuietNaN(); // 矢量接口:将正无穷大广播填充到前 BLOCK_LEN 个元素 NumericLimits<float>::Infinity(dstLocal, BLOCK_LEN); // 此后可结合 maxVal/minVal/nanVal 做边界判断或哨兵比较 // ... outQueue.EnQue(dstLocal); DataCopy(dstGlobal, outQueue.DeQue<float>()); outQueue.FreeTensor(dstLocal); } private: GlobalTensor<float> dstGlobal; TPipe pipe; TQue<TPosition::VECOUT, 1> outQueue; };

使用建议与注意事项

  1. 优先使用标量接口做常量:8 个标量接口均为constexpr,建议直接用于初始化局部常量或参与编译期表达式,避免重复调用产生额外开销。
  2. 区分 Min 与 Lowest 的语义:整数场景两者等价,浮点场景Min()是最小规约正数、Lowest()是负向最大幅值,按业务语义选用,防止哨兵值设置错误。
  3. 注意类型白名单:Max/Lowest/Min不适用于 int64_t、double 等类型;Infinity/NegativeInfinity/QuietNaN/SignalingNaN/DeNormMin仅适用于 half、bfloat16_t、float。非法类型会在编译期被static_assert拦截。
  4. bfloat16_t 与架构强相关:bfloat16_t 仅在 Ascend 950PR & 950DT(架构 3510/5102/5162)可用,跨架构移植 Kernel 时需确认目标产品的类型支持范围。
  5. 矢量接口受 Duplicate 约束:dst起始地址需 32 字节对齐,TPosition 需为 VECIN/VECCALC/VECOUT,count取值需满足 duplicate 接口的约束。
  6. 禁止在 SIMT 编译模式下使用:开启--enable-simt编译时包含该头文件会直接报错,SIMT 场景应选用 SIMT-API 下的等价能力。

相关资源

  • 接口头文件:include/basic_api/kernel_operator_limits_intf.h
  • 类型支持检查工具:impl/basic_api/utils/kernel_utils_ceil_oom_que.h
  • 位编码工具:impl/basic_api/utils/kernel_utils_struct_norm_sort.h
  • 矢量接口依赖的填充接口:Duplicate
  • 目的操作数类型:LocalTensor
  • 内置数据类型:builtin_data_types
  • 接口测试用例:test_operator_numeric_limits.cpp
  • 文档索引:numericlimits_utils.md
  • 人工智能
  • 深度学习
  • 算子库
  • CANN
  • Ascend

【免费下载链接】asc-devkit

本项目是CANN 推出的昇腾AI处理器专用的算子程序开发语言,原生支持C和C++标准规范,主要由类库和语言扩展层构成,提供多层级API,满足多维场景算子开发诉求。

项目地址:https://gitcode.com/cann/asc-devkit
点击查看免费下载

相关推荐

上一篇:Beads 多远程同步架构决策(ADR-0001):从 `--remote` 标志到配置驱动的灾备推送
下一篇:Czkawka 磁盘清理:免费开源,5 分钟把重复文件与临时垃圾一次到位

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进