Skip to content

连续非对齐搬入(LoadUnAlign)

产品支持情况

  • Ascend 950PR/Ascend 950DT:支持
  • Atlas A3 训练系列产品/Atlas A3 推理系列产品:不支持
  • Atlas A2 训练系列产品/Atlas A2 推理系列产品:不支持
  • Atlas 200I/500 A2 推理产品:不支持
  • Atlas 推理系列产品AI Core:不支持
  • Atlas 推理系列产品Vector Core:不支持
  • Atlas 训练系列产品:不支持

功能说明

头文件路径为:"basic_api/reg_compute/kernel_reg_compute_datacopy_intf.h"

为提升对不规则内存地址的处理能力,Reg矢量计算支持在数据搬运过程中对非32字节对齐的地址进行访问,降低非对齐访问带来的性能开销。LoadUnAlign能够实现数据从非对齐的Unified Buffer(UB)连续搬运至RegTensor,利用非对齐寄存器UnalignRegForLoad作为临时缓存区,暂存跨对齐边界的数据,从而实现高效的连续非对齐数据传输。

非对齐搬运有三类接口:普通搬运接口、PostUpdate扩展搬运接口、使用AddrReg寄存器存储偏移量接口。

RegTensor的模板参数regTrait支持RegTraitNumOne及RegTraitNumTwo,具体支持情况如下:

RegTensor模板参数regTrait普通搬运接口PostUpdate扩展搬运接口使用AddrReg寄存器存储偏移量接口
RegTraitNumOne完成一次搬运后,UB地址不会自动更新,每次迭代需要手动更新地址。• POST_MODE_NORMAL模式:不支持。
• POST_MODE_UPDATE模式:完成一次搬运后,UB地址会自动更新,每次迭代不需要手动更新地址。
在每次迭代中,需要先调用CreateAddrReg手动设定地址偏移量,再调用搬运指令。
RegTraitNumTwo不支持• POST_MODE_NORMAL模式:不支持。
• POST_MODE_UPDATE模式:完成一次搬运后,UB地址会自动更新,每次迭代不需要手动更新地址。
不支持

非对齐搬入原理请参考关键特性说明

函数原型

非对齐寄存器UnalignRegForLoad作为临时缓存区,用于暂存跨对齐边界的数据,从而实现高效的连续非对齐数据传输。在读非对齐地址前,应该先通过LoadUnAlignPre进行初始化,保存非32字节对齐的数据,然后再调用LoadUnAlign进行数据搬入。

  • 普通搬运接口:

    C++
    template <typename T>
    __simd_callee__ inline void LoadUnAlignPre(UnalignRegForLoad& ureg, __ubuf__ T* srcAddr)
    
    template <typename T = DefaultType, typename U>
    __simd_callee__ inline void LoadUnAlign(U& dstReg, UnalignRegForLoad& ureg, __ubuf__ T* srcAddr)
    
  • PostUpdate扩展搬运接口:

    C++
    template <typename T>
    __simd_callee__ inline void LoadUnAlignPre(UnalignRegForLoad& ureg, __ubuf__ T* srcAddr)
    
    template <typename T = DefaultType, PostLiteral postMode = PostLiteral::POST_MODE_UPDATE, typename U>
    __simd_callee__ inline void LoadUnAlign(U& dstReg, UnalignRegForLoad& ureg, __ubuf__ T*& srcAddr, uint32_t postUpdateStride)
    
  • 使用AddrReg存储偏移量接口(需要配合CreateAddrReg接口使用):

    C++
    template <typename T>
    __simd_callee__ inline void LoadUnAlignPre(UnalignRegForLoad& ureg, __ubuf__ T* srcAddr, AddrReg& areg)
    
    template <typename T = DefaultType, typename U>
    __simd_callee__ inline void LoadUnAlign(U& dstReg, UnalignRegForLoad& ureg, __ubuf__ T*& srcAddr, AddrReg& areg, uint32_t inc)
    

参数说明

表 1 模板参数说明

参数名描述
T源操作数的数据类型。支持的数据类型请参考数据类型
postMode用于控制是否使能UB地址会自动更新功能(post update),PostLiteral类型。具体参数说明请参考PostLiteral
U目的操作数的RegTensor类型。例如RegTensor<half>,由编译器自动推导,用户不需要手动填写。

表 2 参数说明

