aclnnFlashAttentionUnpaddingScoreGradV5

Applicable Products

ProductSupported
Ascend 950PR/Ascend 950DT×
Atlas A3 training products/Atlas A3 inference products√
Atlas A2 training products/Atlas A2 inference products√
Atlas 200I/500 A2 inference products×
Atlas inference products×
Atlas training products×

Function

  • Description: Implements self-attention computation using the FlashAttention algorithm in training scenarios. Adds the sinkInOptional optional input.

    • Ascend 950PR/Ascend 950DT: The sinkInOptional parameter is not currently supported.
  • Formula:

    The forward attention formula is:

    =Dropout(Softmax(Mask(QKT+psed),atten_mask),keep_prob)V=Dropout(Softmax(Mask(\frac{QK^T+pse}{\sqrt{d}}),atten\_mask),keep\_prob)V

    For ease of expression, variables SS and PP are used to represent the formulas:

    =Mask(QKT+psed),atten_mask=Mask(\frac{QK^T+pse}{\sqrt{d}}),atten\_mask =Dropout(Softmax(S),keep_prob)=Dropout(Softmax(S),keep\_prob) =PV=PV

    The backward propagation formula for attention is:

    V=PTdYV=P^TdY Q=((dS)∗K)dQ=\frac{((dS)*K)}{\sqrt{d}} K=((dS)T∗Q)dK=\frac{((dS)^T*Q)}{\sqrt{d}}

    The computation logic after adding sink is shown below, which mainly modifies the softmax_max and softmax_sum logic computation parts.

    S=Q@KTS = Q @ K^{T} m=max(sink,max(S))m = max(sink, max(S))
Attention=eS−m@V∑eS−m+esink−mAttention = \frac{e^{S - m} @ V}{\sum e^{S-m} + e^{sink - m}} dSink=reduce(−P∗dP∗SimpleSoftmax(sink,x_max,x_sum))dSink = reduce(-P * dP * SimpleSoftmax(sink, x\_max, x\_sum))

Function Prototype

Each operator is divided into a two-phase API. You must first call the "aclnnFlashAttentionUnpaddingScoreGradV5GetWorkspaceSize" API to obtain the workspace size required for computation and the executor that contains the operator computation process, and then call the "aclnnFlashAttentionUnpaddingScoreGradV5" API to perform the computation.

aclnnStatus aclnnFlashAttentionUnpaddingScoreGradV5GetWorkspaceSize(
  const aclTensor   *query,
  const aclTensor   *queryRope,
  const aclTensor   *keyIn,
  const aclTensor   *keyInRope,
  const aclTensor   *value,
  const aclTensor   *dy,
  const aclTensor   *pseShiftOptional,
  const aclTensor   *dropMaskOptional,
  const aclTensor   *paddingMaskOptional,
  const aclTensor   *attenMaskOptional,
  const aclTensor   *softmaxMaxOptional,
  const aclTensor   *softmaxSumOptional,
  const aclTensor   *softmaxInOptional,
  const aclTensor   *attentionInOptional,
  const aclTensor   *sinkInOptional,
  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,
  char              *softmaxInLayout,
  const aclTensor   *dqOut,
  const aclTensor   *dqRopeOut,
  const aclTensor   *dkOut,
  const aclTensor   *dkRopeOut,
  const aclTensor   *dvOut,
  const aclTensor   *dpseOut,
  const aclTensor   *dsinkOut,
  uint64_t          *workspaceSize,
  aclOpExecutor    **executor);
aclnnStatus aclnnFlashAttentionUnpaddingScoreGradV5(
  void             *workspace,
  uint64_t          workspaceSize,
  aclOpExecutor    *executor,
  const aclrtStream stream)

