aclnnQuantGroupedMatmulDequant
Applicable Products
| Product | Supported |
|---|---|
| × | |
| × | |
| × | |
| × | |
| √ | |
| × |
Function
Description: Quantizes the input x, performs grouped matrix multiplication, and then dequantizes the result.
Formula:
- If smoothScaleOptional is input, then
- If xScaleOptional is not input, dynamic quantization is used, and the x quantization coefficient needs to be calculated.
- Quantization
- Grouped matrix multiplication + dequantization
- 4.1 If the data type of the input is FLOAT32:
- 4.2 If the data type of the input is INT64: Special note: In scenario 4.2 above, has already undergone a matrix multiplication operation with before being input, so the operator omits this step during internal computation. This requires that the quantization mode must be per-tensor static quantization. That is, before input, must be processed as follows to obtain data of the INT64 type:
Function Prototype
Each operator is divided into a two-phase API. You must first call the "aclnnQuantGroupedMatmulDequantGetWorkspaceSize" API to obtain the input parameters and calculate the required workspace size based on the process, and then call the "aclnnQuantGroupedMatmulDequant" API to perform the computation.
aclnnStatus aclnnQuantGroupedMatmulDequantGetWorkspaceSize(
const aclTensor *x,
const aclTensor *weight,
const aclTensor *weightScale,
const aclTensor *groupList,
const aclTensor *biasOptional,
const aclTensor *xScaleOptional,
const aclTensor *xOffsetOptional,
const aclTensor *smoothScaleOptional,
char *xQuantMode,
bool transposeWeight,
const aclTensor *out,
uint64_t *workspaceSize,
aclOpExecutor **executor)aclnnStatus aclnnQuantGroupedMatmulDequant(
void *workspace,
uint64_t workspaceSize,
aclOpExecutor *executor,
aclrtStream stream)aclnnQuantGroupedMatmulDequantGetWorkspaceSize
Parameters
Parameter Input/Output Description Instruction Data Type Data Format Dimension (Shape) Non-Contiguous Tensor x Input Represents the left matrix of the input. Required parameter, x in the formula. - Empty tensors are not supported.
- The shape supports 2 dimensions, with each dimension representing: (m, k).
FLOAT16 ND 2 √ weight Input Represents the right matrix of the input. Required parameter. weight_{quantized} in the formula. Empty tensors are not supported. INT8 FRACTAL_NZ, ND 3, 5 Yes weightScale Input Represents the quantization coefficient of weight. Required parameter. Corresponds to scale_{weight} in the formula. - The shape is 2D (g, n), where g and n are consistent with g and n of weight.
- When the data type is INT64, xScaleOptional must be of data type FLOAT16, and xQuantMode must be pertensor.
FLOAT32, INT64 ND 2 √ groupList Input Group in the formula. Represents the prefix sum result of input tensor x grouped in the MoE scenario, with a 1-dimensional shape [g]. - INT64 ND 1 √ biasOptional Input Represents the offset for computation. An optional parameter, the bias in the formula. Currently, only a null pointer is supported. - - - - xScaleOptional Input Represents the quantization coefficient of x, an optional parameter, scale_{x} in the formula. - When xQuantMode is pertensor, the shape is 1-dimensional (1,); when xQuantMode is pertoken, the shape is 1-dimensional (m,), where m is consistent with m of the input x. If empty, dynamic quantization is used.
- When the data type is FLOAT16, it must require that the weightScale data type be INT64 and the xQuantMode value be pertensor.
- Supports empty tensors.
FLOAT32, FLOAT16 ND 1 √ xOffsetOptional Input Represents the offset of x, an optional parameter. Currently, only a null pointer is supported. - - - - smoothScaleOptional Input Represents the smoothing coefficient of x. An optional parameter, which is scale_{smooth} in the formula. - The shape is 1D (k,), where k is consistent with k of x.
- An empty tensor is supported.
FLOAT16 ND 1 √ xQuantMode Input Specifies the quantization mode of input x. Supports pertoken and pertensor. For dynamic quantization, only pertoken is supported. pertoken indicates that each token (a row) has its own quantization parameter; pertensor indicates that the entire tensor uses a unified quantization parameter. STRING - - - transposeWeight Input Indicates whether to transpose the input weight. Currently only supports true. BOOL - - - out Output Computation result. Required parameter. out in the formula. Shape supports 2 dimensions, where each dimension represents: (m, n). Here, m is consistent with m of x, and n is consistent with n of weight. FLOAT16 ND 2 × workspaceSize Output Returns the workspace size to be applied on the Device side. - - - - - executor Output Returns the operator executor, which contains the operator computation process. - - - - - In ND format, the shape of weight supports 3 dimensions.
When transposeWeight is true, each dimension represents: (g, n, k).
When transposeWeight is false, each dimension represents: (g, k, n).
In FRACTAL_NZ format, the shape of weight supports 5 dimensions.
When transposeWeight is true, each dimension represents: (g, k1, n1, n0, k0), where k0 = 32, n0 = 16, and k1 and k of x must satisfy the following relationship: ceilDiv(k, 32) = k1.
When transposeWeight is false, each dimension represents: (g, n1, k1, k0, n0), where k0 = 16, n0 = 32, and k1 and k of x must satisfy the following relationship: ceilDiv(k, 16) = k1.
The aclnnCalculateMatmulWeightSizeV2 API and aclnnTransMatmulWeight API can be used to convert the input format from ND to the FRACTAL_NZ format.
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-in parameter is a required input, output, or required attribute and is a null pointer. ACLNN_ERR_PARAM_INVALID 161002 - If the input parameter type is aclTensor and its data type is not within the supported range.
- n or k in the shape of weight is not divisible by 16.
aclnnAdvanceStepGetWorkspaceSize failed 561002 If the passed-in parameter type is aclTensor and its shape does not match the above parameter description.
aclnnFatreluMul
Parameters
Parameter Input/Output Description workspace Input Device-side workspace memory address. workspaceSize Input Device-side workspace size, obtained from the first-phase API aclnnQuantGroupedMatmulDequantGetWorkspaceSize. executor Input Operator executor that contains the operator computation process. stream Input Specifies the stream that executes the task. Return Value
aclnnStatus: return code. For details, see aclnn Return Code.
Constraints
Deterministic computation:
- aclnnQuantGroupedMatmulDequant defaults to a deterministic implementation.
Both n and k must be integer multiples of 16.
When the weightScale data type is INT64, the mandatory requirement is that the xScaleOptional data type is FLOAT16 and the xQuantMode value is pertensor; when the xScaleOptional data type is FLOAT16, the mandatory requirement is that the weightScale data type is INT64 and the xQuantMode value is pertensor.
Example
The following provides example code for reference only. For details about compilation and running, see Compile and Run Samples.
#include <iostream>
#include <vector>
#include "acl/acl.h"
#include "aclnnop/aclnn_quant_grouped_matmul_dequant.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;
}
void PrintOutResult(std::vector<int64_t> &shape, void** deviceAddr) {
auto size = GetShapeSize(shape);
std::vector<float> resultData(size, 0);
auto ret = aclrtMemcpy(resultData.data(), resultData.size() * sizeof(resultData[0]),
*deviceAddr, 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);
for (int64_t i = 0; i < size; i++) {
LOG_PRINT("mean result[%ld] is: %f\n", i, resultData[i]);
}
}
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);
// Copy data from the host side 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);
// Calculate the strides of a contiguous tensor.
std::vector<int64_t> strides(shape.size(), 1);
for (int64_t i = shape.size() - 2; i >= 0; i--) {
strides[i] = shape[i + 1] * strides[i + 1];
}
// Create an aclTensor by calling the aclCreateTensor API.
*tensor = aclCreateTensor(shape.data(), shape.size(), dataType, strides.data(), 0, aclFormat::ACL_FORMAT_ND,
shape.data(), shape.size(), *deviceAddr);
return 0;
}
int main() {
// 1. (Boilerplate) Initialize the device/stream. See the ACL API manual.
// Set deviceId based on the actual device.
int32_t deviceId = 0;
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.
int G = 4;
int M = 64;
int K = 256;
int N = 512;
char quantMode[16] = "pertoken";
bool transposeWeight = true;
std::vector<int64_t> xShape = {M,K};
std::vector<int64_t> weightShape = {G,N,K};
std::vector<int64_t> weightScaleShape = {G,N};
std::vector<int64_t> xScaleShape = {M};
std::vector<int64_t> smoothScaleShape = {K};
std::vector<int64_t> outShape = {M,N};
std::vector<int64_t> groupListShape = {G};
void* xDeviceAddr = nullptr;
void* weightDeviceAddr = nullptr;
void* weightScaleDeviceAddr = nullptr;
void* groupListDeviceAddr = nullptr;
void* xScaleDeviceAddr = nullptr;
void* smoothScaleDeviceAddr = nullptr;
void* outDeviceAddr = nullptr;
aclTensor* x = nullptr;
aclTensor* weight = nullptr;
aclTensor* weightScale = nullptr;
aclTensor* groupList = nullptr;
aclTensor* bias = nullptr;
aclTensor* xScale = nullptr;
aclTensor* xOffset = nullptr;
aclTensor* smoothScale = nullptr;
aclTensor* out = nullptr;
std::vector<uint16_t> xHostData(GetShapeSize(xShape));
std::vector<uint8_t> weightHostData(GetShapeSize(weightShape));
std::vector<float> weightScaleHostData(GetShapeSize(weightScaleShape));
std::vector<int64_t> groupListHostData(GetShapeSize(groupListShape));
groupListHostData[0] = 7;
groupListHostData[0] = 32;
groupListHostData[0] = 40;
groupListHostData[0] = 64;
std::vector<float> xScaleHostData(GetShapeSize(xScaleShape));
std::vector<uint16_t> smoothScaleHostData(GetShapeSize(smoothScaleShape));
std::vector<uint16_t> outHostData(GetShapeSize(outShape));
ret = CreateAclTensor(xHostData, xShape, &xDeviceAddr, aclDataType::ACL_FLOAT16, &x);
CHECK_RET(ret == ACL_SUCCESS, return ret);
ret = CreateAclTensor(weightHostData, weightShape, &weightDeviceAddr, aclDataType::ACL_INT8, &weight);
CHECK_RET(ret == ACL_SUCCESS, return ret);
ret = CreateAclTensor(weightScaleHostData, weightScaleShape, &weightScaleDeviceAddr, aclDataType::ACL_FLOAT, &weightScale);
CHECK_RET(ret == ACL_SUCCESS, return ret);
ret = CreateAclTensor(groupListHostData, groupListShape, &groupListDeviceAddr, aclDataType::ACL_INT64, &groupList);
CHECK_RET(ret == ACL_SUCCESS, return ret);
ret = CreateAclTensor(xScaleHostData, xScaleShape, &xScaleDeviceAddr, aclDataType::ACL_FLOAT, &xScale);
CHECK_RET(ret == ACL_SUCCESS, return ret);
ret = CreateAclTensor(smoothScaleHostData, smoothScaleShape, &smoothScaleDeviceAddr, aclDataType::ACL_FLOAT16, &smoothScale);
CHECK_RET(ret == ACL_SUCCESS, return ret);
ret = CreateAclTensor(outHostData, outShape, &outDeviceAddr, aclDataType::ACL_FLOAT16, &out);
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;
// Call the first-phase API of aclnnQuantGroupedMatmulDequant.
ret = aclnnQuantGroupedMatmulDequantGetWorkspaceSize(x, weight, weightScale, groupList,
bias, xScale, xOffset, smoothScale,
quantMode, transposeWeight, out,
&workspaceSize, &executor);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnQuantGroupedMatmulDequantGetWorkspaceSize failed. ERROR: %d\n", ret); return ret);
// Allocate device memory based on the 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 aclnnQuantGroupedMatmulDequant.
ret = aclnnQuantGroupedMatmulDequant(workspaceAddr, workspaceSize, executor, stream);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnQuantGroupedMatmulDequant 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.
PrintOutResult(outShape, &outDeviceAddr);
// 6. Release aclTensor and aclScalar. Modify based on the specific API definition.
aclDestroyTensor(x);
aclDestroyTensor(weight);
aclDestroyTensor(weightScale);
aclDestroyTensor(groupList);
aclDestroyTensor(xScale);
aclDestroyTensor(smoothScale);
aclDestroyTensor(out);
// 7. Release device resources.
aclrtFree(xDeviceAddr);
aclrtFree(weightDeviceAddr);
aclrtFree(weightScaleDeviceAddr);
aclrtFree(groupListDeviceAddr);
aclrtFree(xScaleDeviceAddr);
aclrtFree(smoothScaleDeviceAddr);
aclrtFree(outDeviceAddr);
if (workspaceSize > 0) {
aclrtFree(workspaceAddr);
}
aclrtDestroyStream(stream);
aclrtResetDevice(deviceId);
aclFinalize();
return 0;
}