aclnnFlashAttentionScoreV2
Applicable Products
| Product | Supported |
|---|---|
| × | |
| √ | |
| √ | |
| × | |
| × | |
| × |
Function
Description: In training scenarios, uses the FlashAttention algorithm to implement self-attention computation. Compared with the aclnnFlashAttentionScore API, this API adds the pseType parameter:
When pseType=1, the implementation is the same as aclnnFlashAttentionScore.
When pseType is set to other values, a mul operation is performed first, followed by an add operation.
Formula:
The forward computation formula for the attention mechanism is as follows:
When pseType = 1, the formula is the same as that of aclnnFlashAttentionScore.
For other values of pseType, the formula is as follows:
Function Prototype
Each operator uses a two-phase API. You must first call the "aclnnFlashAttentionScoreV2GetWorkspaceSize" API to obtain the workspace size required for computation and the executor that contains the operator computation process, and then call the "aclnnFlashAttentionScoreV2" API to perform the computation.
aclnnStatus aclnnFlashAttentionScoreV2GetWorkspaceSize(
const aclTensor *query,
const aclTensor *key,
const aclTensor *value,
const aclTensor *realShiftOptional,
const aclTensor *dropMaskOptional,
const aclTensor *paddingMaskOptional,
const aclTensor *attenMaskOptional,
const aclIntArray *prefixOptional,
const aclIntArray *qStartIdxOptional,
const aclIntArray *kvStartIdxOptional,
double scaleValue,
double keepProb,
int64_t preTokens,
int64_t nextTokens,
int64_t headNum,
char *inputLayout,
int64_t innerPrecise,
int64_t sparseMode,
int64_t pseType,
const aclTensor *softmaxMaxOut,
const aclTensor *softmaxSumOut,
const aclTensor *softmaxOutOut,
const aclTensor *attentionOutOut,
uint64_t *workspaceSize,
aclOpExecutor **executor)aclnnStatus aclnnFlashAttentionScoreV2(
void *workspace,
uint64_t workspaceSize,
aclOpExecutor *executor,
const aclrtStream stream)aclnnFlashAttentionScoreV2GetWorkspaceSize
Parameters
Parameter Input/Output Description Instruction Data Type Data Format Dimension (Shape) Non-contiguous Tensor query Input Query in the formula. The data type is consistent with that of key/value. FLOAT16, BFLOAT16, FLOAT32 ND [BNSD], [BSND], [BSH], [SBH] √ key input key in the formula. The data type is the same as that of query and value. FLOAT16, BFLOAT16, FLOAT32 ND [BNSD], [BSND], [BSH], [SBH] √ value Input value in the formula. The data type is the same as that of query/key. FLOAT16, BFLOAT16, FLOAT32 ND [BNSD], [BSND], [BSH], [SBH] √ realShiftOptional Optional input pse in the formula. The data type must be the same as that of query. This parameter must be used together with pseType. FLOAT16, BFLOAT16, FLOAT32 ND [B,N,Sq,Skv], [B,N,1,Skv], [1,N,Sq,Skv], [B,N,1024,Skv], [1,N,1024,Skv], [B,N], [N] √ dropMaskOptional Optional input Dropout in the formula. - UINT8 ND 0, 1 √ paddingMaskOptional Optional Input Reserved parameter, not used currently. - - - - - attenMaskOptional optional input atten_mask in the formula. A value of 1 indicates that the bit does not participate in the computation, and 0 indicates that the bit participates in the computation. BOOL, UINT8 ND [B,N,Sq,Skv], [B,1,Sq,Skv], [1,1,Sq,Skv], [Sq,Skv] √ prefixOptional Optional input Indicates the N value of each batch in the prefix sparse computation scenario. - INT64 ND 0, 1 - qStartIdxOptional optional input Indicates the outer cut scenario, representing the start index of the current chunk's query sequence in the global context. - INT64 ND 0, 1 - kvStartIdxOptional Optional input Indicates the starting index of the key and value sequences of the current chunk in the global context in an external slicing scenario. - INT64 ND 0, 1 - scaleValue Optional Input Scale in the formula, representing the scaling factor. - DOUBLE - - - keepProb optional input Represents the proportion of 1s in dropMaskOptional. The value range is (0, 1]. DOUBLE - - - preTokens Optional input Specifies the left boundary of the sliding window for sparse computation. - INT64 - - - nextTokens optional input Used for sparse computation. Indicates the right boundary of the sliding window. - INT64 - - - headNum Input Indicates the number of heads on a single device, that is, the length of the N axis of the input query. - INT64 - - - inputLayout Input Indicates the data layout format of the input query, key, and value. Supports BSH, SBH, BSND, and BNSD. String - - - innerPrecise optional input Used to improve precision. Use the default value 0. INT64 - - - sparseMode Optional input Indicates the sparse mode. Supported configuration values are 0, 1, 2, 3, 4, 5, and 6. INT64 - - - pseType optional input Controls the computation order of mul and add. Supported configuration values: 0, 1, 2, 3. - INT64 - - - softmaxMaxOut Output Stores the intermediate max result of the Softmax computation, used for backward computation. - FLOAT ND [B,N,Sq,8] √ softmaxSumOut Output Intermediate sum result of the Softmax computation, used for backward computation. - FLOAT ND [B,N,Sq,8] √ softmaxOutOut output Reserved parameter. Not used currently. - - - - - attentionOutOut Output Final output of the formula. The data type and shape type are consistent with query. FLOAT16, BFLOAT16, FLOAT32 ND [BNSD], [BSND], [BSH], [SBH] √ workspaceSize output Returns the workspace size to be applied on the Device side. - - - - - executor Output Returns the op executor, which includes the computation process of the operator. - - - - - 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 parameter is a required input, output, or required attribute, and is a null pointer. ACLNN_ERR_PARAM_INVALID 161002 The data type of query, key, value, realShiftOptional, dropMaskOptional, paddingMaskOptional, attenMaskOptional, softmaxMaxOut, softmaxSumOut, softmaxOutOut, or attentionOutOut is not within the supported range. The data format of query, key, value, realShiftOptional, dropMaskOptional, paddingMaskOptional, attenMaskOptional, softmaxMaxOut, softmaxSumOut, softmaxOutOut, or attentionOutOut is not within the supported range.
aclnnFlashAttentionScoreV2
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 aclnnFlashAttentionScoreV2GetWorkspaceSize. executor Input Operator executor that includes the computation flow. stream Input Specifies the stream for task execution. Return Value
aclnnStatus: return code. For details, see aclnn Return Code.
Constraints
Deterministic computation:
- aclnnFlashAttentionScoreV2 defaults to a deterministic implementation.
When this API is used with PyTorch, ensure that the versions of CANN-related packages and PyTorch-related packages are compatible.
Constraints on the input query, key, and value:
B: The batch size must be equal.
D: The head dimension must satisfy (qD == kD && kD >= vD).
The inputLayout must be consistent.
The shapes of the input key and value must be consistent except for D.
For constraints on data shapes, take BSND and BNSD of inputLayout as examples (H = N * D for BSH and SBH), where:
B: value range is 1 to 2M. When prefixOptional is used, B supports a maximum of 2K.
N: value range is 1 to 256.
S: value range is 1 to 1M.
D: value range is 1 to 768.
The data layout format of query, key, and value supports interpretation from multiple dimensions, where B (Batch) indicates the input sample batch size, S (Seq-Length) indicates the input sample sequence length, H (Head-Size) indicates the hidden layer size, N (Head-Num) indicates the number of heads, D (Head-Dim) indicates the minimum unit dimension of the hidden layer, and D = H/N is satisfied.
realShiftOptional: If Sq is greater than 1024, Sq and Skv are of equal length for each batch, and the scenario is a lower triangular mask with sparseMode 0, 2, or 3, alibi position encoding compression can be enabled. In this case, only the last 1024 rows of the original PSE need to be input, achieving memory optimization, i.e., alibi_compress = ori_pse[:, :, -1024:, :], as detailed below:
When the parameter differs for each batch, the shape is BNHSkv (H=1024).
When the parameter is the same for each batch, the shape is 1NHSkv (H=1024).
When pseType is 2 or 3, the data type must be FLOAT32, and the corresponding shape supports the range [B,N] or [N].
If this parameter is not enabled, nullptr must be passed for realShiftOptional, and 1 must be passed for pseType.
innerPrecise: Currently, 0 and 1 are reserved configuration values, and 2 enables invalid row computation. Its function is to avoid precision loss caused by entire-row masks during computation, but this configuration degrades performance. If the operator can determine that an invalid row scenario exists, it automatically enables invalid row computation, for example, when sparseMode is 3 and Sq > Skv.
Meaning of each pseType value
pseType Meaning Note 0 Externally passed pse, multiply first then add. - 1 Externally passed pse, add first then multiply. Consistent with the implementation of FlashAttentionScore. 2 Internally generated pse, multiply first then add. - 3 Internally generated pse, multiply first then add then sqrt. - When pseType is 2 or 3, only equal-length Sq and Skv are supported.
The constraints for sparseMode are as follows:
When all attenMaskOptional shapes are smaller than 2048 and identical, the default mode is recommended to reduce memory usage.
When configured as 1, 2, 3, or 5, the user-configured preTokens and nextTokens do not take effect.
When configured as 0 or 4, ensure that attenMaskOptional is consistent with the ranges of preTokens and nextTokens.
If not explicitly specified by the user, 0 is recommended.
For details about different sparse modes, see sparse mode description.
In some scenarios, if the computation workload is too large, the operator execution may time out (aicore error, with errorStr: timeout or trap error). In this case, axis splitting is recommended. Note: The computation workload is affected by parameters such as B, S, N, and D. Larger values result in a larger computation workload.
In the band scenario, preTokens and nextTokens must have an intersection.
In the prefixOptional sparse computation scenario (sparseMode=5 or sparseMode=6), when Sq > Skv, the N value range of prefix is [0, Skv]; when Sq <= Skv, the N value range of prefix is [Skv-Sq, Skv].
When Sq in realShiftOptional is greater than 1024 and the shape is BNHS or 1NHS, Sq and Skv must be of equal length.
Example
The sample code for the API call is as follows. For details about compilation and execution, see Compile and Run Samples.
#include <iostream>
#include <vector>
#include "acl/acl.h"
#include "aclnnop/aclnn_flash_attention_score.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;
}
void PrintOutResult(std::vector<int64_t> &shape, void** deviceAddr) {
auto size = GetShapeSize(shape);
std::vector<float> resultData(size, 0);
auto ret = aclrtMemcpy(resultData.data(), resultData.size() * sizeof(resultData[0]),
*deviceAddr, 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);
for (int64_t i = 0; i < size; i++) {
LOG_PRINT("mean result[%ld] is: %f\n", i, resultData[i]);
}
}
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);
// 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 aclCreateTensor 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;
}
int main() {
// 1. (Boilerplate) Initialize the device/stream. See the ACL API manual.
// Set deviceId based on the 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 inputs and outputs. Custom construction is required based on the API.
std::vector<int64_t> qShape = {256, 1, 128};
std::vector<int64_t> kShape = {256, 1, 128};
std::vector<int64_t> vShape = {256, 1, 128};
std::vector<int64_t> attenmaskShape = {256, 256};
std::vector<int64_t> attentionOutShape = {256, 1, 128};
std::vector<int64_t> softmaxMaxShape = {1, 1, 256, 8};
std::vector<int64_t> softmaxSumShape = {1, 1, 256, 8};
void* qDeviceAddr = nullptr;
void* kDeviceAddr = nullptr;
void* vDeviceAddr = nullptr;
void* attenmaskDeviceAddr = nullptr;
void* attentionOutDeviceAddr = nullptr;
void* softmaxMaxDeviceAddr = nullptr;
void* softmaxSumDeviceAddr = nullptr;
aclTensor* q = nullptr;
aclTensor* k = nullptr;
aclTensor* v = nullptr;
aclTensor* pse = nullptr;
aclTensor* dropMask = nullptr;
aclTensor* padding = nullptr;
aclTensor* attenmask = nullptr;
aclTensor* attentionOut = nullptr;
aclTensor* softmaxMax = nullptr;
aclTensor* softmaxSum = nullptr;
aclTensor* softmaxOut = nullptr;
std::vector<float> qHostData(32768, 1);
std::vector<float> kHostData(32768, 1);
std::vector<float> vHostData(32768, 1);
std::vector<uint8_t> attenmaskHostData(65536, 0);
std::vector<float> attentionOutHostData(32768, 0);
std::vector<float> softmaxMaxHostData(2048, 3.0);
std::vector<float> softmaxSumHostData(2048, 3.0);
ret = CreateAclTensor(qHostData, qShape, &qDeviceAddr, aclDataType::ACL_FLOAT, &q);
CHECK_RET(ret == ACL_SUCCESS, return ret);
ret = CreateAclTensor(kHostData, kShape, &kDeviceAddr, aclDataType::ACL_FLOAT, &k);
CHECK_RET(ret == ACL_SUCCESS, return ret);
ret = CreateAclTensor(vHostData, vShape, &vDeviceAddr, aclDataType::ACL_FLOAT, &v);
CHECK_RET(ret == ACL_SUCCESS, return ret);
ret = CreateAclTensor(attenmaskHostData, attenmaskShape, &attenmaskDeviceAddr, aclDataType::ACL_UINT8, &attenmask);
CHECK_RET(ret == ACL_SUCCESS, return ret);
ret = CreateAclTensor(attentionOutHostData, attentionOutShape, &attentionOutDeviceAddr, aclDataType::ACL_FLOAT, &attentionOut);
CHECK_RET(ret == ACL_SUCCESS, return ret);
ret = CreateAclTensor(softmaxMaxHostData, softmaxMaxShape, &softmaxMaxDeviceAddr, aclDataType::ACL_FLOAT, &softmaxMax);
CHECK_RET(ret == ACL_SUCCESS, return ret);
ret = CreateAclTensor(softmaxSumHostData, softmaxSumShape, &softmaxSumDeviceAddr, aclDataType::ACL_FLOAT, &softmaxSum);
CHECK_RET(ret == ACL_SUCCESS, return ret);
std::vector<int64_t> prefixOp = {0};
std::vector<int64_t> qStartIdxOp = {0};
std::vector<int64_t> kvStartIdxOp = {0};
aclIntArray *prefix = aclCreateIntArray(prefixOp.data(), 1);
aclIntArray *qStartIdx = aclCreateIntArray(qStartIdxOp.data(), 1);
aclIntArray *kvStartIdx = aclCreateIntArray(kvStartIdxOp.data(), 1);
double scaleValue = 0.088388;
double keepProb = 1;
int64_t preTokens = 65536;
int64_t nextTokens = 65536;
int64_t headNum = 1;
int64_t innerPrecise = 0;
int64_t sparseMode = 0;
int64_t pseType = 1;
char layOut[5] = {'S', 'B', 'H', 0};
// 3. Call the CANN operator library API. Replace with the specific API name.
uint64_t workspaceSize = 0;
aclOpExecutor* executor;
// Call the first-phase API of aclnnFlashAttentionScoreV2.
ret = aclnnFlashAttentionScoreV2GetWorkspaceSize(
q, k, v, pse, dropMask, padding, attenmask, prefix, qStartIdx, kvStartIdx, scaleValue,
keepProb, preTokens, nextTokens, headNum, layOut, innerPrecise,
sparseMode, pseType, softmaxMax, softmaxSum, softmaxOut, attentionOut, &workspaceSize, &executor);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnFlashAttentionScoreV2GetWorkspaceSize 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 aclnnFlashAttentionScoreV2.
ret = aclnnFlashAttentionScoreV2(workspaceAddr, workspaceSize, executor, stream);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnFlashAttentionScoreV2 failed. ERROR: %d\n", ret); return ret);
// 4. (Boilerplate) Synchronously 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 memory to the host side. Modify based on the specific API definition.
PrintOutResult(attentionOutShape, &attentionOutDeviceAddr);
PrintOutResult(softmaxMaxShape, &softmaxMaxDeviceAddr);
PrintOutResult(softmaxSumShape, &softmaxSumDeviceAddr);
// 6. Release aclTensor and aclScalar. Modify based on the specific API definition.
aclDestroyTensor(q);
aclDestroyTensor(k);
aclDestroyTensor(v);
aclDestroyTensor(attenmask);
aclDestroyTensor(attentionOut);
aclDestroyTensor(softmaxMax);
aclDestroyTensor(softmaxSum);
// 7. Release device resources.
aclrtFree(qDeviceAddr);
aclrtFree(kDeviceAddr);
aclrtFree(vDeviceAddr);
aclrtFree(attenmaskDeviceAddr);
aclrtFree(attentionOutDeviceAddr);
aclrtFree(softmaxMaxDeviceAddr);
aclrtFree(softmaxSumDeviceAddr);
if (workspaceSize > 0) {
aclrtFree(workspaceAddr);
}
aclrtDestroyStream(stream);
aclrtResetDevice(deviceId);
aclFinalize();
return 0;
}