aclnnAdvanceStepV2

📄 View Source Code

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:

    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 the inference step, 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:

    blockIdxistheindexofthekernelwherethecurrentcodeisexecuted.blockIdx is the index of the kernel where the current code is executed. blockTablesStride=blockTables.stride(0)blockTablesStride = blockTables.stride(0) inputTokens[blockIdx]=sampledTokenIds[blockIdx]inputTokens[blockIdx] = sampledTokenIds[blockIdx] inputPositions[blockIdx]=seqLens[blockIdx]inputPositions[blockIdx] = seqLens[blockIdx] seqLens[blockIdx]=seqLens[blockIdx]+1seqLens[blockIdx] = seqLens[blockIdx] + 1 slotMapping[blockIdx]=(blockTables[blockIdx]+blockTablesStride∗blockIdx)∗blockSize+(seqLens[blockIdx]%blockSize)slotMapping[blockIdx] = (blockTables[blockIdx] + blockTablesStride * blockIdx) * blockSize + (seqLens[blockIdx] \% blockSize)

Function Prototype

Each operator is divided into a two-phase API. You must first call the "aclnnAdvanceStepV2GetWorkspaceSize" API to obtain the input parameters and calculate the required workspace size based on the computation process, and then call the "aclnnAdvanceStepV2" API to perform the computation.

aclnnStatus aclnnAdvanceStepV2GetWorkspaceSize(
  const aclTensor *inputTokens,
  const aclTensor *sampledTokenIds,
  const aclTensor *inputPositions,
  const aclTensor *seqLens,
  const aclTensor *slotMapping,
  const aclTensor *blockTables,
  const aclTensor *specToken,
  const aclTensor *acceptedNum,
  int64_t          numSeqs,
  int64_t          numQueries,
  int64_t          blockSize,
  uint64_t        *workspaceSize,
  aclOpExecutor  **executor)
aclnnStatus aclnnAdvanceStepV2(
  void            *workspace,
  uint64_t         workspaceSize,
  aclOpExecutor   *executor,
  aclrtStream      stream)

aclnnAdvanceStepV2GetWorkspaceSize

  • Parameters:

    Parameter Input/Output Description Instruction Data Type Data Format Dimension (Shape) Non-contiguous Tensor
    inputTokens (aclTensor*) Input/Output The input/output parameter for the AdvanceStepV2 computation, which is the output inputTokens in the formula, used to update the token values in the vLLM model.
    • Empty tensor is not supported.
    • The first dimension length of the shape is consistent with numSeqs, and the second dimension length is 1 + specNum.
    • The value range is a positive integer greater than 0.
    INT64 ND 2 ×
    sampledTokenIds (aclTensor*) Input Input parameter for the AdvanceStepV2 computation to be performed, used to store token IDs. It is the input sampledTokenIds in the formula.
    • Empty tensor is not supported.
    • The first dimension length of shape is consistent with numSeqs, and the second dimension length is 1+specNum.
    • The value range is a positive integer greater than 0.
    INT64 ND 2 ×
    inputPositions (aclTensor*) Input/Output Input/output parameter for the AdvanceStepV2 computation to be performed. In the formula, the output inputPositions is used to record the token index.
    • Empty tensor not supported.
    • The shape length and numSeqs are consistent.
    • The value range is a positive integer greater than 0.
    INT64 ND 1 ×
    seqLens (aclTensor*) Input/Output Input/output parameter for AdvanceStepV2 computation, used to record the length of seq under different blockIdx, i.e., the input/output seqLens in the formula.
    • Empty tensor not supported.
    • The shape length is consistent with numSeqs.
    • The value range is a positive integer greater than 0.
    INT64 ND 1 ×
    slotMapping (aclTensor*) Input/Output Input/output parameter for the AdvanceStepV2 computation to be performed, representing the output slotMapping in the formula, used to map the position of a token value in the sequence to a physical position.
    • Empty tensor is not supported.
    • The shape length is consistent with numSeqs.
    • The value range is a positive integer greater than 0.
    INT64 ND 1 ×
    blockTables (aclTensor*) Input Input parameter for the AdvanceStepV2 computation to be performed, used to record the size of blocks under different blockIdx, which is the input blockTables in the formula.
    • Empty tensor not supported.
    • The shape length is consistent with numSeqs, and the second dimension is greater than (maximum value in seqLens) / blockSize.
    • The value range is a positive integer greater than 0.
    INT64 ND 2 ×
    specToken (aclTensor*) Input Input parameter for the AdvanceStepV2 computation to be performed, used to record the index of the current speculative model's token.
    • Empty tensor is not supported.
    • The first dimension length of shape is consistent with numSeqs, and the second dimension length is specNum.
    • The value range is a positive integer greater than 0.
    INT64 ND 2 ×
    acceptedNum (aclTensor*) Input Input parameter for the AdvanceStepV2 calculation to be performed, used to record the number of accepted speculative tokens for each request.
    • Empty tensor is not supported.
    • The shape length is consistent with numSeqs.
    • The value range is a positive integer greater than 0.
    INT64 ND 1 ×
    numSeqs (int64_t) Input Records the number of input sequences. Its size is consistent with the length of seqLens.
    • The value range is a positive integer greater than 0.
    • The value of numSeqs is greater than the value of the input numQueries.
    - - - -
    numQueries (int64_t) Input Records the number of input queries. Its size is consistent with the first dimension length 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 applied 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 parameters inputTokens, sampledTokenIds, inputPositions, seqLens, slotMapping, blockTables, specToken, and acceptedNum are null pointers.
