aclnnGroupedMatmulAddV2
Applicable Products
| Product | Supported |
|---|---|
| √ | |
| × | |
| × | |
| × | |
| × | |
| × |
Function
Description: Implements grouped matrix multiplication, where the dimensions of each group can differ. The basic function is matrix multiplication, as in , where g is the number of groups and are the corresponding shapes. Both input and output data types are aclTensor, with K-axis grouping.
K-axis grouping: values differ across groups, but are the same for each group.
New features compared with the GroupedMatmulAdd API:
- Supports using the values in groupList as the size of each group on the grouping axis.
Formula:
Function Prototype
Each operator is divided into a two-phase API. You must first call the "aclnnGroupedMatmulAddV2GetWorkspaceSize" API to obtain the input parameters and calculate the required workspace size based on the computation process, and then call the "aclnnGroupedMatmulAddV2" API to perform the computation.
aclnnStatus aclnnGroupedMatmulAddV2GetWorkspaceSize(
const aclTensor *x,
const aclTensor *weight,
const aclTensor *groupList,
aclTensor *yRef,
bool transposeX,
bool transposeWeight,
int64_t groupType,
int64_t group_list_type,
uint64_t *workspaceSize,
aclOpExecutor **executor)aclnnStatus aclnnGroupedMatmulAddV2(
void *workspace,
uint64_t workspaceSize,
aclOpExecutor *executor,
aclrtStream stream)aclnnGroupedMatmulAddV2GetWorkspaceSize
Parameters:
Parameter Input/Output Description Instruction Data Type Data Format Dimension (Shape) Non-contiguous Tensor x (aclTensor) Input Input x in the formula. - x must be transposed.
- Empty tensors are not supported.
FLOAT16, BFLOAT16 ND 2 √ weight (aclTensor) Input Represents the weight, i.e., weight in the formula. - weight does not support transposition.
- Empty tensors are not supported.
FLOAT16, BFLOAT16 ND 2 √ groupList (aclTensor) Input Indicates the matmul size distribution along the grouping axis for inputs and outputs. - INT64 - - - y (aclTensor) Input. Input matrix for in-place accumulation, of the aclTensor type on the device side, corresponding to y in the formula. - FLOAT32 ND 3 - transposeX (bool) Input Indicates whether to transpose the x matrix. A boolean value on the host side. - Currently, only True is supported.
BOOL - - - transposeWeight (bool) Input Indicates whether the weight matrix is transposed. - Currently, only False is supported.
BOOL - - - groupType (int64_t) Input Indicates the grouping type. - Currently, only 2 (K-axis grouping) is supported.
INT64 - - - groupListType (int64_t) Input Integer parameter. Currently, only two values are supported. - 0: The values in groupList represent the cumsum result (cumulative sum) of the group axis sizes.
- 1: The values in groupList represent the size of each group on the group axis.
INT64 - - - yRef (aclTensor) Output Reference to the input matrix y for in-place accumulation (identical to y), denoted as yRef in the formula. - FLOAT32 ND 3 - workspaceSize (uint64_t) Output Returns the workspace size to be allocated on the Device side. - - - - executor (aclOpExecutor) Output Returns the operator executor, which contains the operator computation process. - - - - - 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 Value Error Code Description ACLNN_ERR_PARAM_NULLPTR 161001 The input x, weight, groupList, y, and yRef are null pointers. ACLNN_ERR_PARAM_INVALID 161002 The data type or data format of x, weight, groupList, y, or yRef is not within the supported range. Inconsistent data types between x and weight. ACLNN_ERR_INNER_TILING_ERROR 561002 The shapes of x, weight, y, and yRef do not meet the matrix multiplication constraints.
aclnnGroupedMatmulAddV2
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 aclnnGroupedMatmulAddV2GetWorkspaceSize. executor Input Operator executor that contains the operator computation process. stream Input Specifies the stream for task execution. Return value
aclnnStatus: return code. For details, see aclnn Return Code.
Constraints
Deterministic computation description: aclnnGroupedMatmulAddV2 defaults to a deterministic implementation.
For each group of tensors in x and weight, the size of each dimension, after 32-byte alignment, must be less than the maximum int32 value of 2147483647.
The supported input type combinations are as follows:
x is FLOAT16, weight is FLOAT16, and y is FLOAT32.
x is BFLOAT16, weight is BFLOAT16, and y is FLOAT32.
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 <memory>
#include "acl/acl.h"
#include "aclnnop/aclnn_grouped_matmul_add_v2.h"
#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 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_New(const std::vector<int64_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 CreateAclTensor(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.
std::vector<T> hostData(size, 0);
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;
}
void Finalize(int32_t deviceId, aclrtStream stream)
{
aclrtDestroyStream(stream);
aclrtResetDevice(deviceId);
aclFinalize();
}
int aclnnGroupedMatmulAddV2Test(int32_t deviceId, aclrtStream &stream) {
auto ret = Init(deviceId, &stream);
// Handle based on your own needs.
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("Init acl failed. ERROR: %d\n", ret); return ret);
// 2. Construct inputs and outputs. Customize the construction based on the API interface.
std::vector<int64_t> xShape = {512, 256};
std::vector<int64_t> weightShape= {512, 256};
std::vector<int64_t> yShape = {2, 256, 256};
std::vector<int64_t> groupListShape = {2};
std::vector<int64_t> groupListData = {256, 512};
void* xDeviceAddr = nullptr;
void* weightDeviceAddr = nullptr;
void* yDeviceAddr = nullptr;
void* groupListDeviceAddr = nullptr;
aclTensor* x = nullptr;
aclTensor* weight = nullptr;
aclTensor* groupedList = nullptr;
aclTensor* y = nullptr;
aclTensor* yRef = nullptr;
bool transpose_x = true;
bool transpose_weight = false;
int group_type = 2;
int group_list_type = 0;
// Create x aclTensorList.
ret = CreateAclTensor<uint16_t>(xShape, &xDeviceAddr, aclDataType::ACL_BF16, &x);
std::unique_ptr<aclTensor, aclnnStatus (*)(const aclTensor *)> xTensorPtr(x, aclDestroyTensor);
std::unique_ptr<void, aclError (*)(void *)> xDeviceAddrPtr(xDeviceAddr, aclrtFree);
CHECK_RET(ret == ACL_SUCCESS, return ret);
// Create weight aclTensorList.
ret = CreateAclTensor<uint16_t>(weightShape, &weightDeviceAddr, aclDataType::ACL_BF16, &weight);
std::unique_ptr<aclTensor, aclnnStatus (*)(const aclTensor *)> weightTensorPtr(weight, aclDestroyTensor);
std::unique_ptr<void, aclError (*)(void *)> weightDeviceAddrPtr(weightDeviceAddr, aclrtFree);
CHECK_RET(ret == ACL_SUCCESS, return ret);
// Create y aclTensorList.
ret = CreateAclTensor<float>(yShape, &yDeviceAddr, aclDataType::ACL_FLOAT, &y);
std::unique_ptr<aclTensor, aclnnStatus (*)(const aclTensor *)> yTensorPtr(y, aclDestroyTensor);
std::unique_ptr<void, aclError (*)(void *)> yDeviceAddrPtr(yDeviceAddr, aclrtFree);
CHECK_RET(ret == ACL_SUCCESS, return ret);
// Create the group_list aclTensor.
ret = CreateAclTensor_New<int64_t>(groupListData, groupListShape, &groupListDeviceAddr, aclDataType::ACL_INT64, &groupedList);
std::unique_ptr<aclTensor, aclnnStatus (*)(const aclTensor *)> groupedListTensorPtr(groupedList, aclDestroyTensor);
std::unique_ptr<void, aclError (*)(void *)> groupListDeviceAddrPtr(groupListDeviceAddr, aclrtFree);
CHECK_RET(ret == ACL_SUCCESS, return ret);
yRef = y;
uint64_t workspaceSize = 0;
aclOpExecutor* executor;
// 3. Call the CANN operator library API.
// Call the first-phase API of aclnnGroupedMatmulAddV2.
ret = aclnnGroupedMatmulAddV2GetWorkspaceSize(x, weight, groupedList, yRef, transpose_x, transpose_weight, group_type, group_list_type, &workspaceSize, &executor);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnGroupedMatmulAddV2GetWorkspaceSize failed. ERROR: %d\n", ret); return ret);
// Apply for 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_RET(ret == ACL_SUCCESS, LOG_PRINT("allocate workspace failed. ERROR: %d\n", ret); return ret);
workspaceAddrPtr.reset(workspaceAddr);
}
// Call the second-phase API of aclnnGroupedMatmulAddV2.
ret = aclnnGroupedMatmulAddV2(workspaceAddr, workspaceSize, executor, stream);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnGroupedMatmulAddV2 failed. ERROR: %d\n", ret); return ret);
// 4. (Boilerplate) Synchronously wait for task execution to finish.
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 based on the specific API definition.
auto size = GetShapeSize(yShape);
std::vector<uint16_t> resultData(size, 0);
ret = aclrtMemcpy(resultData.data(), 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 j = 0; j < size; j++) {
LOG_PRINT("result[%ld] is: %d\n", j, resultData[j]);
}
return ACL_SUCCESS;
}
int main()
{
// 1. (Boilerplate) Initialize the device/stream. See the AscendCL external API list.
// Enter the actual device ID for deviceId.
int32_t deviceId = 0;
aclrtStream stream;
auto ret = aclnnGroupedMatmulAddV2Test(deviceId, stream);
CHECK_FREE_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnGroupedMatmulAddV2Test failed. ERROR: %d\n", ret); return ret);
Finalize(deviceId, stream);
return 0;
}