The following uses the Matmul+LeakyReLU fused operator as an example to describe the design and implementation process of the Mix fused operator. This example can run only on the
The operator design process consists of operator analysis, data flow analysis, and tiling strategy design.
Operator analysis is to specify the mathematical expression, input, output, and kernel function name of an operator.
1
|
c = LeakyRelu(a * b + bias, alpha); |
Based on the preceding analysis, the design specifications of the Ascend C Matmul+LeakyReLU operator are as follows:
|
Name |
Shape |
Data Type |
Format |
|---|---|---|---|
|
a (input) |
[M, K] |
half |
ND |
|
b (input) |
[K, N] |
half |
ND |
|
bias (input) |
[1, N] |
float32 |
ND |
|
z (output) |
[M, N] |
float32 |
ND |
Analyze the data flow of the operator. The data flow direction is as follows: After Matmul compute is completed on the Cube Core, data is moved to the Vector Core for LeakyReLU compute. Based on the preceding data flows and the programming paradigm of the fused operator, plan parallel pipeline tasks. The following figure shows the details:

The LeakyReLU (Leaky Rectified Linear Unit) activation function is a commonly used activation function in artificial neural networks. Its mathematical expression and function graph are shown below.


The first three steps are encapsulated in the Matmul high-level APIs. In this example, there are only three stages. The following figure shows the details:

According to the preceding analysis, the Matmul high-level APIs, LeakyReLU vector compute API, Mask Operations, EnQue, and DeQue are used during the implementation.
Tiling strategy design mainly includes multi-core tiling and intra-core tiling strategies.
The following figure shows the tiling strategy. For details, see Data Tiling.

In Cube Programming (High-Level APIs), Ascend C provides a group of Matmul high-level APIs, which encapsulate common algorithm logic for tiling, data movement, and compute, helping you quickly implement Matmul matrix multiplication. The implementation of cube programming in a fused operator is similar. You can call APIs on the host to automatically obtain tiling parameters. After the parameters are passed to the kernel during initialization, matrix multiplication can be completed by using several simple APIs. With reference to the foregoing programming paradigm of the fused operator, the procedure for implementing the fused operator is as follows. For a complete example, see MatmulLeakyRelu.