ACLNN_ERR_PARAM_INVALID 161002 The data types of inputTokens, sampledTokenIds, inputPositions, seqLens, slotMapping, blockTables, specToken, and acceptedNum are not within the supported range.
aclnnAdvanceStepV2GetWorkspaceSize failed 561002 The first dimension length of the shape of inputTokens, inputPositions, seqLens, slotMapping, blockTables, specToken, and acceptedNum is inconsistent with numSeqs.
The first dimension length of the shape of the input sampledTokenIds is inconsistent with numQueries, or the second dimension length of the shape is not 1.
The second dimension length of the shape of the input inputTokens is not 1 + specNum.
The second dimension length of the shape of the input specToken is not specNum.
The value of the input numSeqs is not equal to the value of the input numQueries.

aclnnAdvanceStepV2

  • 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 aclnnAdvanceStepV2GetWorkspaceSize.
    executor Input Operator executor that contains the operator computation flow.
    stream Input Specifies the stream that executes the task.
  • Return value

    aclnnStatus: return code. For details, see aclnn Return Code.

Constraints

  • Deterministic computation:

    • aclnnAdvanceStepV2 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_v2.h"//Uncertain about the header file name.
#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: 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 apply for memory on the device side.
    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 side to the device memory.
    ret = aclrtMemcpy(*deviceAddr, size, hostData.data(), size, ACL_MEMCPY_HOST_TO_DEVICE);
    CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtMemcpy failed. ERROR: %d\n", ret); return ret);

    // Compute the strides of a contiguous tensor.
    std::vector<int64_t> strides(shape.size(), 1);
    for (int64_t i = shape.size() - 2; i >= 0; i--) {
    strides[i] = shape[i + 1] * strides[i + 1];
    }

    // Call the aclCreateTensor API to create an aclTensor.
    *tensor = aclCreateTensor(shape.data(), shape.size(), dataType, strides.data(), 0, aclFormat::ACL_FORMAT_ND,
                            shape.data(), shape.size(), *deviceAddr);
    return 0;
}

