aclnnDynamicBlockQuant

📄 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: For the input tensor, divides the input into multiple data blocks based on the given rowBlockSize and colBlockSize, and performs quantization at the granularity of each data block. In each block, first computes the quantization parameter scaleOut corresponding to the current block, and quantizes the input based on scaleOut. Outputs the final quantization result and the quantization parameter scaleOut for each block.

  • Formula:

    input_max=block_reduce_max(abs(x))input\_max = block\_reduce\_max(abs(x)) scaleOut=min(input_max/(FP8_MAX/HiF8_MAX/INT8_MAX),1/minScale)scaleOut = min(input\_max / (FP8\_MAX/HiF8\_MAX / INT8\_MAX), 1/minScale) yOut=cast_to_[FP8/HiF8/INT8](x/scaleOut)yOut = cast\_to\_[FP8/HiF8/INT8](x / scaleOut)

    Where block_reduce_max represents finding the maximum value in each block.

Function Prototype

Each operator is divided into a two-phase API. You must first call the "aclnnDynamicBlockQuantGetWorkspaceSize" API to obtain the workspace size required for computation and the executor that encapsulates the operator computation flow, and then call the "aclnnDynamicBlockQuant" API to perform the computation.

aclnnStatus aclnnDynamicBlockQuantGetWorkspaceSize(
  const aclTensor   *x,
  double             minScale,
  char              *roundModeOptional,
  int64_t            dstType,
  int64_t            rowBlockSize,
  int64_t            colBlockSize,
  const aclTensor   *yOut,
  const aclTensor   *scaleOut,
  uint64_t          *workspaceSize,
  aclOpExecutor    **executor)
aclnnStatus aclnnDynamicBlockQuant(
  void          *workspace,
  uint64_t       workspaceSize,
  aclOpExecutor *executor,
  aclrtStream    stream)

aclnnDynamicBlockQuantGetWorkspaceSize

  • Parameters:

    Parameter Input/Output Description Instruction Data Type Data Format Dimension (Shape) Non-contiguous Tensor
    x (aclTensor*) Input Represents the input tensor of the operator. Corresponds to `x` in the formula. Empty tensor is not supported. FLOAT16, BFLOAT16 ND 2-3 √
    minScale (double) Input The minimum scale value used in scaleOut calculation, corresponding to `minScale` in the formula. The value must be no less than 0. - - - -
    roundMode (Optional, char*) Input Optional parameter. Specifies the conversion method for casting to the output. Supported values: rint, round. Defaults to rint. - - - -
    dstType (int64_t) Input Specifies the data type of the output yOut. Supported values: 2, 34, 35, and 36, representing ACL_INT8, HIFLOAT8, FLOAT8_E5M2, and FLOAT8_E4M3FN, respectively. - - - -
    rowBlockSize (int64_t) Input Specifies the row size of a block. Supported values: 1, 128, 256, 512. - - - -
    colBlockSize (int64_t) Input Specifies the column size of a block. Supported values: 64, 128, 192, 256. - - - -
    yOut (aclTensor*) Output Represents the quantized output tensor. Corresponds to `yOut` in the corresponding formula.
    • Empty tensor not supported.
    • The shape must be consistent with x.
    INT8, FLOAT8_E4M3FN, FLOAT8_E5M2, HIFLOAT8 ND 2-3 √
    scaleOut (aclTensor*) Output Represents the scaleOut used for quantization, corresponding to `scaleOut` in the formula.
    • Empty tensor not supported.
    • If the shape of input `x` is [M, N], the shape of output `scaleOut` is [ceil(M/rowBlockSize), ceil(N/colBlockSize)]; if the shape of input `x` is [B, M, N], the shape of output `scaleOut` is [B, ceil(M/rowBlockSize), ceil(N/colBlockSize)].
    FLOAT32 ND 2-3 √
    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. - - - - -
    • Atlas A2 training products/Atlas A2 inference products, Atlas A3 training products/Atlas A3 inference products:

      • The parameter roundModeOptional only supports rint.

      • The parameter dstType only supports value 2, representing ACL_INT8.

      • The parameter rowBlockSize only supports value 1.

      • The parameter colBlockSize only supports value 128.

      • The data type of the parameter yOut only supports INT8.

    • Ascend 950PR/Ascend 950DT:

      • The shape of parameters x, yOut, and scaleOut only supports 2 dimensions.

      • The value of the roundModeOptional parameter has a correspondence with the data type of the yOut parameter:

        • When the data type of output yOut is HIFLOAT8, the roundModeOptional parameter supports being set to round.

        • When the data type of output yOut is FLOAT8_E4M3FN or FLOAT8_E5M2, the roundModeOptional parameter supports being set to rint.

      • The dstType parameter supports values 34, 35, and 36, representing HIFLOAT8, FLOAT8_E5M2, and FLOAT8_E4M3FN respectively.

      • The data type of the parameter yOut does not support INT8.

  • 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 passed x, yOut, or scaleOut is a null pointer.
    ACLNN_ERR_PARAM_INVALID 161002 The input or output data format or data type is not within the supported range.

