- 人工智能
- 深度学习
- 算子库
- CANN
- Ascend
【免费下载链接】asc-devkit
本项目是CANN 推出的昇腾AI处理器专用的算子程序开发语言,原生支持C和C++标准规范,主要由类库和语言扩展层构成,提供多层级API,满足多维场景算子开发诉求。
本文以 asc-devkit 开源仓库 SIMD-API 文档中「基础算术」(basic_arithmetic)专题为核心,系统讲解昇腾 AI 处理器寄存器向量计算(Reg Compute)模式下 25 个基础算术接口的功能语义、函数原型、模板参数、数据类型约束、调用示例,并深入剖析进位(carry)机制、精度模式与 Subnormal(FTZ)处理等关键特性。读者阅读后可掌握 AscendC 寄存器级 SIMD 算术运算的完整使用方法,并能在算子开发中正确选择算法模式、规避精度陷阱。
一、基础算术 API 全貌
「基础算术」是 asc-devkit SIMD-API 中寄存器向量计算(reg_vector_compute)的重要组成部分,全部接口作用于RegTensor寄存器操作数,通过MaskReg掩码控制元素的有效性,按元素(element-wise)完成数学运算。专题由 概述 与 25 个接口子文档组成,按运算元数可分为三类:单目运算、双目运算、标量双目运算。
1.1 单目运算(一元接口)
| 接口 | 描述 |
|---|---|
| Abs | 对实数类型:对 srcReg 中的有效元素逐个取绝对值,$dstReg_i = \lvert srcReg_i \rvert$;对复数类型:逐个取模,$dstReg_i = \lvert srcReg_i \rvert = (\alpha^2 + \beta^2)^{1/2}$($srcReg_i = \alpha + \beta i$,α 为实部,β 为虚部) |
| Exp | $dstReg_i = e^{srcReg_i}$ |
| Ln | $dstReg_i = \ln(srcReg_i)$ |
| Log | $dstReg_i = \log(srcReg_i)$(以 e 为底,与 Ln 等价) |
| Log2 | $dstReg_i = \log_2(srcReg_i)$ |
| Log10 | $dstReg_i = \log_{10}(srcReg_i)$ |
| Neg | $dstReg_i = -srcReg_i$ |
| Relu | $dstReg_i = \max(0, srcReg_i)$ |
| Sqrt | $dstReg_i = (srcReg_i)^{1/2}$ |
1.2 双目运算(二元接口)
| 接口 | 描述 |
|---|---|
| Add | $dstReg_i = srcReg0_i + srcReg1_i$;或保留进位 ${carry_i, dstReg_i} = srcReg0_i + srcReg1_i$ |
| AddC | ${carry_i, dstReg_i} = srcReg0_i + srcReg1_i + carrySrc_i$(带输入进位的加法) |
| Div | $dstReg_i = srcReg0_i / srcReg1_i$ |
| Max | $dstReg_i = \max(srcReg0_i, srcReg1_i)$ |
| Min | $dstReg_i = \min(srcReg0_i, srcReg1_i)$ |
| Mul | $dstReg_i = srcReg0_i \times srcReg1_i$ |
| Mull | $dstReg0_i = (srcReg0_i \times srcReg1_i) \bmod 2^{bit}$,$dstReg1_i = (srcReg0_i \times srcReg1_i) / 2^{bit}$(bit 为操作数位宽,低/高位分别输出) |
| Prelu | $dstReg_i = \begin{cases} srcReg0_i & srcReg0_i > 0 \ srcReg0_i \times srcReg1_i & srcReg0_i \leq 0 \end{cases}$ |
| Sub | $dstReg_i = srcReg0_i - srcReg1_i$;或保留进位 ${carry_i, dstReg_i} = {1'b0, srcReg0_i} + {1'b0, \sim srcReg1_i} + 33'b1$ |
| SubC | ${carry_i, dstReg_i} = {1'b0, srcReg0_i} + {1'b0, \sim srcReg1_i} + {32'b0, carrySrc_i}$(带输入进位的减法) |
1.3 标量双目运算(矢量 + 标量接口)
| 接口 | 描述 |
|---|---|
| Adds | $dstReg_i = srcReg_i + scalarValue$ |
| LeakyRelu | $dstReg_i = \begin{cases} srcReg_i & srcReg_i > 0 \ srcReg_i \times scalarValue & srcReg_i \leq 0 \end{cases}$ |
| Maxs | $dstReg_i = \max(srcReg_i, scalarValue)$ |
| Mins | $dstReg_i = \min(srcReg_i, scalarValue)$ |
| Muls | $dstReg_i = srcReg_i \times scalarValue$ |
二、产品支持情况与头文件位置
基础算术接口目前仅在特定处理器系列上受支持。以 Abs 为代表,各接口的产品支持情况一致,可直接对照:
| 产品系列 | 支持情况 |
|---|---|
| Ascend 950PR & 950DT 系列产品 | 支持 |
| Kirin 9050 处理器系列产品 | 支持 |
| KirinDev0001 处理器系列产品 | 支持 |
| KirinDev0002 处理器系列产品 | 支持 |
| KirinDev0003 处理器系列产品 | 支持 |
| Atlas A3 系列产品 | 不支持 |
| Atlas A2 系列产品 | 不支持 |
| Atlas 200I/500 A2 推理产品 | 不支持 |
| Atlas 推理系列产品 AI Core / Vector Core | 不支持 |
| Atlas 训练系列产品 | 不支持 |
不同类别接口对应不同头文件(均为include/basic_api/reg_compute/下的公开接口头文件):
- 单目运算:
basic_api/reg_compute/kernel_reg_compute_vec_unary_intf.h - 双目运算:
basic_api/reg_compute/kernel_reg_compute_vec_binary_intf.h - 标量双目运算:
basic_api/reg_compute/kernel_reg_compute_vec_binary_scalar_intf.h
仓库源码中,接口声明与实现分离:声明位于 kernel_reg_compute_vec_unary_intf.h、kernel_reg_compute_vec_binary_intf.h、kernel_reg_compute_vec_binary_scalar_intf.h,实现则按芯片架构(如 dav_3510、dav_5161、dav_9201 等)分布在impl/basic_api/reg_compute/与impl/basic_api/dav_*目录中。头文件末尾会根据__NPU_ARCH__宏自动包含对应架构的实现头文件(如kernel_reg_compute_vec_binary_scalar_intf_impl.h),这也是接口具有架构差异化的底层原因。
三、通用模板参数与 MaskMergeMode 机制
所有基础算术接口均遵循统一的模板参数约定。以 Add 为典型原型:
template <typename T = DefaultType, MaskMergeMode mode = MaskMergeMode::ZEROING, typename U> __simd_callee__ inline void Add(U& dstReg, U& srcReg0, U& srcReg1, MaskReg& mask)模板参数通用语义
| 参数名 | 描述 |
|---|---|
| T | 操作数数据类型(不同接口支持范围不同,见各接口「数据类型」小节) |
| mode | MaskMergeMode 枚举,选择 MERGING 或 ZEROING 模式 |
| U / S / V | 操作数的 RegTensor 类型,例如RegTensor<half>,由编译器自动推导,用户无需填写;U 通常还兼作标量源操作数类型 |
MaskMergeMode 定义如下:
enum class MaskMergeMode { UNKNOWN, // 转换类型不支持 MaskMergeMode 时选择 MERGING, // mask 未选择的元素保留 dst 原值 ZEROING // mask 未选择的元素在 dst 中置零 };需要特别注意的是:当前绝大多数基础算术接口仅支持 ZEROING 模式(如 Abs、Exp、Sqrt、Ln、Log、Log2、Log10、Neg、Relu、Prelu、Adds、Maxs、Mins、Muls、LeakyRelu、Div 默认、Mul、Sub、Add),而Add、Sub、Max、Min 同时支持 ZEROING 与 MERGING 两种模式。MERGING 模式下,mask 未筛选的元素在 dstReg 中保留原值,可用于累积式写入场景。
参数级语义方面,dstReg为输出目的操作数,srcReg/srcReg0/srcReg1为输入源操作数,mask为元素操作的有效指示(详见 MaskReg),carry/carrySrc为进位相关 MaskReg。所有接口均无返回值(void)。
四、单目运算接口详解
单目接口统一声明在 kernel_reg_compute_vec_unary_intf.h 中。
4.1 Abs:绝对值(实数)/ 模(复数)
// 实数计算 template <typename T = DefaultType, MaskMergeMode mode = MaskMergeMode::ZEROING, typename U> __simd_callee__ inline void Abs(U& dstReg, U& srcReg, MaskReg& mask) // 复数计算(S、V 由编译器推导) template <typename T = DefaultType, typename U = DefaultType, MaskMergeMode mode = MaskMergeMode::ZEROING, typename S, typename V> __simd_callee__ inline void Abs(S& dstReg, V& srcReg, MaskReg& mask)数据类型组合(srcReg → dstReg):
| srcReg | dstReg |
|---|---|
| int8_t | int8_t |
| int16_t | int16_t |
| half | half |
| int32_t | int32_t |
| float | float |
| complex32 | half |
| int64_t | int64_t |
| complex64 | float |
约束:
- 当目的操作数与源操作数数据类型不一致时(复数场景),两者不可重叠;
- 整型计算超出表示范围时采用非饱和截断:例如 int8_t 类型,srcReg 为 -128 时,其绝对值 128 会被截断为 -128;
- 针对 Ascend 950PR & 950DT 系列产品,仅 complex32 和 complex64 类型的内部计算涉及 Subnormal,处理方式受编译选项
--cce-ftz控制(默认值为 true):--cce-ftz=false时保留 Subnormal 并按实际数值参与运算;--cce-ftz=true时启用 FTZ(Flush-To-Zero)模式,Subnormal 按 0 处理,可能造成精度差异。
调用示例(实数,循环处理多 repeat):
template<typename T> __simd_vf__ inline void AbsVF(__ubuf__ T* dstAddr, __ubuf__ T* srcAddr, uint32_t count, uint16_t oneRepeatSize, uint16_t repeatTimes) { AscendC::Reg::RegTensor<T> srcReg; AscendC::Reg::RegTensor<T> dstReg; AscendC::Reg::MaskReg mask; for (uint16_t i = 0; i < repeatTimes; i++) { mask = AscendC::Reg::UpdateMask<T>(count); AscendC::Reg::LoadAlign(srcReg, srcAddr + i * oneRepeatSize); AscendC::Reg::Abs(dstReg, srcReg, mask); AscendC::Reg::StoreAlign(dstAddr + i * oneRepeatSize, dstReg, mask); } }4.2 Exp:指数运算
原型为template <typename T = DefaultType, auto mode = MaskMergeMode::ZEROING, typename U> __simd_callee__ inline void Exp(U& dstReg, U& srcReg, MaskReg& mask)。注意此处mode为auto类型,可配置为 MaskMergeMode 枚举值,或指向ExpSpecificMode结构体的指针:
enum class ExpAlgo { INTRINSIC = 0, PRECISION_1ULP_FTZ_TRUE, PRECISION_1ULP_FTZ_FALSE, }; struct ExpSpecificMode { MaskMergeMode mrgMode = MaskMergeMode::ZEROING; ExpAlgo algo = ExpAlgo::INTRINSIC; };支持的数据类型仅为half、float。三种算法的最大精度误差均为 1ulp:
ExpAlgo::INTRINSIC(默认):对于 half、float,Subnormal 处理受--cce-ftz控制(默认 true,即默认采用 FTZ 模式);ExpAlgo::PRECISION_1ULP_FTZ_TRUE:单指令计算,始终采用 FTZ 模式;ExpAlgo::PRECISION_1ULP_FTZ_FALSE:通过软件仿真实现,支持 Subnormal 数据计算。
由于保留 Subnormal 需要软件仿真,在默认--cce-ftz=true下一般场景建议使用默认 INTRINSIC 或显式选择PRECISION_1ULP_FTZ_TRUE以获取更优性能;需要精确输出 Subnormal 时才使用PRECISION_1ULP_FTZ_FALSE。文档给出了 half/float 下的 Subnormal 示例(如 half 输入 -9.703125 输出 6.0975551605e-05,二进制尾数0 00000 1111111111)。
使用高精度/Subnormal 模式时通过模板参数传入结构体指针:
// 1ulp精度及Subnormal模式 // static constexpr AscendC::Reg::ExpSpecificMode mode = { // AscendC::Reg::MaskMergeMode::ZEROING, AscendC::ExpAlgo::PRECISION_1ULP_FTZ_FALSE}; // AscendC::Reg::Exp<T, &mode>(dstReg, srcReg, mask);4.3 Sqrt:平方根运算
template <typename T = DefaultType, auto mode = MaskMergeMode::ZEROING, typename U> __simd_callee__ inline void Sqrt(U& dstReg, U& srcReg, MaskReg& mask)SqrtSpecificMode定义:
enum class SqrtAlgo { INTRINSIC = 0, FAST_INVERSE, PRECISION_1ULP_FTZ_TRUE, PRECISION_0ULP_FTZ_FALSE, PRECISION_1ULP_FTZ_FALSE, }; struct SqrtSpecificMode { MaskMergeMode mrgMode = MaskMergeMode::ZEROING; bool precisionMode = false; SqrtAlgo algo = SqrtAlgo::INTRINSIC; };关键点:
- 数据类型仅 half、float;
precisionMode=true时使能更高精度计算(快速求逆算法,仅对 float 生效),最大精度误差 0ulp;SqrtAlgo::INTRINSIC为默认算法(1ulp),--cce-ftz=false保留 Subnormal,true(默认)采用 FTZ;SqrtAlgo::FAST_INVERSE与SqrtAlgo::PRECISION_0ULP_FTZ_FALSE使用快速求逆算法,仅支持 float 并支持 Subnormal 计算(0ulp);SqrtAlgo::PRECISION_1ULP_FTZ_FALSE支持 half 类型的 Subnormal 数据计算(1ulp);- 约束:输入为负数时结果为 nan;输入为 -0 时结果为 -0。
4.4 Ln / Log / Log2 / Log10:对数运算族
四者原型一致:template <typename T = DefaultType, auto mode = MaskMergeMode::ZEROING, typename U> __simd_callee__ inline void Ln|Log|Log2|Log10(U& dstReg, U& srcReg, MaskReg& mask),均仅支持 half、float。
- Ln:自然对数,$dstReg_i = \ln(srcReg_i)$;
- Log:与 Ln 功能等价,命名遵循 C/C++ 标准库中
log()表示自然对数的约定(文档明确说明); - Log2:$dstReg_i = \log_2(srcReg_i)$;
- Log10:$dstReg_i = \log_{10}(srcReg_i)$。
各接口的 SpecificMode 结构体模式完全同构(LnSpecificMode/LogSpecificMode/Log2SpecificMode/Log10SpecificMode),均含mrgMode与algo两个字段,枚举均为INTRINSIC、PRECISION_1ULP_FTZ_TRUE、PRECISION_1ULP_FTZ_FALSE三值,最大精度误差均为 1ulp。此外Log2 与 Log10 具有精度提升特性:当源操作数为 half 时,会先将 half 提升精度到 float 后计算,再将结果转回 half 输出。
4.5 Neg:取相反数
template <typename T = DefaultType, MaskMergeMode mode = MaskMergeMode::ZEROING, typename U> __simd_callee__ inline void Neg(U& dstReg, U& srcReg, MaskReg& mask)支持数据类型:int8_t、int16_t、half、int32_t、float、int64_t。仅支持 ZEROING 模式。无额外约束。
4.6 Relu:线性整流
template <typename T = DefaultType, MaskMergeMode mode = MaskMergeMode::ZEROING, typename U> __simd_callee__ inline void Relu(U& dstReg, U& srcReg, MaskReg& mask)支持数据类型:half、int32_t、float、int64_t。仅支持 ZEROING 模式。
五、双目运算接口详解:含进位算术与高精度除法
双目接口统一声明在 kernel_reg_compute_vec_binary_intf.h 中。
5.1 Add / AddC / Sub / SubC:进位(carry)算术
这四个接口构成一组完整的带进位加减法体系,通过 MaskReg 类型的 carry 寄存器实现多精度(宽位宽)整数算术。Carry flag(进位/借位标志)用于表示加法进位或减法无借位。
Add有两种原型:
// 不保留进位 template <typename T = DefaultType, MaskMergeMode mode = MaskMergeMode::ZEROING, typename U> __simd_callee__ inline void Add(U& dstReg, U& srcReg0, U& srcReg1, MaskReg& mask) // 保留进位 template <typename T = DefaultType, typename U> __simd_callee__ inline void Add(MaskReg& carry, U& dstReg, U& srcReg0, U& srcReg1, MaskReg& mask)- 不保留进位:支持 int8_t、uint8_t、int16_t、uint16_t、half、bfloat16_t、int32_t、uint32_t、float、complex32、int64_t、uint64_t、complex64;
- 保留进位:仅支持 int32_t、uint32_t,且计算结果不保留进位。
- 进位写入规则:若 srcReg0、srcReg1 按位相加后最高位有进位,则在 carry(MaskReg 寄存器)对应位置每 4bit 的最低位写 1,否则写 0。
Sub同理,减法在硬件底层通过补码加法实现:${carry_i, dstReg_i} = {1'b0, srcReg0_i} + {1'b0, \sim srcReg1_i} + 33'b1$。保留进位时仅支持 int32_t、uint32_t。
AddC(带输入进位加法)与SubC(带输入进位减法)将前序运算产生的 carry 作为carrySrc输入继续参与运算:
template <typename T = DefaultType, typename U> __simd_callee__ inline void AddC(MaskReg& carry, U& dstReg, U& srcReg0, U& srcReg1, MaskReg& carrySrc, MaskReg& mask) // SubC 原型同上,函数名替换为 SubC${carry_i, dstReg_i} = srcReg0_i + srcReg1_i + carrySrc_i$(SubC 为 ${1'b0, srcReg0_i} + {1'b0, \sim srcReg1_i} + {32'b0, carrySrc_i}$),支持数据类型同样为 int32_t、uint32_t。Add 输出的 carry 适用场景参考 AddC 功能说明,Sub 输出的 carry 适用场景参考 SubC 功能说明——典型应用即 64 位整数的分段加减:32 位加法产生进位后,通过 AddC 将进位传递到高 32 位段。
带进位的完整调用示例(AddC,carry 以 8×4B=32B 对齐存储):
template <typename T> static __simd_vf__ inline void AddVF(__ubuf__ T* dst0Addr, __ubuf__ T* dst1Addr, __ubuf__ T* src0Addr, __ubuf__ T* src1Addr, uint32_t count, uint16_t repeatTimes, uint16_t oneRepeatSize){ AscendC::Reg::RegTensor<T> srcReg0; AscendC::Reg::RegTensor<T> srcReg1; AscendC::Reg::RegTensor<T> dstReg0; AscendC::Reg::MaskReg mask; AscendC::Reg::MaskReg maskZero = AscendC::Reg::CreateMask<T, AscendC::Reg::MaskPattern::ALLF>(); AscendC::Reg::MaskReg carry = AscendC::Reg::CreateMask<uint8_t>(); for (uint16_t i = 0; i < repeatTimes; i++) { mask = AscendC::Reg::UpdateMask<T>(count); AscendC::Reg::LoadAlign(srcReg0, src0Addr + i * oneRepeatSize); AscendC::Reg::LoadAlign(srcReg1, src1Addr + i * oneRepeatSize); AscendC::Reg::AddC(carry, dstReg0, srcReg0, srcReg1, maskZero, mask); // 8*4B=32B align AscendC::Reg::StoreAlign<uint32_t, AscendC::Reg::MaskDist::DIST_NORM>((__ubuf__ uint32_t*)dst1Addr + i * 8, carry); AscendC::Reg::StoreAlign(dst0Addr + i * oneRepeatSize, dstReg0, mask); } }5.2 Mul / Mull:乘法与高低位分离
Mul:template <typename T = DefaultType, MaskMergeMode mode = MaskMergeMode::ZEROING, typename U> __simd_callee__ inline void Mul(U& dstReg, U& srcReg0, U& srcReg1, MaskReg& mask),支持 int16_t、uint16_t、half、bfloat16_t、int32_t、uint32_t、float、complex32、int64_t、uint64_t、complex64,输出为不饱和模式(溢出直接截断)。
Mull:将乘法结果的低位部分写入 dstReg0、溢出(高位)部分写入 dstReg1,适合 64 位乘法分解等场景:
template <typename T = DefaultType, typename U> __simd_callee__ inline void Mull(U& dstReg0, U& dstReg1, U& srcReg0, U& srcReg1, MaskReg& mask)$dstReg0_i = (srcReg0_i \times srcReg1_i) \bmod 2^{bit}$,$dstReg1_i = (srcReg0_i \times srcReg1_i) / 2^{bit}$,其中 bit 表示操作数位宽。仅支持 int32_t、uint32_t。
5.3 Div:多种算法与 Subnormal 模式
Div 是基础算术中模式最丰富的接口,mode为auto类型,可配置 MaskMergeMode 枚举或指向DivSpecificMode结构体的指针:
enum class DivAlgo { INTRINSIC = 0, DIFF_COMPENSATION, PRECISION_1ULP_FTZ_TRUE, PRECISION_0ULP_FTZ_TRUE, PRECISION_0ULP_FTZ_FALSE, PRECISION_1ULP_FTZ_FALSE }; struct DivSpecificMode { MaskMergeMode mrgMode = MaskMergeMode::ZEROING; bool precisionMode = false; DivAlgo algo = DivAlgo::INTRINSIC; };各模式的数据类型与精度:
| 模式 | 支持数据类型 | 最大精度误差 |
|---|---|---|
DivAlgo::INTRINSIC(默认) | int16_t、uint16_t、half、int32_t、uint32_t、float、complex32、int64_t、uint64_t、complex64 | 1ulp(half/float 受--cce-ftz控制) |
DivAlgo::PRECISION_1ULP_FTZ_TRUE | 同上 | 1ulp(始终 FTZ) |
DivAlgo::DIFF_COMPENSATION | float、complex64 | 0ulp(差值补偿算法,受--cce-ftz控制) |
DivAlgo::PRECISION_0ULP_FTZ_TRUE | float、complex64 | 0ulp(始终 FTZ) |
DivAlgo::PRECISION_0ULP_FTZ_FALSE | float | 0ulp(保留 Subnormal) |
DivAlgo::PRECISION_1ULP_FTZ_FALSE | half、float | 1ulp(保留 Subnormal) |
补充说明:
precisionMode=true时使用差值补偿算法,最大精度误差 0ulp,支持 float、complex64;- 默认算法 INTRINSIC 下,
--cce-ftz=false等效于PRECISION_1ULP_FTZ_FALSE,--cce-ftz=true(默认)等效于PRECISION_1ULP_FTZ_TRUE;整数类型不涉及 Subnormal;complex32/complex64 的实部与虚部分别为 half/float,Subnormal 处理遵循对应元素类型的--cce-ftz配置; - 名称含
FTZ_FALSE的算法始终保留 Subnormal,名称含FTZ_TRUE的算法始终采用 FTZ。
文档给出了 FTZ 差异的数值示例:
| 被除数输入 | 除数输入 | 输出(配置 Subnormal 模式) | 输出(不配置 Subnormal 模式) |
|---|---|---|---|
| 1(0x3F80 0000) | -5.01923249665106e-39(0x8036 A794) | -1.9923364304124947e+38(0xFF15 E301) | -inf(0xFF80 0000) |
| 1.8611191411098982e-39(0x0014 440B) | 7.70406850625423e-39(0x0053 E3CF) | 0.24157613515853882(0x3E77 5FBC) | NaN(0x7FFF FFFF) |
可见不配置 Subnormal 时,微小量相除可能产生 -inf 或 NaN,而保留 Subnormal 可得到正确有限结果——这正是算法对数据精度有特殊要求或需避免除零错误时的关键取舍。使用建议:一般场景使用性能更优的PRECISION_1ULP_FTZ_TRUE或PRECISION_0ULP_FTZ_TRUE;需要精确输出 Subnormal 时使用PRECISION_0ULP_FTZ_FALSE或PRECISION_1ULP_FTZ_FALSE。
高精度模式调用方式:
// 高精度模式 // static constexpr AscendC::Reg::DivSpecificMode mode = {AscendC::Reg::MaskMergeMode::ZEROING, true}; // 0ulp精度及Subnormal模式 // static constexpr AscendC::Reg::DivSpecificMode mode = { // AscendC::Reg::MaskMergeMode::ZEROING, true, AscendC::DivAlgo::PRECISION_0ULP_FTZ_FALSE}; // AscendC::Reg::Div<T, &mode>(dstReg, srcReg0, srcReg1, mask);5.4 Max / Min:按元素取最值
template <typename T = DefaultType, MaskMergeMode mode = MaskMergeMode::ZEROING, typename U> __simd_callee__ inline void Max(U& dstReg, U& srcReg0, U& srcReg1, MaskReg& mask) // Min 同理两者均支持 ZEROING 与 MERGING 模式,数据类型支持 int8_t、uint8_t、int16_t、uint16_t、half、bfloat16_t、int32_t、uint32_t、float、int64_t、uint64_t。约束差异体现在符号零处理上:
- Max:srcReg0 为 -0、srcReg1 为 +0 时,输出 +0;
- Min:srcReg0 为 -0、srcReg1 为 +0 时,输出 -0。
MERGING 模式调用方式(Min 示例):
// Merging Mode模式 // AscendC::Reg::Min<T, MaskMergeMode::MERGING>(dstReg, srcReg0, srcReg1, mask); AscendC::Reg::Min(dstReg, srcReg0, srcReg1, mask);5.5 Prelu:参数化 ReLU
template <typename T = DefaultType, MaskMergeMode mode = MaskMergeMode::ZEROING, typename U> __simd_callee__ inline void Prelu(U &dstReg, U &srcReg0, U &srcReg1, MaskReg &mask)当 srcReg0 元素 > 0 时直接输出该元素,否则输出 srcReg0 与 srcReg1 对应元素之积。支持 half、float,仅 ZEROING 模式。注意它与标量版 LeakyRelu 的区别:Prelu 的负半轴斜率来自矢量 srcReg1(每个元素可不同),LeakyRelu 的斜率是统一标量。
六、标量双目运算接口详解
标量双目接口统一声明在 kernel_reg_compute_vec_binary_scalar_intf.h,原型模式统一为:
template <typename T = DefaultType, typename U, MaskMergeMode mode = MaskMergeMode::ZEROING, typename S> __simd_callee__ inline void Adds(S& dstReg, S& srcReg, U scalarValue, MaskReg& mask) // Muls / Maxs / Mins / LeakyRelu 原型同构,函数名替换即可其中 T 为矢量目的/源操作数数据类型,U 为标量源操作数数据类型,S 为 RegTensor 类型(编译器推导)。此类接口当前均仅支持 ZEROING 模式。
各接口要点:
| 接口 | 支持数据类型 | 约束 |
|---|---|---|
| Adds(+ 标量) | int8_t、uint8_t、int16_t、uint16_t、half、bfloat16_t、int32_t、uint32_t、float、complex32、int64_t、uint64_t、complex64 | 无 |
| Maxs(取与标量较大者) | int8_t、uint8_t、int16_t、uint16_t、half、bfloat16_t、int32_t、uint32_t、float、int64_t、uint64_t | srcReg 为 -0、scalarValue 为 +0 时输出 +0 |
| Mins(取与标量较小者) | 同 Maxs | srcReg 为 -0、scalarValue 为 +0 时输出 -0 |
| Muls(× 标量) | int16_t、uint16_t、half、int32_t、uint32_t、float、complex32、int64_t、uint64_t、complex64 | 无 |
| LeakyRelu(负半轴 × 标量斜率) | half、float | 无 |
标量接口典型调用示例(Adds):
template<typename T> __simd_vf__ inline void AddsVF(__ubuf__ T* dstAddr, __ubuf__ T* srcAddr, T scalarValue, uint32_t count, uint16_t oneRepeatSize, uint16_t repeatTimes) { AscendC::Reg::RegTensor<T> srcReg; AscendC::Reg::RegTensor<T> dstReg; AscendC::Reg::MaskReg mask; for (uint16_t i = 0; i < repeatTimes; i++) { mask = AscendC::Reg::UpdateMask<T>(count); AscendC::Reg::LoadAlign(srcReg, srcAddr + i * oneRepeatSize); AscendC::Reg::Adds(dstReg, srcReg, scalarValue, mask); AscendC::Reg::StoreAlign(dstAddr + i * oneRepeatSize, dstReg, mask); } }七、关键特性专题:精度误差与 Subnormal(FTZ)处理
7.1 精度误差(ulp)
基础算术接口文档统一以ulp(Unit in the Last Place)量化最大精度误差:
- 1ulp 级:Abs、Exp、Ln、Log、Log2、Log10、Neg、Relu、Mul、Div(INTRINSIC 与 1ULP 系列)、Sqrt(INTRINSIC 与 1ULP 系列)、Adds/Muls/Maxs/Mins/LeakyRelu 等;
- 0ulp 级:Div 的 DIFF_COMPENSATION、
PRECISION_0ULP_FTZ_TRUE、PRECISION_0ULP_FTZ_FALSE,Sqrt 的 FAST_INVERSE、PRECISION_0ULP_FTZ_FALSE(以及对应接口precisionMode=true时),它们分别通过差值补偿算法与快速求逆算法实现高精度计算; - Log2、Log10 还具备 half 提升 float 计算的精度提升特性。
7.2 Subnormal 与 FTZ
Subnormal(次正规数)是浮点格式中接近零的极小数值。FTZ(Flush-To-Zero)模式下,结果或参与运算的 Subnormal 被直接清零(近似为 0),而非保留精确微小数值。两种控制途径:
- 编译选项
--cce-ftz(默认 true):影响默认算法(INTRINSIC)及 DIFF_COMPENSATION 等对 half/float 类型的行为;false时保留 Subnormal; - 显式算法选择:
*_FTZ_FALSE后缀算法始终保留 Subnormal(通过软件仿真实现),*_FTZ_TRUE后缀算法始终采用 FTZ(单指令计算,性能更优)。
使用建议总结:默认--cce-ftz=true下,一般场景建议使用默认 INTRINSIC 或显式选择*_FTZ_TRUE算法以获得更好性能;仅当算法对数据精度有特殊要求(如需要精确输出 Subnormal、避免微小量相除产生 -inf/NaN)时,才选择*_FTZ_FALSE系列算法。
八、源码实现与测试佐证
接口声明与各架构实现可在仓库中直接追溯:
- 接口声明:单目接口、双目接口、标量双目接口,均在
AscendC::Reg命名空间下以__simd_callee__ inline模板函数形式对外提供; - 架构实现:
impl/basic_api/reg_compute/下按 dav_3510、dav_5161、dav_9201 等目录组织kernel_reg_compute_vec_unary_impl.h、kernel_reg_compute_vec_binary_impl.h、kernel_reg_compute_vec_binary_scalar_impl.h;impl/basic_api/dav_*下kernel_operator_vec_unary_impl.h、kernel_operator_vec_binary_impl.h、kernel_operator_vec_binary_continuous_impl.h等文件中可检索到DivSpecificMode、ExpSpecificMode等模式结构体的落地实现; - 头文件自检测试:测试用例 host_header_kernel_reg_compute_intf_checker.cpp 通过
using AscendC::Reg::Add;等语句逐一校验 Add、AddC、Div、Max、Min、Mul、Mull、Prelu、Sub、SubC 以及 Adds、LeakyRelu、Maxs、Mins、Muls 等接口在主机侧头文件中的可声明性,可作为接口名称与命名空间用法的直接参考; - 接口头文件会对
--enable-simt编译标志与内部头文件包含守卫(__ASCENDC_INCLUDE_INTERNAL_HEADERS__)进行约束检查,提醒开发者基础算术接口面向 SIMD 编译模式,且应通过公开头文件使用。
九、总结
asc-devkit 的寄存器向量基础算术 API 以RegTensor+MaskReg为操作模型,覆盖绝对值、指数、对数、开方、取反、激活(Relu/Prelu/LeakyRelu)、加减乘除、最值与标量运算共 25 个接口。使用时可遵循以下要点:按运算类别选择 单目/双目/标量双目 头文件;通过模板参数 T 指定数据类型、mode 指定掩码合并模式;利用 Add/AddC/Sub/SubC 的 carry 机制实现宽位宽整数运算;对 Div/Exp/Sqrt/Ln/Log/Log2/Log10 通过 SpecificMode 结构体在精度与性能之间权衡;关注--cce-ftz编译选项对 Subnormal 结果的语义影响。所有接口均需结合 MaskReg 掩码与 RegTensor 寄存器操作数配套使用,并注意当前仅在 Ascend 950 系列与 Kirin 系列产品上受支持。
- 人工智能
- 深度学习
- 算子库
- CANN
- Ascend
【免费下载链接】asc-devkit
本项目是CANN 推出的昇腾AI处理器专用的算子程序开发语言,原生支持C和C++标准规范,主要由类库和语言扩展层构成,提供多层级API,满足多维场景算子开发诉求。
相关推荐
asc-devkit 中基于 RegBase 范式的向量计算实战:以 add 与 gelu 两个样例详解寄存器级 SIMD 编程
asc devkit 中基于 RegBase 范式的向量计算实战:以 add 与 gelu 两个样例详解寄存器级 SIMD 编程 本篇技术指南基于 asc de
人工智能深度学习算子库CANNAscendasc-devkit RegBase 向量计算实战:基于 SIMD C++ API 的 add 与 GELU 样例详解
asc devkit RegBase 向量计算实战:基于 SIMD C++ API 的 add 与 GELU 样例详解 本文基于 asc devkit(CANN
人工智能深度学习算子库CANNAscendPyPTO vf.muls_cast 详解:SIMD 向量寄存器标量乘加与 FP32→FP16 类型转换的融合计算
PyPTO vf.muls_cast 详解:SIMD 向量寄存器标量乘加与 FP32→FP16 类型转换的融合计算 vf.muls_cast 是 PyPTO(P
人工智能编译器模型编译深度学习高性能计算CANNAscend
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考