aclnnFlashAttentionUnpaddingScoreGradV5GetWorkspaceSize

  • Parameters

    Parameter Input/Output Description Instruction Data Type Data Format Dimension (shape) Non-contiguous Tensor
    query Input Q in the formula. The data type must be consistent with keyIn/value. FLOAT16、BFLOAT16 ND [TND] √
    queryRope Optional input The rope part (rotary position encoding) of Q. Data type must be consistent with query. BFLOAT16 ND [TND] √
    keyIn Input K in the formula. The data type must be consistent with query/value. FLOAT16, BFLOAT16 ND [TND] √
    keyInRope Optional input The RoPE part of K. The data type must be consistent with keyIn. BFLOAT16 ND [TND] √
    value Input V in the formula. The data type must be consistent with that of query/keyIn. FLOAT16, BFLOAT16 ND [TND] √
    dy Input dY in the formula. - FLOAT16, BFLOAT16 ND [TND] √
    pseShiftOptional Optional input pse in the formula. The data type must be consistent with that of query. This parameter must be used together with pseType. FLOAT16、BFLOAT16 ND [B,N,1024,Skv]、[1,N,1024,Skv]、[B,N]、[N] √
    dropMaskOptional Optional input Dropout mask. Incompatible with rope. Pass nullptr if not used. UINT8 ND 0, 1 √
    paddingMaskOptional Optional input. Reserved parameter. Not currently used. Pass null when calling. - - - -
    qStartIdxOptional Optional input Represents the start index of the current chunk's query sequence in the global context for the external slicing scenario. - INT64 ND 0, 1 -
    kvStartIdxOptional Optional input Represents the external concatenation scenario, indicating the starting index of the current chunk's key and value sequences in the global sequence. - INT64 ND 0, 1 -
    attenMaskOptional Optional input atten_mask in the formula. A value of 1 indicates that the bit does not participate in the computation, and a value of 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] √
    softmaxMaxOptional Optional input Intermediate output of the forward softmax. - FLOAT ND [TN8], [NT8] √
    softmaxSumOptional Optional input Intermediate output of the forward softmax. - FLOAT ND [TN8], [NT8] √
    softmaxInOptional Optional input Intermediate output of the forward softmax. Reserved parameter, not currently used. - - - -
    attentionInOptional Optional input Forward attention output. Must be consistent with the data type and shape of query. FLOAT16, BFLOAT16, FLOAT32 ND [TND] √
    sinkInOptional Optional input. sink in the formula. Length is headNum. FLOAT32 ND [headNum] √
    prefixOptional Optional input. N of each batch in the prefix sparse scenario. Pass nullptr if not used. INT64 ND 0, 1 -
    actualSeqQLenOptional Optional input Actual Query sequence length. - INT64 ND 1 -
    actualSeqKvLenOptional Optional input. Actual Key/Value sequence length. - INT64 ND 1 -
    scaleValue Optional input Scale scaling factor. Generally set to D^-0.5. DOUBLE - - -
    keepProb Optional input Proportion of 1s in dropMaskOptional. Generally set to 1.0. DOUBLE - - -
    preTokens Optional input Left boundary of the sliding window during sparse computation. 2147483647 is recommended if not explicitly specified. INT64 - - -
    nextTokens Optional input Right boundary of the sliding window during sparse computation. 2147483647 is recommended if not explicitly specified. INT64 - - -
    headNum Input Number of heads on a single card, i.e., the length of the N-axis of Query. For comprehensive constraints, see Constraints. INT64 - - -
    inputLayout Input Input Q/K/V data layout. Supports TND. String - - -
    innerPrecise Optional input. Internal computation precision control. Reserved parameter, not currently used. INT64 - - -
    sparseMode Optional Input Sparse mode. Supports configuration of 0 to 8, excluding 5. When rope is passed in, 6 is not supported. INT64 - - -
    pseType Optional input. pse type. Supports configuration values from 0 to 3. INT64 - - -
    softmaxInLayout Optional input Controls the actual data layout of softmaxMax and softmaxSum. Pass "same_as_input" for TND, or "" for NTD. String - - -
    dqOut Output dQ in the formula, the gradient of Query. - FLOAT16、BFLOAT16 ND [TND] √
    dkOut Output dK in the formula, the gradient of Key. - FLOAT16, BFLOAT16 ND [TND] √
    dvOut Output dV in the formula, representing the Value gradient. - FLOAT16, BFLOAT16 ND [TND] √
    dpseOut Output Gradient of d(pse). Reserved parameter. Not currently used. - - - -
    dsinkOut Output dSink in the formula, representing the gradient of d(sinkInOptional). - FLOAT32 ND [headNum] √
    workspaceSize Output Returns the workspace size to be allocated 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, keyIn, value, dy, pseShiftOptional, dropMaskOptional, paddingMaskOptional, attenMaskOptional, softmaxMaxOptional, softmaxSumOptional, softmaxInOptional, attentionInOptional, sinkInOptional, dqOut, dkOut, dvOut, dsinkOut, or softmaxInLayout is not within the supported range.
    The data format of query, keyIn, value, dy, pseShiftOptional, dropMaskOptional, paddingMaskOptional, attenMaskOptional, softmaxMaxOptional, softmaxSumOptional, softmaxInOptional, attentionInOptional, sinkInOptional, dqOut, dkOut, dvOut, dsinkOut, or softmaxInLayout is not within the supported range.

