aclnnCalculateMatmulWeightSize
Applicable Products
| Product | Supported |
|---|---|
| √ | |
| √ | |
| √ | |
| × | |
| √ | |
| × |
Function
Description:
Calculates the size of the weight to be allocated for the Matmul operator under ND format input. This API is used only to determine how much size is needed for preprocessing the weight tensor so that the Matmul operator achieves optimal execution performance. For example, given an input of [510, 510]: for performance considerations, this function changes the shape to [512, 512], and therefore modifies the referenced input to 262144.
Formula:
Function Prototype
aclnnStatus aclnnCalculateMatmulWeightSize(
const aclIntArray *tensorShape,
uint64_t *weightTensorSize)aclnnCalculateMatmulWeightSize
Parameters:
Parameter Input/Output Description Instruction Data Type Data Format Shape Non-contiguous Tensor tensorShape (aclIntArray*) Input Represents the shape of the weight matrix loaded for this Matmul operation, i.e., Shapesize in the formula. The input shape supports 2 to 6 dimensions, i.e., (batch, n, k), where batch represents the batch size of the weight matrix, supporting 0 to 4 dimensions, n represents the size of the first dimension of a single batch weight matrix, and k represents the size of the second dimension of a single batch weight matrix. Empty arrays are not supported. FLOAT16, BFLOAT16 - 2-6 - weightTensorSize (uint64_t*) Output Calculates the number of elements required for weight under the given input based on the internal MatMul processing logic. Corresponds to result in the formula. - - - - - Return value
aclnnStatus: return code. For details, see aclnn Return Codes.
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 input is a null pointer. ACLNN_ERR_PARAM_INVALID 161002 The computation process fails.
Constraints
Deterministic computation:
- aclnnCalculateMatmulWeightSize 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 <cmath>
#include "acl/acl.h"
#include "aclnnop/aclnn_mm.h"
#include "aclnnop/aclnn_trans_matmul_weight.h"
#include "aclnnop/aclnn_cast.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;
}
// Convert the uint16_t representation of FP16 to float representation.
float Fp16ToFloat(uint16_t h) {
int s = (h >> 15) & 0x1; // sign
int e = (h >> 10) & 0x1F; // exponent
int f = h & 0x3FF; // fraction
if (e == 0) {
// Zero or Denormal
if (f == 0) {
return s ? -0.0f : 0.0f;
}
// Denormals
float sig = f / 1024.0f;
float result = sig * pow(2, -24);
return s ? -result : result;
} else if (e == 31) {
// Infinity or NaN
return f == 0 ? (s ? -INFINITY : INFINITY) : NAN;
}
// Normalized FP32
float result = (1.0f + f / 1024.0f) * pow(2, e - 15);
return s ? -result : result;
}
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 allocate memory on the device side.
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 side 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);
// 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;
}
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 = aclnnCalculateMatmulWeightSize(mat2Size, &size);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnCalculateMatmulWeightSize failed. ERROR: %d\n", ret); return ret);
size *= sizeof(T);
// Call aclrtMalloc to allocate memory on the device side.
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 side 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);
// 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];
}
std::vector<int64_t> storageShape;
storageShape.push_back(GetShapeSize(shape));
// Call the aclCreateTensor API 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;
}
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 = 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> selfShape = {16, 32};
std::vector<int64_t> mat2Shape = {32, 16};
std::vector<int64_t> outShape = {16, 16};
void* selfDeviceAddr = nullptr;
void* mat2DeviceAddr = nullptr;
void* outDeviceAddr = nullptr;
aclTensor* self = nullptr;
aclTensor* mat2 = nullptr;
aclTensor* out = nullptr;
std::vector<uint16_t> selfHostData(512, 0x3C00); // float16_t uses 0x3C00 to represent 1 in int_16.
std::vector<uint16_t> mat2HostData(512, 0x3C00); // float16_t uses 0x3C00 to represent 1 in int_16.
std::vector<uint16_t> outHostData(256, 0);
// Create the self aclTensor.
ret = CreateAclTensor(selfHostData, selfShape, &selfDeviceAddr, aclDataType::ACL_FLOAT16, &self);
CHECK_RET(ret == ACL_SUCCESS, return ret);
// Create the other aclTensor.
ret = CreateAclTensorWeight(mat2HostData, mat2Shape, &mat2DeviceAddr, aclDataType::ACL_FLOAT16, &mat2);
CHECK_RET(ret == ACL_SUCCESS, return ret);
// Create the out aclTensor.
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.
int8_t cubeMathType = 1;
uint64_t workspaceSize = 0;
aclOpExecutor* executor;
// Call TransWeight.
ret = aclnnTransMatmulWeightGetWorkspaceSize(mat2, &workspaceSize, &executor);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnTransMatmulWeightGetWorkspaceSize failed. ERROR: %d\n", ret); return ret);
// Apply for 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 aclnnTransMatmulWeight.
ret = aclnnTransMatmulWeight(workspaceAddr, workspaceSize, executor, stream);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnTransMatmulWeight failed. ERROR: %d\n", ret); return ret);
// Call the first-phase API of aclnnMm.
uint64_t workspaceSizeMm = 0;
ret = aclnnMmGetWorkspaceSize(self, mat2, out, cubeMathType, &workspaceSizeMm, &executor);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnMmGetWorkspaceSize failed. ERROR: %d\n", ret); return ret);
// Apply for device memory based on the workspaceSize calculated by the first-phase API.
void* workspaceAddrMm = nullptr;
if (workspaceSizeMm > 0) {
ret = aclrtMalloc(&workspaceAddrMm, workspaceSizeMm, 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 aclnnMm.
ret = aclnnMm(workspaceAddrMm, workspaceSizeMm, executor, stream);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnMm 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 the device side to the host side. Modify this based on the specific API definition.
auto size = GetShapeSize(outShape);
std::vector<uint16_t> resultData(size, 0);
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);
// In C, fp16 data cannot be printed directly. Use uint16 to read it and manually convert the binary representation to float for fp16.
for (int64_t i = 0; i < size; i++) {
float fp16Float = Fp16ToFloat(resultData[i]);
LOG_PRINT("result[%ld] is: %f\n", i, fp16Float);
}
// 6. Release aclTensor and aclScalar. Modify this based on the specific API definition.
aclDestroyTensor(self);
aclDestroyTensor(mat2);
aclDestroyTensor(out);
// 7. Release device resources. Modify this based on the specific API definition.
aclrtFree(selfDeviceAddr);
aclrtFree(mat2DeviceAddr);
aclrtFree(outDeviceAddr);
if (workspaceSize > 0) {
aclrtFree(workspaceAddr);
}
if (workspaceSizeMm > 0) {
aclrtFree(workspaceAddrMm);
}
aclrtDestroyStream(stream);
aclrtResetDevice(deviceId);
aclFinalize();
return 0;
}