aclnnGroupedMatmulFinalizeRoutingV3
Applicable Products
| Product | Supported |
|---|---|
| √ | |
| √ | |
| √ | |
| × | |
| × | |
| × |
Function
- Description:
A fused operator of GroupedMatmul and MoeFinalizeRouting. The output of GroupedMatmul is combined by index.
Compared with the aclnnGroupedMatmulFinalizeRoutingV2 API, this API adds:
- <term>Atlas A2 training products/Atlas A2 inference products</term> and <term>Atlas A3 training products/Atlas A3 inference products</term>: The input parameter tuningConfigOptional is added as a tuning parameter. The first value in the array indicates the expected number of tokens processed by each expert. The operator tiling is performed based on this expected value for better performance.
- <term>Ascend 950PR/Ascend 950DT</term>: The MX quantization scenario is added. For related information, see [Quantization Introduction](../common/quant_mode_introduction.md).Formula:
- Grouped matrix multiplication (GMM):
- Routing expert and expert output assignment:
For each token j, perform routing and output expert assignment:
Where is the expert index to which token j is assigned. is the computation result of this token under the corresponding expert.
- Shared expert output fusion:
- Shared expert output fusion: The final output is the result of merging all expert outputs with the shared expert output according to rowIndex. The computation process is as follows:
where denotes the set of experts assigned to token j.
Function Prototype
Each operator is divided into a two-phase API. You must first call the "aclnnGroupedMatmulFinalizeRoutingV3GetWorkspaceSize" API to obtain the workspace size required for computation and the executor that contains the operator computation process, and then call the "aclnnGroupedMatmulFinalizeRoutingV3" API to perform the computation.
aclnnStatus aclnnGroupedMatmulFinalizeRoutingV3GetWorkspaceSize(
const aclTensor *x1,
aclTensor *x2,
const aclTensor *scaleOptional,
const aclTensor *biasOptional,
const aclTensor *offsetOptional,
const aclTensor *antiquantScaleOptional,
const aclTensor *antiquantOffsetOptional,
const aclTensor *pertokenScaleOptional,
const aclTensor *groupListOptional,
const aclTensor *sharedInputOptional,
const aclTensor *logitOptional,
const aclTensor *rowIndexOptional,
int64_t dtype,
float sharedInputWeight,
int64_t sharedInputOffset,
bool transposeX1,
bool transposeX2,
int64_t groupListType,
const aclIntArray *tuningConfigOptional,
aclTensor *out,
uint64_t *workspaceSize,
aclOpExecutor **executor)aclnnStatus aclnnGroupedMatmulFinalizeRoutingV3(
void *workspace,
uint64_t workspaceSize,
aclOpExecutor *executor,
aclrtStream stream)aclnnGroupedMatmulFinalizeRoutingV3GetWorkspaceSize
Parameters
Parameter Input/Output Description Instruction Data Type Data Format Dimension (Shape) Non-contiguous Tensor x1 Input Input x (left matrix). - INT8, FLOAT8_E5M2, FLOAT8_E4M3FN, FLOAT4_E2M1 ND (m, k) - x2 Input Input weight (right matrix). - INT4, FLOAT8_E5M2, FLOAT8_E4M3FN, FLOAT4_E2M1 ND Supports 3D. - scaleOptional Input Scale factor in the quantization parameter, a per-channel quantization parameter. - INT64, FLOAT8_E8M0 ND - - biasOptional Input Offset of the matrix. - FLOAT32, BF16 ND Supports 2D. The dimension is (e, n). - offsetOptional Input Offset for asymmetric quantization. - FLOAT32 ND - - antiquantScaleOptional Input Scaling factor for pseudo-quantization. Currently not enabled. FLOAT32 ND - antiquantOffsetOptional Input Offset for pseudo-quantization. Currently not enabled. FLOAT32 ND - pertokenScaleOptional Input Dequantization parameter for matrix computation. FLOAT32, FLOAT8_E8M0 ND - - groupListOptional Input Specifies the matmul size distribution along the input and output grouping axis. INT64 ND supports 1D, dimension is (e) - sharedInputOptional Input Output of the shared expert in MoE computation, which needs to be combined with the output of the MoE expert. BF16 ND - - logitOptional Input The logit value of each token from the MoE expert. FLOAT32 ND The shape supports 1D, and the dimension is (m). - rowIndexOptional Input Specifies the row index for combining MoE expert outputs, where each value serves as the index for scatter-add during combination. INT64 ND shape supports 1D, and the dimension is (m). - dtype Input Output type for computation: 0: FLOAT32; 1: FLOAT16; 2: BFLOAT16. Currently only supports 0. INT64 - sharedInputWeight Input Coefficient for combining the shared expert with the MoE expert. sharedInput is first multiplied by this parameter and then accumulated with the MoE expert result. FLOAT32 - sharedInputOffset Input Offset of the shared expert output in the total output. INT64 - transposeX1 Input Whether the left matrix is transposed. Only false is supported. BOOL - transposeX2 Input Whether the right matrix is transposed. Only supports false. BOOL - groupListType Input Grouping mode: Set to 0 for cumsum mode (prefix sum); set to 1 for count mode. INT64 - tuningConfigOptional Input The first element in the array indicates the expected number of tokens processed by each expert. During tiling, the operator uses the first element of the array to perform reasonable tiling splitting for better performance. Elements starting from the second are reserved and do not need to be filled in by the user. They will be extended in the future. For backward compatibility with historical versions, if the user does not use this parameter, simply do not pass it in (i.e., pass nullptr). INT64 - out Output Output result. Shape is the same as self. FLOAT32 ND (batch, n) - workspaceSize Output Returns the workspace size to be applied for on the Device. - - - - - executor Output Returns the op executor, which contains the operator computation flow. - - - - - Atlas A2 training products/Atlas A2 inference products ,Atlas A3 training products/Atlas A3 inference products :x1 only supports INT8. Its dimension is (m, k), where m ranges from [1, 16*1024*8] and k supports 2048.
x2 only supports INT4. When the input is INT32, its dimension is (e, k, n / 8); when the input is converted to INT4, its dimension is (e, k, n). e ranges from [1, 256], k supports 2048, and n supports 7168.
scaleOptional supports INT64. Its shape supports 3D, with a dimension of (e, 1, n). e and n are consistent with those of w.
biasOptional supports FLOAT32. e and n are consistent with e and n of w.
offsetOptional supports FLOAT32. It supports 3D, and its dimension is (e, 1, n), where e and n are consistent with e and n of w.
perTokenScaleOptional supports FLOAT32. It supports 1D, and its dimension is (m), where m is consistent with m of x.
groupListOptional supports that e is consistent with e of w.
sharedInputOptional supports 2D, and its dimension is (bsdp, n), where bsdp must be less than or equal to batchSize/e, and n is consistent with n of w.
logitOptional supports m, which must be consistent with the m of x.
rowIndexOptional supports m, which must be consistent with the m of x.
x1, x2, and groupListOptional are mandatory parameters. scaleOptional, pertokenScaleOptional, logitOptional, rowIndexOptional, biasOptional, and sharedInputOptional are optional parameters.
Ascend 950PR/Ascend 950DT :x1 does not support INT8.
x2 does not support INT4. The dimension is (e, k, n), or (e, n, k) in the transposed case, where e ranges from [1, 1024].
scaleOptional supports FLOAT8_E8M0. The shape supports 4D with a dimension of (e, n, Ceil(k/64), 2), and the data type only supports FLOAT8_E8M0. The transpose attribute must be consistent with x2.
biasOptional supports BF16.
sharedInputOptional supports 2D with a dimension of (bsdp, n), where bsdp represents batchSize / dataParallelSize.
perTokenScaleOptional supports FLOAT8_E8M0. The shape supports 3D with a dimension of (m, Ceil(k/64), 2).
x1, x2, scaleOptional, pertokenScaleOptional, groupListOptional, logitOptional, and rowIndexOptional are required parameters. biasOptional and sharedInputOptional are optional parameters. The offsetOptional parameter is currently not supported. None of the parameters support empty tensors.
The first dimension of out and sharedInputOffset must be greater than or equal to 0.
x1 supports an empty tensor with M equal to 0.
x2 supports an empty tensor with N equal to 0.
Return Value
aclnnStatus: return code. For details, see aclnn Return Code.
The first-phase API performs input parameter validation and returns an error in the following scenarios:
Return Value Error Code Description ACLNN_ERR_PARAM_NULLPTR 161001 The input parameter is a required input, output, or mandatory attribute, and is a null pointer. ACLNN_ERR_PARAM_INVALID 161002 The data type or data format of x1, x2, scaleOptional, biasOptional, offsetOptional, antiQuantScaleOptional, antiQuantOffsetOptional, perTokenScaleOptional, groupListOptional, sharedInputOptional, logitOptional, rowIndexOptional, sharedInputWeight, sharedInputOffset, transposeX1, transposeX2, or out is not within the supported range. The shape of x1, x2, scaleOptional, biasOptional, offsetOptional, antiQuantScaleOptional, antiQuantOffsetOptional, perTokenScaleOptional, groupListOptional, sharedInputOptional, logitOptional, rowIndexOptional, or out does not meet the validation conditions. The shape of x1, x2, scaleOptional, biasOptional, offsetOptional, antiQuantScaleOptional, antiQuantOffsetOptional, perTokenScaleOptional, groupListOptional, sharedInputOptional, logitOptional, rowIndexOptional, or out is an empty tensor.
aclnnGroupedMatmulFinalizeRoutingV3
Parameters
Parameter Input/Output Description workspace Input Workspace memory address applied for on the Device side. workspaceSize Input Workspace size applied for on the Device side, obtained through the first-phase API aclnnGroupedMatmulFinalizeRoutingV3GetWorkspaceSize. executor Input Operator executor that contains the operator computation flow. stream Input Specifies the stream for task execution. Return Value
aclnnStatus: return code. For details, see aclnn Return Code.
Constraints
Deterministic computation:
Atlas A2 training products/Atlas A2 inference products ,Atlas A3 training products/Atlas A3 inference products : aclnnGroupedMatmulFinalizeRoutingV3 defaults to a non-deterministic implementation. Deterministic computation can be enabled through aclrtCtxSetSysParamOpt.Ascend 950PR/Ascend 950DT : aclnnGroupedMatmulFinalizeRoutingV3 defaults to a non-deterministic implementation. Deterministic computation cannot be enabled through aclrtCtxSetSysParamOpt.
Atlas A2 training products/Atlas A2 inference products ,Atlas A3 training products/Atlas A3 inference products : Only pseudo-quantization scenarios are supported.The input and output support the following data type combinations:
x1 x2 scaleOptional biasOptional offsetOptional antiquantScaleOptional antiquantOffsetOptional pertokenScaleOptional groupListOptional sharedInputOptional logitOptional rowIndexOptional out INT8 INT4 INT64 FLOAT32 FLOAT32 null null FLOAT32 INT64 BFLOAT16 FLOAT32 INT64 FLOAT32 INT8 INT4 INT64 FLOAT32 null null null FLOAT32 INT64 BFLOAT16 FLOAT32 INT64 FLOAT32 In this scenario, scaleOptional represents the result of offline fusion of per-channel and per-group computations.
In this scenario, biasOptional represents an auxiliary result of offline computation. The value must be and accumulated along the first dimension.
This scenario supports both symmetric quantization and asymmetric quantization. For symmetric quantization, offsetOptional must be set to null. For asymmetric quantization, offsetOptional represents an auxiliary result of offline computation, which is the result of .
In this scenario, antiquantScaleOptional and antiquantOffsetOptional must be set to null.
Ascend 950PR/Ascend 950DT : Only supports the MX full quantization scenario.The input and output support the following data type combinations:
MX Quantization Scenario x1 x2 scaleOptional biasOptional pertokenScaleOptional groupListOptional sharedInputOptional logitOptional rowIndexOptional out MXFP8 FLOAT8_E4M3FN / FLOAT8_E5M2 FLOAT8_E4M3FN / FLOAT8_E5M2 FLOAT8_E8M0 BFLOAT16 / null FLOAT8_E8M0 INT64 BFLOAT16 / null FLOAT32 INT64 FLOAT32 MXFP4 FLOAT4_E2M1 FLOAT4_E2M1 FLOAT8_E8M0 BFLOAT16 / null FLOAT8_E8M0 INT64 BFLOAT16 / null FLOAT32 INT64 FLOAT32 In the MXFP4/MXFP8 scenario, offsetOptional, antiQuantScaleOptional, and antiQuantOffsetOptional must be set to null.
In the MXFP4 scenario, the constraint that k must be an even number must be satisfied. When x2 is not transposed, n must be an even number.
In the MXFP4/MXFP8 scenario, x2 transpose or non-transpose is supported. The transpose attributes of x2 and scale must be consistent.
e must be less than or equal to 1024.
In the MXFP4 scenario, k cannot be 2.
Example
The following provides example code for reference only. For details about compilation and running, see Compile and Run Samples.
Atlas A2 training products/Atlas A2 inference products ,Atlas A3 training products/Atlas A3 inference products :#include <iostream> #include <memory> #include <vector> #include "acl/acl.h" #include "aclnnop/aclnn_permute.h" #include "aclnnop/aclnn_grouped_matmul_finalize_routing_v3.h" #include "aclnnop/aclnn_trans_matmul_weight.h" #define CHECK_RET(cond, return_expr) \ do { \ if (!(cond)) { \ return_expr; \ } \ } while (0) #define CHECK_FREE_RET(cond, return_expr) \ do { \ if (!(cond)) { \ Finalize(deviceId, stream); \ return_expr; \ } \ } while (0) #define LOG_PRINT(message, ...) \ do { \ printf(message, ##__VA_ARGS__); \ } while (0) int64_t GetShapeSize(const std::vector<int64_t> &shape) { int64_t shapeSize = 1; for (auto i : shape) { shapeSize *= i; } return shapeSize; } int Init(int32_t deviceId, aclrtStream *stream) { // Boilerplate for resource initialization. auto ret = aclInit(nullptr); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclInit failed. ERROR: %d\n", ret); return ret); ret = aclrtSetDevice(deviceId); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtSetDevice failed. ERROR: %d\n", ret); return ret); ret = aclrtCreateStream(stream); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtCreateStream failed. ERROR: %d\n", ret); return ret); return 0; } template <typename T> int CreateAclTensor(const std::vector<T> &hostData, const std::vector<int64_t> &shape, void **deviceAddr, aclDataType dataType, aclTensor **tensor) { auto size = GetShapeSize(shape) * sizeof(T); // Call aclrtMalloc to apply for device-side memory. auto ret = aclrtMalloc(deviceAddr, size, ACL_MEM_MALLOC_HUGE_FIRST); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtMalloc failed. ERROR: %d\n", ret); return ret); // Copy host-side data to device-side memory by calling aclrtMemcpy. ret = aclrtMemcpy(*deviceAddr, size, hostData.data(), size, ACL_MEMCPY_HOST_TO_DEVICE); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtMemcpy failed. ERROR: %d\n", ret); return ret); // Calculate the strides of a contiguous tensor. std::vector<int64_t> strides(shape.size(), 1); for (int64_t i = shape.size() - 2; i >= 0; i--) { strides[i] = shape[i + 1] * strides[i + 1]; } // Create an aclTensor by calling the aclCreateTensor API. *tensor = aclCreateTensor(shape.data(), shape.size(), dataType, strides.data(), 0, aclFormat::ACL_FORMAT_ND, shape.data(), shape.size(), *deviceAddr); return 0; } template <typename T> int CreateAclTensorWeight(const std::vector<T> &hostData, const std::vector<int64_t> &shape, void **deviceAddr, aclDataType dataType, aclTensor **tensor) { auto size = static_cast<uint64_t>(GetShapeSize(shape)); const aclIntArray *mat2Size = aclCreateIntArray(shape.data(), shape.size()); auto ret = aclnnCalculateMatmulWeightSizeV2(mat2Size, dataType, &size); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnCalculateMatmulWeightSizeV2 failed. ERROR: %d\n", ret); return ret); size *= sizeof(T); // Allocate device-side memory by calling aclrtMalloc. ret = aclrtMalloc(deviceAddr, size, ACL_MEM_MALLOC_HUGE_FIRST); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtMalloc failed. ERROR: %d\n", ret); return ret); // Copy host-side data to device-side memory by calling aclrtMemcpy. ret = aclrtMemcpy(*deviceAddr, size, hostData.data(), size, ACL_MEMCPY_HOST_TO_DEVICE); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtMemcpy failed. ERROR: %d\n", ret); return ret); // Compute the strides of the contiguous tensor. std::vector<int64_t> strides(shape.size(), 1); for (int64_t i = shape.size() - 2; i >= 0; i--) { strides[i] = shape[i + 1] * strides[i + 1]; } std::vector<int64_t> storageShape; storageShape.push_back(GetShapeSize(shape)); // Call the aclCreateTensor API to create an aclTensor. *tensor = aclCreateTensor(shape.data(), shape.size(), dataType, strides.data(), 0, aclFormat::ACL_FORMAT_ND, storageShape.data(), storageShape.size(), *deviceAddr); return 0; } int main() { int32_t deviceId = 0; aclrtStream stream; auto ret = Init(deviceId, &stream); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("Init stream failed. ERROR: %d\n", ret); return ret); // 2. Construct the inputs and outputs. Custom construction is required based on the API. int64_t m = 8; int64_t k = 2048; int64_t n = 7168; int64_t e = 1; int64_t batch = 8; int64_t bsdp = 1; int64_t dtype = 0; float shareInputWeight = 1.0; int64_t sharedInputOffset = 0; bool transposeX = false; bool transposeW = false; int64_t groupListType = 1; std::vector<int64_t> xShape = {m, k}; std::vector<int64_t> wShape = {e, k, n / 8}; std::vector<int64_t> scaleShape = {e, 1, n}; std::vector<int64_t> biasShape = {e, n}; std::vector<int64_t> offsetShape = {e, 1, n}; std::vector<int64_t> pertokenScaleShape = {m}; std::vector<int64_t> groupListShape = {e}; std::vector<int64_t> sharedInputShape = {bsdp, n}; std::vector<int64_t> logitShape = {m}; std::vector<int64_t> rowIndexShape = {m}; std::vector<int64_t> outShape = {batch, n}; std::vector<int64_t> tuningConfigVal = { 1 }; void *xDeviceAddr = nullptr; void *wDeviceAddr = nullptr; void *biasDeviceAddr = nullptr; void *scaleDeviceAddr = nullptr; void *offsetDeviceAddr = nullptr; void *pertokenScaleDeviceAddr = nullptr; void *groupListDeviceAddr = nullptr; void *sharedInputDeviceAddr = nullptr; void *logitDeviceAddr = nullptr; void *rowIndexDeviceAddr = nullptr; void *outDeviceAddr = nullptr; aclTensor* x = nullptr; aclTensor* w = nullptr; aclTensor* bias = nullptr; aclTensor* groupList = nullptr; aclTensor* scale = nullptr; aclTensor* offset = nullptr; aclTensor* pertokenScale = nullptr; aclTensor* sharedInput = nullptr; aclTensor* logit = nullptr; aclTensor* rowIndex = nullptr; aclTensor* out = nullptr; std::vector<int8_t> xHostData(GetShapeSize(xShape)); std::vector<int32_t> wHostData(GetShapeSize(wShape)); std::vector<int64_t> scaleHostData(GetShapeSize(scaleShape)); std::vector<float> biasHostData(GetShapeSize(biasShape)); std::vector<float> offsetHostData(GetShapeSize(offsetShape)); std::vector<float> pertokenScaleHostData(GetShapeSize(pertokenScaleShape)); std::vector<int64_t> groupListHostData(GetShapeSize(groupListShape)); std::vector<uint16_t> sharedInputHostData(GetShapeSize(sharedInputShape)); std::vector<int64_t> logitHostData(GetShapeSize(logitShape)); std::vector<float> rowIndexHostData(GetShapeSize(rowIndexShape)); std::vector<float> outHostData(GetShapeSize(outShape)); // Assign a value to groupList. groupListHostData[0] = 8; // Create the x aclTensor. ret = CreateAclTensor(xHostData, xShape, &xDeviceAddr, aclDataType::ACL_INT8, &x); std::unique_ptr<aclTensor, aclnnStatus (*)(const aclTensor *)> xTensorPtr(x, aclDestroyTensor); std::unique_ptr<void, aclError (*)(void *)> xDeviceAddrPtr(xDeviceAddr, aclrtFree); CHECK_RET(ret == ACL_SUCCESS, return ret); // Create an int32_t w aclTensor, which will be converted to int_4 later. ret = CreateAclTensorWeight(wHostData, wShape, &wDeviceAddr, aclDataType::ACL_INT32, &w); std::unique_ptr<aclTensor, aclnnStatus (*)(const aclTensor *)> wTensorPtr(w, aclDestroyTensor); std::unique_ptr<void, aclError (*)(void *)> wDeviceAddrPtr(wDeviceAddr, aclrtFree); CHECK_RET(ret == ACL_SUCCESS, return ret); // Create a scale aclTensor. ret = CreateAclTensor(scaleHostData, scaleShape, &scaleDeviceAddr, aclDataType::ACL_INT64, &scale); std::unique_ptr<aclTensor, aclnnStatus (*)(const aclTensor *)> scaleTensorPtr(scale, aclDestroyTensor); std::unique_ptr<void, aclError (*)(void *)> scaleDeviceAddrPtr(scaleDeviceAddr, aclrtFree); CHECK_RET(ret == ACL_SUCCESS, return ret); // Create a bias aclTensor. ret = CreateAclTensor(biasHostData, biasShape, &biasDeviceAddr, aclDataType::ACL_FLOAT, &bias); std::unique_ptr<aclTensor, aclnnStatus (*)(const aclTensor *)> biasTensorPtr(bias, aclDestroyTensor); std::unique_ptr<void, aclError (*)(void *)> biasDeviceAddrPtr(biasDeviceAddr, aclrtFree); CHECK_RET(ret == ACL_SUCCESS, return ret); // Create an offset aclTensor. ret = CreateAclTensor(offsetHostData, offsetShape, &offsetDeviceAddr, aclDataType::ACL_FLOAT, &offset); std::unique_ptr<aclTensor, aclnnStatus (*)(const aclTensor *)> offsetTensorPtr(offset, aclDestroyTensor); std::unique_ptr<void, aclError (*)(void *)> offsetDeviceAddrPtr(offsetDeviceAddr, aclrtFree); CHECK_RET(ret == ACL_SUCCESS, return ret); // Create a pertokenScale aclTensor. ret = CreateAclTensor(pertokenScaleHostData, pertokenScaleShape, &pertokenScaleDeviceAddr, aclDataType::ACL_FLOAT, &pertokenScale); std::unique_ptr<aclTensor, aclnnStatus (*)(const aclTensor *)> pertokenScaleTensorPtr(pertokenScale, aclDestroyTensor); std::unique_ptr<void, aclError (*)(void *)> pertokenScaleDeviceAddrPtr(pertokenScaleDeviceAddr, aclrtFree); CHECK_RET(ret == ACL_SUCCESS, return ret); // Create groupList aclTensor. ret = CreateAclTensor(groupListHostData, groupListShape, &groupListDeviceAddr, aclDataType::ACL_INT64, &groupList); std::unique_ptr<aclTensor, aclnnStatus (*)(const aclTensor *)> groupListTensorPtr(groupList, aclDestroyTensor); std::unique_ptr<void, aclError (*)(void *)> groupListDeviceAddrPtr(groupListDeviceAddr, aclrtFree); CHECK_RET(ret == ACL_SUCCESS, return ret); // Create sharedInput aclTensor. ret = CreateAclTensor(sharedInputHostData, sharedInputShape, &sharedInputDeviceAddr, aclDataType::ACL_BF16, &sharedInput); std::unique_ptr<aclTensor, aclnnStatus (*)(const aclTensor *)> sharedInputTensorPtr(sharedInput, aclDestroyTensor); std::unique_ptr<void, aclError (*)(void *)> sharedInputDeviceAddrPtr(sharedInputDeviceAddr, aclrtFree); CHECK_RET(ret == ACL_SUCCESS, return ret); // Create logit aclTensor. ret = CreateAclTensor(logitHostData, logitShape, &logitDeviceAddr, aclDataType::ACL_FLOAT, &logit); std::unique_ptr<aclTensor, aclnnStatus (*)(const aclTensor *)> logitTensorPtr(logit, aclDestroyTensor); std::unique_ptr<void, aclError (*)(void *)> logitDeviceAddrPtr(logitDeviceAddr, aclrtFree); CHECK_RET(ret == ACL_SUCCESS, return ret); // Create rowIndex aclTensor. ret = CreateAclTensor(rowIndexHostData, rowIndexShape, &rowIndexDeviceAddr, aclDataType::ACL_INT64, &rowIndex); std::unique_ptr<aclTensor, aclnnStatus (*)(const aclTensor *)> rowIndexTensorPtr(rowIndex, aclDestroyTensor); std::unique_ptr<void, aclError (*)(void *)> rowIndexDeviceAddrPtr(rowIndexDeviceAddr, aclrtFree); CHECK_RET(ret == ACL_SUCCESS, return ret); // Create out aclTensor. ret = CreateAclTensor(outHostData, outShape, &outDeviceAddr, aclDataType::ACL_FLOAT, &out); std::unique_ptr<aclTensor, aclnnStatus (*)(const aclTensor *)> outTensorPtr(out, aclDestroyTensor); std::unique_ptr<void, aclError (*)(void *)> outDeviceAddrPtr(outDeviceAddr, aclrtFree); CHECK_RET(ret == ACL_SUCCESS, return ret); aclIntArray *tuningConfig = aclCreateIntArray(tuningConfigVal.data(), tuningConfigVal.size()); CHECK_RET(tuningConfig == nullptr, -1); // 3. Call the CANN operator library API. Replace this with the specific API name. uint64_t workspaceSize = 0; aclOpExecutor *executor; void *workspaceAddr = nullptr; // Call the first-phase API of aclnnGroupedMatmulFinalizeRoutingV3. workspaceSize = 0; ret = aclnnGroupedMatmulFinalizeRoutingV3GetWorkspaceSize(x, w, scale, bias, offset, nullptr, nullptr, pertokenScale, groupList, sharedInput, logit, rowIndex, dtype, shareInputWeight, sharedInputOffset, transposeX, transposeW, groupListType, tuningConfig, out, &workspaceSize, &executor); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnGroupedMatmulFinalizeRoutingV3GetWorkspaceSize failed. ERROR: %d\n", ret); return ret); // Apply for device memory based on the workspaceSize calculated by the first-phase API. if (workspaceSize > 0) { ret = aclrtMalloc(&workspaceAddr, workspaceSize, ACL_MEM_MALLOC_HUGE_FIRST); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("allocate workspace failed. ERROR: %d\n", ret); return ret); } // Call the second-phase API of aclnnGroupedMatmulFinalizeRoutingV3. ret = aclnnGroupedMatmulFinalizeRoutingV3(workspaceAddr, workspaceSize, executor, stream); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnGroupedMatmulFinalizeRoutingV3 failed. ERROR: %d\n", ret); return ret); // 4. (Boilerplate) Synchronize and wait for task execution to complete. ret = aclrtSynchronizeStream(stream); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtSynchronizeStream failed. ERROR: %d\n", ret); return ret); // 5. Obtain the output value and copy the result from the device memory to the host memory. Modify based on the specific API definition. auto size = GetShapeSize(outShape); std::vector<float> resultData(size, 0); ret = aclrtMemcpy(resultData.data(), resultData.size() * sizeof(resultData[0]), outDeviceAddr, size * sizeof(resultData[0]), ACL_MEMCPY_DEVICE_TO_HOST); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("copy result from device to host failed. ERROR: %d\n", ret); return ret); for (int64_t i = 0; i < size; i++) { LOG_PRINT("result[%lld] is: %f\n", i, resultData[i]); } // 6. Release aclTensor resources. Modify based on the specific API definition. aclDestroyTensor(x); aclDestroyTensor(w); aclDestroyTensor(scale); aclDestroyTensor(bias); aclDestroyTensor(offset); aclDestroyTensor(pertokenScale); aclDestroyTensor(groupList); aclDestroyTensor(sharedInput); aclDestroyTensor(logit); aclDestroyTensor(rowIndex); aclDestroyTensor(out); // 7. Release device resources. Modify based on the specific API definition. aclrtFree(xDeviceAddr); aclrtFree(wDeviceAddr); aclrtFree(scaleDeviceAddr); aclrtFree(biasDeviceAddr); aclrtFree(offsetDeviceAddr); aclrtFree(pertokenScaleDeviceAddr); aclrtFree(groupListDeviceAddr); aclrtFree(sharedInputDeviceAddr); aclrtFree(logitDeviceAddr); aclrtFree(rowIndexDeviceAddr); aclrtFree(outDeviceAddr); aclDestroyIntArray(tuningConfig); if (workspaceSize > 0) { aclrtFree(workspaceAddr); } aclrtDestroyStream(stream); aclrtResetDevice(deviceId); aclFinalize(); return 0; }Ascend 950PR/Ascend 950DT :#include <iostream> #include <memory> #include <vector> #include "acl/acl.h" #include "aclnnop/aclnn_grouped_matmul_finalize_routing_v3.h" #define CHECK_RET(cond, return_expr) \ do { \ if (!(cond)) { \ return_expr; \ } \ } while (0) #define CHECK_FREE_RET(cond, return_expr) \ do { \ if (!(cond)) { \ Finalize(deviceId, stream); \ return_expr; \ } \ } while (0) #define LOG_PRINT(message, ...) \ do { \ printf(message, ##__VA_ARGS__); \ } while (0) int64_t GetShapeSize(const std::vector<int64_t> &shape) { int64_t shapeSize = 1; for (auto i : shape) { shapeSize *= i; } return shapeSize; } int Init(int32_t deviceId, aclrtStream *stream) { // Boilerplate for resource initialization. auto ret = aclInit(nullptr); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclInit failed. ERROR: %d\n", ret); return ret); ret = aclrtSetDevice(deviceId); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtSetDevice failed. ERROR: %d\n", ret); return ret); ret = aclrtCreateStream(stream); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtCreateStream failed. ERROR: %d\n", ret); return ret); return ACL_SUCCESS; } template <typename T> aclnnStatus CreateAclTensor(const std::vector<T> &hostData, const std::vector<int64_t> &shape, void **deviceAddr, aclDataType dataType, aclTensor **tensor) { auto size = GetShapeSize(shape) * sizeof(T); // Call aclrtMalloc to allocate device memory. auto ret = aclrtMalloc(deviceAddr, size, ACL_MEM_MALLOC_HUGE_FIRST); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtMalloc failed. ERROR: %d\n", ret); return ret); // Call aclrtMemcpy to copy data from the host to the device memory. ret = aclrtMemcpy(*deviceAddr, size, hostData.data(), size, ACL_MEMCPY_HOST_TO_DEVICE); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtMemcpy failed. ERROR: %d\n", ret); return ret); // Calculate the strides of a contiguous tensor. std::vector<int64_t> strides(shape.size(), 1); for (int64_t i = shape.size() - 2; i >= 0; i--) { strides[i] = shape[i + 1] * strides[i + 1]; } // Call aclCreateTensor to create an aclTensor. *tensor = aclCreateTensor(shape.data(), shape.size(), dataType, strides.data(), 0, aclFormat::ACL_FORMAT_ND, shape.data(), shape.size(), *deviceAddr); return ACL_SUCCESS; } template <typename T> aclnnStatus CreateAclTensorWeight(const std::vector<T> &hostData, const std::vector<int64_t> &shape, void **deviceAddr, aclDataType dataType, aclTensor **tensor) { auto size = static_cast<uint64_t>(GetShapeSize(shape)); size *= sizeof(T); // Call aclrtMalloc to allocate device memory. auto ret = aclrtMalloc(deviceAddr, size, ACL_MEM_MALLOC_HUGE_FIRST); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtMalloc failed. ERROR: %d\n", ret); return ret); // Call aclrtMemcpy to copy host-side data to device-side memory. ret = aclrtMemcpy(*deviceAddr, size, hostData.data(), size, ACL_MEMCPY_HOST_TO_DEVICE); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtMemcpy failed. ERROR: %d\n", ret); return ret); // Calculate the strides of a contiguous tensor. std::vector<int64_t> strides(shape.size(), 1); for (int64_t i = shape.size() - 2; i >= 0; i--) { strides[i] = shape[i + 1] * strides[i + 1]; } std::vector<int64_t> storageShape; storageShape.push_back(GetShapeSize(shape)); // Call the aclCreateTensor API to create an aclTensor. *tensor = aclCreateTensor(shape.data(), shape.size(), dataType, strides.data(), 0, aclFormat::ACL_FORMAT_ND, storageShape.data(), storageShape.size(), *deviceAddr); return ACL_SUCCESS; } template <typename T1, typename T2> auto Ceil(T1 a, T2 b) -> T1 { if (b == 0) { return a; } return (a + b - 1) / b; } int main() { // 1. (Boilerplate) Initialize the device/stream. See the AscendCL external API list. int32_t deviceId = 0; aclrtStream stream; auto ret = Init(deviceId, &stream); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("Init stream failed. ERROR: %d\n", ret); return ret); // 2. Construct the inputs and outputs. Custom construction is required based on the API. int64_t m = 8; int64_t k = 2048; int64_t n = 7168; int64_t e = 1; int64_t g = 1; int64_t batch = 8; int64_t bsdp = 1; int64_t dtype = 0; float shareInputWeight = 1.0; int64_t sharedInputOffset = 0; bool transposeX = false; bool transposeW = false; int64_t groupListType = 1; std::vector<int64_t> xShape = {m, k}; std::vector<int64_t> wShape = {e, k, n}; std::vector<int64_t> scaleShape = {g, Ceil(k,64),n,2}; std::vector<int64_t> biasShape = {e, n}; std::vector<int64_t> offsetShape = {e, 1, n}; std::vector<int64_t> pertokenScaleShape = {m,Ceil(k,64),2}; std::vector<int64_t> groupListShape = {e}; std::vector<int64_t> sharedInputShape = {bsdp, n}; std::vector<int64_t> logitShape = {m}; std::vector<int64_t> rowIndexShape = {m}; std::vector<int64_t> outShape = {batch, n}; void *xDeviceAddr = nullptr; void *wDeviceAddr = nullptr; void *biasDeviceAddr = nullptr; void *scaleDeviceAddr = nullptr; void *offsetDeviceAddr = nullptr; void *pertokenScaleDeviceAddr = nullptr; void *groupListDeviceAddr = nullptr; void *sharedInputDeviceAddr = nullptr; void *logitDeviceAddr = nullptr; void *rowIndexDeviceAddr = nullptr; void *outDeviceAddr = nullptr; aclTensor* x = nullptr; aclTensor* w = nullptr; aclTensor* bias = nullptr; aclTensor* groupList = nullptr; aclTensor* scale = nullptr; aclTensor* offset = nullptr; aclTensor* pertokenScale = nullptr; aclTensor* sharedInput = nullptr; aclTensor* logit = nullptr; aclTensor* rowIndex = nullptr; aclTensor* out = nullptr; std::vector<int8_t> xHostData(GetShapeSize(xShape)); std::vector<int32_t> wHostData(GetShapeSize(wShape)); std::vector<int64_t> scaleHostData(GetShapeSize(scaleShape)); std::vector<float> biasHostData(GetShapeSize(biasShape)); std::vector<float> offsetHostData(GetShapeSize(offsetShape)); std::vector<float> pertokenScaleHostData(GetShapeSize(pertokenScaleShape)); std::vector<int64_t> groupListHostData(GetShapeSize(groupListShape)); std::vector<uint16_t> sharedInputHostData(GetShapeSize(sharedInputShape)); std::vector<int64_t> logitHostData(GetShapeSize(logitShape)); std::vector<float> rowIndexHostData(GetShapeSize(rowIndexShape)); std::vector<float> outHostData(GetShapeSize(outShape)); groupListHostData[0] = 8; // Create x aclTensor. ret = CreateAclTensor(xHostData, xShape, &xDeviceAddr, aclDataType::ACL_FLOAT8_E5M2, &x); std::unique_ptr<aclTensor, aclnnStatus (*)(const aclTensor *)> xTensorPtr(x, aclDestroyTensor); std::unique_ptr<void, aclError (*)(void *)> xDeviceAddrPtr(xDeviceAddr, aclrtFree); CHECK_RET(ret == ACL_SUCCESS, return ret); // Create w aclTensor. ret = CreateAclTensorWeight(wHostData, wShape, &wDeviceAddr, aclDataType::ACL_FLOAT8_E5M2, &w); std::unique_ptr<aclTensor, aclnnStatus (*)(const aclTensor *)> wTensorPtr(w, aclDestroyTensor); std::unique_ptr<void, aclError (*)(void *)> wDeviceAddrPtr(wDeviceAddr, aclrtFree); CHECK_RET(ret == ACL_SUCCESS, return ret); // Create scale aclTensor. ret = CreateAclTensor(scaleHostData, scaleShape, &scaleDeviceAddr, aclDataType::ACL_FLOAT8_E8M0, &scale); std::unique_ptr<aclTensor, aclnnStatus (*)(const aclTensor *)> scaleTensorPtr(scale, aclDestroyTensor); std::unique_ptr<void, aclError (*)(void *)> scaleDeviceAddrPtr(scaleDeviceAddr, aclrtFree); CHECK_RET(ret == ACL_SUCCESS, return ret); // Create bias aclTensor. ret = CreateAclTensor(biasHostData, biasShape, &biasDeviceAddr, aclDataType::ACL_BF16, &bias); std::unique_ptr<aclTensor, aclnnStatus (*)(const aclTensor *)> biasTensorPtr(bias, aclDestroyTensor); std::unique_ptr<void, aclError (*)(void *)> biasDeviceAddrPtr(biasDeviceAddr, aclrtFree); CHECK_RET(ret == ACL_SUCCESS, return ret); // Create offset aclTensor. ret = CreateAclTensor(offsetHostData, offsetShape, &offsetDeviceAddr, aclDataType::ACL_FLOAT, &offset); std::unique_ptr<aclTensor, aclnnStatus (*)(const aclTensor *)> offsetTensorPtr(offset, aclDestroyTensor); std::unique_ptr<void, aclError (*)(void *)> offsetDeviceAddrPtr(offsetDeviceAddr, aclrtFree); CHECK_RET(ret == ACL_SUCCESS, return ret); // Create the pertokenScale aclTensor. ret = CreateAclTensor(pertokenScaleHostData, pertokenScaleShape, &pertokenScaleDeviceAddr, ACL_FLOAT8_E8M0, &pertokenScale); std::unique_ptr<aclTensor, aclnnStatus (*)(const aclTensor *)> pertokenScaleTensorPtr(pertokenScale, aclDestroyTensor); std::unique_ptr<void, aclError (*)(void *)> pertokenScaleDeviceAddrPtr(pertokenScaleDeviceAddr, aclrtFree); CHECK_RET(ret == ACL_SUCCESS, return ret); // Create the groupList aclTensor. ret = CreateAclTensor(groupListHostData, groupListShape, &groupListDeviceAddr, aclDataType::ACL_INT64, &groupList); std::unique_ptr<aclTensor, aclnnStatus (*)(const aclTensor *)> groupListTensorPtr(groupList, aclDestroyTensor); std::unique_ptr<void, aclError (*)(void *)> groupListDeviceAddrPtr(groupListDeviceAddr, aclrtFree); CHECK_RET(ret == ACL_SUCCESS, return ret); // Create the sharedInput aclTensor. ret = CreateAclTensor(sharedInputHostData, sharedInputShape, &sharedInputDeviceAddr, aclDataType::ACL_BF16, &sharedInput); std::unique_ptr<aclTensor, aclnnStatus (*)(const aclTensor *)> sharedInputTensorPtr(sharedInput, aclDestroyTensor); std::unique_ptr<void, aclError (*)(void *)> sharedInputDeviceAddrPtr(sharedInputDeviceAddr, aclrtFree); CHECK_RET(ret == ACL_SUCCESS, return ret); // Create the logit aclTensor. ret = CreateAclTensor(logitHostData, logitShape, &logitDeviceAddr, aclDataType::ACL_FLOAT, &logit); std::unique_ptr<aclTensor, aclnnStatus (*)(const aclTensor *)> logitTensorPtr(logit, aclDestroyTensor); std::unique_ptr<void, aclError (*)(void *)> logitDeviceAddrPtr(logitDeviceAddr, aclrtFree); CHECK_RET(ret == ACL_SUCCESS, return ret); // Create the rowIndex aclTensor. ret = CreateAclTensor(rowIndexHostData, rowIndexShape, &rowIndexDeviceAddr, aclDataType::ACL_INT64, &rowIndex); std::unique_ptr<aclTensor, aclnnStatus (*)(const aclTensor *)> rowIndexTensorPtr(rowIndex, aclDestroyTensor); std::unique_ptr<void, aclError (*)(void *)> rowIndexDeviceAddrPtr(rowIndexDeviceAddr, aclrtFree); CHECK_RET(ret == ACL_SUCCESS, return ret); // Create the out aclTensor. ret = CreateAclTensor(outHostData, outShape, &outDeviceAddr, aclDataType::ACL_FLOAT, &out); std::unique_ptr<aclTensor, aclnnStatus (*)(const aclTensor *)> outTensorPtr(out, aclDestroyTensor); std::unique_ptr<void, aclError (*)(void *)> outDeviceAddrPtr(outDeviceAddr, aclrtFree); CHECK_RET(ret == ACL_SUCCESS, return ret); // 3. Call the CANN operator library API. Replace with the specific API name. uint64_t workspaceSize = 0; aclOpExecutor *executor; void *workspaceAddr = nullptr; // Call the first-phase API of aclnnGroupedMatmulFinalizeRoutingV3. ret = aclnnGroupedMatmulFinalizeRoutingV3GetWorkspaceSize(x, w, scale, bias, nullptr, nullptr, nullptr, pertokenScale, groupList, sharedInput, logit, rowIndex, dtype, shareInputWeight, sharedInputOffset, transposeX, transposeW, groupListType, nullptr, out, &workspaceSize, &executor); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnGroupedMatmulFinalizeRoutingV3GetWorkspaceSize failed. ERROR: %d\n", ret); return ret); // Apply for device memory based on the workspaceSize calculated by the first-phase API. if (workspaceSize > 0) { ret = aclrtMalloc(&workspaceAddr, workspaceSize, ACL_MEM_MALLOC_HUGE_FIRST); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("allocate workspace failed. ERROR: %d\n", ret); return ret); } // Call the second-phase API of aclnnGroupedMatmulFinalizeRoutingV3. ret = aclnnGroupedMatmulFinalizeRoutingV3(workspaceAddr, workspaceSize, executor, stream); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnGroupedMatmulFinalizeRoutingV3 failed. ERROR: %d\n", ret); return ret); // 4. (Boilerplate) Synchronously wait for task execution to complete. ret = aclrtSynchronizeStream(stream); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtSynchronizeStream failed. ERROR: %d\n", ret); return ret); // 5. Obtain the output value and copy the result from the device-side memory to the host side. Modify based on the specific API definition. auto size = GetShapeSize(outShape); std::vector<float> resultData(size, 0); ret = aclrtMemcpy(resultData.data(), resultData.size() * sizeof(resultData[0]), outDeviceAddr, size * sizeof(resultData[0]), ACL_MEMCPY_DEVICE_TO_HOST); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("copy result from device to host failed. ERROR: %d\n", ret); return ret); for (int64_t i = 0; i < size; i++) { LOG_PRINT("result[%lld] is: %f\n", i, resultData[i]); } // 6. Release aclTensor and aclTensor. Modify based on the specific API definition. aclDestroyTensor(x); aclDestroyTensor(w); aclDestroyTensor(scale); aclDestroyTensor(bias); aclDestroyTensor(offset); aclDestroyTensor(pertokenScale); aclDestroyTensor(groupList); aclDestroyTensor(sharedInput); aclDestroyTensor(logit); aclDestroyTensor(rowIndex); aclDestroyTensor(out); // 7. Release device resources. Modify based on the specific API definition. aclrtFree(xDeviceAddr); aclrtFree(wDeviceAddr); aclrtFree(scaleDeviceAddr); aclrtFree(biasDeviceAddr); aclrtFree(offsetDeviceAddr); aclrtFree(pertokenScaleDeviceAddr); aclrtFree(groupListDeviceAddr); aclrtFree(sharedInputDeviceAddr); aclrtFree(logitDeviceAddr); aclrtFree(rowIndexDeviceAddr); aclrtFree(outDeviceAddr); if (workspaceSize > 0) { aclrtFree(workspaceAddr); } aclrtDestroyStream(stream); aclrtResetDevice(deviceId); aclFinalize(); return 0; }