aclnnCalculateMatmulWeightSizeV2
Supported Products
| Product | Supported |
|---|---|
| √ | |
| √ | |
| × | |
| √ | |
| × |
Function Description
Description: Calculates the size of the space (in the number of elements) required for converting the Matmul operator from the ND format to the NZ format. This API is used only to determine the size required for preprocessing the weight tensor to optimize the execution performance of the Matmul operator. Example:
When the input shape is [510, 510] and the data type is Float16 or Bfloat16, the function reshapes it to [512, 512]. As a result, the input is interpreted as having 262,144 elements.
When the input shape is [510, 270] and the data type INT8, the function reshapes it to [512, 288]. As a result, the input is interpreted as having 147,456 elements.
Formula:
Prototype
aclnnStatus aclnnCalculateMatmulWeightSizeV2(const aclIntArray *tensorShape, aclDataType dataType, uint64_t *weightTensorSize)
aclnnCalculateMatmulWeightSizeV2
Parameters:
tensorShape(aclIntArray *, computation input): specifies the shape of the weight matrix for the Matmul operation. This parameter corresponds toShapesizein the formula. It is represented as an aclIntArray on the host side. Only 2D to 6D shapes (batch, n, k) are supported, wherebatchis the batch size of the weight matrix (0 to 4 dimensions),nis the size of the first dimension in a single batch, andkis the size of the second dimension in the single batch. Empty arrays are not supported.weightDtype(aclDataType, computation input): data type of the weight tensor. The value can be FLOAT16, BFLOAT16, or INT8.weightTensorSize(uint64_t *, computation output): the size (in number of elements) of the space required for the converted NZ format, corresponding toresultin the formula.
Returns:
aclnnStatus: status code. For details, see aclnn Return Codes.The first-phase API implements input parameter verification. The following errors may be thrown: 161001 (ACLNN_ERR_PARAM_NULLPTR): 1. The input is a null pointer. 161002 (ACLNN_ERR_PARAM_INVALID): 1. Null tensor input is not supported. 2. The input shape dimension does not meet the requirements. 3. The input data type does not meet the requirements. 361001 (ACLNN_ERR_RUNTIME_ERROR): The product model is not supported.
Constraints
- Deterministic computation:
aclnnCalculateMatmulWeightSizeV2defaults to a deterministic implementation.
Example
Atlas A3 training products/Atlas A3 inference products ,Atlas A2 training products/Atlas A2 inference products , andAtlas inference products :The following is the sample code for a two-dimensional
tensorShape, which is for reference only. For details, see Compilation and Running Sample. For details about the sample code for a multi-dimensional (3D–6D) tensorShape, see aclnnQuantMatmulV3. aclnnWeightQuantBatchMatmulV2 and aclnnWeightQuantBatchMatmulV3 are available for fake quantization. aclnnWeightQuantBatchMatmulV2 is used as an example.#include <iostream> #include <vector> #include "acl/acl.h" #include "aclnnop/aclnn_cast.h" #include "aclnnop/aclnn_weight_quant_batch_matmul_v2.h" #include "aclnnop/aclnn_trans_matmul_weight.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 code 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 CreateAclTensorWeight(const std::vector<T>& hostData, const std::vector<int64_t>& shape, void** deviceAddr, aclDataType dataType, aclTensor** tensor) { auto size = static_cast<uint64_t>(GetShapeSize(shape)); const aclIntArray* mat2Size = aclCreateIntArray(shape.data(), shape.size()); auto ret = aclnnCalculateMatmulWeightSizeV2(mat2Size, dataType, &size); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnCalculateMatmulWeightSizeV2 failed. ERROR: %d\n", ret); return ret); size *= sizeof(T); // Call aclrtMalloc to allocate memory on the device. 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 the data from the host to the device. 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]; } std::vector<int64_t> storageShape; storageShape.push_back(GetShapeSize(shape)); // Call aclCreateTensor to create an aclTensor. *tensor = aclCreateTensor(shape.data(), shape.size(), dataType, strides.data(), 0, aclFormat::ACL_FORMAT_ND, storageShape.data(), storageShape.size(), *deviceAddr); return 0; } 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 memory on the device. 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 the data from the host to the device. 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, aclFormat::ACL_FORMAT_ND, shape.data(), shape.size(), *deviceAddr); return 0; } int main() { // 1. Boilerplate code for device/stream initialization. For details, see the ACL API manual. // Set the device ID in use. 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 inputs and outputs based on the API definition. std::vector<int64_t> xShape = {16, 32}; std::vector<int64_t> weightShape = {32, 16}; std::vector<int64_t> yShape = {16, 16}; void* xDeviceAddr = nullptr; void* weightDeviceAddr = nullptr; void* yDeviceAddr = nullptr; aclTensor* x = nullptr; aclTensor* weight = nullptr; aclTensor* y = nullptr; std::vector<float> xHostData(512, 1); std::vector<int8_t> weightHostData(512, 1); std::vector<float> yHostData(256, 0); std::vector<int64_t> antiquantScaleShape = {16}; void* antiquantScaleDeviceAddr = nullptr; aclTensor* antiquantScale = nullptr; std::vector<float> antiquantScaleHostData(16, 1); // Create an x aclTensor. ret = CreateAclTensor(xHostData, xShape, &xDeviceAddr, aclDataType::ACL_FLOAT, &x); CHECK_RET(ret == ACL_SUCCESS, return ret); // Create a weight aclTensor. ret = CreateAclTensorWeight(weightHostData, weightShape, &weightDeviceAddr, aclDataType::ACL_INT8, &weight); CHECK_RET(ret == ACL_SUCCESS, return ret); // Create a y aclTensor. ret = CreateAclTensor(yHostData, yShape, &yDeviceAddr, aclDataType::ACL_FLOAT, &y); CHECK_RET(ret == ACL_SUCCESS, return ret); // Create an antiquantScale aclTensor. ret = CreateAclTensor(antiquantScaleHostData, antiquantScaleShape, &antiquantScaleDeviceAddr, aclDataType::ACL_FLOAT, &antiquantScale); CHECK_RET(ret == ACL_SUCCESS, return ret); // Create an xFp16 aclTensor. void* xFp16DeviceAddr = nullptr; aclTensor* xFp16 = nullptr; ret = CreateAclTensor(xHostData, xShape, &xFp16DeviceAddr, aclDataType::ACL_FLOAT16, &xFp16); CHECK_RET(ret == ACL_SUCCESS, return ret); // Create an antiquantScale aclTensor. void* antiquantScaleFp16DeviceAddr = nullptr; aclTensor* antiquantScaleFp16 = nullptr; ret = CreateAclTensor(antiquantScaleHostData, antiquantScaleShape, &antiquantScaleFp16DeviceAddr, aclDataType::ACL_FLOAT16, &antiquantScaleFp16); CHECK_RET(ret == ACL_SUCCESS, return ret); // Create a yFp16 aclTensor. void* yFp16DeviceAddr = nullptr; aclTensor* yFp16 = nullptr; ret = CreateAclTensor(yHostData, yShape, &yFp16DeviceAddr, aclDataType::ACL_FLOAT16, &yFp16); CHECK_RET(ret == ACL_SUCCESS, return ret); // 3. Call the CANN operator library API, which needs to be replaced with the actual one. uint64_t workspaceSize = 0; aclOpExecutor* executor; void* workspaceAddr = nullptr; // Call TransWeight. ret = aclnnTransMatmulWeightGetWorkspaceSize(weight, &workspaceSize, &executor); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnTransMatmulWeightGetWorkspaceSize failed. ERROR: %d\n", ret); return ret); // Allocate device memory based on workspaceSize calculated by the first-phase API. if (workspaceSize > 0) { ret = aclrtMalloc(&workspaceAddr, workspaceSize, ACL_MEM_MALLOC_HUGE_FIRST); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("allocate workspace failed. ERROR: %d\n", ret); return ret); } // Call the second-phase API of aclnnTransMatmulWeight. ret = aclnnTransMatmulWeight(workspaceAddr, workspaceSize, executor, stream); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnTransMatmulWeight failed. ERROR: %d\n", ret); return ret); workspaceSize = 0; // Call cast to generate the FP16 input. ret = aclnnCastGetWorkspaceSize(x, aclDataType::ACL_FLOAT16, xFp16, &workspaceSize, &executor); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnCastGetWorkspaceSize0 failed. ERROR: %d\n", ret); return ret); // Allocate device memory based on workspaceSize calculated by the first-phase API. if (workspaceSize > 0) { ret = aclrtMalloc(&workspaceAddr, workspaceSize, ACL_MEM_MALLOC_HUGE_FIRST); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("allocate workspace failed. ERROR: %d\n", ret); return ret); } ret = aclnnCast(workspaceAddr, workspaceSize, executor, stream); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnCast0 failed. ERROR: %d\n", ret); return ret); ret = aclrtSynchronizeStream(stream); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtSynchronizeStream failed. ERROR: %d\n", ret); return ret); ret = aclnnCastGetWorkspaceSize(antiquantScale, aclDataType::ACL_FLOAT16, antiquantScaleFp16, &workspaceSize, &executor); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnCastGetWorkspaceSize1 failed. ERROR: %d\n", ret); return ret); // Allocate device memory based on workspaceSize calculated by the first-phase API. if (workspaceSize > 0) { ret = aclrtMalloc(&workspaceAddr, workspaceSize, ACL_MEM_MALLOC_HUGE_FIRST); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("allocate workspace failed. ERROR: %d\n", ret); return ret); } ret = aclnnCast(workspaceAddr, workspaceSize, executor, stream); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnCast1 failed. ERROR: %d\n", ret); return ret); ret = aclrtSynchronizeStream(stream); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtSynchronizeStream failed. ERROR: %d\n", ret); return ret); // Call the first-phase API of aclnnWeightQuantBatchMatmulV2. ret = aclnnWeightQuantBatchMatmulV2GetWorkspaceSize(xFp16, weight, antiquantScaleFp16, nullptr, nullptr, nullptr, nullptr, 0, yFp16, &workspaceSize, &executor); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnWeightQuantBatchMatmulV2GetWorkspaceSize failed. ERROR: %d\n", ret); return ret); // Allocate device memory based on workspaceSize calculated by the first-phase API. if (workspaceSize > 0) { ret = aclrtMalloc(&workspaceAddr, workspaceSize, ACL_MEM_MALLOC_HUGE_FIRST); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("allocate workspace failed. ERROR: %d\n", ret); return ret); } // Call the second-phase API of aclnnWeightQuantBatchMatmulV2. ret = aclnnWeightQuantBatchMatmulV2(workspaceAddr, workspaceSize, executor, stream); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnWeightQuantBatchMatmulV2 failed. ERROR: %d\n", ret); return ret); // 4. (Boilerplate code) 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); // Convert the output into FP32. ret = aclnnCastGetWorkspaceSize(yFp16, aclDataType::ACL_FLOAT, y, &workspaceSize, &executor); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnCastGetWorkspaceSize2 failed. ERROR: %d\n", ret); return ret); // Allocate device memory based on workspaceSize calculated by the first-phase API. if (workspaceSize > 0) { ret = aclrtMalloc(&workspaceAddr, workspaceSize, ACL_MEM_MALLOC_HUGE_FIRST); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("allocate workspace failed. ERROR: %d\n", ret); return ret); } ret = aclnnCast(workspaceAddr, workspaceSize, executor, stream); CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnCast2 failed. ERROR: %d\n", ret); return ret); 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 to the host. Modify the code based on the API definition. auto size = GetShapeSize(yShape); std::vector<float> resultData(size, 0); ret = aclrtMemcpy(resultData.data(), resultData.size() * sizeof(resultData[0]), yDeviceAddr, 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: %f\n", i, resultData[i]); } // 6. Destroy aclTensor and aclScalar. Modify the code based on the API definition. aclDestroyTensor(x); aclDestroyTensor(weight); aclDestroyTensor(antiquantScale); aclDestroyTensor(y); aclDestroyTensor(xFp16); aclDestroyTensor(antiquantScaleFp16); aclDestroyTensor(yFp16); // 7. Free device resources. aclrtFree(xDeviceAddr); aclrtFree(weightDeviceAddr); aclrtFree(antiquantScaleDeviceAddr); aclrtFree(yDeviceAddr); aclrtFree(xFp16DeviceAddr); aclrtFree(antiquantScaleFp16DeviceAddr); aclrtFree(yFp16DeviceAddr); if (workspaceSize > 0) { aclrtFree(workspaceAddr); } aclrtDestroyStream(stream); aclrtResetDevice(deviceId); aclFinalize(); return 0; }