aclnnCalculateConvolutionWeightSize
Applicable Products
| Product | Supported |
|---|---|
| × | |
| × | |
| × | |
| × | |
| √ | |
| × |
Function
Description: Calculates the memory size required for the weight tensor of the Convolution operator under the NCHW format input. Only the Float16 data type is supported. This API is used solely to determine the size needed for preprocessing the weight tensor to achieve optimal performance of the Convolution operator.
Example: For an input of [2, 4, 8, 8], the function changes the shape to [64, 1, 16, 16] for performance reasons, and therefore modifies the input to 16384.
Function Prototype
aclnnStatus aclnnCalculateConvolutionWeightSize(
const aclIntArray* tensorShape,
bool transposed,
int64_t groups,
aclDataType dataType,
uint64_t* weightTensorSize)aclnnCalculateConvolutionWeightSize
Parameters:
Parameter Input/Output Description Instruction Data Type Data Format Dimension (shape) Non-contiguous Tensor tensorShape (aclIntArray*) Input Specifies the shape for loading the weight matrix of this convolution. Only supports a 4-dimensional shape in NCHW format, with each dimension ≥ 0. Supports empty tensors, in which case weightTensorSize is returned as 0. INT64 NCHW - - transposed (bool) Input Indicates whether it is a transposed convolution. Only false is currently supported. BOOL - - - groups (int64_t) Input Indicates the number of block connections from input channels to output channels. Value range: [1, 65535]. INT64 - - - dataType (aclDataType) Input Indicates the data type of the weight after conversion. Only ACL_FLOAT16 is supported. - - - - weightTensorSize (uint64_t*) Output Calculates the number of elements required for the weight tensor based on the internal processing logic of Convolution. - INT64 - - - Return value
aclnnStatus: Return status 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 input is a null pointer. |
| ACLNN_ERR_PARAM_INVALID | 161002 | The input shape verification fails or other inputs do not meet expectations. |
Constraints
Deterministic computation:
- aclnnCalculateConvolutionWeightSize defaults to a deterministic implementation.
Only the forward Conv2D scenario is supported.
Transposed convolution is not supported.
Empty tensors are supported: weightTensorSize is returned as 0.
Example
The following is a sample code for reference only. For details about compilation and execution, see Compile and Run Samples.
#include <iostream>
#include <vector>
#include "acl/acl.h"
#include "aclnnop/aclnn_convolution.h"
#include "aclnnop/aclnn_trans_convolution_weight.h"
using namespace std;
#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 the stream.
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);
// Call aclrtMemcpy to copy host-side data to device-side 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_NCHW,
shape.data(), shape.size(), *deviceAddr);
return 0;
}
template <typename T>
int CreateWeightAclTensor(const std::vector<T> &hostData, const std::vector<int64_t> &shape, void **deviceAddr,
aclDataType dataType, aclTensor **tensor, uint64_t &TransWeightSize)
{
auto size = GetShapeSize(shape) * sizeof(T);
// Call the transweight host API to calculate the actual number of elements.
const aclIntArray* weightSize = aclCreateIntArray(shape.data(), shape.size());
auto ret = aclnnCalculateConvolutionWeightSize(weightSize, false, 1, aclDataType::ACL_FLOAT16, &TransWeightSize);
// Call aclrtMalloc to apply for 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];
}
// Call the aclCreateTensor API to create an aclTensor.
*tensor = aclCreateTensor(shape.data(), shape.size(), dataType, strides.data(), 0, aclFormat::ACL_FORMAT_NCHW,
shape.data(), shape.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);
// Construct the input and output. Customize the construction based on the API.
std::vector<int64_t> inputShape = {1, 4, 16, 16};
std::vector<int64_t> weightShape = {2, 4, 8, 8};
std::vector<int64_t> biasShape = {2};
std::vector<int64_t> outShape = {1, 2, 9, 9};
void* inputDeviceAddr = nullptr;
void* weightDeviceAddr = nullptr;
void* biasDeviceAddr = nullptr;
void* outDeviceAddr = nullptr;
aclTensor* input = nullptr;
aclTensor* weight = nullptr;
aclTensor* bias = nullptr;
aclTensor* out = nullptr;
// aclTensor* transWeight = nullptr;
std::vector<float> inputHostData(1024, 1);
std::vector<float> weightHostData(512, 1);
std::vector<float> biasHostData(2, 1);
std::vector<float> outHostData(162, 0);
uint64_t transWeightSize = 0;
// Create the self aclTensor.
ret = CreateAclTensor(inputHostData, inputShape, &inputDeviceAddr, aclDataType::ACL_FLOAT16, &input);
CHECK_RET(ret == ACL_SUCCESS, return ret);
// Create the other aclTensor.
ret = CreateWeightAclTensor(weightHostData, weightShape, &weightDeviceAddr, aclDataType::ACL_FLOAT16,
&weight, transWeightSize);
CHECK_RET(ret == ACL_SUCCESS, return ret);
// Create the bias aclTensor.
ret = CreateAclTensor(biasHostData, biasShape, &biasDeviceAddr, aclDataType::ACL_FLOAT16, &bias);
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);
// Create the Transweight acltensor.
void* transWeightDeviceAddr = nullptr;
uint64_t size = transWeightSize * sizeof(float) / 2;
// size = 8192 * sizeof(float_t);
ret = aclrtMalloc(&transWeightDeviceAddr, size, ACL_MEM_MALLOC_HUGE_FIRST);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtMalloc failed. ERROR: %d\n", ret);return ret);
std::vector<float> transData;
transData.resize(transWeightSize * 2);
// Call aclrtMemcpy to copy host-side data to transData.data() on the device memory.
ret = aclrtMemcpy(transWeightDeviceAddr, size, transData.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 the contiguous tensor.
vector<int64_t> shape = weightShape;
std::vector<int64_t> s(shape.size(), 1);
for (int64_t i = shape.size() - 2; i >= 0; i--) {
s[i] = shape[i + 1] * s[i + 1];
}
aclTensor* transWeight = aclCreateTensor(shape.data(), shape.size(), aclDataType::ACL_FLOAT16, s.data(), 0, aclFormat::ACL_FORMAT_NCHW,
shape.data(), shape.size(), transWeightDeviceAddr);
// 3. Call the CANN operator library API. Replace with the actual API name.
int8_t cubeMathType = 0;
uint64_t workspaceSize = 0;
aclOpExecutor* executor;
bool transposed = 0;
uint64_t groups = 1;
// Call TransWeight.
ret = aclnnTransConvolutionWeightGetWorkspaceSize(weight, transposed, groups, transWeight,
&workspaceSize, &executor);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnTransConvolutionWeightGetWorkspaceSize 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 aclnnTransConvolutionWeight.
ret = aclnnTransConvolutionWeight(workspaceAddr, workspaceSize, executor, stream);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnTransConvolutionWeight failed. ERROR: %d\n", ret); return ret);
std::vector<int64_t> convStrides = {1, 1, 1, 1};
std::vector<int64_t> convPads = {0, 0, 0, 0};
std::vector<int64_t> convOutPads = {1, 1, 1, 1};
std::vector<int64_t> convDilations = {1, 1, 1, 1};
aclIntArray *strides = aclCreateIntArray(convStrides.data(), 2);
aclIntArray *pads = aclCreateIntArray(convPads.data(), 2);
aclIntArray *outPads = aclCreateIntArray(convOutPads.data(), 2);
aclIntArray *dilations = aclCreateIntArray(convDilations.data(), 2);
// 3. Call the CANN operator library API. Replace it with the specific API.
workspaceSize = 0;
// Call the first-phase API of aclnnConvolution.
ret = aclnnConvolutionGetWorkspaceSize(input, transWeight, bias, strides, pads, dilations, false, outPads, groups,
out, cubeMathType, &workspaceSize, &executor);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnConvolutionGetWorkspaceSize failed. ERROR: %d\n", ret); return ret);
// Apply for device memory based on the workspaceSize calculated by the first-phase API.
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 aclnnConvolution.
ret = aclnnConvolution(workspaceAddr, workspaceSize, executor, stream);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnConvolution failed. ERROR: %d\n", ret); return ret);
// 4. (Boilerplate) Synchronize and 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 side memory to the host side. Modify this based on the specific API definition.
size = GetShapeSize(outShape);
std::vector<float> resultData(size, 0);
ret = aclrtMemcpy(resultData.data(), resultData.size() * sizeof(resultData[0]), outDeviceAddr,
size * sizeof(float), 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. Release aclTensor and aclScalar. Modify this based on the specific API definition.
aclDestroyTensor(input);
aclDestroyTensor(weight);
aclDestroyTensor(transWeight);
aclDestroyTensor(bias);
aclDestroyTensor(out);
aclDestroyIntArray(strides);
aclDestroyIntArray(pads);
aclDestroyIntArray(outPads);
aclDestroyIntArray(dilations);
// 7. Release device resources. Modify based on the specific API definition.
aclrtFree(inputDeviceAddr);
aclrtFree(weightDeviceAddr);
aclrtFree(transWeightDeviceAddr);
aclrtFree(outDeviceAddr);
if (workspaceSize > 0) {
aclrtFree(workspaceAddr);
}
aclrtDestroyStream(stream);
aclrtResetDevice(deviceId);
aclFinalize();
return 0;
}