aclnnTransConvolutionWeight
Applicable Products
| Applicable Products | Supported |
|---|---|
| × | |
| × | |
| × | |
| × | |
| √ | |
| × |
Function
It must be used together with the aclnnCalculateConvolutionWeightSize API to create a weight tensor that is performance-friendly for the Convolution operator computation.
Function Prototype
Each operator consists of a two-phase API. You must first call the "aclnnTransConvolutionWeightGetWorkspaceSize" API to obtain the workspace size required for computation and the executor that encapsulates the operator computation process, and then call the "aclnnTransConvolutionWeight" API to perform the computation.
aclnnStatus aclnnTransConvolutionWeightGetWorkspaceSize(
const aclTensor* weightIn,
bool transposed,
const int64_t groups,
aclTensor* weightOut,
uint64_t* workspaceSize,
aclOpExecutor** executor)aclnnStatus aclnnTransConvolutionWeight(
void* workspace,
uint64_t workspaceSize,
aclOpExecutor* executor,
aclrtStream stream)aclnnTransConvolutionWeightGetWorkspaceSize
Parameters:
Parameter Input/Output Description Instruction Data Type Data Format Shape Non-contiguous Tensor weightIn (uint64_t*) Input Represents the weightTensor of a convolution to be processed. Supports empty tensor input. When weightIn is an empty tensor, weightOut must also be an empty tensor. FLOAT16, FLOAT32 NCHW 4 √ transposed (bool) Input Indicates whether it is a transposed convolution. Currently, only false is supported. BOOL - - - groups (int64_t) Input Indicates the number of block connections from input channels to output channels. The value range is [1, 65535]. INT64 - - - weightOut (aclTensor*) Output Represents the tensor obtained after converting the input weight to a private format. Supports empty tensor output. When weightOut is an empty tensor, weightIn must also be an empty tensor. FLOAT16 NCHW 4 - workspaceSize(uint64_t*) Output Returns the workspace size to be allocated on the Device side. Must not be a null pointer. Returns 0 for an empty tensor scenario. - - - - executor (aclOpExecutor**) Output Returns the operator executor, which contains the operator computation flow. Must not be a null pointer. - - - - 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 data type, data format, or other parameters of the input and output tensors do not meet expectations. For example, the input weightIn is of a non-FLOAT16 or non-FLOAT32 data type, or in a non-NCHW data format; or the empty tensor states of weightIn and weightOut are inconsistent.
aclnnTransConvolutionWeight
Parameters:
Parameter Input/Output Description workspace Input Address of the workspace memory allocated on the Device side. workspaceSize Input Size of the workspace allocated on the Device side, obtained from the first-phase API aclnnTransConvolutionWeightGetWorkspaceSize. executor Input The op executor, which contains the operator computation flow. stream Input Specifies the stream that executes the task. Return value
aclnnStatus: Return status code. For details, see aclnn Return Code.
Constraints
Deterministic computation:
- aclnnTransConvolutionWeight defaults to a deterministic implementation.
Only the forward Conv2D scenario is supported.
Transposed convolution is not supported.
Does not support the cache capability.
Supports empty tensors: when both weightIn and weightOut are empty tensors, no actual conversion is performed and workspaceSize returns 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 <memory>
#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 CHECK_FREE_RET(cond, return_expr) \
do { \
if (!(cond)) { \
Finalize(deviceId, stream); \
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: stream 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 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 data to 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;
}
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.
aclIntArray* weightSize = aclCreateIntArray(shape.data(), shape.size());
std::unique_ptr<aclIntArray, aclnnStatus (*)(const aclIntArray *)> weightSizePtr(weightSize, aclDestroyIntArray);
auto ret = aclnnCalculateConvolutionWeightSize(weightSize, false, 1, aclDataType::ACL_FLOAT16, &TransWeightSize);
// Call aclrtMalloc to allocate device memory.
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 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;
}
void Finalize(int32_t deviceId, aclrtStream& stream)
{
aclrtDestroyStream(stream);
aclrtResetDevice(deviceId);
aclFinalize();
}
int aclnnTransConvolutionWeightTest(int32_t deviceId, aclrtStream& stream) {
auto ret = Init(deviceId, &stream);
CHECK_FREE_RET(ret == ACL_SUCCESS, LOG_PRINT("Init acl failed. ERROR: %d\n", ret); return ret);
// 2. Construct the input and output. Custom construction is required based on the API interface.
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;
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 input aclTensor.
ret = CreateAclTensor(inputHostData, inputShape, &inputDeviceAddr, aclDataType::ACL_FLOAT, &input);
std::unique_ptr<aclTensor, aclnnStatus (*)(const aclTensor *)> selfTensorPtr(input, aclDestroyTensor);
std::unique_ptr<void, aclError (*)(void *)> inputDeviceAddrPtr(inputDeviceAddr, aclrtFree);
CHECK_FREE_RET(ret == ACL_SUCCESS, return ret);
// Create the weight aclTensor.
ret = CreateWeightAclTensor(weightHostData, weightShape, &weightDeviceAddr, aclDataType::ACL_FLOAT,
&weight, transWeightSize);
std::unique_ptr<aclTensor, aclnnStatus (*)(const aclTensor *)> weightTensorPtr(weight, aclDestroyTensor);
std::unique_ptr<void, aclError (*)(void *)> weightDeviceAddrPtr(weightDeviceAddr, aclrtFree);
CHECK_FREE_RET(ret == ACL_SUCCESS, return ret);
// Create the bias aclTensor.
ret = CreateAclTensor(biasHostData, biasShape, &biasDeviceAddr, aclDataType::ACL_FLOAT, &bias);
std::unique_ptr<aclTensor, aclnnStatus (*)(const aclTensor *)> biasTensorPtr(bias, aclDestroyTensor);
std::unique_ptr<void, aclError (*)(void *)> biasDeviceAddrPtr(biasDeviceAddr, aclrtFree);
CHECK_FREE_RET(ret == ACL_SUCCESS, return ret);
// Create the out aclTensor.
ret = CreateAclTensor(outHostData, outShape, &outDeviceAddr, aclDataType::ACL_FLOAT, &out);
std::unique_ptr<aclTensor, aclnnStatus (*)(const aclTensor *)> outTensorPtr(out, aclDestroyTensor);
std::unique_ptr<void, aclError (*)(void *)> outDeviceAddrPtr(outDeviceAddr, aclrtFree);
CHECK_FREE_RET(ret == ACL_SUCCESS, return ret);
// Create the Transweight acltensor.
void* transWeightDeviceAddr = nullptr;
uint64_t size = transWeightSize * sizeof(float) / 2;
ret = aclrtMalloc(&transWeightDeviceAddr, size, ACL_MEM_MALLOC_HUGE_FIRST);
CHECK_FREE_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtMalloc failed. ERROR: %d\n", ret);return ret);
std::vector<float> transData;
transData.resize(transWeightSize * 2);
// Copy the host-side data to the device memory transData.data() by calling aclrtMemcpy.
ret = aclrtMemcpy(transWeightDeviceAddr, size, transData.data(), size, ACL_MEMCPY_HOST_TO_DEVICE);
CHECK_FREE_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);
std::unique_ptr<aclTensor, aclnnStatus (*)(const aclTensor *)> transWeightTensorPtr(transWeight, aclDestroyTensor);
std::unique_ptr<void, aclError (*)(void *)> transWeightDeviceAddrAddrPtr(transWeightDeviceAddr, aclrtFree);
// 3. Call aclnnTransConvolutionWeight.
int8_t cubeMathType = 2; // USE_FP16
uint64_t workspaceSize = 0;
aclOpExecutor* executor;
bool transposed = 0;
uint64_t groups = 1;
// Call TransWeight.
ret = aclnnTransConvolutionWeightGetWorkspaceSize(weight, transposed, groups, transWeight,
&workspaceSize, &executor);
CHECK_FREE_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnTransConvolutionWeightGetWorkspaceSize failed. ERROR: %d\n", ret);
return ret);
// Allocate device memory based on the workspaceSize calculated by the first-phase API.
void* workspaceAddr = nullptr;
std::unique_ptr<void, aclError (*)(void *)> workspaceAddrPtr(nullptr, aclrtFree);
if (workspaceSize > 0) {
ret = aclrtMalloc(&workspaceAddr, workspaceSize, ACL_MEM_MALLOC_HUGE_FIRST);
CHECK_FREE_RET(ret == ACL_SUCCESS, LOG_PRINT("allocate workspace failed. ERROR: %d\n", ret); return ret);
workspaceAddrPtr.reset(workspaceAddr);
}
// Call the second-phase API of aclnnTransConvolutionWeight.
ret = aclnnTransConvolutionWeight(workspaceAddr, workspaceSize, executor, stream);
CHECK_FREE_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnTransConvolutionWeight failed. ERROR: %d\n", ret); return ret);
std::vector<int64_t> convStrides = {1, 1};
std::vector<int64_t> convPads = {0, 0};
std::vector<int64_t> convOutPads = {1, 1};
std::vector<int64_t> convDilations = {1, 1};
aclIntArray *strides = aclCreateIntArray(convStrides.data(), convStrides.size());
std::unique_ptr<aclIntArray, aclnnStatus (*)(const aclIntArray *)> stridesPtr(strides, aclDestroyIntArray);
CHECK_FREE_RET(strides != nullptr, return ACL_ERROR_INTERNAL_ERROR);
aclIntArray *pads = aclCreateIntArray(convPads.data(), convPads.size());
std::unique_ptr<aclIntArray, aclnnStatus (*)(const aclIntArray *)> padsPtr(pads, aclDestroyIntArray);
CHECK_FREE_RET(pads != nullptr, return ACL_ERROR_INTERNAL_ERROR);
aclIntArray *outPads = aclCreateIntArray(convOutPads.data(), convOutPads.size());
std::unique_ptr<aclIntArray, aclnnStatus (*)(const aclIntArray *)> outPadsPtr(outPads, aclDestroyIntArray);
CHECK_FREE_RET(outPads != nullptr, return ACL_ERROR_INTERNAL_ERROR);
aclIntArray *dilations = aclCreateIntArray(convDilations.data(), convDilations.size());
std::unique_ptr<aclIntArray, aclnnStatus (*)(const aclIntArray *)> dilationsPtr(dilations, aclDestroyIntArray);
CHECK_FREE_RET(dilations != nullptr, return ACL_ERROR_INTERNAL_ERROR);
// 4. Call aclnnConvolution.
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_FREE_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_FREE_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_FREE_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnConvolution failed. ERROR: %d\n", ret); return ret);
// 4. (Boilerplate) Synchronously wait for the task execution to complete.
ret = aclrtSynchronizeStream(stream);
CHECK_FREE_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 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_FREE_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]);
}
return ACL_SUCCESS;
}
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 = aclnnTransConvolutionWeightTest(deviceId, stream);
CHECK_FREE_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnTransConvolutionWeightTest failed. ERROR: %d\n", ret); return ret);
Finalize(deviceId, stream);
return 0;
}