aclnnFusedInferAttentionScore
Applicable Products
| Product | Supported or Not |
|---|---|
| × | |
| √ | |
| × | |
| × | |
| × |
Function Description
A FlashAttention operator adapted for both full and incremental inference scenarios. It supports both full computation (
PromptFlashAttention) and incremental computation (IncreFlashAttention). WhenSof the query matrix is 1, the IncreFlashAttention branch is executed. In other scenarios, the PromptFlashAttention branch is executed.Formula
Self-attention constructs an attention model by leveraging the relationships within the input samples. The principle assumes an input sample sequence of length , where each element of is a -dimensional vector. Each -dimensional vector can be regarded as a token embedding. Such a sequence is transformed by three weight matrices to produce three matrices of .
The calculation formula for self-attention is generally defined as follows, where , , and are key attribute elements of the input sample, obtained through spatial transformation and unified into a single feature space. "Attention" in the formula and operator name is an abbreviation for "self-attention."
The score function in this operator employs the softmax function. The self-attention calculation formula is as follows:
The product of and represents the attention to the input . To prevent this value from becoming excessively large, it is typically scaled by dividing by the square root of , followed by row-wise softmax normalization. The result is then multiplied by to produce an matrix.
Prototype
Each operator has two-phase API calls. First, aclnnFusedInferAttentionScoreGetWorkspaceSize is called to obtain the input parameters and compute the required workspace size based on the process. Then, aclnnFusedInferAttentionScore is called to perform computation.
aclnnStatus aclnnFusedInferAttentionScoreGetWorkspaceSize(
const aclTensor *query,
const aclTensorList *key,
const aclTensorList *value,
const aclTensor *pseShift,
const aclTensor *attenMask,
const aclIntArray *actualSeqLengths,
const aclIntArray *actualSeqLengthsKv,
const aclTensor *deqScale1,
const aclTensor *quantScale1,
const aclTensor *deqScale2,
const aclTensor *quantScale2,
const aclTensor *quantOffset2,
const aclTensor *antiquantScale,
const aclTensor *antiquantOffset,
const aclTensor *blockTable,
const aclTensor *queryPaddingSize,
const aclTensor *kvPaddingSize,
int64_t numHeads,
double scaleValue,
int64_t preTokens,
int64_t nextTokens,
char *inputLayout,
int64_t numKeyValueHeads,
int64_t sparseMode,
int64_t innerPrecise,
int64_t blockSize,
int64_t antiquantMode,
bool softmaxLseFlag,
const aclTensor *attentionOut,
const aclTensor *softmaxLse,
uint64_t *workspaceSize,
aclOpExecutor **executor)aclnnStatus aclnnFusedInferAttentionScore(
void *workspace,
uint64_t workspaceSize,
aclOpExecutor *executor,
const aclrtStream stream)aclnnFusedInferAttentionScoreGetWorkspaceSize
Parameters
Parameter Input/Output Description Instruction Data Type Data Format Dimension (Shape) Non-contiguous Tensor query Input Input Q in the formula. - FLOAT16, BFLOAT16, INT8 ND 3-4 × key Input Input K in the formula. - FLOAT16, BFLOAT16, INT8 ND 3-4 × value Input Input V in the formula. - FLOAT16, BFLOAT16, INT8 ND 3-4 × pseShift Input Positional encoding. - Empty tensors are not supported.
- If this parameter is not used, pass nullptr.
- For details about the constraints, see Constraints.
FLOAT16, BFLOAT16 ND 4 × attenMask Input Mask matrix. - Empty tensors are not supported.
- If this parameter is not used, pass nullptr.
- For details about the constraints, see Constraints.
BOOL, INT8, UINT8 ND 2-4 × actualSeqLengths Input Valid sequence length of queries in different batches. - If the sequence length is not specified, pass nullptr.
- For details about the constraints, see Constraints.
INT64 - - - actualSeqLengthsKv Input Valid sequence length of key and value in different batches. - If the sequence length is not specified, pass nullptr, indicating that the sequence length is the same as the S length of the key/value shape.
- Restriction: The valid sequence length of each batch in the input parameter must be less than or equal to that of the corresponding batch in key/value. If the input length of `seqlenKv` is 1, all batches use the same `seqlenKv`. If the input length is greater than or equal to the batch size, the first *N* elements (where *N* equals the batch size) of `seqlenKv` are used. Other lengths are not supported.
- For details about the constraints, see Constraints.
INT64 - - - deqScale1 Input Dequantization factor after BMM1. - Empty tensors are not supported.
- Per-tensor is supported.
- If this parameter is not used, pass nullptr.
- For details about the constraints, see Constraints.
UINT64, FLOAT32 ND 1 - quantScale1 Input Quantization factor before BMM2. - Empty tensors are not supported.
- Per-tensor is supported.
- If this parameter is not used, pass nullptr.
- For details about the constraints, see Constraints.
FLOAT32 ND 1 - deqScale2 Input Dequantization factor after BMM2. - Empty tensors are not supported.
- Per-tensor is supported.
- If this parameter is not used, pass nullptr.
- For details about the constraints, see Constraints.
UINT64, FLOAT32 ND 1 - quantScale2 Input Output quantization factor. - Empty tensors are not supported.
- Per-tensor and per-channel are supported.
- If this parameter is not used, pass nullptr.
- For details about the constraints, see Constraints.
FLOAT32, BFLOAT16 ND 1-4 - quantOffset2 Input Output quantization offset. - Empty tensors are not supported.
- Per-tensor and per-channel are supported.
- If this parameter is not used, pass nullptr.
- For details about the constraints, see Constraints.
FLOAT32, BFLOAT16 ND 1-4 - antiquantScale Input Fake-quantization factor. - Empty tensors are not supported.
- Per-tensor, per-channel, and per-token are supported.
- If this parameter is not used, pass nullptr.
- For details about the constraints, see Constraints.
FLOAT16, BFLOAT16, FLOAT32 ND 1-4 - antiquantOffset Input Fake-quantization offset. - Empty tensors are not supported.
- Per-tensor, per-channel, and per-token are supported.
- The shape must be the same as that of antiquantScale.
- If this parameter is not used, pass nullptr.
- For details about the constraints, see Constraints.
FLOAT16, BFLOAT16, FLOAT32 ND 1-4 - blockTable Input Block mapping table used for KV storage in page attention. - Empty tensors are not supported.
- If this parameter is not used, pass nullptr.
INT32 ND 2 - queryPaddingSize Input Whether the data in each batch of query is right-aligned and the number of right-aligned elements. - Empty tensors are not supported.
- This parameter is valid only when Q_S is greater than 1. In other scenarios, it is invalid.
- If this parameter is not used, pass nullptr.
INT64 ND 1 - kvPaddingSize Input Whether the data in each batch of key/value is right-aligned and the number of right-aligned elements. - Empty tensors are not supported.
- If this parameter is not used, pass nullptr.
INT64 ND 1 - numHeads Input Number of query heads. In the BNSD scenario, the value must be the same as the N-axis value of query in the shape. Otherwise, an exception occurs. INT64 - - - scaleValue Input Reciprocal of the square root of d in the formula. - Its data type must be compatible with that of query according to the type promotion rules.
- If no specific value is required, 1.0 is recommended.
DOUBLE - - - preTokens Input Number of preceding tokens to associate in attention computation for sparse computation. - If no specific value is required, 2147483647 is recommended.
- This parameter is invalid when Q_S is 1.
INT64 - - - nextTokens Input Number of succeeding tokens to associate in attention computation. - If no specific value is required, 2147483647 is recommended.
- This parameter is invalid when Q_S is 1.
INT64 - - - inputLayout Input Layout of the input query, key, and value. - If no specific value is required, BSH is recommended.
- For details about the constraints, see Constraints.
CHAR - - - numKeyValueHeads Input Number of heads in key and value. - If no specific value is required, 0 is recommended, indicating that key, value, and query have the same number of heads.
- For details about the constraints, see Constraints.
INT64 - - - sparseMode Input Sparse mode. For details about the constraints, see Constraints. INT64 - - - innerPrecise Input Choice between high precision and high performance. For details about the constraints, see Constraints. INT64 - - - blockSize Input Maximum number of tokens in each block for KV storage in page attention. The value can only be 0. INT64 - - - antiquantMode Input Fake-quantization mode. - The value 0 indicates per-channel (per-channel contains per-tensor).
- The value 1 indicates per-token.
- If no specific value is required, 0 is recommended.
- When Q_S is 1, an exception occurs if a value other than 0 or 1 is passed. This parameter is invalid when Q_S is greater than or equal to 2.
INT64 - - - softmaxLseFlag Input Whether to output softmax_lse. - Supports S-axis outer splitting (augmented output).
- If no specific value is required, false is recommended.
BOOL - - - attentionOut Output Output in the formula. The D dimension of this input parameter must be the same as that of value, and other dimensions must be the same as the shape of the input query. FLOAT16, BFLOAT16, INT8 ND 3-4 - softmaxLse Output Result of query-key multiplication in ring attention. For details about the constraints, see Constraints. FLOAT32 ND 4 - workspaceSize Output Size of the workspace to be allocated on the device. - - - - - executor Output Operator executor, containing the operator computation process. - - - - - Returns
aclnnStatus: status code. For details, see aclnn Return Codes.The first-phase API implements input parameter verification. The following errors may be thrown.
Return Error Code Description ACLNN_ERR_PARAM_NULLPTR 161001 The passed query, key, value, or attentionOut is a null pointer. ACLNN_ERR_PARAM_INVALID 161002 The data type or data format of query, key, value, pseShift, attenMask, or attentionOut is not supported. ACLNN_ERR_RUNTIME_ERROR 361001 An exception occurred when the NPU Runtime API was called.
aclnnFusedInferAttentionScore
Parameters
Parameter Input/Output Description workspace Input Address of the workspace to be allocated on the device. workspaceSize Input Size of the workspace to be allocated on the device, which is obtained by calling the first-phase API aclnnPromptFlashAttentionGetWorkspaceSize. executor Input Operator executor, containing the operator computation process. stream Input Stream for executing the task. Returns
aclnnStatus: status code. For details, see aclnn Return Codes.
Constraints
Deterministic computation
aclnnPromptFlashAttentiondefaults to a deterministic implementation.
When this API is used together with PyTorch, ensure that the CANN package versions match the PyTorch package versions.
Processing logic for a null input parameter: The operator checks whether
queryis a null pointer. If so, an error is reported. Ifqueryis not an empty tensor butkeyandvalueare empty tensors (that is, S2 is 0),attentionOutis filled with all zeros. IfattentionOutis an empty tensor, the framework will process it. For other input parameters which support the passing of null pointers as described in the preceding parameter description, no processing is performed when they are null pointers.The shapes of the tensors corresponding to the
keyandvalueparameters must be identical. In non-contiguous scenarios, the batch size in the tensor lists ofkeyandvaluecan only be 1, and the number of elements must be equal to the batch size (B) ofquery. The N and D dimensions must be the same. Due to the tensor list restrictions, B cannot be greater than 256 in non-contiguous scenarios.Restrictions on
pseShift- If
Q_Sis not1,querymust be of type FLOAT16 or INT8 whenpseShiftis of type FLOAT16, andquerymust be of type BFLOAT16 whenpseShiftis of type BFLOAT16. The input shape must be (B, N, Q_S, KV_S) or (1, N, Q_S, KV_S), whereQ_Sis the S dimension in the shape ofquery, andKV_Sis the S dimension in the shapes ofkeyandvalue. For scenarios whereKV_SofpseShiftis not 32-byte aligned, it is recommended that the value be padded to 32 bytes to improve performance. There is no requirement on the padding value of the extra part. - If
Q_Sis 1,querymust be of type FLOAT16 whenpseShiftis of type FLOAT16, andquerymust be of type BFLOAT16 whenpseShiftis of type BFLOAT16. The input shape must be (B, N, 1, KV_S) or (1, N, 1, KV_S), whereKV_Sis the S dimension in the shapes ofkeyandvalue. For scenarios whereKV_SofpseShiftis not 32-byte aligned, it is recommended that the value be padded to 32 bytes to improve performance. There is no requirement on the padding value of the extra part.
- If
attenMask: masks the QK product to define the attention visibility between tokens.- If
Q_Sis not 1, the recommended input shapes are (Q_S, KV_S), (B, Q_S, KV_S), (1, Q_S, KV_S), (B, 1, Q_S, KV_S), and (1, 1, Q_S, KV_S). - If
Q_Sis 1, the recommended input shapes are (B, KV_S), (B, 1, KV_S), and (B, 1, 1, KV_S). Q_Srefers to the S dimension in the shape ofquery, whileKV_Srefers to the S dimension in the shapes ofkeyandvalue. IfQ_SorKV_Sis not 16- or 32-byte aligned, it can be rounded up to the nearest aligned value.
- If
When the data type of
attenMaskis INT8 or UINT8, the value in the tensor must be 0 or 1.actualSeqLengths: aclIntArray on the host, indicating the valid sequence lengths ofqueryin different batches. The data type can be INT64. If you do not specifyseqlen,nullptris passed, indicating that the length is the same as that ofSin the shape ofquery. Restriction: The valid sequence length of each batch in the input parameter must be less than or equal to that of the corresponding batch inquery. This parameter is invalid whenQ_Sis1. If the input length ofseqlenis 1, all batches use the sameseqlen. If the input length is greater than or equal to the batch size, the first N elements (where N equals the batch size) ofseqlenare used. Other lengths are not supported.actualSeqLengthsKv: aclIntArray on the host. It can be set tonullptr, indicating the valid sequence lengths ofkey/valuein different batches. The data type can be INT64. If you do not specifyseqlen,nullptris passed, indicating that the length is the same as that ofSin the shape ofkey/value. Restriction: The valid sequence length of each batch in the input parameter must be less than or equal to that of the corresponding batch inkey/value. If the input length ofseqlenKvis 1, all batches use the sameseqlenKv. If the input length is greater than or equal to the batch size, the first N elements (where N equals the batch size) ofseqlenKvare used. Other lengths are not supported.Constraints on the number of input parameters and input and output data formats related to INT8 quantization:
- If both the input and output are of the INT8 type, the input parameters
deqScale1,quantScale1,deqScale2, andquantScale2must exist at the same time.quantOffset2is optional and defaults to 0 if not passed. - If the input is of the INT8 type and the output is of the FLOAT16 type, the input parameters
deqScale1,quantScale1, anddeqScale2must exist at the same time. If the input parameterquantOffset2orquantScale2exists (notnullptr), an error is reported and returned. - When the input is of the FLOAT16 or BFLOAT16 type and the output is of the INT8 type, the input parameter
quantScale2must exist, andquantOffset2is optional (defaults to 0 if not passed). If the input parameterdeqScale1,quantScale1, ordeqScale2exists (notnullptr), an error is reported and returned. - The input parameters
quantScale2andquantOffset2support both the per-tensor and per-channel formats and the FLOAT32 and BFLOAT16 data types. IfquantOffset2is passed, ensure that its type and shape are consistent with those ofquantScale2. When the input is of the BFLOAT16 type, both FLOAT32 and BFLOAT16 are supported. Otherwise, only FLOAT32 is supported. In per-channel format, when the output layout is BSH, the product of all dimensions ofquantScale2must be equal to H. For other layouts, the product must be equal to N × D. (When the output layout is BSH, it is recommended that the shape ofquantScale2be set to [1,1,H] or [H]. When the output layout is BNSD, it is recommended that the shape ofquantScale2be set to [1,N,1,D] or [N,D]. When the output layout is BSND, it is recommended that the shape ofquantScale2be set to [1,1,N,D] or [N,D].)
- If both the input and output are of the INT8 type, the input parameters
Constraints on the fake-quantization parameters
antiquantScaleandantiquantOffset:- Per-channel mode: The shapes of the two parameters can be (2, N, 1, D), (2, N, D), (2, H), where N is
numKeyValueHeads. The data type is the same as that ofquery, andantiquantModeis set to0. - Per-tensor mode: The shapes of the two parameters are (2), the data type is the same as that of
query, andantiquantModeis set to0. - Per-token mode: The shapes of the two parameters are (2, B, S), the data type is fixed at FLOAT32, and
antiquantModeis set to1. - In asymmetric quantization mode, both
antiquantScaleandantiquantOffsetmust be present. - In symmetric quantization mode,
antiquantOffsetcan benullptr. IfantiquantOffsetisnullptr, symmetric quantization is performed. Otherwise, asymmetric quantization is performed. - If
Q_Sis greater than or equal to 2, only FLOAT16 and FLOAT32 are supported. (FLOAT32 is supported only in page attention scenarios.)
- Per-channel mode: The shapes of the two parameters can be (2, N, 1, D), (2, N, D), (2, H), where N is
inputLayout: data layout of the inputquery,key, andvalue. Currently, BSH, BSND, BNSD, and BNSD_BSND are supported. (If the input layout is BNSD, the output layout is BSND, and onlyQ_Sgreater than 1 is supported.) If no specific layout is required, BSH is recommended.- Note: The data layout of
query,key, andvaluecan be interpreted from multiple dimensions. B (Batch) indicates the batch size of input samples, S (Seq-Length) indicates the sequence length of input samples, H (Head-Size) indicates the size of the hidden layer, N (Head-Num) indicates the number of heads, and D (Head-Dim) indicates the minimum unit size of the hidden layer (D = H/N).
- Note: The data layout of
Restrictions on
numKeyValueHeads:numHeadsmust be exactly divided bynumKeyValueHeads, and the ratio ofnumHeadstonumKeyValueHeadscannot be greater than 64. In the BSND, BNSD, and BNSD_BSND scenarios, the value must be the same as the N-axis value ofkey/valuein the shape. Otherwise, an exception occurs.Restrictions on
sparseModesparseMode Mode Description 0 defaultMask mode - If attenmask is not passed, the mask operation is not performed, and preTokens and nextTokens are ignored (internally set to INT_MAX).
- If attenmask is passed, a complete attenmask matrix (S1 × S2) needs to be passed, indicating that the portion between preTokens and nextTokens needs to be calculated.
1 allMask mode A complete attenmask matrix (S1 × S2) must be passed. 2 leftUpCausal mode An optimized attenmask matrix (2048 × 2048) must be passed. 3 rightDownCausal mode This corresponds to a lower-triangular matrix partitioned by the top-right vertex. In this case, an optimized attenmask matrix (2048 × 2048) needs to be passed. 4 band mode An optimized attenmask matrix (2048 × 2048) must be passed. 5 prefix mode This mode is not supported currently. If no specific value is required, 0 is recommended. 6 global mode This mode is not supported currently. If no specific value is required, 0 is recommended. 7 dilated mode This mode is not supported currently. If no specific value is required, 0 is recommended. 8 block_local mode This mode is not supported currently. If no specific value is required, 0 is recommended. Note: This parameter is invalid when Q_S is 1. Restrictions on
innerPreciseThere are four modes (0, 1, 2, and 3) in total, represented by 2-bit combinations. Bit 0 indicates whether to use the high-precision or high-performance mode, and bit 1 indicates whether to perform invalid row correction.
innerPrecise Mode Invalid Row Correction 0 High-precision × 1 High-performance × 2 High-precision √ 3 High-performance √ Note: The high-precision and high-performance modes are applicable to both BFLOAT16 and INT8. Invalid row correction takes effect for FLOAT16, BFLOAT16, and INT8. The values
0and1are reserved. If the masks involved in the computation are all 1s, the precision may be affected. In this case, you can set this parameter to2or3to enable invalid row correction to improve the precision. However, this configuration deteriorates the performance. If the operator can determine that invalid rows exist, the invalid row correction is automatically enabled, such as in scenarios wheresparseModeis set to3and Sq is greater than Skv.
Restrictions on
softmaxLse- In the ring attention algorithm, the product of query and key is first processed to obtain softmax_max. This max value is subtracted from the product before calculating the exponential, which is then summed to yield softmax_sum. Finally, the log of softmax_sum is added back to softmax_max to obtain the final result.
- When
softmaxLseFlagisTrue, the shape must be [B,N,Q_S,1], where inf values represent invalid data. - When
softmaxLseFlagisFalse, if thesoftmaxLsetensor is notnullptr, the tensor data is returned directly. IfsoftmaxLseisnullptr, a tensor of shape {1} filled with zeros is returned.
When Q_S is greater than 1
Restrictions on
query,key, andvalue:The B axis must be less than or equal to 65536. If the input type is INT8 and the D axis is not 32-byte aligned, the maximum value of the B axis is 128. If the input type is FLOAT16 or BFLOAT16 and the D axis is not 16-byte aligned, the maximum value of the B axis is also 128.
The N axis can be less than or equal to 256, and the D axis can be less than or equal to 512. If
inputLayoutis BSH or BSND, N × D must be less than 65535.The S axis must be less than or equal to 20971520 (20M). In some long sequence scenarios, if the computation load is too large, the PFA operator execution may time out (an AI Core error is reported, and
errorStristimeout or trap error). In this case, S axis splitting is recommended. Note: The computation load is affected by parameters such as B, S, N, and D. Larger values indicate larger computation loads. The following lists some typical scenarios with long sequences (that is, the product of B, S, N, and D is large).B Q_N Q_S D KV_N KV_S 1 20 2097152 256 1 2097152 1 2 20971520 256 2 20971520 20 1 2097152 256 1 2097152 1 10 2097152 512 1 2097152 If the data type of
query,key,value, orattentionOutis INT8, the D axis must be 32-element aligned. If the type is FLOAT16 or BFLOAT16, the D axis must be 16-element aligned.
Currently,
sparseModecan only be set to 0, 1, 2, 3, or 4. An error will be reported if it is set to other values.- When
sparseModeis set to 0, ifattenMaskis a null pointer or is passed in the left padding scenario, the input parameterspreTokensandnextTokensare ignored. - When
sparseModeis set to 2, 3, or 4, the shape ofattenMaskmust be (S, S), (1, S, S), or (1, 1, S, S), where S must be fixed at 2048. Additionally, the inputattenMaskmust be a lower triangular matrix. IfattenMaskis not passed or the passed shape is incorrect, an error will be reported. - When
sparseModeis set to 1, 2, or 3, the input parameterspreTokensandnextTokensare ignored, and their values are assigned based on related rules.
- When
In the synthesis parameter scenario of KV cache dequantization, only when
queryis of the FLOAT16 type,keyandvalueof the INT8 type can be dequantized to FLOAT16. If the product of the data ranges of the inputkeyandvalueand the data range of the inputantiquantScalemust be within the range of (–1, 1), the high-performance mode can ensure precision. Otherwise, the high-precision mode needs to be enabled to ensure precision.Page attention scenario
- The prerequisite for enabling page attention is that
blockTableexists and is valid, andkeyandvalueare arranged in a continuous memory based on the indexes inblockTable. In this scenario,inputLayoutofkeyandvalueis invalid.blockTableis filled with block IDs. Currently, the validity of block IDs is not verified. You need to ensure the validity of block IDs. blockSizeis a user-defined parameter. Its value affects the page attention performance. When page attention is enabled, the value ofblockSizemust be a multiple of 128, ranging from 128 to 512. Generally, page attention can improve the throughput but deteriorate the performance.- In the page attention scenario, if the input KV cache layout is BnBsH (blocknum, blocksize, H) and the product of
KV_Nmultiplied byDexceeds 65535, an error will be reported due to hardware instruction restrictions. This problem can be solved by enabling GQA (decreasingKV_N) or adjusting the KV cache layout to BnNBsD (blocknum, KV_N, blocksize, D). WheninputLayoutofqueryis BNSD, the KV cache layout can beBnBsH or BnNBsD. WheninputLayoutofqueryis BSH or BSND, the KV cache layout can only be BnBsH. The value ofblocknumcannot be less than the sum of blocks in each batch calculated based onactualSeqLengthsKvandblockSize. The shapes ofkeyandvaluemust be the same. - Page attention does not support fake-quantization, tensor list, or left padding.
- In the page attention scenario,
actualSeqLengthsKvmust be passed. - In the page attention scenario,
blockTablemust be two-dimensional. The length of the first dimension must be equal to B, and the length of the second dimension must be greater than or equal tomaxBlockNumPerSeq(the maximum number of blocks corresponding toactualSeqLengthsKvin different batches). - In the page attention scenario,
querycannot be of the INT8 type. - When page attention is enabled, the input
KV_Smust be greater than or equal tomaxBlockNumPerSeq×blockSizein the following scenarios:- When
attenMaskis passed, the mask shape is (B, 1, Q_S, KV_S). - When
pseShiftis passed, thepseShiftshape is (B, N, Q_S, KV_S).
- When
- The prerequisite for enabling page attention is that
Left padding for
query- The transfer start point of
queryis calculated as follows:Q_S–queryPaddingSize–actualSeqLengths. The transfer end point ofqueryis calculated as follows:Q_S–queryPaddingSize. The transfer start point ofquerycannot be less than 0, while the end point cannot be greater thanQ_S. Otherwise, the result will not meet the expectation. - If
kvPaddingSizeis less than 0, it will be set to 0. - It must be enabled together with
actualSeqLengths. Otherwise, the default scenario is right padding forquery. - It does not support page attention and cannot be enabled together with
blockTable.
- The transfer start point of
Left padding for
kv:- The transfer start point of
keyandvalueis calculated as follows:KV_S–kvPaddingSize–actualSeqLengthsKv. The transfer end point ofkeyandvalueis calculated as follows:KV_S–kvPaddingSize. The transfer start point ofkeyandvaluecannot be less than 0, while the end point cannot be greater thanKV_S. Otherwise, the result will not meet the expectation. - If
kvPaddingSizeis less than 0, it will be set to 0. - It must be enabled together with
actualSeqLengthsKv. Otherwise, the default scenario is right padding forkv. - It does not support page attention and cannot be enabled together with
blockTable.
- The transfer start point of
When the output is of type INT8 and
quantScale2andquantOffset2are per-channel, left padding, ring attention, or non-32-byte alignment of D is not supported.When the output is of type INT8,
sparsecannot bebandandpreTokensornextTokenscannot be negative.Restrictions on
pseShift- This function is supported when the data type of
queryis FLOAT16, BFLOAT16, or INT8. - When the data type of
queryis FLOAT16 andpseShiftexists, the high-precision mode is forcibly used. The corresponding restrictions are the same as those of the high-precision mode. Q_Smust be greater than or equal toSofquery, andKV_Smust be greater than or equal toSofkey.
- This function is supported when the data type of
When the output of type INT8, if the input parameter
quantOffset2is a non-null pointer and a non-null tensor, andsparseMode,preTokens, andnextTokensmeet the following conditions, certain rows of the matrix will not be involved in computation, resulting in a computation result error. In this scenario, the computation will be intercepted. (Solution: To prevent interception, perform post-quantization outside the FIA interface.)When
sparseModeis 0 andattenMaskis a non-null pointer, interception occurs ifactualSeqLengths–actualSeqLengthsKV–preTokens> 0 ornextTokens< 0 for any batch.When
sparseModeis 1 or 2, interception does not occur.When
sparseModeis 3, interception occurs ifactualSeqLengthsKV–actualSeqLengths< 0 for any batch.When
sparseModeis 4, interception occurs ifpreTokens< 0 ornextTokens+actualSeqLengthsKV–actualSeqLengths< 0 for any batch.
When Q_S is equal to 1
- Restrictions on
query,key, andvalue:- The B axis can be less than or equal to 65536, the N axis can be less than or equal to 256, and the D axis can be less than or equal to 512.
- The input types of
query,key, andvaluecannot be all INT8.
- Page attention scenario
- The prerequisite for enabling page attention is that
blockTableexists and is valid, andkeyandvalueare arranged in a continuous memory based on the indexes inblockTable. The data types ofkeyandvaluecan be FLOAT16, BFLOAT16, or INT8. In this scenario,inputLayoutofkeyandvalueis invalid.blockTableis filled with block IDs. Currently, the validity of block IDs is not verified. You need to ensure the validity of block IDs. blockSizeis a user-defined parameter. Its value affects the page attention performance. When page attention is enabled, the value ofblockSizemust be a multiple of 128, ranging from 128 to 512. Generally, page attention can improve the throughput but deteriorate the performance.- In the page attention scenario, when
inputLayoutofqueryis BNSD, the KV cache layout can be (blocknum, blocksize, H) or (blocknum, KV_N, blocksize, D). WheninputLayoutofqueryis BSH or BSND, the KV cache layout can only be (blocknum, blocksize, H). The value ofblocknumcannot be less than the sum of blocks in each batch calculated based onactualSeqLengthsKvandblockSize. The shapes ofkeyandvaluemust be the same. - In the page attention scenario, the performance is generally better when the KV cache layout is (blocknum, KV_N, blocksize, D) than when it is (blocknum, blocksize, H). Therefore, (blocknum, KV_N, blocksize, D) is recommended.
- In the page attention scenario, if the input KV cache layout is (blocknum, blocksize, H) and the product of
KV_Nmultiplied byDexceeds 64 KB, an error will be reported due to hardware instruction restrictions. This problem can be solved by enabling GQA (decreasingKV_N) or adjusting the KV cache layout to (blocknum, KV_N, blocksize, D). - Page attention does not support the tensor list or left padding.
- In the page attention scenario,
actualSeqLengthsKvmust be passed. - In the page attention scenario,
blockTablemust be two-dimensional. The length of the first dimension must be equal to B, and the length of the second dimension must be greater than or equal tomaxBlockNumPerSeq(the maximum number of blocks corresponding toactualSeqLengthsKvin each batch). - When page attention is enabled, the input
KV_Smust be greater than or equal tomaxBlockNumPerSeq×blockSizein the following scenarios:- When
attenMaskis passed, the mask shape is (B, 1, Q_S, KV_S). - When
pseShiftis passed, thepseShiftshape is (B, N, Q_S, KV_S).
- When
- The prerequisite for enabling page attention is that
- Left padding for
kv:- The transfer start point of
kvCacheis calculated as follows:KV_S–kvPaddingSize–actualSeqLengthsThe transfer end point ofkvCacheis calculated as follows:KV_S–kvPaddingSizeIf the transfer start point or end point is less than 0, the returned data is all 0s. - If
kvPaddingSizeis less than 0, it will be set to 0. - It must be enabled together with
actualSeqLengths. Otherwise, the default scenario is right padding forkv. - When it is enabled together with
attenMask, ensure that the meaning ofattenMaskis correct, that is, invalid data can be correctly masked. Otherwise, precision issues may occur.
- The transfer start point of
- Restrictions on
pseShift- The data types of
pseShiftandquerymust be consistent.
- The data types of
- Restrictions on
Example
The following example is for reference only. For details, see Compilation and Running Sample.
#include <iostream>
#include <vector>
#include <math.h>
#include <cstring>
#include "acl/acl.h"
#include "aclnn/opdev/fp16_t.h"
#include "aclnnop/aclnn_fused_infer_attention_score.h"
using namespace std;
#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;
}
int Init(int32_t deviceId, aclrtStream* stream)
{
// (Boilerplate) Initialize AscendCL.
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) * aclDataTypeSize(dataType);
// Call aclrtMalloc to allocate memory on the device.
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 to the device.
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 the 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 and stream. For details, see the AscendCL external API list.
// Set the device ID in use.
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 based on the API.
int32_t batchSize = 1;
int32_t numHeads = 2;
int32_t numKeyValueHeads = 2;
int32_t sequenceLengthQ = 1;
int32_t sequenceLengthKV = 512;
int32_t headDims = 128;
std::vector<int64_t> queryShape = {batchSize, numHeads, sequenceLengthQ, headDims}; // BNSD
std::vector<int64_t> keyShape = {batchSize, numKeyValueHeads, sequenceLengthKV, headDims}; // BNSD
std::vector<int64_t> valueShape = {batchSize, numKeyValueHeads, sequenceLengthKV, headDims}; // BNSD
std::vector<int64_t> attenMaskShape = {batchSize, 1, sequenceLengthQ, sequenceLengthKV}; // B 1 S1 S2
std::vector<int64_t> outShape = {batchSize, numHeads, sequenceLengthQ, headDims}; // BNSD
void* queryDeviceAddr = nullptr;
void* keyDeviceAddr = nullptr;
void* valueDeviceAddr = nullptr;
void* attenMaskDeviceAddr = nullptr;
void* outDeviceAddr = nullptr;
aclTensor* queryTensor = nullptr;
aclTensor* keyTensor = nullptr;
aclTensor* valueTensor = nullptr;
aclTensor* attenMaskTensor = nullptr;
aclTensor* outTensor = nullptr;
int64_t queryShapeSize = GetShapeSize(queryShape); // BNSD
int64_t keyShapeSize = GetShapeSize(keyShape); // BNSD
int64_t valueShapeSize = GetShapeSize(valueShape); // BNSD
int64_t attenMaskShapeSize = GetShapeSize(attenMaskShape); // B 1 S1 S2
int64_t outShapeSize = GetShapeSize(outShape); // BNSD
std::vector<op::fp16_t> queryHostData(queryShapeSize, 1.0);
std::vector<op::fp16_t> keyHostData(keyShapeSize, 1.0);
std::vector<op::fp16_t> valueHostData(valueShapeSize, 1.0);
std::vector<int8_t> attenMaskHostData(attenMaskShapeSize, 0);
std::vector<op::fp16_t> outHostData(outShapeSize, 1.0);
// Create a query aclTensor.
ret = CreateAclTensor(queryHostData, queryShape, &queryDeviceAddr, aclDataType::ACL_FLOAT16, &queryTensor);
CHECK_RET(ret == ACL_SUCCESS, return ret);
// Create a key aclTensor.
ret = CreateAclTensor(keyHostData, keyShape, &keyDeviceAddr, aclDataType::ACL_FLOAT16, &keyTensor);
CHECK_RET(ret == ACL_SUCCESS, return ret);
int kvTensorNum = 1;
aclTensor* tensorsOfKey[kvTensorNum];
tensorsOfKey[0] = keyTensor;
auto tensorKeyList = aclCreateTensorList(tensorsOfKey, kvTensorNum);
// Create a value aclTensor.
ret = CreateAclTensor(valueHostData, valueShape, &valueDeviceAddr, aclDataType::ACL_FLOAT16, &valueTensor);
CHECK_RET(ret == ACL_SUCCESS, return ret);
aclTensor* tensorsOfValue[kvTensorNum];
tensorsOfValue[0] = valueTensor;
auto tensorValueList = aclCreateTensorList(tensorsOfValue, kvTensorNum);
// Create attenMask aclTensor.
ret = CreateAclTensor(attenMaskHostData, attenMaskShape, &attenMaskDeviceAddr, aclDataType::ACL_INT8, &attenMaskTensor);
CHECK_RET(ret == ACL_SUCCESS, return ret);
// Create an out aclTensor.
ret = CreateAclTensor(outHostData, outShape, &outDeviceAddr, aclDataType::ACL_FLOAT16, &outTensor);
CHECK_RET(ret == ACL_SUCCESS, return ret);
std::vector<int64_t> actualSeqlenVector = {sequenceLengthKV};
auto actualSeqLengths = aclCreateIntArray(actualSeqlenVector.data(), actualSeqlenVector.size());
double scaleValue = 1 / sqrt(headDims); // 1 / sqrt(d)
int64_t preTokens = 65535;
int64_t nextTokens = 65535;
string sLayerOut = "BNSD";
char layerOut[sLayerOut.length()];
strcpy(layerOut, sLayerOut.c_str());
int64_t sparseMode = 0;
int64_t innerPrecise = 0;
int blockSize = 0;
int antiquantMode = 0;
bool softmaxLseFlag = false;
// 3. Call the CANN operator library API.
uint64_t workspaceSize = 0;
aclOpExecutor* executor;
// Call the first-phase API.
ret = aclnnFusedInferAttentionScoreGetWorkspaceSize(
queryTensor, tensorKeyList, tensorValueList, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, numHeads, scaleValue, preTokens, nextTokens,
layerOut, numKeyValueHeads, sparseMode, innerPrecise, blockSize, antiquantMode, softmaxLseFlag, outTensor,
nullptr, &workspaceSize, &executor);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnFusedInferAttentionScoreGetWorkspaceSize failed. ERROR: %d\n", ret);
return ret);
// Allocate device memory based on the computed workspaceSize.
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.
ret = aclnnFusedInferAttentionScore(workspaceAddr, workspaceSize, executor, stream);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnFusedInferAttentionScore failed. ERROR: %d\n", ret); return ret);
// 4. (Boilerplate) Wait until the task execution is 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 to the host. Modify the code based on the API definition.
auto size = GetShapeSize(outShape);
std::vector<op::fp16_t> resultData(size, 0);
ret = aclrtMemcpy(resultData.data(), resultData.size() * sizeof(resultData[0]), outDeviceAddr,
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 i = 0; i < size; i++) {
std::cout << "index: " << i << ": " << static_cast<float>(resultData[i]) << std::endl;
}
// 6. Release resources.
aclDestroyTensor(queryTensor);
aclDestroyTensor(keyTensor);
aclDestroyTensor(valueTensor);
aclDestroyTensor(attenMaskTensor);
aclDestroyTensor(outTensor);
aclDestroyIntArray(actualSeqLengths);
aclrtFree(queryDeviceAddr);
aclrtFree(keyDeviceAddr);
aclrtFree(valueDeviceAddr);
aclrtFree(attenMaskDeviceAddr);
aclrtFree(outDeviceAddr);
if (workspaceSize > 0) {
aclrtFree(workspaceAddr);
}
aclrtDestroyStream(stream);
aclrtResetDevice(deviceId);
aclFinalize();
return 0;
}