aclnnAscendQuant
Applicable Products
| Product | Supported |
|---|---|
| × | |
| √ | |
| √ | |
| × | |
| √ | |
| × |
Function
Description: Quantizes the input x based on the input scale and offset. The size of scale and offset must be the last dimension of x or 1.
Formula:
When sqrtMode is false, the formula is:
When sqrtMode is true, the formula is:
Function Prototype
Each operator is divided into a two-phase API. The "aclnnAscendQuantGetWorkspaceSize" API must be called first to obtain the workspace size required for computation and the executor that encapsulates the operator computation flow. Then, the "aclnnAscendQuant" API is called to perform the computation.
aclnnStatus aclnnAscendQuantGetWorkspaceSize(
const aclTensor *x,
const aclTensor *scale,
const aclTensor *offset,
bool sqrtMode,
const char *roundMode,
int32_t dstType,
const aclTensor *y,
uint64_t *workspaceSize,
aclOpExecutor **executor)aclnnStatus aclnnAscendQuant(
void *workspace,
uint64_t workspaceSize,
aclOpExecutor *executor,
aclrtStream stream)aclnnAscendQuantGetWorkspaceSize
Parameters:
Parameter Input/Output Description Instruction Data Type Data Format Dimension (Shape) Non-contiguous Tensor x (aclTensor*) Input The input to be quantized. Corresponds to `x` in the formula. - Supports null Tensor.
- If `dstType` is 3, the last dimension of the shape must be divisible by 8. If `dstType` is 29, the last dimension of the shape must be divisible by 2.
- When the data format is NZ, the shape supports only 3 dimensions, and the last dimension of the shape must be divisible by 8.
FLOAT32, FLOAT16, BFLOAT16 ND, NZ 1-8 √ scale (aclTensor*) Input Scale value in quantization. Corresponds to `scale` in the formula. - Supports empty tensor.
- `scale` supports a 1D tensor or a multi-dimensional tensor (when the shape is a 1D tensor, the 0th dimension of `scale` must equal the last dimension of `x` or equal 1; when the shape is a multi-dimensional tensor, the dimensions of `scale` must be consistent with `x`, the value of the last dimension must be consistent with `x`, and other dimensions must be 1).
- If the dtype of `x` is not FLOAT32, it must be consistent with the dtype of `x`.
- When the data format is NZ, all element values of `scale` are 1.
FLOAT32, FLOAT16, BFLOAT16 ND, NZ 1-8 √ offset (aclTensor*) Input Optional parameter, the offset value in dequantization. Corresponds to `offset` in the formula. - Supports null Tensor.
- The data type and shape must be consistent with `scale`.
- When the data format is NZ, the value is null, and the data type of offset is consistent with x.
FLOAT32, FLOAT16, BFLOAT16 ND, NZ 1-8 √ sqrtMode (bool) Input Specifies the logic for scale participation in computation. Corresponds to `sqrtMode` in the formula. When set to true, the formula is y = round((x * scale * scale) + offset); when set to false, the formula is y = round((x * scale) + offset). - - - - roundMode (char*) Input Specifies the conversion method for casting to INT8 output. - Supported values: round/ceil/trunc/floor.
- When the data format of input `x` is NZ, only round is supported.
- - - - dstType (int32_t) Input Specifies the data type of the output. - Supports values 2, 3, and 29, representing INT8, INT32, and INT4 respectively.
- When the data format of input `x` is NZ, supports value 3, representing INT32.
- - - - y(aclTensor*) Output Quantized computation output. Corresponds to `y` in the formula. - Supports null tensor.
- When the data type is INT32, the last dimension of shape is 1/8 of the last dimension of
x, and the other dimensions are consistent withx; for other types, the shape is consistent withx.
INT8, INT32, INT4 ND, NZ 1-8 √ workspaceSize (uint64_t*) Output Returns the workspace size to be requested on the Device side. - - - - - executor (aclOpExecutor**) Output Returns the operator executor, which contains the operator computation flow. - - - - - Atlas A2 training products/Atlas A2 inference products ,Atlas A3 training products/Atlas A3 inference products :When the data format of
x,scale, andoffsetis NZ, only FLOAT32 is supported as the data type.When the data format is NZ, the output
ysupports INT32 as the data type.
Atlas inference products :The data type of input
x,scale, andoffsetdoes not support BFLOAT16, and the data format does not support NZ.The output
ysupports only the INT8 data type and does not support the NZ data format.The input
dstTypesupports only the value 2, which indicates INT8.
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 Code Error Code Description ACLNN_ERR_PARAM_NULLPTR 161001 x, scale, or y is a null pointer. ACLNN_ERR_PARAM_INVALID 161002 The data types of x, scale, offset, and y are not within the supported range. The shapes of x, scale, offset, and y do not meet the constraints. roundMode is not within the valid value range. dstType is not within the valid value range. When the data type of y is INT4, the size of the last axis of x is not an even number. When the data type of y is INT32, the last axis of y's shape is not 1/8 of the size of the last axis of x's shape, or the sizes of the non-last axes of x and y are inconsistent.
aclnnAscendQuant
Parameters:
Parameter Input/Output Description workspace Input Workspace memory address applied for on the Device side. workspaceSize Input Workspace size applied for on the Device side, obtained from the first-phase API aclnnAscendQuantGetWorkspaceSize. executor Input Operator executor, which 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:
- aclnnAscendQuant defaults to a deterministic implementation.
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_ascend_quant.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 shape_size = 1;
for (auto i : shape) {
shape_size *= i;
}
return shape_size;
}
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 host data to device memory using aclrtMemcpy.
ret = aclrtMemcpy(*deviceAddr, size, hostData.data(), size, ACL_MEMCPY_HOST_TO_DEVICE);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtMemcpy failed. ERROR: %d\n", ret); return ret);
// Calculate strides for 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 using aclCreateTensor.
*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 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);
// Handle based on your own needs.
CHECK_RET(ret == 0, LOG_PRINT("Init acl failed. ERROR: %d\n", ret); return ret);
// 2. Construct inputs and outputs. Custom construction is required based on the API interface.
std::vector<int64_t> selfShape = {4, 2};
std::vector<int64_t> scaleShape = {2};
std::vector<int64_t> offsetShape = {2};
std::vector<int64_t> outShape = {4, 2};
void* selfDeviceAddr = nullptr;
void* scaleDeviceAddr = nullptr;
void* offsetDeviceAddr = nullptr;
void* outDeviceAddr = nullptr;
aclTensor* self = nullptr;
aclTensor* scale = nullptr;
aclTensor* offset = nullptr;
aclTensor* out = nullptr;
std::vector<float> selfHostData = {0, 1, 2, 3, 4, 5, 6, 7};
std::vector<float> scaleHostData = {1, 2};
std::vector<float> offsetHostData = {1, 2};
std::vector<int8_t> outHostData = {0, 0, 0, 0, 0, 0, 0, 0};
// Create the self aclTensor.
ret = CreateAclTensor(selfHostData, selfShape, &selfDeviceAddr, aclDataType::ACL_FLOAT, &self);
CHECK_RET(ret == ACL_SUCCESS, return ret);
// Create the scale aclTensor.
ret = CreateAclTensor(scaleHostData, scaleShape, &scaleDeviceAddr, aclDataType::ACL_FLOAT, &scale);
CHECK_RET(ret == ACL_SUCCESS, return ret);
// Create the offset aclTensor.
ret = CreateAclTensor(offsetHostData, offsetShape, &offsetDeviceAddr, aclDataType::ACL_FLOAT, &offset);
CHECK_RET(ret == ACL_SUCCESS, return ret);
// Create the out aclTensor.
ret = CreateAclTensor(outHostData, outShape, &outDeviceAddr, aclDataType::ACL_INT8, &out);
CHECK_RET(ret == ACL_SUCCESS, return ret);
const int32_t dstType = 2;
bool sqrtMode = false;
const char* roundMode = "round";
// 3. Call the CANN operator library API. Replace with the specific API.
uint64_t workspaceSize = 0;
aclOpExecutor* executor;
// Call the first-phase API of aclnnAscendQuant.
ret = aclnnAscendQuantGetWorkspaceSize(
self, scale, offset, sqrtMode, roundMode, dstType, out, &workspaceSize, &executor);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnAscendQuantGetWorkspaceSize failed. ERROR: %d\n", ret); return ret);
// Allocate device memory based on the workspaceSize calculated 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 aclnnAscendQuant.
ret = aclnnAscendQuant(workspaceAddr, workspaceSize, executor, stream);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnAscendQuant failed. ERROR: %d\n", ret); return ret);
// 4. (Boilerplate) Wait for 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 device memory to host memory. Modify based on the specific API definition, and check the data in resultData.
auto size = GetShapeSize(outShape);
std::vector<int8_t> resultData(size, 0);
ret = aclrtMemcpy(
resultData.data(), resultData.size() * sizeof(resultData[0]), outDeviceAddr, size * sizeof(int8_t),
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: %d\n", i, resultData[i]);
}
// 6. Release aclTensor. Modify based on the specific API definition.
aclDestroyTensor(self);
aclDestroyTensor(scale);
aclDestroyTensor(offset);
aclDestroyTensor(out);
// 7. Release device resources. Modify based on the specific API definition.
aclrtFree(selfDeviceAddr);
aclrtFree(scaleDeviceAddr);
aclrtFree(offsetDeviceAddr);
aclrtFree(outDeviceAddr);
if (workspaceSize > 0) {
aclrtFree(workspaceAddr);
}
aclrtDestroyStream(stream);
aclrtResetDevice(deviceId);
aclFinalize();
return 0;
}