多核场景
多核切分
为了实现多核并行,提升计算效率,需要将矩阵数据进行切分,分配到不同的核上进行处理。主要的切分策略有切分K轴和不切分K轴两种。
不切分K轴、仅切分M、N轴的策略如下:
- 对于A矩阵,沿着M轴进行切分,切分成多份的singleCoreM,单核上处理SingleCoreM * K大小的数据。
- 对于B矩阵,沿着N轴进行切分,切分成多份的singleCoreN,单核上处理K * SingleCoreN大小的数据。
- 对于C矩阵,SingleCoreM * K大小的A矩阵和K * SingleCoreN大小的B矩阵相乘得到SingleCoreM * SingleCoreN大小的C矩阵,即为单核上输出的C矩阵大小。
比如,下图中共有8个核参与计算,将A矩阵沿着M轴划分为4块,将B矩阵沿着N轴切分为两块,单核上仅处理某一分块(比如图中绿色部分为core5上参与计算的数据):SingleCoreM * K大小的A矩阵分块和SingleCoreN * K大小的B矩阵分块相乘得到SingleCoreM * SingleCoreN大小的C矩阵分块。
切分M、N、K轴的策略如下图所示:
- 对于A矩阵,沿着M轴进行切分,切分成多份的singleCoreM,沿着K轴切分,切分成多份的singleCoreK,单核上处理singleCoreM * singleCoreK大小的数据。
- 对于B矩阵,沿着K轴进行切分,切分成多份的singleCoreK,沿着N轴进行切分,切分成多份的singleCoreN,单核上处理singleCoreK * singleCoreN大小的数据。
- 对于C矩阵,singleCoreM * singleCoreK大小的A矩阵与singleCoreK * singleCoreN大小的B矩阵相乘并累加得到singleCoreM * singleCoreN大小的C矩阵分块。
比如下图中,C矩阵中的R矩阵块,是通过a*a+b*b+c*c累加得到的,其中,a*a、b*b、c*c可在多个核上并行计算。
- CUBE_ONLY(只有矩阵计算)场景,本节内容以CUBE_ONLY模式举例。
SetBlockDim加载的核数就是Matmul API实际计算会用到的核数,SetDim和SetBlockDim设置的值是一样的。
- MIX模式(包含矩阵计算和矢量计算)的设置规则请参考MIX场景核数设置规则。
1 2 3 4 5 6 7 8 9 10 |
// 构造多核Tiling对象 auto ascendcPlatform = platform_ascendc::PlatformAscendC(context->GetPlatformInfo()); matmul_tiling::MultiCoreMatmulTiling tiling(ascendcPlatform); // 设置可参与矩阵乘运算的核数 tiling.SetDim(5); tiling.SetAType(AscendC::TPosition::GM, CubeFormat::ND, matmul_tiling::DataType::DT_FLOAT16); ... optiling::TCubeTiling tilingData; // 获取Tiling参数 int ret = tiling.GetTiling(tilingData); // if ret = -1, gen tiling failed |
非对齐场景尾块处理
多核场景,对矩阵进行切分时,若M、N、K无法整除singleCoreM 、singleCoreN、 singleCoreK时,就会出现尾块,如下图矩阵A、B的最后一行和最后一列的矩阵块:
此时,C矩阵中的R矩阵块,依然是通过a*a+b*b+c*c+d*d累加得到的,处理a*a、b*b、c*c、d*d等尾块时,需在kernel侧设置尾块大小,在不改变原有tiling的情况下,重新设置本次计算的singleCoreM/singleCoreN/singleCoreK,在处理尾块的时候按照设置的值也就是tailM/tailN/tailK进行搬运和计算。如下的示例中,当tailM < singleCoreM被认为需要处理尾块,此时可以调用SetTail接口进行设置。SetTail接口需要在Iterate/IterateAll之前调用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
template<typename aType, typename bType, typename cType, typename biasType __aicore__ inline void MatmulKernel<aType, bType, cType, biasType>::CalcOffset(int32_t blockIdx, const TCubeTiling& tiling, int32_t& offsetA, int32_t& offsetB, int32_t& offsetC, int32_t& offsetBias, bool isAtrans, bool isBtrans){ auto mSingleBlocks = Ceil(tiling.M, tiling.singleCoreM); auto mCoreIndx = blockIdx % mSingleBlocks; auto nCoreIndx = blockIdx / mSingleBlocks; ... // 处理尾块 int tailM = tiling.M - mCoreIndx * tiling.singleCoreM; tailM = tailM < tiling.singleCoreM ? tailM : tiling.singleCoreM; int tailN = tiling.N - nCoreIndx * tiling.singleCoreN; tailN = tailN < tiling.singleCoreN ? tailN : tiling.singleCoreN; if (tailM < tiling.singleCoreM || tailN < tiling.singleCoreN) { matmulObj.SetTail(tailM, tailN); } } |