aclnnFlashAttentionVarLenScoreV2
Applicable Products
| Product | Supported |
|---|---|
| √ | |
| √ | |
| √ | |
| × | |
| × | |
| × |
Function
Description: In the training scenario, the FlashAttention algorithm is used to implement self-attention computation. The difference from aclnnFlashAttentionScoreV2 is that this API supports variable-length S computation, meaning that multiple sequences of unequal lengths can be passed in at once. When using this API, query, key, and value are passed in TND format, where T stands for total number, representing the sum of lengths of all sequences. Meanwhile, actualSeqQLenOptional and actualSeqKvLenOptional are used to pass the cumulative length of each sequence in order to distinguish different sequences. The attention result is computed separately for each sequence.
Formula:
The forward computation formula for attention is as follows:
When psetype=1, the formula is the same as that of aclnnFlashAttentionVarLenScore.
When psetype is set to other values, the formula is as follows:
Function Prototype
Each operator is divided into a two-phase API. You must first call the "aclnnFlashAttentionVarLenScoreV2GetWorkspaceSize" API to obtain the workspace size required for computation and the executor that contains the operator computation flow, and then call the "aclnnFlashAttentionVarLenScoreV2" API to perform the computation.
aclnnStatus aclnnFlashAttentionVarLenScoreV2GetWorkspaceSize(
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 *actualSeqQLenOptional,
const aclIntArray *actualSeqKvLenOptional,
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 aclnnFlashAttentionVarLenScoreV2(
void *workspace,
uint64_t workspaceSize,
aclOpExecutor *executor,
const aclrtStream stream)aclnnFlashAttentionVarLenScoreV2GetWorkspaceSize
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 the same as that of key/value. FLOAT16, BFLOAT16, FLOAT32 ND [TND] √ key Input key in the formula. The data type is the same as that of query/value. FLOAT16, BFLOAT16, FLOAT32 ND [TND] √ value Input The value in the formula. The data type is the same as that of query/key. FLOAT16, BFLOAT16, FLOAT32 ND [TND] √ realShiftOptional Optional input pse in the formula. The data type is the same as that of query. This parameter must be used together with pseType. FLOAT16, BFLOAT16, FLOAT32 ND [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 position does not participate in the computation, and a value of 0 indicates that the position participates in the computation. BOOL, UINT8 ND [B,N,Sq,Skv], [B,1,Sq,Skv], [1,1,Sq,Skv], [Sq,Skv] √ prefixOptional Optional input Represents the N value of each batch in the prefix sparse computation scenario. - INT64 ND 0, 1 - actualSeqQLenOptional Optional input Describes the sequence length of the query corresponding to each batch. - INT64 ND 0, 1 - actualSeqKvLenOptional Optional input Describes the sequence length of the key/value corresponding to each batch. - INT64 ND 0, 1 - qStartIdxOptional Optional input Represents the external scenario, indicating the start index of the current chunk's query sequence in the global sequence. - INT64 ND 0, 1 - kvStartIdxOptional Optional input Represents the start index of the current chunk's key and value sequence in the global context for the outer cut 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. - DOUBLE - - - preTokens optional input Specifies the left boundary of the sliding window for sparse computation. - INT64 - - - nextTokens Optional input Specifies the right boundary of the sliding window for sparse computation. - INT64 - - - headNum Input Represents the number of heads on a single card, i.e., the length of the N-axis of the input query. - INT64 - - - inputLayout Input Represents the data layout format of the input query, key, and value. TND is supported. String - - - innerPrecise Optional input Used to improve precision. Defaults to 0. INT64 - - - sparseMode Optional input Indicates the sparse mode. Supports configuration values 0, 1, 2, 3, 4, 6, 7, and 8. INT64 - - - pseType Optional input Controls the computation order of mul and add. Supports configuration values of 0, 1, 2, and 3. - INT64 - - - softmaxMaxOut Output Intermediate max result of the softmax computation, used for backward computation. - FLOAT ND [N,T,8] √ softmaxSumOut Output Intermediate sum result of the Softmax computation, used for backward computation. - FLOAT ND [N,T,8] √ softmaxOutOut Output Reserved parameter. Not used currently. - - - - - attentionOutOut Output Represents the final output of the formula. The data type and shape are consistent with those of query. FLOAT16, BFLOAT16, FLOAT32 ND [TND] √ workspaceSize Output Returns the workspace size to be applied for on the Device side. - - - - - executor Output Returns the operator executor, which contains the operator computation flow. - - - - - 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.
aclnnFlashAttentionVarLenScoreV2
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 aclnnFlashAttentionVarLenScoreV2GetWorkspaceSize. executor Input Specifies the operator executor, which 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:
- aclnnFlashAttentionVarLenScoreV2 defaults to a deterministic implementation.
When this API is used with PyTorch, ensure that the versions of CANN-related packages and PyTorch-related packages match.
The constraints on the input query, key, and value are as follows:
B: The batch sizes must be equal.
D: The head dimensions must satisfy (qD == kD && kD >= vD).
The inputLayout must be consistent.
Constraints on data shapes, where:
T: The value range is 1 to 1M.
N: value range is 1 to 256.
D: value range is 1 to 768.
The data layout format of query, key, and value supports only TND, where T is the tightly packed data of the B and S concatenated axes (SeqLenQ and SeqLenKV of each batch). B (Batch) indicates the input sample batch size, S (Seq-Length) indicates the input sample sequence length, H (Head-Size) indicates the hidden size, N (Head-Num) indicates the number of heads, and D (Head-Dim) indicates the minimum unit size of the hidden layer, satisfying D = H/N.
realShiftOptional: If Sq is greater than 1024, and Sq and Skv of each batch are of equal length, and the scenario is a lower triangular mask with sparseMode being 0, 2, or 3, you can enable alibi position encoding compression. In this case, only the last 1024 rows of the original PSE need to be input to achieve memory optimization, that is, alibi_compress = ori_pse[:, :, -1024:, :], as detailed below:
When each batch differs, the shape is BNHSkv (H = 1024).
When each batch is the same, the shape is 1NHSkv (H=1024).
If pseType is 2 or 3, the data type must be FLOAT32, and the corresponding shape support range is [B,N] or [N].
If this parameter is not enabled, nullptr must be passed for realShiftOptional, and 1 must be passed for pseType.
Meaning of each pseType value
pseType Meaning Note 0 Externally passed pse, multiply then add - 1 Externally passed pse, add then multiply Consistent with the implementation of FlashAttentionUnpaddingScoreGrad. 2 Internally generated pse, multiply then add - 3 Internally generated pse, multiply then add then sqrt - When pseType is 2 or 3, only equal-length Sq and Skv are supported.
innerPrecise: Currently, 0 and 1 are reserved configuration values, and 2 enables invalid row computation, which prevents precision loss caused by entire-row masks during computation. However, 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.
The constraints of sparseMode are as follows:
When all attenMaskOptional shapes are smaller than 2048 and identical, it is recommended to use the default mode to reduce memory usage.
When sparseMode is set to 1, 2, or 3, the user-configured preTokens and nextTokens do not take effect.
When set to 0 or 4, ensure that the ranges of attenMaskOptional, preTokens, and nextTokens are consistent.
If not explicitly specified, 0 is recommended.
For details about different sparse modes, see sparse mode description.
When set to 3, invalid row computation is not supported, and Sq <= Skv must be satisfied for each batch.
When set to 7, the optional input realShiftOptional is not supported.
When configured as 8, the optional input realShiftOptional is supported when q and kv of each sequence have equal lengths, and pse is generated globally. Outer cutting in the q direction is supported, which requires that q and kv of each sequence have equal lengths before outer cutting, and after outer cutting, actualSeqQLenOptional[0] - actualSeqKvLenOptional[0] + qStartIdxOptional - kvStartIdxOptional == 0 (this feature is experimental).
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 here is affected by parameters such as B, S, N, and D. Larger values result in a heavier computation workload.
In the band scenario, preTokens and nextTokens must have an intersection.
In the prefixOptional sparse computation scenario (sparseMode=6), when Sq > Skv, the value range of prefix N is [0, Skv]; when Sq <= Skv, the value range of prefix N is [Skv-Sq, Skv].
The actualSeqQLenOptional input supports an S length of 0 on a certain batch, but the optional input realShiftOptional is not supported in this case. The length of actualSeqQLenOptional ranges from 1 to 2K. When the prefixOptional input is present, its maximum supported length is 1K.
The attenMaskOptional input does not support padding, meaning that attenMaskOptional cannot contain a row where all values are 1.
Supports S length of 0 for a batch in actualSeqQLenOptional. If S is 0, pse input is not supported.
For example, if the actual S lengths are [2, 2, 0, 2, 2], the actualSeqQLenOptional passed in is [2, 4, 4, 6, 8].
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_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: initialize resources.
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 allocate 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);
// Compute 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;
}
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);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("Init acl failed. ERROR: %d\n", ret); return ret);
// 2. Construct the input and output. Customize the construction based on the API interface.
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 = {256, 1, 8};
std::vector<int64_t> softmaxSumShape = {256, 1, 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};
aclIntArray *prefix = aclCreateIntArray(prefixOp.data(), 1);
std::vector<int64_t> qStartIdxOp = {0};
std::vector<int64_t> kvStartIdxOp = {0};
aclIntArray *qStartIdx = aclCreateIntArray(qStartIdxOp.data(), 1);
aclIntArray *kvStartIdx = aclCreateIntArray(kvStartIdxOp.data(), 1);
std::vector<int64_t> acSeqQLenOp = {256};
std::vector<int64_t> acSeqKvLenOp = {256};
aclIntArray* acSeqQLen = aclCreateIntArray(acSeqQLenOp.data(), acSeqQLenOp.size());
aclIntArray* acSeqKvLen = aclCreateIntArray(acSeqKvLenOp.data(), acSeqKvLenOp.size());
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] = {'T', 'N', 'D', 0};
// 3. Call the CANN operator library API. Modify it to the specific API name.
uint64_t workspaceSize = 0;
aclOpExecutor* executor;
// Call the first-phase API of aclnnFlashAttentionVarLenScoreV2.
ret = aclnnFlashAttentionVarLenScoreV2GetWorkspaceSize(
q, k, v, pse, dropMask, padding, attenmask, prefix, acSeqQLen, acSeqKvLen, qStartIdx, kvStartIdx,
scaleValue, keepProb, preTokens, nextTokens, headNum, layOut, innerPrecise,
sparseMode, pseType, softmaxMax, softmaxSum, softmaxOut, attentionOut, &workspaceSize, &executor);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnFlashAttentionVarLenScoreV2GetWorkspaceSize 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 aclnnFlashAttentionVarLenScoreV2.
ret = aclnnFlashAttentionVarLenScoreV2(workspaceAddr, workspaceSize, executor, stream);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnFlashAttentionVarLenScoreV2 failed. ERROR: %d\n", ret); return ret);
// 4. (Boilerplate) Wait for task execution to complete synchronously.
ret = aclrtSynchronizeStream(stream);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtSynchronizeStream failed. ERROR: %d\n", ret); return ret);
// 5. Obtain the output values and copy the results from the device memory to the host memory. 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;
}