aclnnGroupedMatmulSwigluQuantWeightNZ
Supported Products
| Product | Supported |
|---|---|
| √ | |
| √ | |
| × | |
| × | |
| × |
Function
Description: Fuses
GroupedMatmul,dquant,swiglu, andquant. For details, see the formulas. This API is the weightNZ specialization version of aclnnGroupedMatmulSwigluQuant.Formulas:
- Quantization scenario (A8W8):
Definition
- ⋅ indicates matrix multiplication.
- ⊙ indicates element-wise multiplication.
- indicates rounding
xto the nearest integer.
Input
- : input matrix (left matrix), where
Mindicates the total number of tokens andKindicates the feature dimension. - : grouped weight matrix (right matrix), where
Eindicates the number of experts,Kindicates the feature dimension, andNindicates the output dimension. - : per-channel scale factor for the grouped weight matrix (right matrix), where
Eindicates the number of experts andNindicates the output dimension. - : per-token scale factor for the input matrix (left matrix), where
Mindicates the total number of tokens. - : grouped index list of prefix sums.
- : input matrix (left matrix), where
Output
- : quantized output matrix.
- : quantization scale factor.
Computation process
- Determine the tokens of the current group based on
groupList[i], where .
Example: Assume that
groupList=[3,4,4,6]. The counting starts from 0.Zero-th right matrix
W[0,:,:], corresponding to tokensx[0:3](3-0=3 tokens) at index positions [0,3), corresponding tox_scale[0:3],w_scale[0],bias[0],offset[0],Q[0:3],Q_scale[0:3], andQ_offset[0:3]First right matrix
W[1,:,:], corresponding to tokenx[3:4](4-3=1 token) at index position [3,4), corresponding tox_scale[3:4],w_scale[1],bias[1],offset[1],Q[3:4],Q_scale[3:4], andQ_offset[3:4]Second right matrix
W[2,:,:], corresponding to tokenx[4:4](4-4=0 token) at index position [4,4), corresponding tox_scale[4:4],w_scale[2],bias[2],offset[2],Q[4:4],Q_scale[4:4], andQ_offset[4:4]Third right matrix
W[3,:,:], corresponding to tokensx[4:6](6-4=2 tokens) at index positions [4,6), corresponding tox_scale[4:6],w_scale[3],bias[3],offset[3],Q[4:6],Q_scale[4:6], andQ_offset[4:6]Note: Any portion not specified in
groupListwill not be updated. AssumegroupList=[12,14,18]and the shape ofXis [30,:].The shape of the first output
Qwill be [30,:]. The portionQ[18:,:]will not be updated or initialized, and the data therein is consistent with the original data when the device memory is allocated.Similarly, the second output
Qhas a shape of [30]. The portionQ_scale[18:]will not be updated or initialized, and the data therein is consistent with the original data when the device memory is allocated.In other words, only
Q[:grouplist[-1],:]andQ_scale[:grouplist[-1]]constitute the valid data portions.- Determine the tokens of the current group based on
- Perform the following computation based on the input parameters determined by grouping:
where
- Quantize the output.
- MSD scenario (A8W4):
- Definition
- ⋅ indicates matrix multiplication.
- ⊙ indicates element-wise multiplication.
- indicates rounding
xto the nearest integer.
- Input
- : input matrix (left matrix), where
Mindicates the total number of tokens andKindicates the feature dimension. - : grouped weight matrix (right matrix), where
Eindicates the number of experts,Kindicates the feature dimension, andNindicates the output dimension. - : auxiliary matrix for matrix multiplication (the computation process for generating the auxiliary matrix is described below).
- : per-channel scale factor for the grouped weight matrix (right matrix), where
Eindicates the number of experts,K_group_numindicates the number of groups along the K-axis, andNindicates the output dimension. - : per-token scale factor for the input matrix (left matrix), where
Mindicates the total number of tokens. - : grouped index list of prefix sums.
- : input matrix (left matrix), where
- Output
- : quantized output matrix.
- : quantization scale factor.
- Computation process
- Determine the tokens of the current group based on
groupList[i], where .
- The grouping logic is the same as that of A8W8.
- Determine the tokens of the current group based on
- Compute the auxiliary matrix (
bias). (Note that the bias computation is performed offline and provided as an input, rather than being executed within the operator.)
For per-channel quantization ( is 2D):
For per-group quantization ( is 3D):
Note:
- Compute the auxiliary matrix (
- Perform the following computation based on the input parameters determined by grouping:
3.1. Convert the left matrix into two components that represent the high and low bits.
3.2. Enable per-channel or per-group quantization during matrix multiplication.
Per-channel:
Per-group:
3.3. Restore the matrix multiplication results of the high and low bits into the overall result.
where
- Quantize the output.
- Definition
- Quantization scenario (A8W8):
Prototype
Each operator has two-phase API calls. First, aclnnGroupedMatmulSwigluQuantWeightNZGetWorkspaceSize is called to obtain the workspace size required for computation and the executor that contains the operator computation process. Then, aclnnGroupedMatmulSwigluQuantWeightNZ is called to perform computation.
aclnnStatus aclnnGroupedMatmulSwigluQuantWeightNZGetWorkspaceSize(const aclTensor* x, const aclTensor* weight, const aclTensor* bias, const aclTensor* offset, const aclTensor* weightScale, const aclTensor* xScale, const aclTensor* groupList, aclTensor* output, aclTensor* outputScale, aclTensor* outputOffset, uint64_t* workspaceSize, aclOpExecutor** executor)aclnnStatus aclnnGroupedMatmulSwigluQuantWeightNZ(void* workspace, uint64_t workspaceSize, aclOpExecutor* executor, aclrtStream stream)
aclnnGroupedMatmulSwigluQuantWeightNZGetWorkspaceSize
Parameters
x (aclTensor*, computation input): left matrix, in the formula, aclTensor on the device. The shape supports 2D. Assuming the shape is [M,K], K must be less than 65536. The data type can be INT8, and the data format can be ND. Non-contiguous tensors are supported.
weight (aclTensor*, computation input): weight matrix, in the formula, aclTensor on the device. The shape supports 5D. The data type can be INT8, INT4, or INT32 (INT32 is used for adaptation. Actually, one INT32 data record is interpreted as eight INT4 data records). The data format can be FRACTAL_NZ. Non-contiguous tensors are supported. Note that this API ignores the weight data format and considers it as the FRACTAL_NZ format.
bias (aclTensor*, computation input): matrix multiplication bias, in the formula. The shape supports 2D. The data type can be INT32. This input is reserved and is not supported currently. You need to pass a null pointer.
offset (aclTensor*, computation input): offset of per-channel asymmetric dequantization, in the formula. The shape supports 2D. The data type is float. This input is reserved and is not supported currently. You need to pass a null pointer.
weightScale (aclTensor*, computation input): quantization factor of the right matrix, in the formula, aclTensor on the device. The shape supports 2D. The length of the first axis must be the same as the first axis of
weight. The length of the last axis must be the same as the last axis ofweightrestored to the ND format. The data type can be FLOAT, FLOAT16, or BFLOAT16. The data format can be ND. Non-contiguous tensors are supported.xScale (aclTensor*, computation input): quantization factor of the left matrix, in the formula, aclTensor on the device. The shape supports 1D. The length must be the same as the first axis of
x. The data type can be FLOAT. The data format can be ND. Non-contiguous tensors are supported.groupList (aclTensor*, computation input): number of tokens involved in the computation of each group, in the formula, aclTensor on the device. The shape supports 1D. The length must be the same as the first axis of
weight. The data type can be INT64. The data format can be ND. Non-contiguous tensors are supported. The last value ingrouplistconstrains the valid portion of the output data. For details, see the computation process.output (aclTensor*, computation output): quantization result, in the formula, aclTensor on the device. The data type can be INT8, and the shape can be 2D. The data format can be ND. Non-contiguous tensors are supported.
outputScale (aclTensor*, computation output): quantization factor, in the formula, aclTensor on the device. The data type can be FLOAT, and the shape can be 1D. The data format can be ND. Non-contiguous tensors are supported.
outputOffset (aclTensor*, computation output): offset of asymmetric quantization, in the formula, aclTensor on the device. The shape can be 1D, and the data type can be FLOAT. This input is reserved and is not supported currently. You need to pass a null pointer.
workspaceSize (uint64_t*, output parameter): size of the workspace to be allocated on the NPU device.
executor (aclOpExecutor**, computation output): operator executor, containing the operator computation process.
Return
aclnnStatusstatus code. For details, see aclnn Return Codes.The first-phase API implements input parameter validation. The following errors may be thrown: 161001 (ACLNN_ERR_PARAM_NULLPTR): 1. The input x, weight, weightScale, xScale, groupList, output, or outputScale is a null pointer. 161002 (ACLNN_ERR_PARAM_INVALID): 1. The data dimensions of the input x, weight, weightScale, xScale, groupList, output, or outputScale do not comply with the constraints. 2. The shape of the input x, weight, weightScale, xScale, groupList, output, or outputScale does not comply with the constraints. 3. The format of the input x, weight, weightScale, xScale, groupList, output, or outputScale does not comply with the constraints. 4. The number of elements in groupList is greater than the length of the first axis of weight. 5. The length of the N-axis exceeds 10240. 6. In the A8W8 scenario, the length of the last axis of x is greater than or equal to 65536. 7. In the A8W4 scenario, the length of the last axis of x is greater than or equal to 20000.
aclnnGroupedMatmulSwigluQuantWeightNZ
Parameters
- workspace (void*, input parameter): address of the workspace to be allocated on the device.
- workspaceSize (uint64_t, input parameter): workspace size to be allocated on the device, which is obtained by the first-phase API
aclnnGroupedMatmulSwigluQuantWeightNZGetWorkspaceSize. - executor (aclOpExecutor*, input parameter): operator executor, containing the operator computation process.
- stream (aclrtStream, input parameter): stream for executing the task.
Return
aclnnStatusstatus code. For details, see aclnn Return Codes.
Constraints
- Deterministic computation:
aclnnGroupedMatmulSwigluQuantWeightNZdefaults to a deterministic implementation.
A8W8 scenario (A: activation matrix (left matrix); W: weight matrix (right matrix); 8: INT8)
- The length of the last axis of
xcannot be greater than or equal to 65536.
- The length of the last axis of
- The length of the N-axis cannot exceed 10240.
A8W4 scenario (A: activation matrix (left matrix); W: weight matrix (right matrix); 8: INT8; 4: INT4)
- The length of the last axis of
xcannot be greater than or equal to 20000.
- The length of the last axis of
- The length of the N-axis cannot exceed 10240.
Calling Example
The following example is for reference only. For details, see Compilation and Running Sample.
#include <iostream>
#include <vector>
#include "acl/acl.h"
#include "aclnnop/aclnn_grouped_matmul_swiglu_quant_weight_nz.h"
#define CHECK_RET(cond, return_expr) \
do { \
if (!(cond)) { \
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, aclFormat formatType, aclTensor** tensor) {
auto size = GetShapeSize(shape) * sizeof(T);
// Call aclrtMalloc to allocate device memory.
auto ret = aclrtMalloc(deviceAddr, size, ACL_MEM_MALLOC_HUGE_FIRST);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtMalloc failed. ERROR: %d\n", ret); return ret);
// Call aclrtMemcpy to copy data from the host to the device memory.
ret = aclrtMemcpy(*deviceAddr, size, hostData.data(), size, ACL_MEMCPY_HOST_TO_DEVICE);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtMemcpy failed. ERROR: %d\n", ret); return ret);
// Compute the strides of the contiguous tensor.
std::vector<int64_t> strides(shape.size(), 1);
for (int64_t i = shape.size() - 2; i >= 0; i--) {
strides[i] = shape[i + 1] * strides[i + 1];
}
// Call aclCreateTensor to create an aclTensor.
*tensor = aclCreateTensor(shape.data(), shape.size(), dataType, strides.data(), 0, formatType,
shape.data(), shape.size(), *deviceAddr);
return 0;
}
int main() {
// 1. (Boilerplate) Initialize the device and stream. For details, see the ACL API manual.
// Set the device ID in use.
int32_t deviceId = 0;
aclrtStream stream;
auto ret = Init(deviceId, &stream);
// Customize error handling based on your requirements.
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("Init acl failed. ERROR: %d\n", ret); return ret);
// 2. Construct inputs and outputs based on API definitions.
int64_t E = 4;
int64_t M = 8192;
int64_t N = 4096;
int64_t K = 7168;
std::vector<int64_t> xShape = {M, K};
std::vector<int64_t> weightShape = {E, N / 32 ,K / 16, 16, 32};
std::vector<int64_t> weightScaleShape = {E, N};
std::vector<int64_t> xScaleShape = {M};
std::vector<int64_t> groupListShape = {E};
std::vector<int64_t> outputShape = {M, N / 2};
std::vector<int64_t> outputScaleShape = {M};
void* xDeviceAddr = nullptr;
void* weightDeviceAddr = nullptr;
void* weightScaleDeviceAddr = nullptr;
void* xScaleDeviceAddr = nullptr;
void* groupListDeviceAddr = nullptr;
void* outputDeviceAddr = nullptr;
void* outputScaleDeviceAddr = nullptr;
aclTensor* x = nullptr;
aclTensor* weight = nullptr;
aclTensor* weightScale = nullptr;
aclTensor* xScale = nullptr;
aclTensor* groupList = nullptr;
aclTensor* output = nullptr;
aclTensor* outputScale = nullptr;
std::vector<int8_t> xHostData(M * K, 0);
std::vector<int8_t> weightHostData(E * N * K, 0);
std::vector<float> weightScaleHostData(E * N, 0);
std::vector<float> xScaleHostData(M, 0);
std::vector<int64_t> groupListHostData(E, 0);
std::vector<int8_t> outputHostData(M * N / 2, 0);
std::vector<float> outputScaleHostData(M, 0);
// Create an x aclTensor.
ret = CreateAclTensor(xHostData, xShape, &xDeviceAddr, aclDataType::ACL_INT8, aclFormat::ACL_FORMAT_ND, &x);
CHECK_RET(ret == ACL_SUCCESS, return ret);
// Create a weight aclTensor.
ret = CreateAclTensor(weightHostData, weightShape, &weightDeviceAddr, aclDataType::ACL_INT8, aclFormat::ACL_FORMAT_FRACTAL_NZ, &weight);
CHECK_RET(ret == ACL_SUCCESS, return ret);
// Create a weightScale aclTensor.
ret = CreateAclTensor(weightScaleHostData, weightScaleShape, &weightScaleDeviceAddr, aclDataType::ACL_FLOAT, aclFormat::ACL_FORMAT_ND, &weightScale);
CHECK_RET(ret == ACL_SUCCESS, return ret);
// Create an xScale aclTensor.
ret = CreateAclTensor(xScaleHostData, xScaleShape, &xScaleDeviceAddr, aclDataType::ACL_FLOAT, aclFormat::ACL_FORMAT_ND, &xScale);
CHECK_RET(ret == ACL_SUCCESS, return ret);
// Create a groupList aclTensor.
ret = CreateAclTensor(groupListHostData, groupListShape, &groupListDeviceAddr, aclDataType::ACL_INT64, aclFormat::ACL_FORMAT_ND, &groupList);
CHECK_RET(ret == ACL_SUCCESS, return ret);
// Create an output aclTensor.
ret = CreateAclTensor(outputHostData, outputShape, &outputDeviceAddr, aclDataType::ACL_INT8, aclFormat::ACL_FORMAT_ND, &output);
CHECK_RET(ret == ACL_SUCCESS, return ret);
// Create an outputScale aclTensor.
ret = CreateAclTensor(outputScaleHostData, outputScaleShape, &outputScaleDeviceAddr, aclDataType::ACL_FLOAT, aclFormat::ACL_FORMAT_ND, &outputScale);
CHECK_RET(ret == ACL_SUCCESS, return ret);
uint64_t workspaceSize = 0;
aclOpExecutor* executor;
// 3. Call the CANN operator library API.
// Call the first-phase API of aclnnGroupedMatmulSwigluQuantWeightNZ.
ret = aclnnGroupedMatmulSwigluQuantWeightNZGetWorkspaceSize(x, weight, nullptr, nullptr, weightScale, xScale,
groupList, output, outputScale, nullptr,
&workspaceSize, &executor);
CHECK_RET(ret == ACL_SUCCESS,
LOG_PRINT("aclnnGroupedMatmulSwigluQuantWeightNZGetWorkspaceSize failed. ERROR: %d\n", ret); return ret);
// Allocate device memory based on workspaceSize computed by the first-phase API.
void* workspaceAddr = nullptr;
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 aclnnGroupedMatmulSwigluQuantWeightNZ.
ret = aclnnGroupedMatmulSwigluQuantWeightNZ(workspaceAddr, workspaceSize, executor, stream);
CHECK_RET(ret == ACL_SUCCESS,
LOG_PRINT("aclnnGroupedMatmulSwigluQuantWeightNZ failed. ERROR: %d\n", ret); return ret);
// 4. (Boilerplate) Wait until the task execution is 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. Modify the code based on the API definition.
auto size = GetShapeSize(outputShape);
std::vector<int8_t> out1Data(size, 0);
ret = aclrtMemcpy(out1Data.data(), out1Data.size() * sizeof(out1Data[0]), outputDeviceAddr,
size * sizeof(out1Data[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 j = 0; j < size; j++) {
LOG_PRINT("result[%d] is: %d\n", j, out1Data[j]);
}
size = GetShapeSize(outputScaleShape);
std::vector<float> out2Data(size, 0);
ret = aclrtMemcpy(out2Data.data(), out2Data.size() * sizeof(out2Data[0]), outputScaleDeviceAddr,
size * sizeof(out2Data[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 j = 0; j < size; j++) {
LOG_PRINT("result[%d] is: %f\n", j, out2Data[j]);
}
// 6. Release aclTensors and aclScalars. Modify the code based on the API definition.
aclDestroyTensor(x);
aclDestroyTensor(weight);
aclDestroyTensor(weightScale);
aclDestroyTensor(xScale);
aclDestroyTensor(groupList);
aclDestroyTensor(output);
aclDestroyTensor(outputScale);
// 7. Release device resources. Modify the code based on the API definition.
aclrtFree(xDeviceAddr);
aclrtFree(weightDeviceAddr);
aclrtFree(weightScaleDeviceAddr);
aclrtFree(xScaleDeviceAddr);
aclrtFree(groupListDeviceAddr);
aclrtFree(outputDeviceAddr);
aclrtFree(outputScaleDeviceAddr);
if (workspaceSize > 0) {
aclrtFree(workspaceAddr);
}
aclrtDestroyStream(stream);
aclrtResetDevice(deviceId);
aclFinalize();
return 0;
}