The following figure shows the code framework implemented on the kernel. After the Matmul object is initialized and the left matrix A, right matrix B, and bias are set, the subsequent Matmul compute, LeakyReLU compute, and CopyOut process are completed by adding a single Iterate loop and a while loop.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
template<typename aType, typename bType, typename cType, typename biasType> __aicore__ inline void MatmulLeakyKernel<aType, bType, cType, biasType>::Process(){ uint32_t computeRound = 0; // Set the input of Matmul (including the left matrix, right matrix, and bias). matmulObj.SetTensorA(aGlobal); matmulObj.SetTensorB(bGlobal); matmulObj.SetBias(biasGlobal); // Call Matmul iterate to obtain the compute result of [baseM, baseN]. while (matmulObj.template Iterate<true>()) { MatmulCompute(); LeakyReluCompute(); CopyOut(computeRound); computeRound++; } matmulObj.End(); } // Kernel entrypoint function, Mix scenario, and AIC:AIV = 1:2 __global__ __mix__(1, 2) void matmul_leakyrelu_custom(GM_ADDR a, GM_ADDR b, GM_ADDR bias, GM_ADDR c, __kfc_workspace__ GM_ADDR workspace, AscendC::tiling::TCubeTiling tiling) { AscendC::TPipe pipe; MatmulLeakyKernel<half, half, float, float> matmulLeakyKernel; matmulLeakyKernel.Init(a, b, bias, c, workspace, tiling, &pipe); // Initialize the Matmul object. REGIST_MATMUL_OBJ(&pipe, GetSysWorkSpacePtr(), matmulLeakyKernel.matmulObj, &matmulLeakyKernel.tiling); matmulLeakyKernel.Process(&pipe); } |
The implementation code of Matmul compute, LeakyReLU compute, and CopyOut is as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
template<typename aType, typename bType, typename cType, typename biasType> __aicore__ inline void MatmulLeakyKernel<aType, bType, cType, biasType>::Process(){ uint32_t computeRound = 0; // ... // Call Matmul iterate to obtain the compute result of [baseM, baseN]. while (matmulObj.template Iterate<true>()) { MatmulCompute(); // ... computeRound++; } matmulObj.End(); } template<typename aType, typename bType, typename cType, typename biasType> __aicore__ inline void MatmulLeakyKernel<aType, bType, cType, biasType>::MatmulCompute(){ reluOutLocal = reluOutQueue_.AllocTensor<cType>(); // Call GetTensorC to move the Matmul compute result to the Vector Core. matmulObj.template GetTensorC<true>(reluOutLocal, false, true); } |
1 2 3 4 5 6 |
// Call the LeakyRule API to compute. template<typename aType, typename bType, typename cType, typename biasType> __aicore__ inline void MatmulLeakyKernel<aType, bType, cType, biasType>::LeakyReluCompute(){ AscendC::LeakyRelu(reluOutLocal, reluOutLocal, (cType)alpha, tiling.baseM * tiling.baseN); reluOutQueue_.EnQue(reluOutLocal); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 |
// Move the result to the global memory. template<typename aType, typename bType, typename cType, typename biasType> __aicore__ inline void MatmulLeakyKernel<aType, bType, cType, biasType>::CopyOut(uint32_t count){ reluOutQueue_.DeQue<cType>(); const uint32_t roundM = tiling.singleCoreM / tiling.baseM; const uint32_t roundN = tiling.singleCoreN / tiling.baseN; uint32_t startOffset = (count % roundM * tiling.baseM * tiling.N + count / roundM * tiling.baseN); AscendC::DataCopyParams copyParam = {(uint16_t)tiling.baseM, (uint16_t)(tiling.baseN * sizeof(cType) / DEFAULT_C0_SIZE), 0, (uint16_t)((tiling.N - tiling.baseN) * sizeof(cType) / DEFAULT_C0_SIZE)}; AscendC::DataCopy(cGlobal[startOffset], reluOutLocal, copyParam); reluOutQueue_.FreeTensor(reluOutLocal); } |
Implement the GenerateTiling function on the host to automatically obtain tiling parameters. The key procedure is as follows:
1 2 |
auto ascendcPlatform = platform_ascendc::PlatformAscendC(context->GetPlatformInfo()); matmul_tiling::MultiCoreMatmulTiling cubeTiling(ascendcPlatform); |
When creating an object, you need to pass the hardware platform information, which can be obtained by calling GetPlatformInfo.
1 2 3 4 |
cubeTiling.SetAType(matmul_tiling::TPosition::GM, matmul_tiling::CubeFormat::ND, matmul_tiling::DataType::DT_FLOAT16); cubeTiling.SetBType(matmul_tiling::TPosition::GM, matmul_tiling::CubeFormat::ND, matmul_tiling::DataType::DT_FLOAT16); cubeTiling.SetCType(matmul_tiling::TPosition::LCM, matmul_tiling::CubeFormat::ND, matmul_tiling::DataType::DT_FLOAT); cubeTiling.SetBiasType(matmul_tiling::TPosition::GM, matmul_tiling::CubeFormat::ND, matmul_tiling::DataType::DT_FLOAT); |
1 2 |
cubeTiling.SetShape(M, N, K); cubeTiling.SetOrgShape(M, N, K); |
1
|
cubeTiling.SetBufferSpace(-1, -1, -1); |
1
|
cubeTiling.SetBias(true); |
1 2 3 4 |
MatmulLeakyreluCustomTilingData tiling; if (cubeTiling.GetTiling(tiling.cubeTilingData) == -1){ return ge::GRAPH_FAILED; } |
1 2 3 4 |
size_t userWorkspaceSize = 0; size_t systemWorkspaceSize = ascendcPlatform.GetLibApiWorkSpaceSize(); size_t *currentWorkspace = context->GetWorkspaceSizes(1); currentWorkspace[0] = userWorkspaceSize + systemWorkspaceSize; |
1 2 3 4 5 |
// The workspace space must be set when Matmul is used. SetSysWorkspace(workspace); if (GetSysWorkSpacePtr() == nullptr) { return; } |
1 2 3 4 5 6 7 8 9 10 11 12 |
#define ASCENDC_CUBE_ONLY // Specify that Matmul operators run on AICs. KERNEL_TASK_TYPE_DEFAULT(KERNEL_TYPE_MIX_AIC_1_2); // Set the kernel type to KERNEL_TYPE_MIX_xxx. if ASCEND_IS_AIC { ... // The AIC Core performs Matmul compute. // After the AIC Core completes the compute, the synchronization flag is sent by using AscendC::CrossCoreSetFlag<modeId, pipe>(flagId). } if ASCEND_IS_AIV { ... // The AIV Core receives the synchronization flag by using AscendC::CrossCoreWaitFlag(flagId). // The AIV Core performs LeakyReLU compute. } |
For a complete example, see BareMix sample.