aclnnAdaLayerNormV2
Applicable Products
| Product | Supported |
|---|---|
| √ | |
| √ | |
| √ | |
| × | |
| × | |
| × |
Function
Description: The AdaLayerNormV2 operator fuses LayerNorm with the downstream Add and Mul operations, and adjusts the normalization process through adaptive parameters scale and shift. Compared with the AdaLayerNorm operator, the output adds two new parameters (the mean of the input and the reciprocal of the standard deviation of the input); the data types supported by weight and bias have additional corresponding constraints.
Formula:
LayerNorm formula:
where E(x) indicates the mean of the input, and Var(x) indicates the variance of the input.
Function Prototype
Each operator is divided into a two-phase API. The "aclnnAdaLayerNormV2GetWorkspaceSize" API must be called first to obtain the workspace size required for computation and the executor that encapsulates the operator computation flow. Then, the "aclnnAdaLayerNormV2" API is called to perform the computation.
aclnnStatus aclnnAdaLayerNormV2GetWorkspaceSize(
const aclTensor* x,
const aclTensor* scale,
const aclTensor* shift,
const aclTensor* weightOptional,
const aclTensor* biasOptional,
double epsilon,
aclTensor* out,
aclTensor* meanOutOptional,
aclTensor* rstdOutOptional,
uint64_t* workspaceSize,
aclOpExecutor** executor)aclnnStatus aclnnAdaLayerNormV2(
void *workspace,
uint64_t workspaceSize,
aclOpExecutor *executor,
aclrtStream stream)aclnnAdaLayerNormV2GetWorkspaceSize
Parameters
Parameter Input/Output Description Instruction Data Type Data Format Dimension (Shape) Non-Contiguous Tensor x (aclTensor*) Input Indicates the input tensor for computation. Corresponds to `x` in the corresponding formula. - Does not support null tensor.
- The shape is [B, S, H], where B supports 0 to 6 dimensions.
FLOAT32, FLOAT16, BFLOAT16 ND 2-8 √ scale (aclTensor*) Input Indicates the adaptive scaling parameter. Corresponds to `scale` in the corresponding formula. - Does not support null tensor.
- The data type remains consistent with the data type of input parameter
x. - The shape is [B, H] or [B, 1, H], where B supports 0 to 6 dimensions, the number and size of dimensions remain consistent with B in
x, and H is dimension consistent with the H dimension inx.
FLOAT32, FLOAT16, BFLOAT16 ND 2-8 √ shift (aclTensor*) Input Indicates the adaptive shift parameter. Corresponds to `shift` in the corresponding formula. - Does not support null tensor.
- The data type is consistent with the data type of input parameter `x`.
- The shape is [B, H] or [B, 1, H], where B supports 0 to 6 dimensions, the number and size of dimensions remain consistent with B in `x`, and H is dimension-consistent with the H dimension in `x`.
FLOAT32, FLOAT16, BFLOAT16 ND 2-8 √ weightOptional (aclTensor*) Input Optional input parameter, indicating the normalization scaling parameter. Corresponds to `weightOptional` in the corresponding formula. - Does not support null tensor.
- The data type is consistent with the data type of input parameter `x` or is FLOAT32, and when `biasOptional` exists, the data types of `weightOptional` and `biasOptional` are the same.
- The shape is [H], where H is dimension consistent with the H dimension in `x`.
FLOAT32, FLOAT16, BFLOAT16 ND 1 √ biasOptional (aclTensor*) Input Optional input parameter, indicating the normalization bias parameter. Corresponds to `biasOptional` in the corresponding formula. - Does not support null tensor.
- The data type is consistent with the data type of input parameter `x` or is FLOAT32, and when `weightOptional` exists, `biasOptional` has the same data type as `weightOptional`.
- The shape is [H], where H is dimension consistent with the H dimension in `x`.
FLOAT32, FLOAT16, BFLOAT16 ND 1 √ epsilon (double) Input Indicates the value added to the denominator to ensure numerical stability. Corresponds to `epsilon` in the corresponding formula. - - - - - out (aclTensor*) Output Indicates the output tensor of the computation. Corresponds to `out` in the corresponding formula. - Does not support null tensor.
- The data type is consistent with the data type of input parameter
x. - The shape remains consistent with
x.
FLOAT32, FLOAT16, BFLOAT16 ND 2-8 √ meanOutOptional (aclTensor*) Output Optional output, indicating the mean after normalization. Corresponds to `mean` in the formula. - Does not support null tensor.
- The data type is consistent with that of input parameter
x. - The shape is [B, S, 1], where the last dimension is fixed to 1 and the other dimensions are consistent with
x.
FLOAT32, FLOAT16, BFLOAT16 ND 2-8 √ rstdOutOptional (aclTensor*) Output Optional output, indicating the reciprocal of the standard deviation after normalization. Corresponds to `rstd` in the corresponding formula. - Does not support null tensor.
- The data type remains consistent with the data type of input parameter `x`.
- The shape is [B, S, 1], where the last dimension is fixed to 1 and the other dimensions remain consistent with `x`.
FLOAT32, FLOAT16, BFLOAT16 ND 2-8 √ workspaceSize (uint64_t*) Output Returns the workspace size to be applied 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 Code Error Code Description ACLNN_ERR_PARAM_NULLPTR 161001 The input x, scale, shift, and out are null pointers. ACLNN_ERR_PARAM_INVALID 161002 The data type or data format of x, scale, shift, or out is not within the supported range. When weightOptional is not a null pointer, its data type or data format is not within the supported range. When biasOptional is not a null pointer, its data type or data format is not within the supported range. The data type of scale, shift, out, meanOutOptional, or rstdOutOptional is inconsistent with that of x. The data type of weightOptional or biasOptional is not FLOAT32 and is inconsistent with that of x. The shapes of x, scale, shift, weightOptional, biasOptional, out, meanOutOptional, and rstdOutOptional are inconsistent with the parameter description.
aclnnAdaLayerNormV2
Parameters
Parameter Input/Output Description workspace Input Workspace memory address applied on the Device side. workspaceSize Input Workspace size applied on the Device side, obtained from the first-phase API aclnnAdaLayerNormV2GetWorkspaceSize. executor Input Operator executor that contains the operator computation flow. stream Input Specifies the stream for task execution. Return Value
aclnnStatus: return code. For details, see aclnn Return Code.
Constraints
Deterministic computation:
- aclnnAdaLayerNormV2 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 "acl/acl.h"
#include "aclnnop/aclnn_ada_layer_norm_v2.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 shape_size = 1;
for (auto i : shape) {
shape_size *= i;
}
return shape_size;
}
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 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);
// Copy host-side data to device-side memory by calling aclrtMemcpy.
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];
}
// Create an aclTensor by calling the aclCreateTensor API.
*tensor = aclCreateTensor(shape.data(), shape.size(), dataType, strides.data(), 0, ACL_FORMAT_ND,
shape.data(), shape.size(), *deviceAddr);
return 0;
}
int main() {
// 1. (Boilerplate) Initialize the device/stream. See the ACL API manual.
// Fill in the deviceId based on your actual device.
int32_t deviceId = 0;
aclrtStream stream;
auto ret = Init(deviceId, &stream);
// Perform check based on your own requirements.
CHECK_RET(ret == 0, LOG_PRINT("Init acl failed. ERROR: %d\n", ret); return ret);
// 2. Construct inputs and outputs. Custom construction is required based on the API.
std::vector<int64_t> xShape = {2, 4, 8};
std::vector<int64_t> scaleShape = {2, 8};
std::vector<int64_t> shiftShape = {2, 8};
std::vector<int64_t> weightShape = {8};
std::vector<int64_t> biasShape = {8};
std::vector<int64_t> outShape = {2, 4, 8};
std::vector<int64_t> meanShape = {2, 4, 1};
std::vector<int64_t> rstdShape = {2, 4, 1};
double epsilon = 1e-5;
void* xDeviceAddr = nullptr;
void* scaleDeviceAddr = nullptr;
void* shiftDeviceAddr = nullptr;
void* weightDeviceAddr = nullptr;
void* biasDeviceAddr = nullptr;
void* outDeviceAddr = nullptr;
void* meanDeviceAddr = nullptr;
void* rstdDeviceAddr = nullptr;
aclTensor* x = nullptr;
aclTensor* scale = nullptr;
aclTensor* shift = nullptr;
aclTensor* weight = nullptr;
aclTensor* bias = nullptr;
aclTensor* out = nullptr;
aclTensor* mean = nullptr;
aclTensor* rstd = nullptr;
std::vector<float> xHostData(2 * 4 * 8, 1);
std::vector<float> scaleHostData(2 * 8, 1);
std::vector<float> shiftHostData(2 * 8, 1);
std::vector<float> weightHostData(8, 1);
std::vector<float> biasHostData(8, 1);
std::vector<float> outHostData(2 * 4 * 8, 0);
std::vector<float> meanHostData(2 * 4 * 1, 0);
std::vector<float> rstdHostData(2 * 4 * 1, 0);
// Create the x aclTensor.
ret = CreateAclTensor(xHostData, xShape, &xDeviceAddr, aclDataType::ACL_FLOAT, &x);
CHECK_RET(ret == ACL_SUCCESS, return ret);
// Create the scale aclTensor.
ret = CreateAclTensor(scaleHostData, scaleShape, &scaleDeviceAddr, aclDataType::ACL_FLOAT, &scale);
CHECK_RET(ret == ACL_SUCCESS, return ret);
// Create the shift aclTensor.
ret = CreateAclTensor(shiftHostData, shiftShape, &shiftDeviceAddr, aclDataType::ACL_FLOAT, &shift);
CHECK_RET(ret == ACL_SUCCESS, return ret);
// Create weight aclTensor.
ret = CreateAclTensor(weightHostData, weightShape, &weightDeviceAddr, aclDataType::ACL_FLOAT, &weight);
CHECK_RET(ret == ACL_SUCCESS, return ret);
// Create bias aclTensor.
ret = CreateAclTensor(biasHostData, biasShape, &biasDeviceAddr, aclDataType::ACL_FLOAT, &bias);
CHECK_RET(ret == ACL_SUCCESS, return ret);
// Create out aclTensor.
ret = CreateAclTensor(outHostData, outShape, &outDeviceAddr, aclDataType::ACL_FLOAT, &out);
CHECK_RET(ret == ACL_SUCCESS, return ret);
// Create mean aclTensor.
ret = CreateAclTensor(meanHostData, meanShape, &meanDeviceAddr, aclDataType::ACL_FLOAT, &mean);
CHECK_RET(ret == ACL_SUCCESS, return ret);
// Create rstd aclTensor.
ret = CreateAclTensor(rstdHostData, rstdShape, &rstdDeviceAddr, aclDataType::ACL_FLOAT, &rstd);
CHECK_RET(ret == ACL_SUCCESS, return ret);
// 3. Call the CANN operator library API. Replace with the specific API.
uint64_t workspaceSize = 0;
aclOpExecutor* executor;
// Call the first-phase API of aclnnAdaLayerNormV2.
ret = aclnnAdaLayerNormV2GetWorkspaceSize(x, scale, shift, weight, bias, epsilon, out, mean, rstd, &workspaceSize, &executor);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnAdaLayerNormV2GetWorkspaceSize failed. ERROR: %d\n", ret); return ret);
// Allocate device memory based on the workspaceSize computed 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 aclnnAdaLayerNormV2.
ret = aclnnAdaLayerNormV2(workspaceAddr, workspaceSize, executor, stream);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnAdaLayerNormV2 failed. ERROR: %d\n", ret); return ret);
// 4. (Boilerplate) Synchronize and wait for task completion.
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(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 based on the specific API definition.
aclDestroyTensor(x);
aclDestroyTensor(scale);
aclDestroyTensor(shift);
aclDestroyTensor(weight);
aclDestroyTensor(bias);
aclDestroyTensor(out);
// 7. Release device resources. Modify based on the specific API definition.
aclrtFree(xDeviceAddr);
aclrtFree(scaleDeviceAddr);
aclrtFree(shiftDeviceAddr);
aclrtFree(weightDeviceAddr);
aclrtFree(biasDeviceAddr);
aclrtFree(outDeviceAddr);
if (workspaceSize > 0) {
aclrtFree(workspaceAddr);
}
aclrtDestroyStream(stream);
aclrtResetDevice(deviceId);
aclFinalize();
return 0;
}