参数名输入/输出描述
dstReg输出目的操作数,类型为RegTensor
ureg输入/输出非对齐寄存器,UnalignRegForLoad类型,用于存储非32字节的数据,寄存器大小为32字节。
srcAddr输入/输出源操作数,UB起始地址,不需要32字节对齐。
postUpdateStride输入地址更新步长,uint32_t类型,单位:元素个数。根据postMode的取值有两种情况:
• POST_MODE_NORMAL:不支持。
• POST_MODE_UPDATE:实际搬运UB起始地址为srcAddr,搬运后执行地址更新 srcAddr += postUpdateStride。
areg输入地址偏移寄存器,AddrReg类型,需要在搬运前调用CreateAddrReg设定地址偏移量,实际搬运UB地址为srcAddr + 寄存器中存储的偏移量。
inc输入未使用,设置为0即可。

数据类型

目的操作数与源操作数的数据类型需要保持一致。

  • 当RegTensor模板参数regTrait为RegTraitNumOne时,支持的数据类型为:b8、b16、b32、b64。
  • 当RegTensor模板参数regTrait为RegTraitNumTwo时,支持的数据类型为:complex32、b64。

返回值说明

约束说明

  • LoadUnAlignPre与LoadUnAlign接口需要组合使用。

  • PostUpdate扩展搬运接口只支持POST_MODE_UPDATE模式。

  • 当RegTensor模板参数RegTrait为RegTraitNumOne和RegTraitNumTwo时,支持情况如下:

    RegTensor模板参数RegTrait取值支持的接口支持的数据类型
    RegTraitNumOne所有接口b8、b16、b32、b64
    RegTraitNumTwoPostUpdate扩展搬运接口的POST_MODE_UPDATE模式complex32、b64

关键特性说明

1、非对齐数据搬入原理

图 1 非对齐搬入示例

图1 非对齐搬入示例所示,从UB地址srcAddr ~ 304读取数据,并将其搬运至目标寄存器dstReg(256B)。处理流程如下:

① 调用LoadUnAlignPre进行非对齐搬入初始化。非对齐寄存器ureg缓存UB地址32 ~ 64的有效数据,作为后续非对齐访问的前置数据缓存。

② 调用LoadUnAlign,硬件指令将UB地址64 ~ 320的对齐数据搬入临时寄存器tmpReg,并将ureg中srcAddr ~ 64对应的数据与tmpReg中地址64 ~ 304对应的数据拼接在一起,将结果写入dstReg。此外,UB地址288 ~ 320的数据会被写入ureg。

2、连续非对齐搬入搬出示例

图 2 连续非对齐搬入搬出示例(数据类型uint32_t)

连续非对齐搬入时,LoadUnAlign会将后续未对齐的数据缓存至ureg,所以下一次搬入不需要再次调用LoadUnAlignPre,只需在迭代开始前调用一次LoadUnAlignPre,从而实现非对齐搬入的性能优化。

连续非对齐搬出时,下次迭代的StoreUnAlign会将本次迭代StoreUnAlign缓存至ureg中的数据写入UB,所以本次迭代不需要调用StoreUnAlignPost将ureg数据写入UB,只需在迭代结束后调用一次StoreUnAlignPost,从而实现非对齐搬出的性能优化。

图2 连续非对齐搬入搬出示例,将UB地址48 ~ 560的uint32_t数据[1, 2, 3, ..., 128]搬入至dstReg,再搬回UB,需要两次搬入搬出操作,即for循环执行两次,初始化和后处理移至for循环外。postUpdateStride = 256B / sizeof(T) (即每次地址偏移256B),repeatTimes = dataSize / 256B(即迭代次数=总数据量/VL)。

PostUpdate扩展搬运接口POST_MODE_UPDATE模式示例片段如下:

C++
template <typename T>
__simd_vf__ inline void LoadUnAlignVF(__ubuf__ T* dstAddr, __ubuf__ T* srcAddr, uint16_t postUpdateStride, uint16_t repeatTimes)
{
    AscendC::Reg::RegTensor<T> dstReg;
    AscendC::Reg::UnalignRegForLoad ureg1;
    AscendC::Reg::UnalignRegForStore ureg2;
    // 非对齐搬入初始化,只需在迭代开始前调用一次。
    AscendC::Reg::LoadUnAlignPre(ureg1, srcAddr);
    for (uint16_t i = 0; i < repeatTimes; ++i) {
        AscendC::Reg::LoadUnAlign(dstReg, ureg1, srcAddr, postUpdateStride);
        AscendC::Reg::StoreUnAlign(dstAddr, dstReg, ureg2, postUpdateStride);
    }
    // 非对齐搬出后处理,只需在迭代结束后调用一次。
    AscendC::Reg::StoreUnAlignPost(dstAddr, ureg2, 0);
}

