aclnnQuantMatmulV5
Applicable Products
| Product | Supported |
|---|---|
| √ | |
| √ | |
| √ | |
| × | |
| × | |
| × |
Function
Description: Performs quantized matrix multiplication computation.
Atlas A2 training products/Atlas A2 inference products ,Atlas A3 training products/Atlas A3 inference products :Compatible with the API functions of aclnnQuantMatmulV3 and aclnnQuantMatmulV4. Performs quantized matrix multiplication computation. The minimum supported input dimension is 1, and the maximum supported input dimension is 2. A similar API is aclnnMm (matrix multiplication that only supports 2D tensors as input).
Ascend 950PR/Ascend 950DT :Compatible with the aclnnQuantMatmulV3 and aclnnQuantMatmulV4 APIs, and additionally supports features such as G-B, B-B, T-CG, and mxquantization mode, with newly added support for x1 and x2 inputs with dtype FLOAT8_E4M3FN, FLOAT8_E5M2, HIFLOAT8, and FLOAT4_E2M1. Performs quantized matrix multiplication, with a minimum input dimension of 2 and a maximum input dimension of 6. Similar APIs include aclnnMm (which only supports 2D tensors as input for matrix multiplication) and aclnnBatchMatMul (which only supports 3D matrix multiplication, where the first dimension is the batch dimension).
Formula:
Atlas A2 training products/Atlas A2 inference products ,Atlas A3 training products/Atlas A3 inference products :Supports K-C && K-T, T-C && T-T, G-B, and K-G quantization modes. For the input/output data type combinations corresponding to different quantization modes, see Constraints.
K-G Quantization Mode
x1 is INT8, x2 is INT32, x1Scale is FLOAT32, x2Scale is UINT64/INT64, and yOffset is FLOAT32:
x1 and x2 are INT4, x1Scale and x2Scale are FLOAT32, x2Offset is FLOAT16, and out is FLOAT16/BFLOAT16 (pertoken-pergroup asymmetric quantization):
K-C && K-T Quantization Mode
x1Scale without bias:
x1Scale, bias INT32 (no offset in this scenario):
x1Scale, bias BFLOAT16/FLOAT16/FLOAT32 (no offset in this scenario):
T-C && T-T Quantization Mode
Without x1Scale and without bias:
bias INT32:
bias BFLOAT16/FLOAT32 (no offset in this scenario):
G-B quantization mode
- x1 and x2 are INT8, x1Scale and x2Scale are FLOAT32, bias is FLOAT32, and out is FLOAT16/BFLOAT16 (pergroup-perblock quantization):
Atlas inference products :Supports K-C quantization mode. For the input/output data type combinations corresponding to different quantization modes, see Constraints.
K-C Quantization Mode
x1Scale without bias:
x1Scale, bias INT32 (no offset in this scenario):
Ascend 950PR/Ascend 950DT :Supports T-C && T-T, K-C && K-T, G-B, B-B, mx, and T-CG quantization modes. For the input/output data type combinations corresponding to different quantization modes, see Constraints.
T-C && T-T Quant Mode
x1 and x2 are INT8, no x1Scale, x2Scale is INT64/UINT64, optional parameter x2Offset is FLOAT32, and optional parameter bias is INT32:
x1 and x2 are INT8, without x1Scale, x2Scale is INT64/UINT64, and the optional parameter bias is INT32;
Or x1 and x2 are FLOAT8_E4M3FN/FLOAT8_E5M2/HIFLOAT8, without x1Scale, x2Scale is INT64/UINT64, and the optional parameter bias is FLOAT32:
x1 and x2 are INT8, without x1Scale, x2Scale is BFLOAT16/FLOAT32, and the optional parameter bias is BFLOAT16/FLOAT32:
x1 and x2 are FLOAT8_E4M3FN/FLOAT8_E5M2/HIFLOAT8, x1Scale is FLOAT32, x2Scale is FLOAT32, and the optional parameter bias is FLOAT32:
K-C && K-T Quantization Mode
- x1 and x2 are INT8, x1Scale is FLOAT32, x2Scale is BFLOAT16/FLOAT32, and the optional parameter bias is INT32;
Or x1 and x2 are FLOAT8_E4M3FN/FLOAT8_E5M2/HIFLOAT8, x1Scale is FLOAT32, x2Scale is FLOAT32, and the optional parameter bias is FLOAT32:
- x1 and x2 are INT8, x1Scale is FLOAT32, x2Scale is BFLOAT16/FLOAT32, and the optional parameter bias is BFLOAT16/FLOAT32;
or x1 and x2 are INT8, x1Scale is FLOAT32, x2Scale is FLOAT32, and the optional parameter bias is FLOAT16/FLOAT32:
G-B && B-B && mx quantization mode
Where gsM, gsN, and gsK represent groupSizeM, groupSizeN, and groupSizeK, respectively; x1Slice represents the vector of length groupSizeK in the m-th row of x1, and x2Slice represents the vector of length groupSizeK in the n-th column of x2. The K-axis slices start from j*groupSizeK, with j in the range [0, kLoops), where kLoops = ceil(K / groupSizeK) and K is the K-axis length. The last slice is allowed to be shorter than groupSizeK. The bias is included only in the mx quantization mode. For the G-B, B-B, and mx quantization modes, the supported [groupSizeM, groupSizeN, groupSizeK] value combinations are [1, 128, 128], [128, 128, 128], and [1, 1, 32], respectively.
T-CG Quantization Mode
Function Prototype
Each operator is divided into a two-phase API. The "aclnnQuantMatmulV5GetWorkspaceSize" API must be called first to obtain the workspace size required for computation and the executor that encapsulates the operator computation flow. Then, the "aclnnQuantMatmulV5" API is called to perform the computation.
aclnnStatus aclnnQuantMatmulV5GetWorkspaceSize(
const aclTensor *x1,
const aclTensor *x2,
const aclTensor *x1Scale,
const aclTensor *x2Scale,
const aclTensor *yScale,
const aclTensor *x1Offset,
const aclTensor *x2Offset,
const aclTensor *yOffset,
const aclTensor *bias,
bool transposeX1,
bool transposeX2,
int64_t groupSize,
aclTensor *out,
uint64_t *workspaceSize,
aclOpExecutor **executor)aclnnStatus aclnnQuantMatmulV5(
void *workspace,
uint64_t workspaceSize,
aclOpExecutor *executor,
aclrtStream stream)aclnnQuantMatmulV5GetWorkspaceSize
Parameters
Parameter Input/Output Description Instruction Data Type Data Format Dimension (Shape) Non-contiguous Tensor x1 (aclTensor*) Input Input x1 in the formula. INT41, INT8, INT321, FLOAT8_E4M3FN2, FLOAT8_E5M22, HIFLOAT82, FLOAT4_E2M12 ND 2-6 √ x2(aclTensor*) Input Input x2 in the formula. INT41, INT8, INT321, FLOAT8_E4M3FN2, FLOAT8_E5M22, HIFLOAT82, FLOAT4_E2M12 ND, NZ 2-8 √ x1Scale (aclTensor*) Optional input Input x1Scale in the formula. - Null tensors are not supported.
FLOAT32, FLOAT8_E8M02, FLOAT8_E4M3FN2, FLOAT8_E5M22, HIFLOAT82 ND 1-6 √ x2Scale(aclTensor*) Input Represents the quantization parameter, which is the input x2Scale in the formula. - Empty tensors are not supported.
UINT64, INT64, FLOAT32, BFLOAT16, FLOAT8_E8M02 ND 1-6 √ yScale (aclTensor*) Optional input Dequantization scale parameter for output y. - Null tensors are not supported.
- If this parameter is not used, pass nullptr.
UINT642, INT642 ND 2 × x1Offset (aclTensor*) Optional input Input x1Offset in the formula. - Reserved parameter. Not supported in the current version. Pass nullptr.
- - - - x2Offset(aclTensor*) Optional input Input x2Offset in the formula. - Null tensors are not supported.
FLOAT16, FLOAT32 ND 1-2 - yOffset (aclTensor*) Optional input Input yOffset in the formula. - FLOAT32 ND 1 - bias(aclTensor*) Optional input Input bias in the formula. - Null tensors are not supported.
INT32, FLOAT32, BFLOAT16, FLOAT16 ND 1-3 × transposeX1(bool) Input Indicates whether to transpose the input shape of x1. - BOOL - - - transposeX2(bool) Input Specifies whether to transpose the input shape of x2. - BOOL - - - groupSize(int64_t) Optional Input Specifies the quantization group size in the m, n, and k directions. Consists of three values concatenated from three directions: groupSizeM, groupSizeN, and groupSizeK. Each value occupies 16 bits, occupying the lower 48 bits of the int64_t type groupSize (the upper 16 bits of groupSize are invalid). See the formula below the table. INT64 - - - out (aclTensor) Output Output out in the formula. - Null tensors are not supported.
FLOAT16, INT8, BFLOAT16, INT32, FLOAT32 ND 2 - workspaceSize(uint64_t) Output Returns the workspace size to be applied for on the Device side. - - - - - executor(aclOpExecutor) Output Returns the op executor, which contains the operator computation flow. - - - - - Note: Optional inputs refer to optional quantization parameters, which support passing nullptr.
Ascend 950PR/Ascend 950DT :The superscript "1" in the data type column of the table above indicates a data type not supported by this series.
The input parameters x1 and x2 do not support INT4 or INT32 types.
When x2 is in ND format, if the input parameter x1 is an empty tensor with m=0 or x2 is an empty tensor with n=0, the output is an empty tensor. When x2 is in FRACTAL_NZ format, if the input parameter x1 is an empty tensor with m=0, the output is an empty tensor.
Atlas A2 training products/Atlas A2 inference products ,Atlas A3 training products/Atlas A3 inference products :- The superscript "2" in the data type column of the table above indicates a data type not supported by this series.
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 passed x1, x2, x1Scale, x2Scale, yOffset, or out is a null pointer. ACLNN_ERR_PARAM_INVALID 161002 x1, x2, bias, x2Scale, x2Offset, or out is a null tensor. The data type or data format of x1, x2, bias, x1Scale, x2Scale, x2Offset, or out is not within the supported range. The shape of x1, x2, bias, x1Scale, x2Scale, x2Offset, or out does not meet the validation conditions. The passed groupSize does not meet the validation conditions, or when the passed groupSize is 0, the groupSize cannot be inferred from the shape relationship between x1, x2 and x1Scale, x2Scale.
aclnnQuantMatmulV5
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 through the first-phase API aclnnQuantMatmulV5GetWorkspaceSize. 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:
- aclnnQuantMatmulV5 defaults to a deterministic implementation.
Atlas inference products
General Constraints:
- In the current version, yScale, x1Offset, x2Offset, and yOffset are not supported. Pass nullptr for these parameters.
K-C Quantization Scenario Constraints:
- The input and output support the following data type combinations:
x1 x2 x1Scale x2Scale x1Offset x2Offset yScale yOffset bias out INT8 INT8 FLOAT32 FLOAT32 null null null null null/INT32 FLOAT16 Constraints on x1:
- The last dimension of x1 cannot exceed 65535, and transposeX1 supports only false.
Constraints on x2:
The last dimension size of x2 cannot exceed 65535, and transposeX2 only supports true.
The dimensions are: (batch, k1, n1, n0, k0), where batch may be absent, k0 = 32, n0 = 16. The k in x1 shape and k1 in x2 shape must satisfy the following relationship: ceil(k / 32) = k1.
x2 needs to be processed by aclnnTransMatmulWeight on the ND-format x2 to obtain the processor affinity data layout format.
x1Scale constraint: The data format supports ND. The shape is 1D (t,), where t = m, and m is consistent with the m of x1.
x2Scale constraint: The data format supports ND. The shape is 1D (t,), where t = n, and n is consistent with the n of x2.
Bias constraint: The data format supports ND, and the shape supports 1D (n,) or 3D (batch, 1, n), where n is consistent with n of x2.
Atlas A2 training products/Atlas A2 inference products , Atlas A3 training products/Atlas A3 inference products
General constraint:
When transposeX1 is false, the dimensions of X1 represent: (batch, m, k). When transposeX1 is true, the dimensions of X1 represent: (batch, k, m). The batch dimension may be absent.
The constraints on transposeX2 are as follows:
In ND format, when transposeX2 is false, the dimensions of X2 are: (batch, k, n). When transposeX2 is true, the dimensions are: (batch, n, k). The batch dimension may be absent. The k dimension must be consistent with the k dimension in the shape of X1.
In NZ format:
When set to true, the X2 dimensions are: (batch, k1, n1, n0, k0), where batch may be absent, k0 = 32, n0 = 16. The k in x1 shape and k1 in x2 shape must satisfy the following relationship: ceil(k / 32) = k1.
When set to false, the X2 dimensions are: (batch, n1, k1, k0, n0), where batch may be absent, k0 = 16, n0 = 32. The k in x1 shape and k1 in x2 shape must satisfy the following relationship: ceil(k / 16) = k1.
The aclnnCalculateMatmulWeightSizeV2 API and aclnnTransMatmulWeight API can be used to convert the input format from ND to the NZ format.
When the original input type of x2Scale does not meet the combination in the quantization scenario constraints, call the aclnnTransQuantParamV2 API in advance to convert the scale to the INT64 or UINT64 data type.
yScale is not supported in the current version. Pass nullptr.
The shape of out supports 2 to 6 dimensions, (batch, m, n), where batch is optional. The supported data types are FLOAT16, INT8, BFLOAT16, and INT32.
When x1 and x2 are INT8, out is INT32, and bias is INT32 or nullptr, the scale factors do not participate in the computation. The formula is as follows:
bias INT32
Without bias
G-B quantization scenario constraints:
- The input and output support the following data type combinations:
x1 x2 x1Scale x2Scale x2Offset yScale bias yOffset out INT8 INT8 FLOAT32 FLOAT32 null null FLOAT32 null BFLOAT16 Relationship between x1 shape, x2 shape, x1Scale shape, x2Scale shape, bias shape, and groupSize:
Quantization Type x1 shape x2 shape x1Scale shape x2Scale shape bias shape [gsM, gsN, gsK] G-B quantization (m, k) (n, k) (m, ceil(k / 128)) (ceil(n / 128),ceil(k / 128)) (n, ) [1, 128, 128] Note: In the table above, gsM, gsK, and gsN represent groupSizeM, groupSizeK, and groupSizeN, respectively.
Constraints on x1: Currently, k must be aligned with 128 and be a multiple of 4 * 128, and transposeX1 must be false.
Constraints on x2: Currently, n must be aligned to 256, k must be aligned to 128 and be a multiple of 4 * 128, and transposeX2 must be true.
T-C && T-T && K-C && K-T Quantization Scenario Constraints:
- The input and output support the following data type combinations:
x1 x2 x1Scale x2Scale x2Offset yScale bias yOffset out INT8 INT32 FLOAT32 UINT64/INT64 null null null FLOAT32 FLOAT16/BFLOAT16 INT8 INT8 null UINT64/INT64 null null null/INT32 null FLOAT16 INT8 INT8 null UINT64/INT64 null/FLOAT32 null null/INT32 null INT8 INT8 INT8 null/FLOAT32 FLOAT32/BFLOAT16 null null null/INT32/BFLOAT16/FLOAT32 null BFLOAT16 INT8 INT8 FLOAT32 FLOAT32 null null null/INT32/FLOAT16/FLOAT32 null FLOAT16 INT4/INT32 INT4/INT32 null UINT64/INT64 null null null/INT32 null FLOAT16 INT8 INT8 null FLOAT32/BFLOAT16 null null null/INT32 null INT32 INT8 INT8 FLOAT32 FLOAT32 null null FLOAT32 null BFLOAT16 INT4/INT32 INT4/INT32 FLOAT32 FLOAT32/BFLOAT16 null null null/INT32/BFLOAT16/FLOAT32 null BFLOAT16 INT4/INT32 INT4/INT32 FLOAT32 FLOAT32 null null null/INT32/FLOAT16/FLOAT32 null FLOAT16 Constraints on x1:
When the data type is INT4, transposeX1 is false. The dimensions are (m, k), and k must be an even number.
When the data type is INT32, transposeX1 is false. Each INT32 value stores eight INT4 values, and the corresponding dimension representation is (m, ceil(k / 8)). k must be a multiple of 8.
When the data type is INT8 and the data type of x2 is INT32, transposeX1 is false. The dimensions are (m, k), and k must be an even number.
Constraints on x2:
When the data type is INT4:
Currently, only 2D ND format is supported.
When transposeX2 is true, the dimensions are (n, k), and k must be an even number.
When transposeX2 is false, the dimensions are (k, n), and n must be an even number.
When the data type is INT32, each INT32 value stores eight INT4 values.
Currently, only 2D ND format is supported.
When transposeX2 is true, the dimension is (n, ceil(k / 8)), and k must be a multiple of 8.
When transposeX2 is false, the dimension is (k, ceil(n / 8)), and n must be a multiple of 8.
You can use the aclnnConvertWeightToINT4Pack API to convert x2 from INT32 (one INT32 stores one INT4 in bits 0–3) to INT32 (one INT32 stores eight INT4 values) or INT4 (one INT4 represents one INT4 value). For details, see aclnnConvertWeightToINT4Pack API.
Constraints on x1Scale: The data format supports ND, and the shape is 1D (t,), where t = m, and m is the same as m of x1.
x2Scale constraint: The data format supports ND. The shape is 1D (t,), where t = 1 or n, and n is consistent with n of x2.
x2Offset constraint: The data format supports ND. The shape is 1D (t,), where t = 1 or n, and n is consistent with n of x2.
bias constraint:
The data format supports ND. The shape supports 1D (n,) or 3D (batch, 1, n), where n is consistent with n of x2.
When x1 and x2 are INT32 or INT4, the shape of bias supports only 1D (n,).
When the shape of out is 2, 4, 5, or 6 dimensions, the shape of bias supports only 1 dimension (n,).
Constraints on yOffset: The shape supports 1 dimension (n). It is an auxiliary result computed offline during the calculation process. The value must be 8 x2 x2Scale, and accumulated along the first dimension.
K-G Quantization Scenario Constraints:
- The input and output support the following data type combinations:
| x1 | x2 | x1Scale | x2Scale | x2Offset | yScale | bias | yOffset | out | | ------------------------- | ------------------------- | ----------- | ----------- | ----------- | ------- | ------------ | -----------| -------------------------------------- | | INT8 | INT32 | FLOAT32 | UINT64/INT64 | null | null | null | FLOAT32 | FLOAT16/BFLOAT16 | | INT4 | INT4 | FLOAT32 | FLOAT32 | FLOAT16 | null | null | null | BFLOAT16 |Relationship between x1, x2, x1Scale, x2Scale, and groupSize values:
Quantization Type x1 Data Type x2 Data Type x1Scale Data Type x2Scale Data Type x1 Shape x2 Shape x1Scale Shape x2Scale Shape x2Offset Shape yOffset Shape [gsM, gsN, gsK] K-G quantization INT8 INT32 FLOAT32 UINT64/INT64 (m, k) (k, ceil(n / 8)) (m, 1) (ceil(k / 256), n) null (n) [0, 0, 256] K-G quantization INT4 INT4 FLOAT32 FLOAT32 (m, k) (n, k) (m, 1) (ceil(k / 256), n) (ceil(k / 256), n) null [0, 0, 256] x1 constraints:
When the data type is INT8, k must be aligned to 256 and less than 29576. transposeX1 is false.
When the data type is INT4, k must be aligned to 1024. transposeX1 is false.
Constraints on x2:
When the data type is INT32, k must be aligned to 256. transposeX2 is false.
When the data type is INT4, k must be aligned to 1024, and n must be aligned to 256. transposeX2 is true.
Constraints on x2Scale:
When the data type is UINT64/INT64, since TransQuantParamV2 only supports one dimension, you need to view x2Scale as one-dimensional (k / groupSize * n), then call the aclnn API of the TransQuantParamV2 operator to convert x2Scale to the UINT64/INT64 data type, and then view the output as two-dimensional (k / groupSize, n), where the groupSize value is 256.
When x1 and x2 are INT4, the shape of x2Scale is (ceil(k / 256), n).
Ascend 950PR/Ascend 950DT
General Constraints:
When transposeX1 is false, the shape of x1 is (batch, m, k). When transposeX1 is true, the shape of x1 is (batch, k, m). Here, batch represents the first 0 to 4 dimensions, where 0 dimension indicates that batch does not exist.
When transposeX2 is false, the shape of x2 is (batch, k, n). When transposeX2 is true, the shape of x2 is (batch, n, k). Here, batch represents the first 0 to 4 dimensions, and 0 dimension means batch does not exist. k is consistent with k in the shape of x1.
When the original input type of x2Scale does not meet the combination in the quantization scenario constraints, call the aclnnTransQuantParamV2 API in advance to convert the scale to INT64 or UINT64 data type.
yScale is supported only when x1 is FLOAT8_E4M3FN and x2 is FLOAT4_E2M1. It is not supported in other scenarios. The shape is 2D (1, n), where n is consistent with n of x2.
x2Offset is supported only when the data types of x1 and x2 are INT8 and the data type of out is INT8. For other input types, nullptr must be passed. The shape is 1D (t, ), where t = 1 or n, and n is consistent with n of x2.
yOffset is a reserved parameter and is not supported in the current version. Pass nullptr or an empty tensor.
bias-related constraints:
Optional parameter. Supports passing nullptr.
When the shape of out is 2, 4, 5, or 6 dimensions, the shape of bias supports 1D (n,) or 2D (1, n).
When the shape of out is 3 dimensions, the shape of bias supports 1D (n,) or 3D (batch, 1, n).
Constraints on groupSize:
Takes effect only in mx, G-B, B-B, and T-CG quantization modes.
The groupSize value is valid only when both x1Scale and x2Scale inputs are 2-dimensional or higher. In other scenarios, pass 0.
The input groupSize is internally decomposed into groupSizeM, groupSizeN, and groupSizeK using the following formula. When one or more of them are 0, groupSizeM, groupSizeN, and groupSizeK are reset based on the input shapes of x1, x2, x1Scale, and x2Scale for computation. Principle: If groupSizeM = 0, it indicates that the quantization group value in the m direction is inferred by the API. The inference formula is groupSizeM = m / scaleM (m must be divisible by scaleM), where m is consistent with the m in the x1 shape, and scaleM is consistent with the m in the x1Scale shape.
The shape of out supports 2 to 6 dimensions, (batch, m, n). batch may be absent. Broadcasting of the batch dimensions of x1 and x2 is supported. The output batch is consistent with the batch after broadcasting, m is consistent with the m of x1, and n is consistent with the n of x2.
When x1 and x2 are INT8, out is INT32, and bias is INT32 or nullptr, each scale does not actually participate in the computation. The formula is as follows:
bias INT32
Without bias
T-C Quantization && T-T Quantization Scenario Constraints:
- The input and output support the following data type combinations:
| x1 | x2 | x1Scale | x2Scale | x2Offset | yScale | bias | out | | ------------------------- | ------------------------- | ----------- | ----------- | -------- | -------| ------- | -------------------------------------- | | INT8 | INT8 | null | UINT64/INT64 | null | null | null/INT32 | FLOAT16/BFLOAT16 | | INT8 | INT8 | null | UINT64/INT64 | null/FLOAT32 | null | null/INT32 | INT8 | | INT8 | INT8 | null | FLOAT32/BFLOAT16 | null | null | null/INT32/FLOAT32/BFLOAT16 | BFLOAT16 | | INT8 | INT8 | null | FLOAT32/BFLOAT16 | null | null | null/INT32 | INT32 | | FLOAT8_E4M3FN/FLOAT8_E5M2 | FLOAT8_E4M3FN/FLOAT8_E5M2 | null | UINT64/INT64 | null | null | null/FLOAT32 | FLOAT16/BFLOAT16/FLOAT32 | | HIFLOAT8 | HIFLOAT8 | null | UINT64/INT64 | null | null | null/FLOAT32 | FLOAT16/BFLOAT16/FLOAT32 | | FLOAT8_E4M3FN/FLOAT8_E5M2 | FLOAT8_E4M3FN/FLOAT8_E5M2 | FLOAT32 | FLOAT32 | null | null | null/FLOAT32 | FLOAT16/BFLOAT16/FLOAT32 | | HIFLOAT8 | HIFLOAT8 | FLOAT32 | FLOAT32 | null | null | null/FLOAT32 | FLOAT16/BFLOAT16/FLOAT32 |In the T-T quantization scenario, the shape of x1Scale is (1,) or nullptr, and the shape of x2Scale is (1,).
In the T-C quantization scenario, the shape of x1Scale is (1,) or nullptr, and the shape of x2Scale is (n,), where n is consistent with the n of x2.
When the data type of x1/x2 is FLOAT8_E4M3FN/FLOAT8_E5M2/HIFLOAT8, static quantization and dynamic quantization are distinguished. For static quantization, the data type of x2Scale is UINT64/INT64; for dynamic quantization, the data type of x2Scale is FLOAT32. When the data type of x1/x2 is INT8, dynamic T-C or dynamic T-T quantization is not supported.
Bias is not supported in dynamic T-C quantization scenarios.
K-C Quantization && K-T Quantization Constraints:
- The input and output support the following data type combinations:
| x1 | x2 | x1Scale | x2Scale | x2Offset | yScale | bias | out | | ------------------------- | ------------------------- | ----------- | ----------- | -------- | ------ | --------- | -------------------------------------- | | INT8 | INT8 | FLOAT32 | FLOAT32/BFLOAT16 | null | null | null/INT32/FLOAT32/BFLOAT16 | BFLOAT16 | | INT8 | INT8 | FLOAT32 | FLOAT32 | null | null | null/INT32/FLOAT32/FLOAT16 | FLOAT16 | | FLOAT8_E4M3FN/FLOAT8_E5M2 | FLOAT8_E4M3FN/FLOAT8_E5M2 | FLOAT32 | FLOAT32 | null | null | null/FLOAT32 | FLOAT16/BFLOAT16/FLOAT32 | | HIFLOAT8 | HIFLOAT8 | FLOAT32 | FLOAT32 | null | null | null/FLOAT32 | FLOAT16/BFLOAT16/FLOAT32 |In the K-C quantization scenario, the shape of x1Scale is (m,) and the shape of x2Scale is (n,), where m is consistent with the m of x1 and n is consistent with the n of x2.
In the K-T quantization scenario, the shape of x1Scale is (m,) and the shape of x2Scale is (1,), where m is consistent with the m of x1.
G-B Quantization && B-B Quantization Scenario Constraints:
- The input and output support the following data type combinations:
| x1 | x2 | x1Scale | x2Scale | x2Offset | yScale | bias | out | | ------------------------- | ------------------------- | ----------- | ----------- | -------- | -------| ------- | -------------------------------------- | | FLOAT8_E4M3FN/FLOAT8_E5M2 | FLOAT8_E4M3FN/FLOAT8_E5M2 | FLOAT32 | FLOAT32 | null | null | null | FLOAT16/BFLOAT16/ FLOAT32 | | HIFLOAT8 | HIFLOAT8 | FLOAT32 | FLOAT32 | null | null | null | FLOAT16/BFLOAT16/ FLOAT32 | | INT8 |INT8 | FLOAT32 | FLOAT32 | null | null | FLOAT32 |BFLOAT16 |Relationship between x1, x2, x1Scale, x2Scale, and groupSize:
Quantization Type x1 shape x2 shape x1Scale shape x2Scale shape yScale shape [gsM, gsN, gsK] groupSize B-B quantization - Non-transposed: (batch, m, k)
- Transposed: (batch, k, m)
- Non-transposed: (batch, k, n)
- Transposed: (batch, n, k)
- Non-transposed: (batch, ceil(m / 128), ceil(k / 128))
- Transposed: (batch, ceil(k / 128), ceil(m / 128))
- Non-transposed: (batch, ceil(k / 128), ceil(n / 128))
- Transposed: (batch, ceil(n / 128), ceil(k / 128))
null [128, 128, 128] 549764202624 G-B quantization - Non-transposed: (batch, m, k)
- Transposed: (batch, k, m)
- Non-transposed: (batch, k, n)
- Transposed: (batch, n, k)
- Non-transposed: (batch, m, ceil(k / 128))
- Transposed: (batch, ceil(k / 128), m)
- Non-transposed: (batch, ceil(k / 128), ceil(n / 128))
- Transposed: (batch, ceil(n / 128), ceil(k / 128))
null [1, 128, 128] 4303356032 Note: In the table above, gsM, gsK, and gsN represent groupSizeM, groupSizeK, and groupSizeN, respectively.
In G-B quantization and B-B quantization scenarios, the transpose attributes of x1 and x1Scale must be consistent, and the transpose attributes of x2 and x2Scale must be consistent.
In G-B quantization scenarios, bias is supported only for INT8 input. Bias is not supported in other scenarios.
In B-B quantization scenarios, INT8 input is not supported, and bias is not supported.
mx Quantization Scenario Constraints:
- The input and output support the following data type combinations:
|Quantization Type| x1 | x2 | x1Scale | x2Scale | x2Offset | yScale | bias | out | |-------| ------------------------- | ------------------------- | ----------- | ----------- | -------- | -------| ------- | -------------------------------------- | |mx full quantization| FLOAT8_E4M3FN/FLOAT8_E5M2 | FLOAT8_E4M3FN/FLOAT8_E5M2 | FLOAT8_E8M0 | FLOAT8_E8M0 | null | null | null/FLOAT32 | FLOAT16/BFLOAT16/FLOAT32 | |mx full quantization| FLOAT4_E2M1 | FLOAT4_E2M1 | FLOAT8_E8M0 | FLOAT8_E8M0 | null | null | null/FLOAT32 | FLOAT16/BFLOAT16/FLOAT32 | |mx pseudo quantization| FLOAT8_E4M3FN | FLOAT4_E2M1 | FLOAT8_E8M0 | FLOAT8_E8M0 | null | null | null/BFLOAT16| BFLOAT16 |Relationship among the x1 data type, x2 data type, x1, x2, x1Scale, x2Scale, and groupSize:
Quantization Type x1 Data Type x2 Data Type x1 Shape x2 Shape x1Scale Shape x2Scale Shape bias Shape yScale Shape [gsM, gsN, gsK] Group Size mx full quantization FLOAT8_E4M3FN/FLOAT8_E5M2 FLOAT8_E4M3FN/FLOAT8_E5M2 - Non-transposed: (batch, m, k)
- Transposed: (batch, k, m)
- Non-transposed: (batch, k, n)
- Transposed: (batch, n, k)
- Non-transposed: (m, ceil(k / 64), 2)
- Transposed: (ceil(k / 64), m, 2)
- Non-transposed: (ceil(k / 64), n, 2)
- Transposed: (n, ceil(k / 64), 2)
(n,) or (batch, 1, n) null [1, 1, 32] 4295032864 mx full quantization FLOAT4_E2M1 FLOAT4_E2M1 (batch, m, k) (batch, n, k) (m, ceil(k / 64), 2) (n, ceil(k / 64), 2) (n,) or (batch, 1, n) null [1, 1, 32] 4295032864 mx pseudo quantization FLOAT8_E4M3FN FLOAT4_E2M1 (m, k) (n, k) (m, ceil(k / 64), 2) (n, ceil(k / 64), 2) (1, n) null [0, 0, 32]/[1, 1, 32] 32/4295032864 In the mx full quantization scenario, when the x2 data type is FLOAT8_E4M3FN/FLOAT8_E5M2, the transpose attributes of x1 and x1Scale must remain consistent, and the transpose attributes of x2 and x2Scale must remain consistent.
In the mx full quantization scenario, when the x2 data type is FLOAT4_E2M1, only transposeX1 set to false and transposeX2 set to true are supported, and k must be an even number with ceil(k / 32) also being an even number.
In the mx pseudo-quantization scenario, when the x2 data type is FLOAT4_E2M1, transposeX1 is false and transposeX2 is true, and the batch axis is not supported. The supported data formats are ND and AI processor affinity data layout formats. When the data format is ND, k must be a multiple of 64. When the data format is the AI processor affinity data layout format, both k and n must be multiples of 64.
In the mx pseudo-quantization scenario, bias is an optional parameter. The supported data type is BFLOAT16, the supported data format is ND, and the supported shape is 2-dimensional, representing (1, n). Pass nullptr if this parameter is not needed.
In the mx pseudo-quantization scenario, the supported value combinations for [groupSizeM, groupSizeN, groupSizeK] are [0, 0, 32] and [1, 1, 32], with the corresponding groupSize values being 32 and 4295032864, respectively.
T-CG Quantization Scenario Constraints:
- The input and output support the following data type combinations:
| x1 | x2 | x1Scale | x2Scale | x2Offset | yScale | bias | out | | ------------------------- | ------------------------- | ----------- | ----------- | -------- | -------| ------- | -------------------------------------- | | FLOAT8_E4M3FN | FLOAT4_E2M1 | null | BFLOAT16 | null | INT64/UINT64 | null | BFLOAT16 |Relationship between x1, x2, x1Scale, x2Scale, and groupSize values:
Quantization Type x1 Shape x2 Shape x1Scale Shape x2Scale Shape yScale Shape [gsM, gsN, gsK] groupSize T-CG quantization (m, k) (n, k)/(k, n) null (n, ceil(k / 32))/(ceil(k / 32), n) (1, n) [0, 0, 32]/[1, 1, 32] 32/4295032864 In T-CG quantization mode, the yScale data type supports INT64 and UINT64, the data format supports ND, and the shape supports 2 dimensions, expressed as (1, n). When the original input type does not meet the data type combinations in the constraints and restrictions, call the aclnn API of the TransQuantParamV2 operator in advance to convert it to the UINT64 data type. When the input data type is INT64, INT64 is internally treated as UINT64.
In T-CG quantization mode, bias is a reserved parameter and is not supported in the current version. Pass nullptr.
In T-CG quantization mode, transposeX1 is false. The data format supports ND and AI processor affinity data layout formats. When the data format is ND, k must be a multiple of 64, and transposeX2 is true. When the data format is the AI processor affinity data layout format, both k and n must be multiples of 64, and transposeX2 is false.
In T-CG quantization mode, the [groupSizeM, groupSizeN, groupSizeK] value combinations support [0, 0, 32] and [1, 1, 32], with corresponding groupSize values of 32 and 4295032864, respectively.
Example
The following provides example code for reference only. For details about compilation and running, see Compile and Run Samples.
Ascend 950PR/Ascend 950DT :
x1 is FLOAT8_E4M3FN, x2 is FLOAT4_E2M1, x2Scale is BFLOAT16, and yScale is UINT64.
#include <iostream>
#include <memory>
#include <vector>
#include "acl/acl.h"
#include "aclnnop/aclnn_quant_matmul_v5.h"
#include "aclnnop/aclnn_trans_quant_param_v2.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: 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 allocate 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 the 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 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];
}
// 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;
}
void Finalize(int32_t deviceId, aclrtStream stream)
{
aclrtDestroyStream(stream);
aclrtResetDevice(deviceId);
aclFinalize();
}
// Convert the uint16_t representation of bfloat16 to a float representation.
float Bf16ToFloat(uint16_t h)
{
uint32_t sign = (h & 0x8000U) ? 0x80000000U : 0x00000000U; // sign bit
uint32_t exponent = (h >> 7) & 0x00FFU; // exponent bits
uint32_t mantissa = h & 0x007FU; // mantissa bits
// Keep the exponent bias unchanged.
// Shift mantissa left by 23 - 7, and pad the remaining bits with zeros.
uint32_t fBits = sign | (exponent << 23) | (mantissa << (23 - 7));
// Cast to float.
return *reinterpret_cast<float*>(&fBits);
}
int AclnnQuantMatmulV5Test(int32_t deviceId, aclrtStream& stream)
{
auto ret = Init(deviceId, &stream);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("Init acl failed. ERROR: %d\n", ret); return ret);
// 2. Construct the input and output. Customize the construction based on the API interface.
std::vector<int64_t> x1Shape = {5, 64};
std::vector<int64_t> x2Shape = {8, 64};
std::vector<int64_t> x2ScaleShape = {8, 2};
std::vector<int64_t> yScaleShape = {1, 8};
std::vector<int64_t> outShape = {5, 8};
void* x1DeviceAddr = nullptr;
void* x2DeviceAddr = nullptr;
void* x2ScaleDeviceAddr = nullptr;
void* yScaleDeviceAddr = nullptr;
void* quantParamDeviceAddr = nullptr;
void* outDeviceAddr = nullptr;
aclTensor* x1 = nullptr;
aclTensor* x2 = nullptr;
aclTensor* x2Scale = nullptr;
aclTensor* yScale = nullptr;
aclTensor* yOffset = nullptr;
aclTensor* quantParam = nullptr;
aclTensor* bias = nullptr;
aclTensor* out = nullptr;
std::vector<uint8_t> x1HostData(5 * 64, 0b00111000); // 0b00111000 represents 1.0 in fp8_e4m3fn.
std::vector<uint8_t> x2HostData(8 * 64 / 2, 0b0010 + (0b0010 << 4)); // 0b0010 represents 1.0 in fp4_e2m1. Here uint8 is used to represent two fp4 values.
std::vector<uint16_t> x2ScaleHostData(8 * 2, 0b0011111110000000); // 1.0 in bf16.
std::vector<float> yScaleHostData(8, 1.0); // 1.0 in fp32.
std::vector<uint64_t> quantParamHostData(8, 0);
std::vector<uint16_t> outHostData(40, 0); // Actually bfloat16.
// Create x1 aclTensor.
ret = CreateAclTensor(x1HostData, x1Shape, &x1DeviceAddr, aclDataType::ACL_FLOAT8_E4M3FN, &x1);
std::unique_ptr<aclTensor, aclnnStatus (*)(const aclTensor*)> x1TensorPtr(x1, aclDestroyTensor);
std::unique_ptr<void, aclError (*)(void*)> x1DeviceAddrPtr(x1DeviceAddr, aclrtFree);
CHECK_RET(ret == ACL_SUCCESS, return ret);
// Create x2 aclTensor.
ret = CreateAclTensor(x2HostData, x2Shape, &x2DeviceAddr, aclDataType::ACL_FLOAT4_E2M1, &x2);
std::unique_ptr<aclTensor, aclnnStatus (*)(const aclTensor*)> x2TensorPtr(x2, aclDestroyTensor);
std::unique_ptr<void, aclError (*)(void*)> x2DeviceAddrPtr(x2DeviceAddr, aclrtFree);
CHECK_RET(ret == ACL_SUCCESS, return ret);
// Create x2Scale aclTensor.
ret = CreateAclTensor(x2ScaleHostData, x2ScaleShape, &x2ScaleDeviceAddr, aclDataType::ACL_BF16, &x2Scale);
std::unique_ptr<aclTensor, aclnnStatus (*)(const aclTensor*)> x2ScaleTensorPtr(x2Scale, aclDestroyTensor);
std::unique_ptr<void, aclError (*)(void*)> x2ScaleDeviceAddrPtr(x2ScaleDeviceAddr, aclrtFree);
CHECK_RET(ret == ACL_SUCCESS, return ret);
// Create yScale aclTensor.
ret = CreateAclTensor(yScaleHostData, yScaleShape, &yScaleDeviceAddr, aclDataType::ACL_FLOAT, &yScale);
std::unique_ptr<aclTensor, aclnnStatus (*)(const aclTensor*)> yScaleTensorPtr(yScale, aclDestroyTensor);
std::unique_ptr<void, aclError (*)(void*)> yScaleDeviceAddrPtr(yScaleDeviceAddr, aclrtFree);
CHECK_RET(ret == ACL_SUCCESS, return ret);
// Create quantParam aclTensor.
ret = CreateAclTensor(quantParamHostData, yScaleShape, &quantParamDeviceAddr, aclDataType::ACL_UINT64, &quantParam);
std::unique_ptr<aclTensor, aclnnStatus (*)(const aclTensor*)> quantParamTensorPtr(quantParam, aclDestroyTensor);
std::unique_ptr<void, aclError (*)(void*)> quantParamDeviceAddrPtr(quantParamDeviceAddr, aclrtFree);
CHECK_RET(ret == ACL_SUCCESS, return ret);
// Create the output aclTensor.
ret = CreateAclTensor(outHostData, outShape, &outDeviceAddr, aclDataType::ACL_BF16, &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);
bool transposeX1 = false;
bool transposeX2 = true;
int64_t groupSize = 32;
// 3. Call the CANN operator library API. Replace with the specific API name.
uint64_t workspaceSize = 0;
aclOpExecutor* executor = nullptr;
// Call the first-phase API of aclnnTransQuantParamV2.
ret = aclnnTransQuantParamV2GetWorkspaceSize(yScale, yOffset, quantParam, &workspaceSize, &executor);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnTransQuantParamV2GetWorkspaceSize failed. ERROR: %d\n", ret);
return ret);
// Apply for device memory based on the workspaceSize calculated by the first-phase API.
void* workspaceQuantParamAddr = nullptr;
std::unique_ptr<void, aclError (*)(void*)> workspaceQuantParamAddrPtr(nullptr, aclrtFree);
if (workspaceSize > 0) {
ret = aclrtMalloc(&workspaceQuantParamAddr, workspaceSize, ACL_MEM_MALLOC_HUGE_FIRST);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("allocate workspace failed. ERROR: %d\n", ret); return ret);
workspaceQuantParamAddrPtr.reset(workspaceQuantParamAddr);
}
// Call the second-phase API of aclnnTransQuantParamV2.
ret = aclnnTransQuantParamV2(workspaceQuantParamAddr, workspaceSize, executor, stream);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnTransQuantParamV2 failed. ERROR: %d\n", ret); return ret);
workspaceSize = 0;
executor = nullptr;
// Call the first-phase API of aclnnQuantMatmulV5.
ret = aclnnQuantMatmulV5GetWorkspaceSize(
x1, x2, nullptr, x2Scale, quantParam, nullptr, nullptr, nullptr, bias, transposeX1, transposeX2, groupSize, out,
&workspaceSize, &executor);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnQuantMatmulV5GetWorkspaceSize failed. ERROR: %d\n", ret); return ret);
// Apply for device memory based on the workspaceSize calculated by the first-phase API.
void* workspaceAddr = nullptr;
std::unique_ptr<void, aclError (*)(void*)> workspaceAddrPtr(nullptr, aclrtFree);
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);
workspaceAddrPtr.reset(workspaceAddr);
}
// Call the second-phase API of aclnnQuantMatmulV5.
ret = aclnnQuantMatmulV5(workspaceAddr, workspaceSize, executor, stream);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnQuantMatmulV5 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 this step based on the specific API definition.
auto size = GetShapeSize(outShape);
std::vector<uint16_t> resultData(
size, 0); // In C language, fp16 data cannot be printed directly. It needs to be read as uint16 and manually converted to fp16 via binary.
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[%ld] is: %.1f\n", i, Bf16ToFloat(resultData[i]));
}
return ACL_SUCCESS;
}
int main()
{
// 1. (Boilerplate) Initialize device/stream. Refer to the ACL API manual.
// Fill in the deviceId based on your actual device.
int32_t deviceId = 0;
aclrtStream stream;
auto ret = AclnnQuantMatmulV5Test(deviceId, stream);
CHECK_FREE_RET(ret == ACL_SUCCESS, LOG_PRINT("AclnnQuantMatmulV5Test failed. ERROR: %d\n", ret); return ret);
Finalize(deviceId, stream);
return 0;
}Ascend 950PR/Ascend 950DT :
x1 is INT8, x2 is INT8, x1Scale is FLOAT32, x2Scale is FLOAT32, and bias is INT32.
#include <iostream>
#include <memory>
#include <vector>
#include "acl/acl.h"
#include "aclnnop/aclnn_quant_matmul_v5.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, 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);
// Call aclrtMemcpy to copy host-side data to the 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 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];
}
// 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;
}
void Finalize(int32_t deviceId, aclrtStream stream)
{
aclrtDestroyStream(stream);
aclrtResetDevice(deviceId);
aclFinalize();
}
int aclnnQuantMatmulV5Test(int32_t deviceId, aclrtStream &stream)
{
auto ret = Init(deviceId, &stream);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("Init acl failed. ERROR: %d\n", ret); return ret);
// 2. Construct inputs and outputs based on the API's interface definition.
std::vector<int64_t> x1Shape = {5, 16};
std::vector<int64_t> x2Shape = {16, 8};
std::vector<int64_t> biasShape = {8};
std::vector<int64_t> x2OffsetShape = {8};
std::vector<int64_t> x1ScaleShape = {5};
std::vector<int64_t> x2ScaleShape = {8};
std::vector<int64_t> outShape = {5, 8};
void *x1DeviceAddr = nullptr;
void *x2DeviceAddr = nullptr;
void *x2ScaleDeviceAddr = nullptr;
void *x2OffsetDeviceAddr = nullptr;
void *x1ScaleDeviceAddr = nullptr;
void *biasDeviceAddr = nullptr;
void *outDeviceAddr = nullptr;
aclTensor *x1 = nullptr;
aclTensor *x2 = nullptr;
aclTensor *bias = nullptr;
aclTensor *x2Scale = nullptr;
aclTensor *x2Offset = nullptr;
aclTensor *x1Scale = nullptr;
aclTensor *out = nullptr;
std::vector<int8_t> x1HostData(80, 1);
std::vector<int8_t> x2HostData(128, 1);
std::vector<int32_t> biasHostData(8, 1);
std::vector<float> x2ScaleHostData(8, 1);
std::vector<float> x2OffsetHostData(8, 1);
std::vector<float> x1ScaleHostData(5, 1);
std::vector<uint16_t> outHostData(40, 1); // Actually in float16 half-precision format.
// Create the x1 aclTensor.
ret = CreateAclTensor(x1HostData, x1Shape, &x1DeviceAddr, aclDataType::ACL_INT8, &x1);
std::unique_ptr<aclTensor, aclnnStatus (*)(const aclTensor *)> x1TensorPtr(x1, aclDestroyTensor);
std::unique_ptr<void, aclError (*)(void *)> x1DeviceAddrPtr(x1DeviceAddr, aclrtFree);
CHECK_RET(ret == ACL_SUCCESS, return ret);
// Create the x2 aclTensor.
ret = CreateAclTensor(x2HostData, x2Shape, &x2DeviceAddr, aclDataType::ACL_INT8, &x2);
std::unique_ptr<aclTensor, aclnnStatus (*)(const aclTensor *)> x2TensorPtr(x2, aclDestroyTensor);
std::unique_ptr<void, aclError (*)(void *)> x2DeviceAddrPtr(x2DeviceAddr, aclrtFree);
CHECK_RET(ret == ACL_SUCCESS, return ret);
// Create the x1Scale aclTensor.
ret = CreateAclTensor(x1ScaleHostData, x1ScaleShape, &x1ScaleDeviceAddr,
aclDataType::ACL_FLOAT, &x1Scale);
std::unique_ptr<aclTensor, aclnnStatus (*)(const aclTensor *)> x1ScaleTensorPtr(x1Scale,
aclDestroyTensor);
std::unique_ptr<void, aclError (*)(void *)> x1ScaleDeviceAddrPtr(x1ScaleDeviceAddr, aclrtFree);
CHECK_RET(ret == ACL_SUCCESS, return ret);
// Create the x2Scale aclTensor.
ret = CreateAclTensor(x2ScaleHostData, x2ScaleShape, &x2ScaleDeviceAddr, aclDataType::ACL_FLOAT, &x2Scale);
std::unique_ptr<aclTensor, aclnnStatus (*)(const aclTensor *)> scaleTensorPtr(x2Scale, aclDestroyTensor);
std::unique_ptr<void, aclError (*)(void *)> x2ScaleDeviceAddrPtr(x2ScaleDeviceAddr, aclrtFree);
CHECK_RET(ret == ACL_SUCCESS, return ret);
// Create the bias aclTensor.
ret = CreateAclTensor(biasHostData, biasShape, &biasDeviceAddr, aclDataType::ACL_INT32, &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 the out aclTensor.
ret = CreateAclTensor(outHostData, outShape, &outDeviceAddr, aclDataType::ACL_FLOAT16, &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);
bool transposeX1 = false;
bool transposeX2 = false;
// 3. Call the CANN operator library API. Replace it with the specific API name.
uint64_t workspaceSize = 0;
aclOpExecutor *executor = nullptr;
// Call the first-phase API of aclnnQuantMatmulV5.
ret = aclnnQuantMatmulV5GetWorkspaceSize(x1, x2, x1Scale, x2Scale, nullptr, nullptr, nullptr, nullptr, bias,
transposeX1, transposeX2, 0, out, &workspaceSize, &executor);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnQuantMatmulV5GetWorkspaceSize failed. ERROR: %d\n", ret);
return ret);
// Apply for device memory based on the workspaceSize calculated by the first-phase API.
void *workspaceAddr = nullptr;
std::unique_ptr<void, aclError (*)(void *)> workspaceAddrPtr(nullptr, aclrtFree);
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);
workspaceAddrPtr.reset(workspaceAddr);
}
// Call the second-phase API of aclnnQuantMatmulV5.
ret = aclnnQuantMatmulV5(workspaceAddr, workspaceSize, executor, stream);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnQuantMatmulV5 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 device memory to host memory. Modify this based on the specific API definition.
auto size = GetShapeSize(outShape);
std::vector<uint16_t> resultData(
size, 0); // In C, fp16 data cannot be printed directly. Read it as uint16 and manually convert it to fp16 through binary conversion.
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[%ld] is: %u\n", i, resultData[i]);
}
return ACL_SUCCESS;
}
int main()
{
// 1. (Boilerplate) Initialize the device/stream. Refer to the acl API manual.
// Fill in the deviceId based on your actual device.
int32_t deviceId = 0;
aclrtStream stream;
auto ret = aclnnQuantMatmulV5Test(deviceId, stream);
CHECK_FREE_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnQuantMatmulV5Test failed. ERROR: %d\n", ret); return ret);
Finalize(deviceId, stream);
return 0;
}Ascend 950PR/Ascend 950DT :
x1 and x2 are FLOAT8_E4M3FN, x1Scale is FLOAT32, x2Scale is FLOAT32, no x2Offset, and bias is FLOAT32.
#include <iostream>
#include <memory>
#include <vector>
#include "acl/acl.h"
#include "aclnnop/aclnn_quant_matmul_v5.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: 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 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 data to 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 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;
}
void Finalize(int32_t deviceId, aclrtStream stream)
{
aclrtDestroyStream(stream);
aclrtResetDevice(deviceId);
aclFinalize();
}
int aclnnQuantMatmulV5Test(int32_t deviceId, aclrtStream &stream)
{
auto ret = Init(deviceId, &stream);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("Init acl failed. ERROR: %d\n", ret); return ret);
// 2. Construct inputs and outputs. Customize the construction based on the API.
std::vector<int64_t> x1Shape = {16, 16};
std::vector<int64_t> x2Shape = {16, 16};
std::vector<int64_t> biasShape = {16};
std::vector<int64_t> x2OffsetShape = {16};
std::vector<int64_t> x1ScaleShape = {1};
std::vector<int64_t> x2ScaleShape = {1};
std::vector<int64_t> outShape = {16, 16};
void *x1DeviceAddr = nullptr;
void *x2DeviceAddr = nullptr;
void *x2ScaleDeviceAddr = nullptr;
void *x2OffsetDeviceAddr = nullptr;
void *x1ScaleDeviceAddr = nullptr;
void *biasDeviceAddr = nullptr;
void *outDeviceAddr = nullptr;
aclTensor *x1 = nullptr;
aclTensor *x2 = nullptr;
aclTensor *bias = nullptr;
aclTensor *x2Scale = nullptr;
aclTensor *x2Offset = nullptr;
aclTensor *x1Scale = nullptr;
aclTensor *out = nullptr;
std::vector<int8_t> x1HostData(256, 1);
std::vector<int8_t> x2HostData(256, 1);
std::vector<int32_t> biasHostData(16, 1);
std::vector<float> x2ScaleHostData(1, 1);
std::vector<float> x2OffsetHostData(16, 1);
std::vector<float> x1ScaleHostData(1, 1);
std::vector<uint16_t> outHostData(256, 1); // Actually in float16 half-precision format.
// Create x1 aclTensor.
ret = CreateAclTensor(x1HostData, x1Shape, &x1DeviceAddr, aclDataType::ACL_FLOAT8_E4M3FN, &x1);
std::unique_ptr<aclTensor, aclnnStatus (*)(const aclTensor *)> x1TensorPtr(x1, aclDestroyTensor);
std::unique_ptr<void, aclError (*)(void *)> x1DeviceAddrPtr(x1DeviceAddr, aclrtFree);
CHECK_RET(ret == ACL_SUCCESS, return ret);
// Create x2 aclTensor.
ret = CreateAclTensor(x2HostData, x2Shape, &x2DeviceAddr, aclDataType::ACL_FLOAT8_E4M3FN, &x2);
std::unique_ptr<aclTensor, aclnnStatus (*)(const aclTensor *)> x2TensorPtr(x2, aclDestroyTensor);
std::unique_ptr<void, aclError (*)(void *)> x2DeviceAddrPtr(x2DeviceAddr, aclrtFree);
CHECK_RET(ret == ACL_SUCCESS, return ret);
// Create x2Scale aclTensor.
ret = CreateAclTensor(x2ScaleHostData, x2ScaleShape, &x2ScaleDeviceAddr, aclDataType::ACL_FLOAT, &x2Scale);
std::unique_ptr<aclTensor, aclnnStatus (*)(const aclTensor *)> scaleTensorPtr(x2Scale, aclDestroyTensor);
std::unique_ptr<void, aclError (*)(void *)> x2ScaleDeviceAddrPtr(x2ScaleDeviceAddr, aclrtFree);
CHECK_RET(ret == ACL_SUCCESS, return ret);
// Create x1Scale aclTensor.
ret = CreateAclTensor(x1ScaleHostData, x1ScaleShape, &x1ScaleDeviceAddr,
aclDataType::ACL_FLOAT, &x1Scale);
std::unique_ptr<aclTensor, aclnnStatus (*)(const aclTensor *)> x1ScaleTensorPtr(x1Scale,
aclDestroyTensor);
std::unique_ptr<void, aclError (*)(void *)> x1ScaleDeviceAddrPtr(x1ScaleDeviceAddr, aclrtFree);
CHECK_RET(ret == ACL_SUCCESS, return ret);
// Create 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 out aclTensor.
ret = CreateAclTensor(outHostData, outShape, &outDeviceAddr, aclDataType::ACL_FLOAT16, &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);
bool transposeX1 = false;
bool transposeX2 = false;
// 3. Call the CANN operator library API. Replace with the specific API name.
uint64_t workspaceSize = 0;
aclOpExecutor *executor = nullptr;
// Call the first-phase API of aclnnQuantMatmulV5.
ret = aclnnQuantMatmulV5GetWorkspaceSize(x1, x2, x1Scale, x2Scale, nullptr, nullptr, x2Offset, nullptr, bias,
transposeX1, transposeX2, 0, out, &workspaceSize, &executor);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnQuantMatmulV5GetWorkspaceSize failed. ERROR: %d\n", ret);
return ret);
// Apply for device memory based on the workspaceSize calculated by the first-phase API.
void *workspaceAddr = nullptr;
std::unique_ptr<void, aclError (*)(void *)> workspaceAddrPtr(nullptr, aclrtFree);
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);
workspaceAddrPtr.reset(workspaceAddr);
}
// Call the second-phase API of aclnnQuantMatmulV5.
ret = aclnnQuantMatmulV5(workspaceAddr, workspaceSize, executor, stream);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnQuantMatmulV5 failed. ERROR: %d\n", ret); return ret);
// 4. (Boilerplate) Wait for the task execution to finish synchronously.
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 this based on the specific API definition.
auto size = GetShapeSize(outShape);
std::vector<uint16_t> resultData(
size, 0); // In C language, fp16 data cannot be printed directly. Read it as uint16 and convert it to fp16 manually through binary conversion.
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[%ld] is: %u\n", i, resultData[i]);
}
return ACL_SUCCESS;
}
int main()
{
// 1. (Boilerplate) Initialize the device/stream. Refer to the ACL API manual.
// Fill in the deviceId based on your actual device.
int32_t deviceId = 0;
aclrtStream stream;
auto ret = aclnnQuantMatmulV5Test(deviceId, stream);
CHECK_FREE_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnQuantMatmulV5Test failed. ERROR: %d\n", ret); return ret);
Finalize(deviceId, stream);
return 0;
}Atlas A2 training products/Atlas A2 inference products ,Atlas A3 training products/Atlas A3 inference products :
x1 is INT8, x2 is INT32, x1Scale is FLOAT32, and x2Scale is UINT64.
#include <iostream>
#include <memory>
#include <vector>
#include "acl/acl.h"
#include "aclnnop/aclnn_quant_matmul_v5.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 the host-side data to the 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 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;
}
void Finalize(int32_t deviceId, aclrtStream stream)
{
aclrtDestroyStream(stream);
aclrtResetDevice(deviceId);
aclFinalize();
}
int aclnnQuantMatmulV5Test(int32_t deviceId, aclrtStream &stream)
{
auto ret = Init(deviceId, &stream);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("Init acl failed. ERROR: %d\n", ret); return ret);
// 2. Construct the input and output. Custom construction is required based on the API.
std::vector<int64_t> x1Shape = {1, 8192}; // (m,k)
std::vector<int64_t> x2Shape = {8192, 128}; // (k,n)
std::vector<int64_t> yoffsetShape = {1024};
std::vector<int64_t> x1ScaleShape = {1,1};
std::vector<int64_t> x2ScaleShape = {32, 1024}; // x2ScaleShape = [KShape / groupsize, N]
std::vector<int64_t> outShape = {1, 1024};
void *x1DeviceAddr = nullptr;
void *x2DeviceAddr = nullptr;
void *x2ScaleDeviceAddr = nullptr;
void *x1ScaleDeviceAddr = nullptr;
void *yoffsetDeviceAddr = nullptr;
void *outDeviceAddr = nullptr;
aclTensor *x1 = nullptr;
aclTensor *x2 = nullptr;
aclTensor *yoffset = nullptr;
aclTensor *x2Scale = nullptr;
aclTensor *x2Offset = nullptr;
aclTensor *x1Scale = nullptr;
aclTensor *out = nullptr;
std::vector<int8_t> x1HostData(GetShapeSize(x1Shape), 1);
std::vector<int32_t> x2HostData(GetShapeSize(x2Shape), 1);
std::vector<int32_t> yoffsetHostData(GetShapeSize(yoffsetShape), 1);
std::vector<int32_t> x1ScaleHostData(GetShapeSize(x1ScaleShape), 1);
float tmp = 1;
uint64_t ans = static_cast<uint64_t>(*reinterpret_cast<int32_t*>(&tmp));
std::vector<int64_t> x2ScaleHostData(GetShapeSize(x2ScaleShape), ans);
std::vector<uint16_t> outHostData(GetShapeSize(outShape), 1); // Actually in float16 half-precision format.
// Create the x1 aclTensor.
ret = CreateAclTensor(x1HostData, x1Shape, &x1DeviceAddr, aclDataType::ACL_INT8, &x1);
std::unique_ptr<aclTensor, aclnnStatus (*)(const aclTensor *)> x1TensorPtr(x1, aclDestroyTensor);
std::unique_ptr<void, aclError (*)(void *)> x1DeviceAddrPtr(x1DeviceAddr, aclrtFree);
CHECK_RET(ret == ACL_SUCCESS, return ret);
// Create x2 aclTensor.
ret = CreateAclTensor(x2HostData, x2Shape, &x2DeviceAddr, aclDataType::ACL_INT32, &x2);
std::unique_ptr<aclTensor, aclnnStatus (*)(const aclTensor *)> x2TensorPtr(x2, aclDestroyTensor);
std::unique_ptr<void, aclError (*)(void *)> x2DeviceAddrPtr(x2DeviceAddr, aclrtFree);
CHECK_RET(ret == ACL_SUCCESS, return ret);
// Create x1Scale aclTensor.
ret = CreateAclTensor(x1ScaleHostData, x1ScaleShape, &x1ScaleDeviceAddr, aclDataType::ACL_FLOAT, &x1Scale);
std::unique_ptr<aclTensor, aclnnStatus (*)(const aclTensor *)> x1ScaleTensorPtr(x1Scale, aclDestroyTensor);
std::unique_ptr<void, aclError (*)(void *)> x1ScaleDeviceAddrPtr(x1ScaleDeviceAddr, aclrtFree);
CHECK_RET(ret == ACL_SUCCESS, return ret);
// Create x2Scale aclTensor.
ret = CreateAclTensor(x2ScaleHostData, x2ScaleShape, &x2ScaleDeviceAddr, aclDataType::ACL_UINT64, &x2Scale);
std::unique_ptr<aclTensor, aclnnStatus (*)(const aclTensor *)> scaleTensorPtr(x2Scale, aclDestroyTensor);
std::unique_ptr<void, aclError (*)(void *)> x2ScaleDeviceAddrPtr(x2ScaleDeviceAddr, aclrtFree);
CHECK_RET(ret == ACL_SUCCESS, return ret);
// Create yoffset aclTensor.
ret = CreateAclTensor(yoffsetHostData, yoffsetShape, &yoffsetDeviceAddr, aclDataType::ACL_FLOAT, &yoffset);
std::unique_ptr<aclTensor, aclnnStatus (*)(const aclTensor *)> yoffsetTensorPtr(yoffset, aclDestroyTensor);
std::unique_ptr<void, aclError (*)(void *)> yoffsetDeviceAddrPtr(yoffsetDeviceAddr, aclrtFree);
CHECK_RET(ret == ACL_SUCCESS, return ret);
// Create out aclTensor.
ret = CreateAclTensor(outHostData, outShape, &outDeviceAddr, aclDataType::ACL_FLOAT16, &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);
bool transposeX1 = false;
bool transposeX2 = false;
int64_t groupSize = 256;
// 3. Call the CANN operator library API. Replace it with the specific API name.
uint64_t workspaceSize = 0;
aclOpExecutor *executor = nullptr;
ret = aclnnQuantMatmulV5GetWorkspaceSize(x1, x2, x1Scale, x2Scale, nullptr, nullptr, nullptr, yoffset, nullptr,
transposeX1, transposeX2, groupSize, out, &workspaceSize, &executor);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnQuantMatmulV5GetWorkspaceSize failed. ERROR: %d\n", ret); return ret);
// Apply for device memory based on the workspaceSize calculated by the first-phase API.
void *workspaceAddr = nullptr;
std::unique_ptr<void, aclError (*)(void *)> workspaceAddrPtr(nullptr, aclrtFree);
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);
workspaceAddrPtr.reset(workspaceAddr);
}
// Call the second-phase API of aclnnQuantMatmulV5.
ret = aclnnQuantMatmulV5(workspaceAddr, workspaceSize, executor, stream);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnQuantMatmulV5 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. Copy the result from the device memory to the host memory. Modify based on the specific API definition.
auto size = GetShapeSize(outShape);
std::vector<uint16_t> resultData(size, 0); // fp16 data cannot be directly printed in C. Use uint16 to read it, and then convert it to fp16 through binary conversion.
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[%ld] is: %u\n", i, resultData[i]);
}
return ACL_SUCCESS;
}
int main()
{
// 1. (Boilerplate) Initialize the device/stream. See the ACL API manual.
// Set deviceId based on the actual device.
int32_t deviceId = 1;
aclrtStream stream;
auto ret = aclnnQuantMatmulV5Test(deviceId, stream);
CHECK_FREE_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnQuantMatmulV5Test failed. ERROR: %d\n", ret); return ret);
Finalize(deviceId, stream);
return 0;
}