aclnnFlashAttentionUnpaddingScoreGradV5

  • 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 aclnnFlashAttentionUnpaddingScoreGradV5GetWorkspaceSize.
    executor Input 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:

    • aclnnFlashAttentionUnpaddingScoreGradV5 defaults to a non-deterministic implementation. Deterministic computation can be enabled by calling aclrtCtxSetSysParamOpt.
  • When this API is used with PyTorch, ensure that the CANN-related packages and PyTorch-related packages have matching versions.

  • The B (batchsize) dimension of the inputs query, queryRope, key, keyRope, value, and dy must be equal.

  • The inputLayout of query, key, value, and dy must be consistent.

  • The inputLayout of query, queryRope, key, keyRope, value, and dy must be TND.

  • The data type of query, key, value, and pseShiftOptional must be consistent.

  • The shape of key and value must be consistent. When the d dimensions of query, key, and value are the same, the shape of query and dy must be consistent. Note: The current version only supports the case where the d dimensions of query, key, and value are the same.

  • The N of query and the N of key/value may differ, but they must be proportional, that is, Nq/Nkv must be a non-zero integer, with Nq in the value range of 1 to 256.

  • Constraints on data shape, taking TND of inputLayout as an example, where:

    • T(B*S): The value range is 1 to 1M.

    • B: The value range is 1 to 20000. When prefixOptional is present, B supports a maximum of 1K.

    • N: The value range is 1 to 256.

    • S: The value range is 1 to 1M.

    • D: value range: 1 to 768.

    • KeepProb: value range: (0, 1].

  • The data layout of query, key, and value supports only TND, where T is the tightly packed data of B and S 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 layer 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.

  • pseShiftOptional: 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 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:, :]. Details are as follows:

    • When the parameter differs for each batch, 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 supports the range [B,N] or [N].

    • If this parameter is not enabled, pseShiftOptional must be passed as nullptr, and pseType must be passed as 1.

  • Meaning of each pseType value

    pseTypeMeaningNote
    0Externally passed pse, first mul then add.-
    1Externally passed pse, first add then mul.Consistent with the implementation of FlashAttentionScoreGrad.
    2Internally generated pse, first mul then add.-
    3Internally generated pse, first mul then add then sqrt.-
  • The constraints on sparseMode are as follows:

    • When the shapes of all attenMaskOptional are smaller than 2048 and identical, it is recommended to use the default mode to reduce memory usage.

    • When configured as 1, 2, or 3, the user-configured preTokens and nextTokens do not take effect.

    • When sparseMode is configured as 0 or 4, ensure that attenMaskOptional is consistent with the ranges of preTokens and nextTokens.

    • If the user does not specify a value explicitly, it is recommended to pass 0.

    • For details about different sparse modes, see sparse mode description.

    • When configured as 7, the optional input pseShiftOptional is not supported.

    • When configured as 8, the optional input pseShiftOptional is supported when q and kv of each sequence have equal lengths, and pse is generated globally. Outward slicing in the q direction is supported, which requires that q and kv of each sequence have equal lengths before slicing, and after slicing, actualSeqQLenOptional[0] - actualSeqKvLenOptional[0] + qStartIdxOptional - kvStartIdxOptional == 0 (this is an experimental feature).

  • For details about different data formats, see data format.

  • 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.

  • The prefixOptional sparse computation only supports the compressed scenario, 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].

  • The actualSeqQLenOptional input supports a case where the S length of a batch is 0, but the optional input pseShiftOptional is not supported in this case.

  • Constraints on the softmaxMax and softmaxSum parameters: The input format is fixed as [B, N, S, 8], except for the TND input format, which is [T, N, 8]. Note: T=B*S.

  • The value of headNum must be consistent with the N value in the input query.

  • When the data layout of softmaxSum and softmaxMax is TND, softmaxInLayout must be "same_as_input".

  • The dimension of sinkInOptional is 1, and its length must be the same as the headNum of query.

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_grad.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, aclrtContext* context, aclrtStream* stream) {
  // Boilerplate: 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 = aclrtCreateContext(context, deviceId);
  CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtCreateContext failed. ERROR: %d\n", ret); return ret);
  ret = aclrtSetCurrentContext(*context);
  CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtSetCurrentContext 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 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;
  aclrtContext context;
  aclrtStream stream;
  auto ret = Init(deviceId, &context, &stream);
  CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("Init acl failed. ERROR: %d\n", ret); return ret);

  // 2. Construct the 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> dxShape = {256, 1, 128};
  std::vector<int64_t> attenmaskShape = {256, 256};
  std::vector<int64_t> softmaxMaxShape = {256, 1, 8};
  std::vector<int64_t> softmaxSumShape = {256, 1, 8};
  std::vector<int64_t> attentionInShape = {256, 1, 128};
  std::vector<int64_t> sinkInOptionalShape = {1};
  std::vector<int64_t> dqShape = {256, 1, 128};
  std::vector<int64_t> dkShape = {256, 1, 128};
  std::vector<int64_t> dvShape = {256, 1, 128};
  std::vector<int64_t> dsinkShape = {1};

  void* qDeviceAddr = nullptr;
  void* kDeviceAddr = nullptr;
  void* vDeviceAddr = nullptr;
  void* dxDeviceAddr = nullptr;
  void* attenmaskDeviceAddr = nullptr;
  void* softmaxMaxDeviceAddr = nullptr;
  void* softmaxSumDeviceAddr = nullptr;
  void* attentionInDeviceAddr = nullptr;
  void* sinkInOptionalDeviceAddr = nullptr;

  void* dqDeviceAddr = nullptr;
  void* dkDeviceAddr = nullptr;
  void* dvDeviceAddr = nullptr;
  void* dsinkDeviceAddr = nullptr;

  aclTensor* q = nullptr;
  aclTensor* k = nullptr;
  aclTensor* v = nullptr;
  aclTensor* dx = nullptr;
  aclTensor* pse = nullptr;
  aclTensor* dropMask = nullptr;
  aclTensor* padding = nullptr;
  aclTensor* attenmask = nullptr;
  aclTensor* softmaxMax = nullptr;
  aclTensor* softmaxSum = nullptr;
  aclTensor* softmaxIn = nullptr;
  aclTensor* attentionIn = nullptr;
  aclTensor* sinkInOptional = nullptr;
  aclTensor* dq = nullptr;
  aclTensor* dk = nullptr;
  aclTensor* dv = nullptr;
  aclTensor* dpse = nullptr;
  aclTensor* dsink = nullptr;

  std::vector<float> qHostData(32768, 1);
  std::vector<float> kHostData(32768, 1);
  std::vector<float> vHostData(32768, 1);
  std::vector<float> dxHostData(32768, 1);
  std::vector<uint8_t> attenmaskHostData(65536, 0);
  std::vector<float> softmaxMaxHostData(2048, 3.0);
  std::vector<float> softmaxSumHostData(2048, 3.0);
  std::vector<float> attentionInHostData(32768, 1);
  std::vector<float> sinkInOptionalHostData(1, 0);
  std::vector<float> dqHostData(32768, 0);
  std::vector<float> dkHostData(32768, 0);
  std::vector<float> dvHostData(32768, 0);
  std::vector<float> dsinkHostData(1, 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(dxHostData, dxShape, &dxDeviceAddr, aclDataType::ACL_FLOAT, &dx);
  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(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);
  ret = CreateAclTensor(attentionInHostData, attentionInShape, &attentionInDeviceAddr, aclDataType::ACL_FLOAT, &attentionIn);
  CHECK_RET(ret == ACL_SUCCESS, return ret);
  ret = CreateAclTensor(sinkInOptionalHostData, sinkInOptionalShape, &sinkInOptionalDeviceAddr, aclDataType::ACL_FLOAT, &sinkInOptional);
  CHECK_RET(ret == ACL_SUCCESS, return ret);
  ret = CreateAclTensor(dqHostData, dqShape, &dqDeviceAddr, aclDataType::ACL_FLOAT, &dq);
  CHECK_RET(ret == ACL_SUCCESS, return ret);
  ret = CreateAclTensor(dkHostData, dkShape, &dkDeviceAddr, aclDataType::ACL_FLOAT, &dk);
  CHECK_RET(ret == ACL_SUCCESS, return ret);
  ret = CreateAclTensor(dvHostData, dvShape, &dvDeviceAddr, aclDataType::ACL_FLOAT, &dv);
  CHECK_RET(ret == ACL_SUCCESS, return ret);
  ret = CreateAclTensor(dsinkHostData, dsinkShape, &dsinkDeviceAddr, aclDataType::ACL_FLOAT, &dsink);
  CHECK_RET(ret == ACL_SUCCESS, return ret);
  std::vector<int64_t> prefixOp = {0};
  aclIntArray* prefix = aclCreateIntArray(prefixOp.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());
  std::vector<int64_t> qStartIdxOp = {0};
  std::vector<int64_t> kvStartIdxOp = {0};
  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 softmaxInLayoutArr[] = "same_as_input";
  char layOut[5] = {'T', 'N', 'D', 0};

  // 3. Call the CANN operator library API. Replace with the actual API name.
  uint64_t workspaceSize = 0;
  aclOpExecutor* executor;

  // Call the first-phase API of aclnnFlashAttentionUnpaddingScoreGradV5.
  ret = aclnnFlashAttentionUnpaddingScoreGradV5GetWorkspaceSize(q, nullptr, k, nullptr, v, dx, pse, dropMask, padding,
              attenmask, softmaxMax, softmaxSum, softmaxIn, attentionIn, sinkInOptional, prefix, acSeqQLen, acSeqKvLen,
              qStartIdx, kvStartIdx, scaleValue, keepProb, preTokens, nextTokens, headNum, layOut, innerPrecise, sparseMode,
              pseType, softmaxInLayoutArr, dq, nullptr, dk, nullptr, dv, dpse, dsink, &workspaceSize, &executor);
  CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnFlashAttentionUnpaddingScoreGradV5GetWorkspaceSize 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 aclnnFlashAttentionUnpaddingScoreGradV5.
  ret = aclnnFlashAttentionUnpaddingScoreGradV5(workspaceAddr, workspaceSize, executor, stream);
  CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnFlashAttentionUnpaddingScoreGradV5 failed. ERROR: %d\n", ret); return ret);

  // 4. (Boilerplate) Synchronize and 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 side memory to the host side. Modify based on the specific API definition.
  PrintOutResult(dqShape, &dqDeviceAddr);
  PrintOutResult(dkShape, &dkDeviceAddr);
  PrintOutResult(dvShape, &dvDeviceAddr);
  PrintOutResult(dsinkShape, &dsinkDeviceAddr);

  // 6. Release aclTensor and aclScalar. Modify based on the specific API definition.
  aclDestroyTensor(q);
  aclDestroyTensor(k);
  aclDestroyTensor(v);
  aclDestroyTensor(dx);
  aclDestroyTensor(attenmask);
  aclDestroyTensor(softmaxMax);
  aclDestroyTensor(softmaxSum);
  aclDestroyTensor(attentionIn);
  aclDestroyTensor(sinkInOptional);
  aclDestroyTensor(dq);
  aclDestroyTensor(dk);
  aclDestroyTensor(dv);
  aclDestroyTensor(dsink);
  // 7. Release device resources.
  aclrtFree(qDeviceAddr);
  aclrtFree(kDeviceAddr);
  aclrtFree(vDeviceAddr);
  aclrtFree(dxDeviceAddr);
  aclrtFree(attenmaskDeviceAddr);
  aclrtFree(softmaxMaxDeviceAddr);
  aclrtFree(softmaxSumDeviceAddr);
  aclrtFree(attentionInDeviceAddr);
  aclrtFree(sinkInOptionalDeviceAddr);
  aclrtFree(dqDeviceAddr);
  aclrtFree(dkDeviceAddr);
  aclrtFree(dvDeviceAddr);
  aclrtFree(dsinkDeviceAddr);
  if (workspaceSize > 0) {
    aclrtFree(workspaceAddr);
  }
  aclrtDestroyStream(stream);
  aclrtDestroyContext(context);
  aclrtResetDevice(deviceId);
  aclFinalize();

  return 0;
}