【优先级】高
【描述】Unified Buffer中bank结构如下图所示。UB总大小(假设为192K)会被分成48个bank:每个bank由128(bank_depth)行,长度为32B(C0)的二维结构组成;bank间组成为16个bank group(bank_group = 16),每个group有3个bank(bank_num = 3,bank15,bank31,bank47组成一个bank group),即bank_group * bank_num的二维结构。
Vector单元每拍(一拍为一个指令周期)能够从每个bank group中读取一行数据,因此每拍最多能够读取16组 * C0(32B)= 512B数据。Vector单元每拍能够向每个bank group中写入一行数据,因此每拍最多能够写入16组 * C0(32B)= 512B数据。当多个操作尝试同时访问同一个bank或者bank group时,可能会发生bank冲突,这种冲突会导致访问排队,降低性能。
bank冲突会出现在以下三种场景:
假设,0x10000地址在bank16上,0x10020在bank17上,0x20020在bank33上,如下图所示:
序号 |
src0地址 |
src1地址 |
bank |
bank_group |
结论 |
---|---|---|---|---|---|
示例1 |
0x10020 |
0x10000 |
bank_id0 != bank_id1 |
bank_group_id0 != bank_group_id1 |
根据地址计算,src0和src1,分别属于bank16和bank17,故无冲突。 |
示例2 |
0x10020 |
0x10E20 |
bank_id0 == bank_id1 |
bank_group_id0 == bank_group_id1 |
src0和src1的地址都在bank17,故存在冲突。 |
序号 |
dst地址 |
blk_stride |
block0_addr |
block1_addr |
block2_addr |
... |
结论 |
---|---|---|---|---|---|---|---|
示例1 |
0x1FE00 |
16 |
0x1FE00 |
0x20000 |
0x20200 |
... |
8个datablock均在一个bank group下,故全部冲突,8拍完成一个repeat。 |
示例2 |
0x1FE00 |
8 |
0x1FE00 |
0x1FF00 |
0x20000 |
... |
datablock0和datablock2在一个bank group,存在冲突,4拍完成一个repeat。 |
序号 |
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地址 |
blk_stride |
block0_addr |
block1_addr |
block2_addr |
... |
结论 |
---|---|---|---|---|---|---|---|
示例1 |
0x1FE00 |
16 |
0x1FE00 |
0x20000 |
0x20200 |
... |
8个datablock均在一个bank group下,故全部冲突,8拍完成一个repeat。 |
示例2 |
0x1FE00 |
8 |
0x1FE00 |
0x1FF00 |
0x20000 |
... |
datablock0和datablock2在同一个bank group下,存在冲突,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); } |
【正例】
通过修改取数规则为“连续读,跳写”避免冲突问题,示例代码如下:
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); } |
【正例】
如果在申请的一块workBuffer中有两个输入向量,则两个向量的起始地址不能在同一个bank group中,避免方法为:多申请32B字节LocalTensor,使得workBuffer的2个输入错开32字节。
例如,计算z = x + y时,x从workBuffer0地址开始,长度为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); |