AsendC:A1->A2,B1->B2搬运优化
目前matmul A1->A2,B1->B2的搬运,均使用loadData的load2d接口来进行搬运。如下图的搬运流程。需要将A1中zN格式数据,搬运到A2中变成zZ格式的数据。
使用load2d指令搬运需要再M方向循环搬运。这样搬运会让内核scalar占比过高。后述介绍如何使用load3d接口来进行数据搬运。
for (uint32_t i = 0; i < M / 16; i++)
{
AscendC::LoadData(
dstTensor[i * K * 16],
srcTensor[i * 16 * 16],
AscendC::LoadData2dParams(
0,
static_cast<uint16_t>(K / 16),
M / 16,
0,
0,
false,
0));
}
load3d接口介绍
load3的接口的介绍可查看loadData接口介绍load3d可将卷积的输入数据,映射为矩阵乘法的形式。当然可完成矩阵乘法的数据搬运。
load3d指令需要设置寄存器,可通过SetFMatrix API设置。SetFMatrix介绍
SetFMatrix函数原型。
__aicore__ inline void SetFmatrix(uint16_t l1H, uint16_t l1W, const uint8_t padList[4], const FmatrixMode& fmatrixMode);
注意:
- 矩阵乘法计算时,需要将l1H设置为1。
- FmatrixMode 有FMATRIX_LEFT和FMATRIX_RIGHT两种。和load3d中的fMatrixCtrl对应。FMATRIX_LEFT对应fMatrixCtrl=0,FMATRIX_RIGHT对应fMatrixCtrl=1。原因:AsendC将load3d多余的参数放置在寄存器中,并给予了两套寄存器。分别给A1->A2,B1->B2使用。设置寄存器通常比较耗时,并且都为重复操作。在最开始设置,可减少寄存器开销。
下面介绍在矩阵乘法搬运中的四种情况。
情况一
将A1上zN的数据,转换为A2 zZ的数据。不需要转置矩阵。
uint8_t padList[4] = {0, 0, 0, 0};
AscendC::SetFmatrix(
1, // l1H
M, // l1W
padList,
AscendC::FmatrixMode::FMATRIX_LEFT
); // 设置一次即可,最好不要多次设置
static constexpr AscendC::IsResetLoad3dConfig config = {false, false}; // 防止重复设置SetFmatrix
AscendC::LoadData3DParamsV2<Element> loadDataParams;
loadDataParams.L1H=1;
loadDataParams.L1W=M; // 矩阵在A1的M
loadDataParams.kExtension = K; // A2上K方向的大小
loadDataParams.mExtension = M; // A2上M方向的大小
loadDataParams.kStartPt = 0; // 从L1开始读取的位置在L1W,L1H的坐标
loadDataParams.mStartPt = 0; // 从L1开始读取的位置在L1W,L1H的坐标
loadDataParams.strideW = 1; // 默认为1
loadDataParams.strideH = 1;
loadDataParams.filterW = 1;
loadDataParams.filterH = 1;
loadDataParams.dilationFilterW = 1;
loadDataParams.dilationFilterH = 1;
loadDataParams.filterSizeW = 0;
loadDataParams.filterSizeH = 0;
loadDataParams.enTranspose = 0; // A1->A2中是否将整个矩阵转置,包含分形间和分型内都转置。B1->B2中默认分形转置。该选项不启用。
loadDataParams.fMatrixCtrl = 0; // 使用FMATRIX_LEFT还是使用FMATRIX_RIGHT,=0使用FMATRIX_LEFT,=1使用FMATRIX_RIGHT。
loadDataParams.channelSize = K; // 矩阵在A1的K
AscendC::LoadData<Element,config>(
A2Tensor,
A1Tensor,
loadDataParams
);
以情况一介绍一下为什么可以搬运完成。如下图所示:图片数据格式一般为(N,C,H,W),在A1上需要(N,C1,H,W,C0)其中C0为32字节,C1=C/C0。其中卷积窗口设置为1x1。其他参数默认。则矩阵乘法A1->A2可转换为下图形式。其中K=C、L1W=M、L1H=1。该形式可利用load3d完成。
情况二
A1上zN的数据,转换为A2上zZ的数据。
uint8_t padList[4] = {0, 0, 0, 0};
AscendC::SetFmatrix(1, K, padList, AscendC::FmatrixMode::FMATRIX_LEFT); // 设置一次即可,最好不要多次设置
static constexpr AscendC::IsResetLoad3dConfig config = {false, false}; // 防止重复设置SetFmatrix
AscendC::LoadData3DParamsV2<Element> loadDataParams;
loadDataParams.L1H=1;
loadDataParams.L1W=K;
loadDataParams.kExtension = M;
loadDataParams.mExtension = K;
loadDataParams.kStartPt = 0;
loadDataParams.mStartPt = 0;
loadDataParams.strideW = 1;
loadDataParams.strideH = 1;
loadDataParams.filterW = 1;
loadDataParams.filterH = 1;
loadDataParams.dilationFilterW = 1;
loadDataParams.dilationFilterH = 1;
loadDataParams.filterSizeW = 0;
loadDataParams.filterSizeH = 0;
loadDataParams.enTranspose = 1;
loadDataParams.fMatrixCtrl = 0;
loadDataParams.channelSize = M;
AscendC::LoadData<Element, config>(
A2Tensor,
A1Tensor,
loadDataParams
);
情况三
B1上zN的数据,转换为B2上nZ的数据。不需要转置。(load3d指令无法实现,原因:B1->B2的数据搬运,分形必须转置)两者布局非常相似。如果A2上的N等于A1上的N可直接使用一条load2d指令搬运,因为无分形间数据布局变化。
AscendC::LoadData(
B2Tensor,
B1Tensor,
AscendC::LoadData2dParams(
0,
static_cast<uint16_t>(N / 16 * K / 16),
1,
0,
0,
false,
0)
);
情况四
B1上zN的数据,转换为B2上nZ的数据。需要转置。可使用load3d指令实现。
uint8_t padList[4] = {0, 0, 0, 0};
AscendC::SetFmatrix(1, K, padList, AscendC::FmatrixMode::FMATRIX_RIGHT); // 设置一次即可,最好不要多次设置
static constexpr AscendC::IsResetLoad3dConfig config = {false, false}; // 防止重复设置SetFmatrix
AscendC::LoadData3DParamsV2<Element> loadDataParams;
loadDataParams.L1H=1;
loadDataParams.L1W=K;
loadDataParams.kExtension = N;
loadDataParams.mExtension = K;
loadDataParams.kStartPt = 0;
loadDataParams.mStartPt = 0;
loadDataParams.strideW = 1;
loadDataParams.strideH = 1;
loadDataParams.filterW = 1;
loadDataParams.filterH = 1;
loadDataParams.dilationFilterW = 1;
loadDataParams.dilationFilterH = 1;
loadDataParams.filterSizeW = 0;
loadDataParams.filterSizeH = 0;
loadDataParams.enTranspose = 1; // B1->B2的搬运中不起作用,默认分形转置
loadDataParams.fMatrixCtrl = 1; // 使用FMATRIX_LEFT还是使用FMATRIX_RIGHT,=0使用FMATRIX_LEFT,=1使用FMATRIX_RIGHT。
loadDataParams.channelSize = N;
AscendC::LoadData<Element>(
B2Tensor,
B1Tensor,
loadDataParams
);
注意:
load3d指令执行A1->A2,B1->B2搬运,不一定比load2d指令搬运快,但load3d可减少大量的scalar操作。