aclnnDynamicBlockQuant

  • Parameters:

    Parameter Input/Output Description
    workspace Input Workspace memory address applied on the Device side.
    workspaceSize Input Workspace size applied on the Device side, obtained from the first-phase API aclnnDynamicBlockQuantGetWorkspaceSize.
    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:

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

  • Atlas A2 training products/Atlas A2 inference products, Atlas A3 training products/Atlas A3 inference products:

    #include <iostream>
    #include <vector>
    #include "acl/acl.h"
    #include "aclnnop/aclnn_dynamic_block_quant.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<int8_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: %d\n", i, resultData[i]);
      }
    }
    
    int Init(int32_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 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);
      // Copy host-side data to device-side memory using 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];
      }
    
      // 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.
      // Set deviceId based on the 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> xShape = {4, 2};
      std::vector<int64_t> yShape = {4, 2};
      std::vector<int64_t> scaleShape = {4, 1};
    
      void* xDeviceAddr = nullptr;
      void* yDeviceAddr = nullptr;
      void* scaleDeviceAddr = nullptr;
    
      aclTensor* x = nullptr;
      aclTensor* y = nullptr;
      aclTensor* scale = nullptr;
    
      std::vector<aclFloat16> xHostData = {1, 2, 3, 4, 5, 6, 7, 8};
      std::vector<int8_t> yHostData(8, 0);
      std::vector<float> scaleHostData = {0, 0, 0, 0};
    
      // Create the x aclTensor.
      ret = CreateAclTensor(xHostData, xShape, &xDeviceAddr, aclDataType::ACL_FLOAT16, &x);
      CHECK_RET(ret == ACL_SUCCESS, return ret);
      // Create the y aclTensor.
      ret = CreateAclTensor(yHostData, yShape, &yDeviceAddr, aclDataType::ACL_INT8, & y);
      CHECK_RET(ret == ACL_SUCCESS, return ret);
      // Create the scale aclTensor.
      ret = CreateAclTensor(scaleHostData, scaleShape, &scaleDeviceAddr, aclDataType::ACL_FLOAT, &scale);
      CHECK_RET(ret == ACL_SUCCESS, return ret);
    
      // 3. Call the CANN operator library API. Replace with the specific API name.
      uint64_t workspaceSize = 0;
      aclOpExecutor* executor;
    
      const char* roundMode = "rint";
    
      // Call the first-phase API of aclnnDynamicBlockQuant.
      ret = aclnnDynamicBlockQuantGetWorkspaceSize(x, 0, (char *)roundMode, aclDataType::ACL_INT8, 1, 128, y, scale, &workspaceSize, &executor);
      CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnDynamicBlockQuantGetWorkspaceSize 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 aclnnDynamicBlockQuant.
      ret = aclnnDynamicBlockQuant(workspaceAddr, workspaceSize, executor, stream);
      CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnDynamicBlockQuant 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-side memory to the host side. Modify this based on the specific API definition.
      PrintOutResult(yShape, &yDeviceAddr);
    
      // 6. Release aclTensor and aclScalar. Modify based on the specific API definition.
      aclDestroyTensor(x);
      aclDestroyTensor(y);
      aclDestroyTensor(scale);
    
      // 7. Release device resources.
      aclrtFree(xDeviceAddr);
      aclrtFree(yDeviceAddr);
      aclrtFree(scaleDeviceAddr);
      if (workspaceSize > 0) {
        aclrtFree(workspaceAddr);
      }
      aclrtDestroyStream(stream);
      aclrtResetDevice(deviceId);
      aclFinalize();
    
      return 0;
    }
  • Ascend 950PR/Ascend 950DT:

    #include <iostream>
    #include <vector>
    #include "acl/acl.h"
    #include "aclnnop/aclnn_dynamic_block_quant.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, 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 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 data from the host side to the 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);
    
      // 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.
      // Set deviceId based on the 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.
      std::vector<int64_t> xShape = {4, 2};
      std::vector<int64_t> yShape = {4, 2};
      std::vector<int64_t> scaleShape = {4, 1};
    
      void* xDeviceAddr = nullptr;
      void* yDeviceAddr = nullptr;
      void* scaleDeviceAddr = nullptr;
    
      aclTensor* x = nullptr;
      aclTensor* y = nullptr;
      aclTensor* scale = nullptr;
    
      std::vector<float> xHostData = {1, 2, 3, 4, 5, 6, 7, 8};
      std::vector<float> yHostData(8, 0);
      std::vector<float> scaleHostData = {0, 0, 0, 0};
    
      // Create the x aclTensor.
      ret = CreateAclTensor(xHostData, xShape, &xDeviceAddr, aclDataType::ACL_FLOAT16, &x);
      CHECK_RET(ret == ACL_SUCCESS, return ret);
      // Create the y aclTensor.
      ret = CreateAclTensor(yHostData, yShape, &yDeviceAddr, aclDataType::ACL_FLOAT8_E5M2, &y);
      CHECK_RET(ret == ACL_SUCCESS, return ret);
      // Create the scale aclTensor.
      ret = CreateAclTensor(scaleHostData, scaleShape, &scaleDeviceAddr, aclDataType::ACL_FLOAT, &scale);
      CHECK_RET(ret == ACL_SUCCESS, return ret);
    
      // 3. Call the CANN operator library API. Replace it with the specific API name.
      uint64_t workspaceSize = 0;
      aclOpExecutor* executor;
    
      const char* roundMode = "rint";
    
      // Call the first-phase API of aclnnDynamicBlockQuant.
      ret = aclnnDynamicBlockQuantGetWorkspaceSize(x, 0.1, (char *)roundMode, aclDataType::ACL_FLOAT8_E5M2, 1, 128, y, scale, &workspaceSize, &executor);
      CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnDynamicBlockQuantGetWorkspaceSize 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 aclnnDynamicBlockQuant.
      ret = aclnnDynamicBlockQuant(workspaceAddr, workspaceSize, executor, stream);
      CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnDynamicBlockQuant 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. Copy the result from the device side memory to the host side. Modify this step based on the specific API definition.
      PrintOutResult(yShape, &yDeviceAddr);
    
      // 6. Release aclTensor and aclScalar. Modify based on the specific API definition.
      aclDestroyTensor(x);
      aclDestroyTensor(y);
      aclDestroyTensor(scale);
    
      // 7. Release device resources.
      aclrtFree(xDeviceAddr);
      aclrtFree(yDeviceAddr);
      aclrtFree(scaleDeviceAddr);
      if (workspaceSize > 0) {
        aclrtFree(workspaceAddr);
      }
      aclrtDestroyStream(stream);
      aclrtResetDevice(deviceId);
      aclFinalize();
    
      return 0;
    }