昇腾社区首页
中文
注册

优化bank分配以提升读写性能

【优先级】高

【描述】Unified Buffer中bank结构如下图所示。UB总大小(假设为192K)会被分成48个bank:bank内组成为128行(bank_depth = 128),每一行长度为C0=32B,即bank_depth * C0二维结构;bank间组成为16bank group(bank_group = 16),每个group3bank(bank_num = 3),即bank_group * bank_num的二维结构

图1 bank结构示意图

Vector单元每拍(一拍为一个指令周期)能够从每个bank group中读取一份数据,因此每拍最多能够读取16组 * C0(32B)= 512B数据。Vector单元每拍能够向每个bank group中写入一份数据,因此每拍最多能够写入16组 * C0(32B)= 512B数据。当多个操作尝试同时访问同一个bank或者bank group时,可能会发生bank冲突,这种冲突会导致访问排队,降低性能。

bank冲突会出现在以下三种场景:

  • 读写冲突:在同时读写的时候,读口与写口在操作同一个bank时会发生bank冲突。
  • 写写冲突:同时写一块bank group时,会发生bank冲突。
  • 读读冲突:同时读一块bank group时,会发生bank冲突。

假设,0x10000地址在bank16上,0x10020在bank17上,0x20020在bank33上,如下图所示:

图2 地址分配示意图
  • 读写冲突Vector指令的输入输出地址之间,当src/dst同时读写到同一个bank时会造成读写冲突,示例如下
    表1 读写冲突示例

    序号

    src0地址

    src1地址

    bank

    bank_group

    结论

    示例1

    0x10020

    0x10000

    bank_id0 != bank_id1

    bank_group_id0 != bank_group_id1

    无冲突

    示例2

    0x10020

    0x10E20

    bank_id0 == bank_id1

    bank_group_id0 == bank_group_id1

    存在冲突

  • 写写冲突,Vector指令一拍可同时写8datablock,当datablock0~datablock7同时写到一个bank_group时会造成写写冲突,举例如下
    表2 写写冲突示例

    序号

    dst地址

    blk_stride

    block0_addr

    block1_addr

    block2_addr

    ...

    结论

    示例1

    0x1FE00

    16

    0x1FE00

    0x20000

    0x20200

    ...

    8个datablock全冲突,8拍完成一个repeat

    示例2

    0x1FE00

    8

    0x1FE00

    0x1FF00

    0x20000

    ...

    datablock0和datablock2冲突,4拍完成一个repeat

  • 读读冲突
    • src场景,对于Add这类双输入指令,src0src1分别占据一个读口,当src0/src1同时读到同一个bank_group时会造成读读冲突,举例如下:
      表3 双src场景读读冲突示例

      序号

      src0地址

      src1地址

      bank

      bank_group

      结论

      示例1

      0x10020

      0x20020

      bank_id0 != bank_id1

      bank_group_id0 == bank_group_id1

      存在冲突

      示例2

      0x10020

      0x10000

      bank_id0 != bank_id1

      bank_group_id0 != bank_group_id1

      无冲突

    • src场景Vector指令一拍可同时读8datablock当datablock0~datablock7同时读到一个bank_group时会造成读读冲突,举例如下:
      表4 单src场景读读冲突示例

      序号

      src地址

      blk_stride

      block0_addr

      block1_addr

      block2_addr

      ...

      结论

      示例1

      0x1FE00

      16

      0x1FE00

      0x20000

      0x20200

      ...

      8个datablock全冲突,8拍完成一个repeat

      示例2

      0x1FE00

      8

      0x1FE00

      0x1FF00

      0x20000

      ...

      datablock0和datablock2冲突,4拍完成一个repeat

通过MsProf工具可以进行资源冲突占比的相关性能数据采集。

工具的具体使用方法请参考msProf。资源冲突占比文件性能数据文件说明请参考ResourceConflictRatio(资源冲突占比)

【反例】

对一个输入或输出tensor使用tensor高维切分接口实现跳读跳写,当dataBlockStride为16的整数倍时将发生读读冲突。假设我们要对一个shape为(8, 16, 16)的输入做(1, 0, 2)的transpose,输出shape为(16, 8, 16)。

如下“跳读,连续写”的代码,将导致同一repeat内输入的8个datablock都在同一个bank_group而发生读读冲突。

1
2
3
4
5
6
7
uint64_t mask = 128;
UnaryRepeatParams params
params.dstBlkStride  = 1;
params.srcBlkStride = 16;
for(uint32_t i=0; i<16; i++)   {
    AscendC::Adds(dstLocal[i * 128], srcLocal[i * 16], 0, mask, 1, params);
}
图3 “跳读,连续写”数据读写示意图

【正例】

通过修改取数规则为“连续读,跳写”避免冲突问题,示例代码如下:

1
2
3
4
5
6
7
uint64_t mask = 128;
UnaryRepeatParams params
params.dstBlkStride  = 8;
params.srcBlkStride = 1;
for(uint32_t i=0; i<8; i++)   {
    AscendC::Adds(dstLocal[i * 16], srcLocal[i * 256], 0, mask, 2, params);
}
图4 “连续读,跳写”数据读写示意图

【正例】

如果在申请的一块workBuffer中有两个输入向量,则两个向量的起始地址不能在同一个bank group中,避免方法为:多申请32B字节LocalTensor,使得workBuffer2个输入错开32字节

例如,计算z = x + y时,xworkBuffer0地址开始,长度为8K字节,y从8k字节地址开始,长度为8K字节,则x和y物理地址会落到同一块bank中。在分配地址中,增加一定长度,可避免bank冲突,示例代码和地址分配示意图如下:

1
2
3
4
LocalTensor<float> srcLocal;
LocalTensor<float> dstLocal;
UnaryRepeatParams params
AscendC::Add(dstLocal, srcLocal[0], srcLocal[(8 * 1024 + 32) / sizeof(float)], mask, (8 * 1024) / 256 , params);
图5 地址分配示意图