第四部分:计算类 API

4.1 一元算术(kernel_reg_compute_vec_unary_intf.h)

Abs(dstReg, srcReg, mask);    // dst = |src|
Relu(dstReg, srcReg, mask);   // dst = max(src, 0)
Exp(dstReg, srcReg, mask);    // dst = e^src
Sqrt(dstReg, srcReg, mask);   // dst = √src
Ln(dstReg, srcReg, mask);     // dst = ln(src)
Log(dstReg, srcReg, mask);    // dst = log10(src)  // 注意:这里 Log 是 log10
Log2(dstReg, srcReg, mask);
Log10(dstReg, srcReg, mask);
Neg(dstReg, srcReg, mask);    // dst = -src
Not(dstReg, srcReg, mask);    // 按位取反

精度配置(来自 kernel_reg_compute_utils.h):

AscendC::ExpConfig cfg { AscendC::ExpAlgo::PRECISION_1ULP_FTZ_TRUE };
AscendC::Reg::Exp<AscendC::DefaultType, AscendC::ExpSpecificMode{
    AscendC::Reg::MaskMergeMode::ZEROING, AscendC::ExpAlgo::PRECISION_1ULP_FTZ_TRUE}>(dst, src, mask);

可选枚举:INTRINSIC(默认)、FAST_INVERSE(仅 Sqrt/Rsqrt)、PRECISION_1ULP_FTZ_TRUE/FALSEPRECISION_0ULP_FTZ_TRUE/FALSEFTZ = Flush-To-Zero 处理非规格化数。

4.2 二元算术(kernel_reg_compute_vec_binary_intf.h)

Add(dst, src0, src1, mask);
Sub(dst, src0, src1, mask);
Mul(dst, src0, src1, mask);
Div(dst, src0, src1, mask);   // 支持 DivAlgo 精度模式
Max(dst, src0, src1, mask);
Min(dst, src0, src1, mask);
And/Or/Xor(dst, src0, src1, mask);
ShiftLeft/ShiftRight(dst, src0, src1, mask);
Prelu(dst, src0, src1, mask);  // PReLU: dst = src0<0 ? src1*src0 : src0
Mull(dst0, dst1, src0, src1, mask);  // 64 位乘法,输出双寄存器
MulAddDst(dst, src0, src1, mask);    // dst = dst + src0 * src1
AddC(carryMask, dst, src0, src1, carrySrc, mask);  // 带进位加法
SubC(carryMask, dst, src0, src1, carrySrc, mask);  // 带借位减法
Add(carryMask, dst, src0, src1, mask);             // 输出 carry mask 的 Add
Sub(carryMask, dst, src0, src1, mask);

4.3 标量二元(kernel_reg_compute_vec_binary_scalar_intf.h)

*s 后缀 = vector-scalar 运算,第二个操作数是标量立即数或寄存器标量:

Adds(dst, src, scalarValue, mask);    // dst = src + scalar
Muls(dst, src, scalarValue, mask);    // dst = src * scalar
Maxs(dst, src, scalarValue, mask);
Mins(dst, src, scalarValue, mask);
ShiftLefts(dst, src, scalarValue, mask);
ShiftRights(dst, src, scalarValue, mask);
LeakyRelu(dst, src, scalarValue, mask);   // dst = src<0 ? scalar*src : src

性能优势:标量操作数直接从立即数或 aux scalar 寄存器取,省去一次 LoadAlign 加载 scalar 向量,减少 UB 占用和 Load 带宽。

4.4 复合计算(kernel_reg_compute_vec_fused_intf.h)

MulsCast(dst, src, scalar, mask);     // dst = Cast(src * scalar),融合乘法与类型转换
Axpy(dst, src, scalar, mask);         // dst = dst + scalar * src  (y += αx)
MulAddDst(dst, src0, src1, mask);     // dst = dst + src0 * src1
MulDstAdd(dst, src0, src1, mask);     // dst = src0 * src1 + dst
AbsSub(dst, src0, src1, mask);        // dst = |src0 - src1|
ExpSub(dst, src0, src1, mask);        // dst = exp(src0) - src1

MulsCast 价值:传统做法需要 MulsCast 两拍且中间需写回寄存器;融合后单拍完成,对量化场景(int8↔fp32 转换 + 缩放)收益明显。

4.5 比较与选择(kernel_reg_compute_vec_cmpsel_intf.h)

