aclnnGroupedMatmulFinalizeRoutingWeightNzV2
Applicable Products
| Product | Supported |
|---|---|
| √ | |
| √ | |
| √ | |
| × | |
| × | |
| × |
Function
A fused operator of GroupedMatmul and MoeFinalizeRouting. The output computed by GroupedMatmul is combined by index. Supports w in the AI processor-friendly data layout (NZ).
Compared with aclnnGroupedMatmulFinalizeRoutingWeightNz, this API adds the following:
Added input parameters offsetOptional, antiQuantScaleOptional, antiQuantOffsetOptional, and tuningConfigOptional. The first three parameters are currently reserved and not effective; pass null pointers for them.
Atlas A2 training products/Atlas A2 inference products ,Atlas A3 training products/Atlas A3 inference products : Added support for INT4 weight matrices and the tuningConfigOptional tuning parameter. The first value in the array indicates the expected number of tokens processed by each expert. During operator tiling, the tiling split is performed reasonably based on this expected value, achieving better performance. Select the appropriate API based on the actual situation.Ascend 950PR/Ascend 950DT : Added pertoken-perchannel and static pertensor-perchannel quantization scenarios. For related information, see Quantization Introduction.
Function Prototype
Each operator uses a two-phase API. You must first call the "aclnnGroupedMatmulFinalizeRoutingWeightNzV2GetWorkspaceSize" API to obtain the workspace size required for computation and the executor that contains the operator computation process, and then call the "aclnnGroupedMatmulFinalizeRoutingWeightNzV2" API to perform the computation.
aclnnStatus aclnnGroupedMatmulFinalizeRoutingWeightNzV2GetWorkspaceSize(
const aclTensor *x1,
const aclTensor *x2,
const aclTensor *scale,
const aclTensor *bias,
const aclTensor *offsetOptional,
const aclTensor *antiquantScaleOptional,
const aclTensor *antiquantOffsetOptional,
const aclTensor *pertokenScaleOptional,
const aclTensor *groupList,
const aclTensor *sharedInput,
const aclTensor *logit,
const aclTensor *rowIndex,
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 aclnnGroupedMatmulFinalizeRoutingWeightNzV2(
void *workspace,
uint64_t workspaceSize,
aclOpExecutor *executor,
aclrtStream stream)aclnnGroupedMatmulFinalizeRoutingWeightNzV2GetWorkspaceSize
Parameters
Parameter Input/Output Description Instruction Data Type Data Format Dimension (Shape) Non-contiguous Tensor x1 Input Input x (left matrix). - INT8, FLOAT8_E4M3FN, HIFLOAT8 ND (m, k) - x2 Input Input weight (right matrix). - INT4, INT8, FLOAT8_E4M3FN, HIFLOAT8 NZ Supports three dimensions. - scale Input The scale factor in quantization parameters, a per-channel quantization parameter. - INT64, FLOAT32, BF16 ND The shape supports three dimensions: (e, 1, n), where e and n are consistent with those of x2. - bias Input Offset of the matrix. - BF16, FLOAT32 ND Supports two-dimensional shape with dimensions (e, n), where e and n are consistent with those of x2. - 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. Not enabled yet. FLOAT32 ND - - pertokenScaleOptional Input Dequantization parameter for matrix computation. - FLOAT32 ND Supports one-dimensional shape with dimension (m), where m is consistent with m of x1. - groupList Input Distribution of matmul sizes along the grouped axis for inputs and outputs. - INT64 ND Supports one-dimensional shape with dimension (e), where e is consistent with e of x2. - sharedInput Input Output of the shared expert in MoE computation, which needs to be combined with the output of the MoE expert. - BF16 ND Supports two dimensions with the shape (bsdp, n). bsdp must be less than or equal to batchSize/e, and n must be consistent with n of x2. - logit Input Logits of MoE experts for each token. - FLOAT32 ND shape supports one dimension with size (m), where m is consistent with m of x1. - rowIndex Input The MoE expert outputs are combined according to this rowIndex, where each value serves as the index for scatter-add during combination. - INT64, INT32 ND Shape supports one-dimensional, with dimension (m), where m is consistent with m of x1. - dtype Input Output type for computation: 0: FLOAT32; 1: FLOAT16; 2: BFLOAT16. Currently only 0 is supported. - INT64 - - - sharedInputWeight Input Coefficient for combining the shared expert with the MoE expert. sharedInput is first multiplied by this parameter, then added to the MoE expert result. - FLOAT32 - - - sharedInputOffset Input Offset of the shared expert output in the total output. - INT64 - - - transposeX1 Input Specifies whether to transpose the left matrix. Only false is supported. - BOOL - - - transposeX2 Input. Whether the right matrix is transposed. Only false is supported. - BOOL - - - groupListType Input Grouping mode: 0 for cumsum mode (prefix sum), and 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 to perform reasonable tiling splitting for better performance. If the second element in the array is set to 1, the operator attempts to use a more suitable algorithm based on the actual input during tiling. When k <= 2048, performance may be better. Elements starting from the third are reserved and do not need to be filled in by the user. They will be extended in the future. For backward compatibility, if the user does not use this parameter, simply do not pass it in (i.e., pass nullptr). - INT64 - - - out Output Output result. Has the same shape as self. FLOAT32 ND (batch, n) - workspaceSize Output Returns the workspace size to be applied for on the Device side. - - - - - executor Output Returns the operator 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. The value range of dimension m is [1, 16*1024*8], and k supports 2048.
x2 supports INT4 and INT32. When the input is INT32, the dimension is (e, k, n / 8); when the input is converted to INT4, the dimension is (e, k, n). The value range of e is [1, 256], k supports 2048, and n supports 7168.
offsetOptional supports a three-dimensional shape with dimensions (e, 1, n), where e and n are consistent with those of weight.
scaleOptional supports INT64, FLOAT32, and BF16.
rowIndex supports INT64 and INT32.
x1, x2, and groupListOptional are required parameters. scaleOptional, pertokenScaleOptional, logitOptional, rowIndexOptional, biasOptional, and sharedInputOptional are optional parameters.
Ascend 950PR/Ascend 950DT :x1 supports INT8, FLOAT8_E4M3FN, and HIFLOAT8 data types.
x2 supports INT8, FLOAT8_E4M3FN, and HIFLOAT8 data types. Its dimensions are (e, k, n), with e in the value range [1, 1024].
scaleOptional supports FLOAT32 and BF16.
When the data type of x1 and x2 is INT8, rowIndex supports INT64 and INT32. When the data type of x1 and x2 is FLOAT8_E4M3FN or HIFLOAT8, rowIndex supports INT64.
x1, x2, scaleOptional, groupListOptional, logitOptional, and rowIndexOptional are mandatory parameters. pertokenScaleOptional, sharedInputOptional, and biasOptional are optional parameters. The offsetOptional parameter is currently not supported.
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, scale, bias, offsetOptional, antiQuantScaleOptional, antiQuantOffsetOptional, perTokenScaleOptional, groupList, sharedInputOptional, logit, rowIndex, sharedInputWeight, sharedInputOffset, transposeX, transposeX2, or out is not within the supported range. The shape of x1, x2, scale, bias, offsetOptional, antiQuantScaleOptional, antiQuantOffsetOptional, perTokenScaleOptional, groupList, sharedInputOptional, logit, rowIndex, or out does not meet the validation conditions. The shape of x1, x2, scale, bias, offsetOptional, antiQuantScaleOptional, antiQuantOffsetOptional, perTokenScaleOptional, groupList, sharedInputOptional, logit, rowIndex, or out is an empty tensor.
aclnnGroupedMatmulFinalizeRoutingWeightNzV2
Parameters
Parameter Input/Output Description workspace Input Specifies the workspace memory address applied for on the Device side. workspaceSize Input Specifies the workspace size applied for on the Device side. Obtained from the first-phase API aclnnGroupedMatmulFinalizeRoutingWeightNzV2GetWorkspaceSize. 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 : aclnnGroupedMatmulFinalizeRoutingWeightNzV2 defaults to a non-deterministic implementation and supports enabling deterministic computation through aclrtCtxSetSysParamOpt.Ascend 950PR/Ascend 950DT : aclnnGroupedMatmulFinalizeRoutingWeightNzV2 defaults to a non-deterministic implementation and does not support enabling deterministic computation through aclrtCtxSetSysParamOpt.
Atlas A2 training products/Atlas A2 inference products : The following data type combinations are supported for inputs and outputs.x1 x2 scale bias offsetOptional antiquantScaleOptional antiquantOffsetOptional pertokenScaleOptional groupList sharedInput logit rowIndex out tuningConfigOptional INT8 INT8 FLOAT32 null null null null FLOAT32 INT64 BFLOAT16 FLOAT32 INT64 FLOAT IntArray INT8 INT8 FLOAT32 null null null null FLOAT32 INT64 BFLOAT16 FLOAT32 INT64 FLOAT IntArray INT8 INT4 INT64 FLOAT32 FLOAT32 null null FLOAT32 INT64 BFLOAT16 FLOAT32 INT64 FLOAT IntArray INT8 INT4 INT64 FLOAT32 null null null FLOAT32 INT64 BFLOAT16 FLOAT32 INT64 FLOAT IntArray Ascend 950PR/Ascend 950DT : The input and output support the following data type combinations:x1 x2 scale bias offsetOptional antiquantScaleOptional antiquantOffsetOptional pertokenScaleOptional groupList sharedInput logit rowIndex out tuningConfigOptional INT8 INT8 FLOAT/BFLOAT16 BFLOAT16/null null null null FLOAT/null INT64 BFLOAT16 FLOAT INT64/INT32 FLOAT null FLOAT8_E4M3FN FLOAT8_E4M3FN FLOAT/BFLOAT16 BFLOAT16/null null null null FLOAT/null INT64 BFLOAT16 FLOAT INT64 FLOAT null HIFLOAT8 HIFLOAT8 FLOAT/BFLOAT16 BFLOAT16/null null null null FLOAT/null INT64 BFLOAT16 FLOAT INT64 FLOAT null
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_weight_nz_v2.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. Initialize resources. 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 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); // Copy host-side data to the device memory using 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 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 the aclCreateTensor API to create an aclTensor. *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 memory using 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 the device memory using 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 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 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 inputs and outputs. Custom construction is required based on the API. int64_t m = 192; int64_t k = 2048; int64_t n = 7168; int64_t e = 4; int64_t batch = 24; int64_t bsdp = 8; 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 = {e, 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 *scaleDeviceAddr = nullptr; void *pertokenScaleDeviceAddr = nullptr; void *groupListDeviceAddr = nullptr; void *sharedInputDeviceAddr = nullptr; void *logitDeviceAddr = nullptr; void *rowIndexDeviceAddr = nullptr; void *outDeviceAddr = nullptr; void *tuningConfigDeviceAddr = nullptr; aclTensor* x = nullptr; aclTensor* w = nullptr; aclTensor* bias = nullptr; aclTensor* groupList = nullptr; aclTensor* scale = 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<int8_t> wHostData(GetShapeSize(wShape)); std::vector<float> scaleHostData(GetShapeSize(scaleShape)); std::vector<float> pertokenScaleHostData(GetShapeSize(pertokenScaleShape)); std::vector<int64_t> groupListHostData(GetShapeSize(groupListShape)); groupListHostData[0] = 7; groupListHostData[1] = 32; groupListHostData[2] = 40; groupListHostData[3] = 64; 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)); // 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 the w aclTensor in AI processor-friendly data layout format. ret = CreateAclTensorWeight(wHostData, wShape, &wDeviceAddr, aclDataType::ACL_INT8, &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 the scale aclTensor. ret = CreateAclTensor(scaleHostData, scaleShape, &scaleDeviceAddr, aclDataType::ACL_FLOAT, &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 the 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 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); // Create the tuningConfig aclIntArray. aclIntArray *tuningConfig = aclCreateIntArray(tuningConfigVal.data(), tuningConfigVal.size()); std::unique_ptr<aclIntArray, aclnnStatus (*)(const aclIntArray *)> tuningConfigIntArrayPtr(tuningConfig, aclDestroyIntArray); std::unique_ptr<void, aclError (*)(void *)> tuningConfigDeviceAddrPtr(tuningConfigDeviceAddr, aclrtFree); CHECK_RET(tuningConfig != nullptr, -1); // 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 aclnnTransMatmulWeight. ret = aclnnTransMatmulWeightGetWorkspaceSize(w, &workspaceSize, &executor); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnTransMatmulWeightGetWorkspaceSize failed. ERROR: %d\n", ret); return ret); // Allocate 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 aclnnTransMatmulWeight. ret = aclnnTransMatmulWeight(workspaceAddr, workspaceSize, executor, stream); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnTransMatmulWeight failed. ERROR: %d\n", ret); return ret); // Call the first-phase API of aclnnGroupedMatmulFinalizeRoutingWeightNzV2. workspaceSize = 0; ret = aclnnGroupedMatmulFinalizeRoutingWeightNzV2GetWorkspaceSize(x, w, scale, nullptr, nullptr, nullptr, nullptr, pertokenScale, groupList, sharedInput, logit, rowIndex, dtype, shareInputWeight, sharedInputOffset, transposeX, transposeW, groupListType, tuningConfig, out, &workspaceSize, &executor); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnGroupedMatmulFinalizeRoutingWeightNzV2GetWorkspaceSize failed. ERROR: %d\n", ret); return ret); // Allocate 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 aclnnGroupedMatmulFinalizeRoutingWeightNzV2. ret = aclnnGroupedMatmulFinalizeRoutingWeightNzV2(workspaceAddr, workspaceSize, executor, stream); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnGroupedMatmulFinalizeRoutingWeightNzV2 failed. ERROR: %d\n", ret); return ret); // 4. (Boilerplate) Synchronously wait for the task execution to finish. 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. 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 the aclTensor resource. Modify based on the specific API definition. aclDestroyTensor(x); aclDestroyTensor(w); aclDestroyTensor(scale); aclDestroyTensor(pertokenScale); aclDestroyTensor(groupList); aclDestroyTensor(sharedInput); aclDestroyTensor(logit); aclDestroyTensor(rowIndex); aclDestroyTensor(out); // 7. Release the device resource. Modify based on the specific API definition. aclrtFree(xDeviceAddr); aclrtFree(wDeviceAddr); aclrtFree(scaleDeviceAddr); 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_permute.h" #include "aclnnop/aclnn_grouped_matmul_finalize_routing_weight_nz_v2.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. Initialize resources. 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); // 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]; } // 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 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); // Call aclrtMalloc to allocate memory on the device side. 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 side 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]; } 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 inputs and outputs. Custom construction is required based on the API. int64_t m = 192; int64_t k = 2048; int64_t n = 7168; int64_t e = 4; int64_t batch = 24; int64_t bsdp = 8; 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 = {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}; void *xDeviceAddr = nullptr; void *wDeviceAddr = nullptr; void *scaleDeviceAddr = 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 *pertokenScale = nullptr; aclTensor *sharedInput = nullptr; aclTensor *logit = nullptr; aclTensor *rowIndex = nullptr; aclTensor *out = nullptr; std::vector<int8_t> xHostData(GetShapeSize(xShape)); std::vector<int8_t> wHostData(GetShapeSize(wShape)); std::vector<float> scaleHostData(GetShapeSize(scaleShape)); std::vector<float> pertokenScaleHostData(GetShapeSize(pertokenScaleShape)); std::vector<int64_t> groupListHostData(GetShapeSize(groupListShape)); groupListHostData[0] = 7; groupListHostData[1] = 32; groupListHostData[2] = 40; groupListHostData[3] = 64; 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)); // Create 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 w aclTensor in AI processor-friendly data layout format. ret = CreateAclTensorWeight(wHostData, wShape, &wDeviceAddr, aclDataType::ACL_INT8, &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_FLOAT, &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 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 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 aclnnTransMatmulWeight. ret = aclnnTransMatmulWeightGetWorkspaceSize(w, &workspaceSize, &executor); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnTransMatmulWeightGetWorkspaceSize 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 aclnnTransMatmulWeight. ret = aclnnTransMatmulWeight(workspaceAddr, workspaceSize, executor, stream); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnTransMatmulWeight failed. ERROR: %d\n", ret); return ret); // Call the first-phase API of aclnnGroupedMatmulFinalizeRoutingWeightNzV2. workspaceSize = 0; ret = aclnnGroupedMatmulFinalizeRoutingWeightNzV2GetWorkspaceSize( x, w, scale, nullptr, 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("aclnnGroupedMatmulFinalizeRoutingWeightNzV2GetWorkspaceSize 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 aclnnGroupedMatmulFinalizeRoutingWeightNzV2. ret = aclnnGroupedMatmulFinalizeRoutingWeightNzV2(workspaceAddr, workspaceSize, executor, stream); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnGroupedMatmulFinalizeRoutingWeightNzV2 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 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(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(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; }