aclnnAdvanceStep
Applicable Products
| Product | Supported |
|---|---|
| No | |
| Yes | |
| Yes | |
| No | |
| No | |
| No |
Function
Description:
vLLM is a high-performance LLM inference and serving framework focused on optimizing the inference efficiency of large-scale language models. Its core features include PageAttention and efficient memory management. The primary function of the advance_step operator is to advance inference steps, i.e., updating the model state at each generation step and generating new inputTokens, inputPositions, seqLens, and slotMapping, thereby improving efficiency for vLLM inference.
Formula:
Function Prototype
Each operator is divided into a two-phase API. You must first call the "aclnnAdvanceStepGetWorkspaceSize" API to obtain the input parameters and calculate the required workspace size based on the process, and then call the "aclnnAdvanceStep" API to perform the computation.
aclnnStatus aclnnAdvanceStepGetWorkspaceSize(
const aclTensor *inputTokens,
const aclTensor *sampledTokenIds,
const aclTensor *inputPositions,
const aclTensor *seqLens,
const aclTensor *slotMapping,
const aclTensor *blockTables,
int64_t numSeqs,
int64_t numQueries,
int64_t blockSize,
uint64_t *workspaceSize,
aclOpExecutor **executor)aclnnStatus aclnnAdvanceStep(
void *workspace,
uint64_t workspaceSize,
aclOpExecutor *executor,
aclrtStream stream)aclnnAdvanceStepGetWorkspaceSize
Parameters:
Parameter Input/Output Description Instruction Data Type Data Format Shape Non-contiguous Tensor inputTokens (aclTensor*) Input Input/output parameter for AdvanceStep computation, corresponding to the output inputTokens in the formula, used to update token values in the vLLM model. - Empty tensors are not supported.
- The shape length must be consistent with numSeqs.
- The value range is a positive integer greater than 0.
INT64 ND 1 × sampledTokenIds (aclTensor*) Input Input parameter for the AdvanceStep computation, used to store token IDs. Corresponds to the input sampledTokenIds in the formula. - Empty tensors are not supported.
- The first dimension of shape must be consistent with numQueries, and the second dimension must be 1.
- The value range is a positive integer greater than 0.
INT64 ND 2 × inputPositions (aclTensor*) Input/Output Specifies the input/output tensor for the AdvanceStep computation. It corresponds to the output inputPositions in the formula and records the token index. - Does not support empty tensors.
- The shape length must be consistent with numSeqs.
- The value range consists of positive integers greater than 0.
INT64 ND 2 × seqLens (aclTensor*) Input/Output Specifies the input/output parameters for the AdvanceStep computation, recording the sequence length under different blockIdx values. Corresponds to the input/output seqLens in the formula. - Empty tensors are not supported.
- The shape length must match numSeqs.
- The value range consists of positive integers greater than 0.
INT64 ND 1 × slotMapping (aclTensor*) Input/Output Input/output parameter for the AdvanceStep computation, representing the output slotMapping in the formula, which maps token positions in a sequence to physical positions. - Does not support empty tensors.
- The shape length is consistent with numSeqs.
- The value range is a positive integer greater than 0.
INT64 ND 1 × blockTables (aclTensor*) Input The input parameter for the AdvanceStep computation, which records the block sizes for different blockIdx values. This corresponds to the input blockTables in the formula. - Empty tensors are not supported.
- The first dimension of the shape must match numSeqs, and the second dimension must be greater than (the maximum value in seqLens) / blockSize.
- The value range is positive integers greater than 0.
INT64 ND 2 × numSeqs (int64_t) Input Records the number of input sequences, with a size equal to the length of seqLens. - The value range is a positive integer less than 200000000.
- The value of numSeqs is greater than the value of input numQueries.
- - - - numQueries (int64_t) Input Records the number of input queries, which equals the length of the first dimension of sampledTokenIds. The value range is a positive integer greater than 0. - - - - blockSize (int64_t) Input Size of each block, corresponding to blockSize in the formula. The value range is a positive integer greater than 0. - - - - workspaceSize (uint64_t*) Output Returns the workspace size to be requested on the Device side. - - - - - executor (aclOpExecutor**) Output Returns the op 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 Code | Error Code | Description |
|---|---|---|
| ACLNN_ERR_PARAM_NULLPTR | 161001 | The input inputTokens, sampledTokenIds, inputPositions, seqLens, slotMapping, or blockTables is a null pointer. |
| ACLNN_ERR_PARAM_INVALID | 161002 | The data type of inputTokens, sampledTokenIds, inputPositions, seqLens, slotMapping, or blockTables is not within the supported range. |
| aclnnAdvanceStepGetWorkspaceSize failed | 561002 | The first dimension of the shape of inputTokens, inputPositions, seqLens, slotMapping, or blockTables is inconsistent with numSeqs. |
| The first dimension of the shape of sampledTokenIds is inconsistent with numQueries, or the second dimension is not 1. | ||
| The value of input numSeqs is less than or equal to the value of input numQueries. |
aclnnAdvanceStep
Parameters:
Parameter Input/Output Description workspace Input Address of the workspace memory applied for on the device. workspaceSize Input Size of the workspace applied for on the device, obtained through the first-phase API aclnnAdvanceStepGetWorkspaceSize. executor Input Operator executor that 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:
- aclnnAdvanceStep defaults to a deterministic implementation.
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_advance_step.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<int64_t> 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: %ld\n", i, resultData[i]);
}
}
int Init(int64_t deviceId, aclrtStream* stream) {
// Boilerplate for resource initialization.
auto ret = aclInit(nullptr);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclInit failed. ERROR: %d\n", ret); return ret);
ret = aclrtSetDevice(deviceId);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtSetDevice failed. ERROR: %d\n", ret); return ret);
ret = aclrtCreateStream(stream);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtCreateStream failed. ERROR: %d\n", ret); return ret);
return 0;
}
template <typename T>
int CreateAclTensor(const std::vector<T>& hostData, const std::vector<int64_t>& shape, void** deviceAddr,
aclDataType dataType, aclTensor** tensor) {
auto size = GetShapeSize(shape) * sizeof(T);
// Call aclrtMalloc to allocate device 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);
// Copy data from the host to the device memory by calling aclrtMemcpy.
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];
}
// Create an aclTensor by calling aclCreateTensor.
*tensor = aclCreateTensor(shape.data(), shape.size(), dataType, strides.data(), 0, aclFormat::ACL_FORMAT_ND,
shape.data(), shape.size(), *deviceAddr);
return 0;
}
int main() {
// 1. (Boilerplate) Initialize the device/stream. See the ACL API manual.
// Set deviceId based on 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> inputShape = {8,1};
std::vector<int64_t> input2Shape = {4,1};
std::vector<int64_t> inputHostData = {0, 1, 2, 3, 4, 5, 6, 7};
std::vector<int64_t> input2HostData = {0, 1, 2, 3};
void* input1DeviceAddr = nullptr;
aclTensor* input1 = nullptr;
void* input2DeviceAddr = nullptr;
aclTensor* input2 = nullptr;
void* input3DeviceAddr = nullptr;
aclTensor* input3 = nullptr;
void* input4DeviceAddr = nullptr;
aclTensor* input4 = nullptr;
void* input5DeviceAddr = nullptr;
aclTensor* input5 = nullptr;
void* input6DeviceAddr = nullptr;
aclTensor* input6 = nullptr;
// Create the input aclTensor.
ret = CreateAclTensor(inputHostData, inputShape, &input1DeviceAddr, aclDataType::ACL_INT64, &input1);
CHECK_RET(ret == ACL_SUCCESS, return ret);
ret = CreateAclTensor(input2HostData, input2Shape, &input2DeviceAddr, aclDataType::ACL_INT64, &input2);
CHECK_RET(ret == ACL_SUCCESS, return ret);
ret = CreateAclTensor(inputHostData, inputShape, &input3DeviceAddr, aclDataType::ACL_INT64, &input3);
CHECK_RET(ret == ACL_SUCCESS, return ret);
ret = CreateAclTensor(inputHostData, inputShape, &input4DeviceAddr, aclDataType::ACL_INT64, &input4);
CHECK_RET(ret == ACL_SUCCESS, return ret);
ret = CreateAclTensor(inputHostData, inputShape, &input5DeviceAddr, aclDataType::ACL_INT64, &input5);
CHECK_RET(ret == ACL_SUCCESS, return ret);
ret = CreateAclTensor(inputHostData, inputShape, &input6DeviceAddr, aclDataType::ACL_INT64, &input6);
CHECK_RET(ret == ACL_SUCCESS, return ret);
int64_t numseq = 8;
int64_t numqueries = 4;
int64_t blocksize = 2;
// 3. Call the CANN operator library API. Replace with the specific API name.
uint64_t workspaceSize = 16 * 1024 * 1024;
aclOpExecutor* executor;
// Call the first-phase API of aclnnAdvanceStep.
ret = aclnnAdvanceStepGetWorkspaceSize(
input1,input2,input3,input4,input5,input6,
numseq,numqueries,blocksize,
&workspaceSize,
&executor);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnAdvanceStepGetWorkspaceSize failed. ERROR: %d\n", ret); return ret);
// Allocate 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 aclnnAdvanceStep.
ret = aclnnAdvanceStep(
workspaceAddr,
workspaceSize,
executor,
stream);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnAdvanceStep failed. ERROR: %d\n", ret); return ret);
// 4. (Boilerplate) Synchronously wait for task execution to complete.
ret = aclrtSynchronizeStream(stream);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtSynchronizeStream failed. ERROR: %d\n", ret); return ret);
// 5. Obtain the output value and copy the result from the device memory to the host memory. Modify based on the specific API definition.
PrintOutResult(inputShape, &input1DeviceAddr);
PrintOutResult(inputShape, &input3DeviceAddr);
PrintOutResult(inputShape, &input4DeviceAddr);
PrintOutResult(inputShape, &input5DeviceAddr);
// 6. Release aclTensor. Modify based on the specific API definition.
aclDestroyTensor(input1);
aclDestroyTensor(input2);
aclDestroyTensor(input3);
aclDestroyTensor(input4);
aclDestroyTensor(input5);
aclDestroyTensor(input6);
// 7. Release device resources. Modify based on the specific API definition.
aclrtFree(input1DeviceAddr);
aclrtFree(input2DeviceAddr);
aclrtFree(input3DeviceAddr);
aclrtFree(input4DeviceAddr);
aclrtFree(input5DeviceAddr);
aclrtFree(input6DeviceAddr);
if (workspaceSize > 0) {
aclrtFree(workspaceAddr);
}
aclrtDestroyStream(stream);
aclrtResetDevice(deviceId);
aclFinalize();
return 0;
}