// Compare: 向量-向量比较,结果写 MaskReg
Compare<T, CMPMODE::EQ>(maskDst, src0, src1, mask);   // ==
Compare<T, CMPMODE::NE>(maskDst, src0, src1, mask);   // !=
Compare<T, CMPMODE::GT>(maskDst, src0, src1, mask);   // >
Compare<T, CMPMODE::GE>(maskDst, src0, src1, mask);   // >=
Compare<T, CMPMODE::LT>(maskDst, src0, src1, mask);   // <
Compare<T, CMPMODE::LE>(maskDst, src0, src1, mask);   // <=

// Compares: 向量-标量比较
Compares<T, CMPMODE::GT>(maskDst, src, scalarValue, mask);

// Select: 三元选择 dst = mask ? src0 : src1
Select(dst, src0, src1, mask);

典型模式:先 Compare 生成 mask,再 Select 做条件融合(等价于 torch.where):

AscendC::Reg::MaskReg maskCmp = AscendC::Reg::CreateMask<T>();
AscendC::Reg::Compare<T, AscendC::CMPMODE::GT>(maskCmp, xReg, yReg, maskAll);
AscendC::Reg::Select(zReg, xReg, yReg, maskCmp);   // z = x > y ? x : y,等价于 max

4.6 类型转换(kernel_reg_compute_vec_vconv_intf.h)

template <typename DstT, typename SrcT, const CastTrait& trait = castTrait, typename S, typename V>
void Cast(S& dstReg, V& srcReg, MaskReg& mask);

template <typename T, RoundMode roundMode = CAST_NONE, MaskMergeMode mode = ZEROING, typename S>
void Truncate(S& dstReg, S& srcReg, MaskReg& mask);   // 仅 f16→f16 / f32→f32 / bf16→bf16 截断

CastTrait 结构

struct CastTrait {
    RegLayout layoutMode = RegLayout::UNKNOWN;   // ZERO/ONE/TWO/THREE: 控制 lane 重排
    SatMode satMode = SatMode::UNKNOWN;          // NO_SAT / SAT: 溢出处理
    MaskMergeMode mrgMode = MaskMergeMode::UNKNOWN;
    RoundMode roundMode = RoundMode::UNKNOWN;    // CAST_FLOOR/CAST_ROUND/CAST_RINT/CAST_NONE...
};

位宽小→大(如 half → int32)时,加载用 LoadDist 解压缩模式:

// 样例 cast.asc 场景 1: half → int32_t
static constexpr AscendC::Reg::CastTrait castTrait = {
    AscendC::Reg::RegLayout::ZERO,
    AscendC::Reg::SatMode::UNKNOWN,
    AscendC::Reg::MaskMergeMode::ZEROING,
    AscendC::RoundMode::CAST_FLOOR
};
AscendC::Reg::LoadAlign<half, AscendC::Reg::PostLiteral::POST_MODE_UPDATE,
    AscendC::Reg::LoadDist::DIST_UNPACK_B16>(xReg, xAddr, oneRepeatSize);  // 加载 VL/2 数据 unpack 成 VL/4
AscendC::Reg::Cast<int32_t, half, castTrait>(yReg, xReg, mask);             // half → int32,1:2 扩展
AscendC::Reg::StoreAlign<int32_t, AscendC::Reg::PostLiteral::POST_MODE_UPDATE>(
    yAddr, yReg, oneRepeatSize, mask);                                      // 按 int32 宽度写出

位宽大→小(如 float → int16)时,写出用 StoreDist 压缩模式:

// 样例 cast.asc 场景 2: float → int16_t
AscendC::Reg::LoadAlign<float, AscendC::Reg::PostLiteral::POST_MODE_UPDATE>(xReg, xAddr, oneRepeatSize);
AscendC::Reg::Cast<int16_t, float, castTrait>(yReg, xReg, mask);           // float → int16,2:1 压缩
AscendC::Reg::StoreAlign<int16_t, AscendC::Reg::PostLiteral::POST_MODE_UPDATE,
    AscendC::Reg::StoreDist::DIST_PACK_B32>(yAddr, yReg, oneRepeatSize, mask);  // 压缩写出

4.7 归约(kernel_reg_compute_vec_reduce_intf.h)

// Reduce: 把一个 RegTensor 归约为标量(存在 dstReg 的 lane 0)
Reduce<ReduceType::SUM>(dst, src, mask);     // 求和
Reduce<ReduceType::MAX>(dst, src, mask);     // 求最大
Reduce<ReduceType::MIN>(dst, src, mask);     // 求最小

