[AI][昇腾950]Simd-VF 编程(3)
第四部分:计算类 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/FALSE、PRECISION_0ULP_FTZ_TRUE/FALSE。FTZ = 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 价值:传统做法需要 Muls → Cast 两拍且中间需写回寄存器;融合后单拍完成,对量化场景(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); // 仅最终结果写回
}
更多推荐



所有评论(0)