具体的搬运步骤如下:

  1. 非对齐搬入初始化:更新ureg1 = [1, 2, 3, 4]。
  2. 非对齐搬入:tmpReg = [5, 6, 7, ..., 68],tmpReg部分数据和ureg1数据写入dstReg = [1, 2, 3, ..., 64],更新ureg1 = [61, 62, 63, ..., 68]。
  3. 非对齐搬出:dstReg部分数据[1, 2, 3, ..., 60]写入UB地址48 ~ 288,更新ureg2 = [61, 62, 63, 64]。
  4. 非对齐搬入:tmpReg = [69, 70, 71, ...,128],tmpReg数据和ureg1部分数据写入dstReg = [65, 66, 67, ..., 128]。
  5. 非对齐搬出:ureg2数据[61, 62, 63, 64]和dstReg部分数据[65, 66, 67, ..., 124]写入UB地址288 ~ 544,更新ureg2 = [125, 126, 127, 128]。
  6. 非对齐搬出后处理:将ureg2中缓存的数据[125, 126, 127, 128]写入UB地址544 ~ 560。

调用示例

  • 普通搬运接口

    C++
    template <typename T>
    __simd_vf__ inline void LoadUnAlignVF1(__ubuf__ T* dstAddr, __ubuf__ T* srcAddr, uint16_t postUpdateStride, uint16_t repeatTimes)
    {
        AscendC::Reg::RegTensor<T> srcReg;
        AscendC::Reg::UnalignRegForLoad ureg0;
        AscendC::Reg::UnalignRegForStore ureg1;
        AscendC::Reg::LoadUnAlignPre(ureg0, srcAddr);
        for (uint16_t i = 0; i < repeatTimes; ++i) {
            AscendC::Reg::LoadUnAlign(srcReg, ureg0, srcAddr + i * postUpdateStride);
            AscendC::Reg::StoreUnAlign(dstAddr, srcReg, ureg1, postUpdateStride);
        }
        AscendC::Reg::StoreUnAlignPost(dstAddr, ureg1, 0);
    }
    
  • PostUpdate扩展搬运接口

    C++
    template <typename T>
    __simd_vf__ inline void LoadUnAlignVF2(__ubuf__ T* dstAddr, __ubuf__ T* srcAddr, uint16_t postUpdateStride, uint16_t repeatTimes)
    {
        AscendC::Reg::RegTensor<T> srcReg;
        AscendC::Reg::UnalignRegForLoad ureg0;
        AscendC::Reg::UnalignRegForStore ureg1;
        AscendC::Reg::LoadUnAlignPre(ureg0, srcAddr);
        for (uint16_t i = 0; i < repeatTimes; ++i) {
            AscendC::Reg::LoadUnAlign(srcReg, ureg0, srcAddr, postUpdateStride);
            AscendC::Reg::StoreUnAlign(dstAddr, srcReg, ureg1, postUpdateStride);
        }
        AscendC::Reg::StoreUnAlignPost(dstAddr, ureg1, 0);
    }
    
  • 使用AddrReg寄存器存储偏移量接口

    C++
    template <typename T>
    __simd_vf__ inline void LoadUnAlignVF3(__ubuf__ T* dstAddr, __ubuf__ T* srcAddr, uint16_t oneRepeatSize, uint16_t repeatTimes)
    {
        AscendC::Reg::RegTensor<T> srcReg;
        AscendC::Reg::UnalignRegForLoad ureg0;
        AscendC::Reg::UnalignRegForStore ureg1;
        AscendC::Reg::AddrReg aReg;
        for (uint16_t i = 0; i < repeatTimes; ++i) {
            aReg = AscendC::Reg::CreateAddrReg<T>(i, oneRepeatSize);
            AscendC::Reg::LoadUnAlignPre(ureg0, srcAddr, aReg);
            AscendC::Reg::LoadUnAlign(srcReg, ureg0, srcAddr, aReg, 0);
            AscendC::Reg::StoreUnAlign(dstAddr, srcReg, ureg1, aReg);
        }
        AscendC::Reg::StoreUnAlignPost(dstAddr, ureg1, aReg);
    }
    

免责声明:本站内容由 asc-devkit 仓 master 分支自动编译生成,属于持续开发版本,可能存在缺陷,仅供预览与参考。如需稳定及商用资料,请查阅官方 昇腾社区