// ReduceDataBlock: 按 32B DataBlock 独立归约(结果存在每个 Block 的 lane 0)
ReduceDataBlock<ReduceType::SUM>(dst, src, mask);

// PairReduceElem: 配对归约(lane i 和 lane i+VL/2 配对)
PairReduceElem<PairReduce::SUM>(dst, src, mask);

典型模式:多拍累加归约(样例 reduce.asc):

AscendC::Reg::Duplicate(accReg, (T)0, mask);              // acc = 0
for (uint16_t i = 0; i < repeatTimes; ++i) {
    AscendC::Reg::LoadAlign(srcReg, srcAddr + i * oneRepeatSize);
    AscendC::Reg::Reduce<AscendC::Reg::ReduceType::SUM>(dstReg, srcReg, mask);
    AscendC::Reg::Add(accReg, accReg, dstReg, mask);       // 累加到 accReg(无需写回 UB)
}
AscendC::Reg::StoreAlign(dstAddr, accReg, mask);          // 仅最终结果写回 UB

性能关键点dstReg 作为 Add 的源操作数直接复用,避免每拍都 StoreAlign 中间结果。这是 Reg API 相对 MemBase 的核心优势之一。

4.8 数据填充(Duplicate)

Duplicate(dstReg, scalarValue, mask);   // 把 scalar 填到 dstReg 每个 lane
Duplicate(dstReg, scalarValue);          // 不带 mask 重载

等价于 AscendC::Duplicate(localTensor, scalar, count),但完全在寄存器侧完成,不需要先在 UB 中准备好常量向量。常用于归约初始化、清零、生成常量掩码等。

4.9 离散与聚合

// Squeeze: 按 mask 压缩 srcReg 中激活 lane 到 dstReg 低位
Squeeze<T, GatherMaskMode::STORE_REG>(dst, src, mask);

// Gather: 同 3.7,按 index 离散取数
// Scatter: 同 3.7,按 index 离散写

Squeeze 样例squeeze.asc):

AscendC::Reg::MaskReg mask = AscendC::Reg::CreateMask<T, AscendC::Reg::MaskPattern::M4>();  // 每 4 lane 取 1
AscendC::Reg::Squeeze<T, AscendC::Reg::GatherMaskMode::STORE_REG>(yReg, xReg, mask);
AscendC::Reg::StoreUnAlign(yAddr, yReg, ureg);   // 输出长度 = VL/4,非对齐写出

4.10 数据重排与压缩

// Pack / UnPack: 256B 寄存器高/低半部分压缩/展开
Pack<HighLowPart::LOWEST>(dstMask, srcMask);
Pack<HighLowPart::HIGHEST>(dstMask, srcMask);
UnPack<HighLowPart::LOWEST>(dst, src);
UnPack<HighLowPart::HIGHEST>(dst, src);

// Interleave / DeInterleave: 交织 / 解交织
Interleave<T>(dst0, dst1, src0, src1);
DeInterleave<T>(dst0, dst1, src0, src1);

4.11 MaskReg 计算(kernel_reg_compute_maskreg_intf.h)

Not(dst, src, mask);
And(dst, src0, src1, mask);
Or(dst, src0, src1, mask);
Xor(dst, src0, src1, mask);
Move(dst, src, mask);
Move(dst, src);                  // 不带 mask
Select(dst, src0, src1, mask);   // mask ? src0 : src1
Interleave<T>(dst0, dst1, src0, src1);
DeInterleave<T>(dst0, dst1, src0, src1);
Pack<part>(dst, src);
UnPack<part>(dst, src);
MoveMask<T>();                   // 把 MaskReg 移到通用 RegTensor

MaskGenWithRegTensor<T, offset>(maskDst, srcReg);   // 从数据生成 mask

MaskGenWithRegTensor 用途:把 int32_t 的 condition 数组转换为 mask(非零→1,零→0),常用于 cond mask 的下采样。样例 move_reg_scenario_1.asc

AscendC::Reg::LoadAlign(condReg, condAddr);              // 加载 int32 条件向量
AscendC::Reg::MaskGenWithRegTensor<int32_t, 0>(maskReg, condReg);  // 生成 mask
AscendC::Reg::StoreAlign(maskOutAddr, maskReg);          // 输出 mask 字节流

4.12 其他辅助 API

Arange(dstReg, scalarValue);     // 生成 0,1,2,...,scalarValue-1 的等差数列
Histograms(dst, src, mask);      // 直方图统计(支持 Frequency/Accumulate 两种)
LocalMemBar<MemType::VEC_LOAD, MemType::VEC_STORE>();   // 同流水内 load/store 屏障

