aclnnLightningIndexer
Applicable Products
| Product | Supported |
|---|---|
| × | |
| √ | |
| √ | |
| × | |
| × | |
| × |
Function
Description:
lightning_indexerobtains the Top- positions corresponding to each token through a series of operations.Formula:
For the Index Query corresponding to a token, given the context Index Key , where is the group size of GQA, is the dimension of each head, and is the context length.
Function Prototype
Each operator is divided into a two-phase API. You must first call the "aclnnLightningIndexerGetWorkspaceSize" API to obtain the workspace size required for computation and the executor that contains the operator computation process, and then call the "aclnnLightningIndexer" API to execute the computation.
aclnnStatus aclnnLightningIndexerGetWorkspaceSize(
const aclTensor *query,
const aclTensor *key,
const aclTensor *weights,
const aclTensor *actualSeqLengthsQueryOptional,
const aclTensor *actualSeqLengthsKeyOptional,
const aclTensor *blockTableOptional,
char *layoutQueryOptional,
char *layoutKeyOptional,
int64_t sparseCount,
int64_t sparseMode,
int64_t preTokens,
int64_t nextTokens,
bool returnValues,
const aclTensor *sparseIndicesOut,
const aclTensor *sparseValuesOut,
uint64_t *workspaceSize,
aclOpExecutor **executor)aclnnStatus aclnnLightningIndexer(
void *workspace,
uint64_t workspaceSize,
aclOpExecutor *executor,
const aclrtStream stream)aclnnLightningIndexerGetWorkspaceSize
- Parameters:
- Dimension meanings of query, key, and weights parameters: B (Batch Size) indicates the batch size of input samples, S (Sequence 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, D (Head Dim) indicates the minimum unit size of the hidden layer, satisfying D = H/N, and T indicates the cumulative sum of sequence lengths of all batch input samples.
- S1 indicates S in the query shape, S2 indicates S in the key shape, T1 indicates T in the query shape, T2 indicates T in the key shape, N1 indicates N in the query shape, and N2 indicates N in the key shape.
| Parameter | Input/Output | Description | Instruction | Data Type | Data Format | Dimension (Shape) | Non-contiguous Tensor |
|---|---|---|---|---|---|---|---|
| query | Input | Input Q in the formula. | Empty tensors are not supported. | FLOAT16, BFLOAT16 | ND |
|
x |
| key | Input | Input K in the formula. |
|
FLOAT16, BFLOAT16 | ND |
|
x |
| weights | Input | Input W in the formula. | Empty tensors are not supported. | FLOAT16, BFLOAT16, FLOAT | ND |
|
x |
| actualSeqLengthsQueryOptional | Input | Number of valid tokens of Query in each batch. |
|
INT32 | ND | (B,) | x |
| actualSeqLengthsKeyOptional | Input | The number of valid tokens of Key in each batch. |
|
INT32 | ND | (B,) | x |
| blockTableOptional | Input | Indicates the block mapping table used for KV storage in PageAttention. |
|
INT32 | ND | Shape supports (B, S2/block_size). | x |
| layoutQueryOptional | Input | Identifies the data layout format of the input Query. |
|
STRING | - | - | - |
| layoutKeyOptional | Input | Identifies the data layout format of the input Key. |
|
STRING | - | - | - |
| sparseCount | Input | Number of blocks to retain in the topK phase. | Supports [1, 2048], as well as 3072, 4096, 5120, 6144, 7168, and 8192. | INT32 | - | - | - |
| sparseMode | Input | Indicates the sparse mode. |
|
INT32 | - | - | - |
| preTokens | Input | Used for sparse computation. Indicates the number of previous tokens that attention needs to compute correlation with. | Only the default value 2^63-1 is supported. | INT64 | - | - | - |
| nextTokens | Input | Used for sparse computation, indicating the number of subsequent tokens that attention needs to compute correlation with. | Only the default value 2^63-1 is supported. | INT64 | - | - | - |
| returnValues | Input | Indicates whether to output sparseValuesOut. |
|
BOOL | - | - | - |
| sparseIndicesOut | Output | Indices output in the formula. | Empty tensors are not supported. | INT32 | - |
|
x |
| sparseValuesOut | Output | The value corresponding to the Indices output in the formula. | Empty tensors are not supported. | FLOAT16, BFLOAT16 | ND | The shape is consistent with sparseIndicesOut. | x |
| 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 If the passed parameter is a mandatory input, output, or mandatory attribute and is a null pointer, 161001 is returned. ACLNN_ERR_PARAM_INVALID 161002 The data type or data format of query, key, weights, actualSeqLengthsQueryOptional, actualSeqLengthsKeyOptional, layoutQueryOptional, layoutKeyOptional, sparseCount, sparseMode, returnValues, sparseIndicesOut, or sparseValuesOut is not within the supported range.
aclnnLightningIndexer
Parameters:
Parameter Input/Output Description workspace Input Address of the workspace memory applied for on the Device side. workspaceSize Input Size of the workspace applied for on the Device side, obtained through the first-phase API aclnnLightningIndexerGetWorkspaceSize. 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
The N dimension in the query parameter supports values less than or equal to 64, and the N dimension in key supports 1.
headdim supports 128.
block_size must be a multiple of 16, with a maximum supported value of 1024.
The data types of the query and key parameters must be consistent.
When the parameter weights is not
float32, the data types of the parameters query, key, and weights must be consistent.
Example
The following provides example code for reference only. For details about compilation and running, see Compile and Run Samples.
/**
* Copyright (c) 2026 Huawei Technologies Co., Ltd.
* This program is free software, you can redistribute it and/or modify it under the terms and conditions of
* CANN Open Software License Agreement Version 2.0 (the "License").
* Please refer to the License for details. You may not use this file except in compliance with the License.
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
* See LICENSE in the root of the software repository for the full text of the License.
*/
/*!
* \file test_incre_flash_attention_v4.cpp
* \brief
*/
//testci
#include <iostream>
#include <vector>
#include <cmath>
#include <cstring>
#include "securec.h"
#include "acl/acl.h"
#include "aclnnop/aclnn_lightning_indexer.h"
using namespace std;
namespace {
#define CHECK_RET(cond) ((cond) ? true :(false))
#define LOG_PRINT(message, ...) \
do { \
(void)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) {
auto ret = aclInit(nullptr);
if (!CHECK_RET(ret == ACL_SUCCESS)) {
LOG_PRINT("aclInit failed. ERROR: %d\n", ret);
return ret;
}
ret = aclrtSetDevice(deviceId);
if (!CHECK_RET(ret == ACL_SUCCESS)) {
LOG_PRINT("aclrtSetDevice failed. ERROR: %d\n", ret);
return ret;
}
ret = aclrtCreateStream(stream);
if (!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);
auto ret = aclrtMalloc(deviceAddr, size, ACL_MEM_MALLOC_HUGE_FIRST);
if (!CHECK_RET(ret == ACL_SUCCESS)) {
LOG_PRINT("aclrtMalloc failed. ERROR: %d\n", ret);
return ret;
}
ret = aclrtMemcpy(*deviceAddr, size, hostData.data(), size, ACL_MEMCPY_HOST_TO_DEVICE);
if (!CHECK_RET(ret == ACL_SUCCESS)) {
LOG_PRINT("aclrtMemcpy failed. ERROR: %d\n", ret);
return ret;
}
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];
}
*tensor = aclCreateTensor(shape.data(), shape.size(), dataType, strides.data(), 0, aclFormat::ACL_FORMAT_ND,
shape.data(), shape.size(), *deviceAddr);
return 0;
}
struct TensorResources {
void* queryDeviceAddr = nullptr;
void* keyDeviceAddr = nullptr;
void* weightsDeviceAddr = nullptr;
void* sparseIndicesDeviceAddr = nullptr;
void* sparseValuesDeviceAddr = nullptr;
aclTensor* queryTensor = nullptr;
aclTensor* keyTensor = nullptr;
aclTensor* weightsTensor = nullptr;
aclTensor* sparseIndicesTensor = nullptr;
aclTensor* sparseValuesTensor = nullptr;
};
int InitializeTensors(TensorResources& resources) {
std::vector<int64_t> queryShape = {1, 2, 1, 128};
std::vector<int64_t> keyShape = {1, 2, 1, 128};
std::vector<int64_t> weightsShape = {1, 2, 1};
std::vector<int64_t> sparseIndicesShape = {1, 2, 1, 2048};
std::vector<int64_t> sparseValuesShape = {1, 2, 1, 2048};
int64_t queryShapeSize = GetShapeSize(queryShape);
int64_t keyShapeSize = GetShapeSize(keyShape);
int64_t weightsShapeSize = GetShapeSize(weightsShape);
int64_t sparseIndicesShapeSize = GetShapeSize(sparseIndicesShape);
int64_t sparseValuesShapeSize = GetShapeSize(sparseValuesShape);
std::vector<float> queryHostData(queryShapeSize, 1);
std::vector<float> keyHostData(keyShapeSize, 1);
std::vector<float> weightsHostData(weightsShapeSize, 1);
std::vector<int32_t> sparseIndicesHostData(sparseIndicesShapeSize, 1);
std::vector<float> sparseValuesHostData(sparseValuesShapeSize, 1);
int ret = CreateAclTensor(queryHostData, queryShape, &resources.queryDeviceAddr,
aclDataType::ACL_FLOAT16, &resources.queryTensor);
if (!CHECK_RET(ret == ACL_SUCCESS)) {
return ret;
}
ret = CreateAclTensor(keyHostData, keyShape, &resources.keyDeviceAddr,
aclDataType::ACL_FLOAT16, &resources.keyTensor);
if (!CHECK_RET(ret == ACL_SUCCESS)) {
return ret;
}
ret = CreateAclTensor(weightsHostData, weightsShape, &resources.weightsDeviceAddr,
aclDataType::ACL_FLOAT16, &resources.weightsTensor);
if (!CHECK_RET(ret == ACL_SUCCESS)) {
return ret;
}
ret = CreateAclTensor(sparseIndicesHostData, sparseIndicesShape, &resources.sparseIndicesDeviceAddr,
aclDataType::ACL_INT32, &resources.sparseIndicesTensor);
if (!CHECK_RET(ret == ACL_SUCCESS)) {
return ret;
}
ret = CreateAclTensor(sparseValuesHostData, sparseValuesShape, &resources.sparseValuesDeviceAddr,
aclDataType::ACL_FLOAT16, &resources.sparseValuesTensor);
if (!CHECK_RET(ret == ACL_SUCCESS)) {
return ret;
}
return ACL_SUCCESS;
}
int ExecuteLightningIndexer(TensorResources& resources, aclrtStream stream,
void** workspaceAddr, uint64_t* workspaceSize) {
int64_t sparseCount = 2048;
int64_t sparseMode = 3;
int64_t preTokens = 9223372036854775807;
int64_t nextTokens = 9223372036854775807;
bool returnValue = true;
constexpr const char layerOutStr[] = "BSND";
constexpr size_t layerOutLen = sizeof(layerOutStr);
char layoutQuery[layerOutLen];
char layoutKey[layerOutLen];
errno_t memcpyRet = memcpy_s(layoutQuery, sizeof(layoutQuery), layerOutStr, layerOutLen);
if (!CHECK_RET(memcpyRet == 0)) {
LOG_PRINT("memcpy_s layoutQuery failed. ERROR: %d\n", memcpyRet);
return -1;
}
memcpyRet = memcpy_s(layoutKey, sizeof(layoutKey), layerOutStr, layerOutLen);
if (!CHECK_RET(memcpyRet == 0)) {
LOG_PRINT("memcpy_s layoutKey failed. ERROR: %d\n", memcpyRet);
return -1;
}
aclOpExecutor* executor;
int ret = aclnnLightningIndexerGetWorkspaceSize(resources.queryTensor, resources.keyTensor, resources.weightsTensor, nullptr, nullptr, nullptr,
layoutQuery, layoutKey, sparseCount, sparseMode, preTokens, nextTokens,returnValue,
resources.sparseIndicesTensor, resources.sparseValuesTensor, workspaceSize, &executor);
if (!CHECK_RET(ret == ACL_SUCCESS)) {
LOG_PRINT("aclnnLightningIndexerGetWorkspaceSize failed. ERROR: %d\n", ret);
return ret;
}
if (*workspaceSize > 0ULL) {
ret = aclrtMalloc(workspaceAddr, *workspaceSize, ACL_MEM_MALLOC_HUGE_FIRST);
if (!CHECK_RET(ret == ACL_SUCCESS)) {
LOG_PRINT("allocate workspace failed. ERROR: %d\n", ret);
return ret;
}
}
ret = aclnnLightningIndexer(*workspaceAddr, *workspaceSize, executor, stream);
if (!CHECK_RET(ret == ACL_SUCCESS)) {
LOG_PRINT("aclnnLightningIndexer failed. ERROR: %d\n", ret);
return ret;
}
return ACL_SUCCESS;
}
int PrintValueOutResult(std::vector<int64_t> &shape, void** deviceAddr) {
auto size = GetShapeSize(shape);
std::vector<aclFloat16> resultData(size, 0);
auto ret = aclrtMemcpy(resultData.data(), resultData.size() * sizeof(resultData[0]),
*deviceAddr, size * sizeof(resultData[0]), ACL_MEMCPY_DEVICE_TO_HOST);
if (!CHECK_RET(ret == ACL_SUCCESS)) {
LOG_PRINT("copy result from device to host failed. ERROR: %d\n", ret);
return ret;
}
for (int64_t i = 0; i < size; i++) {
LOG_PRINT("mean result[%ld] is: %f\n", i, aclFloat16ToFloat(resultData[i]));
}
return ACL_SUCCESS;
}
int PrintIndicesOutResult(std::vector<int64_t> &shape, void** deviceAddr) {
auto size = GetShapeSize(shape);
std::vector<int32_t> resultData(size, 0);
auto ret = aclrtMemcpy(resultData.data(), resultData.size() * sizeof(resultData[0]),
*deviceAddr, size * sizeof(resultData[0]), ACL_MEMCPY_DEVICE_TO_HOST);
if (!CHECK_RET(ret == ACL_SUCCESS)) {
LOG_PRINT("copy result from device to host failed. ERROR: %d\n", ret);
return ret;
}
for (int64_t i = 0; i < size; i++) {
LOG_PRINT("mean result[%ld] is: %d\n", i, resultData[i]);
}
return ACL_SUCCESS;
}
void CleanupResources(TensorResources& resources, void* workspaceAddr,
aclrtStream stream, int32_t deviceId) {
if (resources.queryTensor) {
aclDestroyTensor(resources.queryTensor);
}
if (resources.keyTensor) {
aclDestroyTensor(resources.keyTensor);
}
if (resources.weightsTensor) {
aclDestroyTensor(resources.weightsTensor);
}
if (resources.sparseIndicesTensor) {
aclDestroyTensor(resources.sparseIndicesTensor);
}
if (resources.sparseValuesTensor) {
aclDestroyTensor(resources.sparseValuesTensor);
}
if (resources.queryDeviceAddr) {
aclrtFree(resources.queryDeviceAddr);
}
if (resources.keyDeviceAddr) {
aclrtFree(resources.keyDeviceAddr);
}
if (resources.weightsDeviceAddr) {
aclrtFree(resources.weightsDeviceAddr);
}
if (resources.sparseIndicesDeviceAddr) {
aclrtFree(resources.sparseIndicesDeviceAddr);
}
if (resources.sparseValuesDeviceAddr) {
aclrtFree(resources.sparseValuesDeviceAddr);
}
if (workspaceAddr) {
aclrtFree(workspaceAddr);
}
if (stream) {
aclrtDestroyStream(stream);
}
aclrtResetDevice(deviceId);
aclFinalize();
}
} // namespace
int main() {
int32_t deviceId = 0;
aclrtStream stream = nullptr;
TensorResources resources = {};
void* workspaceAddr = nullptr;
uint64_t workspaceSize = 0;
std::vector<int64_t> sparseIndicesShape = {1, 2, 1, 2048};
std::vector<int64_t> sparseValuesShape = {1, 2, 1, 2048};
int ret = ACL_SUCCESS;
// 1. Initialize device and stream
ret = Init(deviceId, &stream);
if (!CHECK_RET(ret == ACL_SUCCESS)) {
LOG_PRINT("Init acl failed. ERROR: %d\n", ret);
return ret;
}
// 2. Initialize tensors
ret = InitializeTensors(resources);
if (!CHECK_RET(ret == ACL_SUCCESS)) {
CleanupResources(resources, workspaceAddr, stream, deviceId);
return ret;
}
// 3. Execute the operation
ret = ExecuteLightningIndexer(resources, stream, &workspaceAddr, &workspaceSize);
if (!CHECK_RET(ret == ACL_SUCCESS)) {
CleanupResources(resources, workspaceAddr, stream, deviceId);
return ret;
}
// 4. Synchronize stream
ret = aclrtSynchronizeStream(stream);
if (!CHECK_RET(ret == ACL_SUCCESS)) {
LOG_PRINT("aclrtSynchronizeStream failed. ERROR: %d\n", ret);
CleanupResources(resources, workspaceAddr, stream, deviceId);
return ret;
}
// 5. Process results
PrintIndicesOutResult(sparseIndicesShape, &resources.sparseIndicesDeviceAddr);
PrintValueOutResult(sparseValuesShape, &resources.sparseValuesDeviceAddr);
// 6. Cleanup resources
CleanupResources(resources, workspaceAddr, stream, deviceId);
return 0;
}