int main() {
    // 1. (Boilerplate) Initialize the device/stream. See the acl API manual.
    // Fill in the deviceId based on your actual device.
    int32_t deviceId = 0;
    aclrtStream stream;
    auto ret = Init(deviceId, &stream);
    CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("Init acl failed. ERROR: %d\n", ret); return ret);

    // 2. Construct the inputs and outputs. Custom construction is required based on the API interface.
    std::vector<int64_t> input1Shape = {16};
    std::vector<int64_t> input2Shape = {8,2};
    std::vector<int64_t> input3Shape = {8,1000};
    std::vector<int64_t> input4Shape = {8,1};
    std::vector<int64_t> input5Shape = {8};
    std::vector<int64_t> input1HostData = {0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7};
    std::vector<int64_t> input2HostData = {0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7};
    std::vector<int64_t> input3HostData(8000, 7);
    std::vector<int64_t> input4HostData = {0, 1, 2, 3, 4, 5, 6, 7};
    std::vector<int64_t> input5HostData = {0, 1, 2, 3, 4, 5, 6, 7};

    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;
    void* input7DeviceAddr = nullptr;
    aclTensor* input7 = nullptr;
    void* input8DeviceAddr = nullptr;
    aclTensor* input8 = nullptr;
    // Create the input aclTensor.
    ret = CreateAclTensor(input1HostData, input1Shape, &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(input1HostData, input1Shape, &input3DeviceAddr, aclDataType::ACL_INT64, &input3);
    CHECK_RET(ret == ACL_SUCCESS, return ret);
    ret = CreateAclTensor(input1HostData, input1Shape, &input4DeviceAddr, aclDataType::ACL_INT64, &input4);
    CHECK_RET(ret == ACL_SUCCESS, return ret);
    ret = CreateAclTensor(input1HostData, input1Shape, &input5DeviceAddr, aclDataType::ACL_INT64, &input5);
    CHECK_RET(ret == ACL_SUCCESS, return ret);
    ret = CreateAclTensor(input3HostData, input3Shape, &input6DeviceAddr, aclDataType::ACL_INT64, &input6);
    CHECK_RET(ret == ACL_SUCCESS, return ret);
    ret = CreateAclTensor(input4HostData, input4Shape, &input5DeviceAddr, aclDataType::ACL_INT64, &input7);
    CHECK_RET(ret == ACL_SUCCESS, return ret);
    ret = CreateAclTensor(input5HostData, input5Shape, &input6DeviceAddr, aclDataType::ACL_INT64, &input8);
    CHECK_RET(ret == ACL_SUCCESS, return ret);

    int64_t numseq = 8;
    int64_t specnum = 8;
    int64_t blocksize = 8;

    // 3. Call the CANN operator library API. Modify it to the specific API name.
    uint64_t workspaceSize = 16 * 1024 * 1024;
    aclOpExecutor* executor;

    // Call the first-phase API of aclnnAdvanceStepV2.
    ret = aclnnAdvanceStepV2GetWorkspaceSize(
    input1,input2,input3,input4,input5,input6,input7,input8,
    numseq,numseq,blocksize,
    &workspaceSize,
    &executor);
    CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnAdvanceStepV2GetWorkspaceSize failed. ERROR: %d\n", ret); return ret);

    // Apply for device memory based on the workspaceSize calculated by the first-phase API.
    void* workspaceAddr = nullptr;
    if (workspaceSize > 0) {
    ret = aclrtMalloc(&workspaceAddr, workspaceSize, ACL_MEM_MALLOC_HUGE_FIRST);
    CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("allocate workspace failed. ERROR: %d\n", ret); return ret);
    }

    // Call the second-phase API of aclnnAdvanceStepV2.
    ret = aclnnAdvanceStepV2(
    workspaceAddr,
    workspaceSize,
    executor,
    stream);
    CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnAdvanceStepV2 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 memory to the host memory. Modify based on the specific API definition.
    PrintOutResult(input1Shape, &input1DeviceAddr);
    PrintOutResult(input2Shape, &input2DeviceAddr);
    PrintOutResult(input3Shape, &input3DeviceAddr);
    PrintOutResult(input4Shape, &input4DeviceAddr);
    PrintOutResult(input5Shape, &input5DeviceAddr);

    // 6. Release aclTensor and aclTensor. Modify based on the specific API definition.
    aclDestroyTensor(input1);
    aclDestroyTensor(input2);
    aclDestroyTensor(input3);
    aclDestroyTensor(input4);
    aclDestroyTensor(input5);
    aclDestroyTensor(input6);
    aclDestroyTensor(input7);
    aclDestroyTensor(input8);

    // 7. Release device resources. Modify based on the specific API definition.
    aclrtFree(input1DeviceAddr);
    aclrtFree(input2DeviceAddr);
    aclrtFree(input3DeviceAddr);
    aclrtFree(input4DeviceAddr);
    aclrtFree(input5DeviceAddr);
    aclrtFree(input6DeviceAddr);
    aclrtFree(input7DeviceAddr);
    aclrtFree(input8DeviceAddr);

    if (workspaceSize > 0) {
    aclrtFree(workspaceAddr);
    }
    aclrtDestroyStream(stream);
    aclrtResetDevice(deviceId);
    aclFinalize();

    return 0;
}