第五部分:同步控制

5.1 LocalMemBar —— 同流水内屏障

问题场景:在 SIMD VF 函数内,连续两次写入同一 UB 地址,且第二次读前一次写的结果(RAW 依赖)。VF 流水可能乱序执行,需要显式屏障。

template <MemType src, MemType dst>
void LocalMemBar();

MemType 枚举(来自 kernel_reg_compute_utils.h):

  • VEC_LOAD / VEC_STORE:Vector 单元的 load/store 端口
  • SCALAR_LOAD / SCALAR_STORE:Scalar 单元的 load/store 端口
  • VEC_ALL / SCALAR_ALL:所有 Vector / Scalar 端口

典型用法

AscendC::Reg::StoreAlign(addr, regA, mask);
AscendC::Reg::LocalMemBar<AscendC::Reg::MemType::VEC_STORE, AscendC::Reg::MemType::VEC_LOAD>();
// 确保上面 StoreAlign 完成后才能执行下面的 LoadAlign
AscendC::Reg::LoadAlign(regB, addr, mask);

第六部分:完整算子样例

6.1 样例:多步融合 Sigmoid(Reg API 优势场景)

Sigmoid = 1 / (1 + exp(-x)),传统 MemBase 需 4 次 UB 往返,Reg API 全程寄存器内完成:

template <typename T>
__simd_vf__ inline void SigmoidVF(__ubuf__ T* xAddr, __ubuf__ T* yAddr,
                                   uint32_t repeatTimes, uint32_t oneRepeatSize)
{
    AscendC::Reg::MaskReg mask = AscendC::Reg::CreateMask<T, AscendC::Reg::MaskPattern::ALL>();
    AscendC::Reg::RegTensor<T> xReg, negReg, expReg, oneReg, sumReg, dstReg;
    const T one = static_cast<T>(1.0);

    for (uint16_t i = 0; i < repeatTimes; ++i) {
        AscendC::Reg::LoadAlign(xReg, xAddr + i * oneRepeatSize);
        AscendC::Reg::Neg(negReg, xReg, mask);                  // negReg = -x
        AscendC::Reg::Exp(expReg, negReg, mask);                // expReg = e^(-x)
        AscendC::Reg::Duplicate(oneReg, one, mask);             // oneReg = 1
        AscendC::Reg::Add(sumReg, expReg, oneReg, mask);        // sumReg = e^(-x) + 1
        AscendC::Reg::Div(dstReg, oneReg, sumReg, mask);        // dstReg = 1 / (1 + e^(-x))
        AscendC::Reg::StoreAlign(yAddr + i * oneRepeatSize, dstReg, mask);
    }
    // 整个 4 步计算只在循环头尾 LoadAlign/StoreAlign 各一次,中间无 UB 访问
}

对比 MemBase 写法(4 次 UB 往返):

AscendC::Neg(tmpLocal, xLocal, count);                  // 写回 UB
AscendC::Exp(tmpLocal, tmpLocal, count);                // 读 + 写 UB
AscendC::Adds(tmpLocal, tmpLocal, 1.0f, count);         // 读 + 写 UB
AscendC::Reciprocal(yLocal, tmpLocal, count);           // 读 + 写 UB
// 共 4 次读 UB + 4 次写 UB

6.2 样例:ReduceSum 多拍累加

来源:reduce.asc。展示寄存器侧累加避免中间写回:

template <typename T>
__simd_vf__ inline void ReduceSumVF(__ubuf__ T* dstAddr, __ubuf__ T* srcAddr,
                                     uint32_t repeatTimes, uint32_t oneRepeatSize)
{
    AscendC::Reg::MaskReg mask = AscendC::Reg::CreateMask<T, AscendC::Reg::MaskPattern::ALL>();
    AscendC::Reg::RegTensor<T> srcReg, dstReg, accReg;

    AscendC::Reg::Duplicate(accReg, (T)0, mask);                          // acc = 0
    for (uint16_t i = 0; i < repeatTimes; ++i) {
        AscendC::Reg::LoadAlign(srcReg, srcAddr + i * oneRepeatSize);
        AscendC::Reg::Reduce<AscendC::Reg::ReduceType::SUM>(dstReg, srcReg, mask);   // dstReg[0] = Σ srcReg
        AscendC::Reg::Add(accReg, accReg, dstReg, mask);                  // acc += dstReg
    }
    AscendC::Reg::StoreAlign(dstAddr, accReg, mask);                      // 仅最终结果写回
}
Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