aclnnSimThreadExponential

Applicable Products

  • Ascend 950PR&950DT products: Supported
  • Atlas A3 products: Supported
  • Atlas A2 products: Supported
  • Atlas 200I/500 A2 inference products: Not supported
  • Atlas inference products: Not supported
  • Atlas training products: Not supported

Function

  • Description: Generates random numbers following an exponential distribution with parameter lambda and fills them into the selfRef tensor.

  • Formula:

    f(x)=−1λln⁡(1−u),u∼Uniform(0,1]f(x) = -\frac{1}{\lambda} \ln(1 - u), u \sim \text{Uniform}(0, 1]

Function Prototype

This operator uses a two-phase API. You must first call the "aclnnSimThreadExponentialGetWorkspaceSize" API to obtain the input parameters and calculate the required workspace size based on the computation process, and then call the "aclnnSimThreadExponential" API to perform the computation.

aclnnStatus aclnnSimThreadExponentialGetWorkspaceSize(
  aclTensor       *selfRef,
  int64_t          count,
  double           lambda,
  int64_t          seed,
  int64_t          offset,
  uint64_t        *workspaceSize,
  aclOpExecutor   **executor)
aclnnStatus aclnnSimThreadExponential(
  void              *workspace,
  uint64_t           workspace_size,
  aclOpExecutor     *executor,
  aclrtStream  stream)

aclnnSimThreadExponentialGetWorkspaceSize

  • Parameters

    Parameter Input/Output Description Instruction Data Type Data Format Dimensions (shape) Non-contiguous Tensor
    selfRef Input/Output f(x) in the formula. - FLOAT16, BFLOAT16, FLOAT32 ND 8 dimensions or less -
    count Input Number of random numbers to generate. - INT64 - - -
    lambda Input lambda in the formula. Must be greater than 0. DOUBLE - - -
    seed Input Random number seed. - INT64 - - -
    offset Input Random number offset. - INT64 - - -
    workspaceSize Output Returns the workspace size that the user needs to request on the Device side. - - - 1 -
    executor Output Returns the op executor that contains the operator computation process. - - - 1 -
  • Return Value

    aclnnStatus: return status code. For details, see aclnn Return Codes.

    The first-phase API performs input parameter validation and returns an error in the following scenarios:

    Return Values Error Codes Description
    ACLNN_ERR_PARAM_NULLPTR 161001 selfRef is a null pointer.
    ACLNN_ERR_PARAM_INVALID 161002 The data type of selfRef is not within the supported range.
    The shape of selfRef exceeds 8 dimensions.
    lambda is less than or equal to 0.

aclnnSimThreadExponential

  • Parameters:

    Parameter Input/Output Description
    workspace Input Workspace memory address requested on the device.
    workspaceSize Input Workspace size applied for on the Device side, obtained by the first-phase API aclnnSimThreadExponentialGetWorkspaceSize.
    executor Input Op executor, which contains the operator computation flow.
    stream Input Stream used to execute the task.
  • Return value:

Constraints

  • lambda must be greater than 0.
  • Deterministic computation:
    • aclnnSimThreadExponential defaults to a deterministic implementation.
  • Ascend 950PR&950DT products:
    • offset must be a multiple of 4.
    • count must be equal to the total number of elements in the selfRef tensor.

Example

The following sample code is for reference only. For details about compilation and execution, see Compile and Run Samples.

#include "acl/acl.h"
#include "aclnnop/aclnn_sim_thread_exponential.h"
#include <iostream>
#include <vector>

#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 shape_size = 1;
    for (auto i : shape) {
        shape_size *= i;
    }
    return shape_size;
}

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 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);

    // Call aclrtMemcpy to copy host-side data to 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 the 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 aclCreateTensor 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: device/stream initialization. Refer to the acl API manual.
    // Fill in deviceId based on the actual device.
    int32_t deviceId = 0;
    aclrtStream stream;
    auto ret = Init(deviceId, &stream);
    // Handle checks as required.
    CHECK_RET(ret == 0, LOG_PRINT("Init acl failed. ERROR: %d\n", ret); return ret);

    // 2. Construct the inputs and outputs based on the API definition.
    std::vector<int64_t> selfRefShape = {2, 5};
    void* selfRefDeviceAddr = nullptr;
    aclTensor* selfRef = nullptr;
    std::vector<float> selfRefHostData = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
    // Create a selfRef aclTensor.
    ret = CreateAclTensor(selfRefHostData, selfRefShape, &selfRefDeviceAddr, aclDataType::ACL_FLOAT, &selfRef);
    CHECK_RET(ret == ACL_SUCCESS, return ret);
    int64_t count = 10;
    double lambda = 1.0;
    int64_t seed = 5;
    int64_t offset = 0;
    // 3. Call the CANN operator library API. Replace it with the actual API.
    uint64_t workspaceSize = 0;
    aclOpExecutor* executor;
    // Call the aclnnSimThreadExponential first-phase API.
    ret = aclnnSimThreadExponentialGetWorkspaceSize(selfRef, count, lambda, seed, offset, &workspaceSize, &executor);
    CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnSimThreadExponentialGetWorkspaceSize 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 aclnnSimThreadExponential second-phase API.
    ret = aclnnSimThreadExponential(workspaceAddr, workspaceSize, executor, stream);
    CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnSimThreadExponential failed. ERROR: %d\n", ret); return ret);
    // 4. Boilerplate: wait for the 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 device memory to host memory. Modify the code based on the specific API definition.
    auto size = GetShapeSize(selfRefShape);
    std::vector<float> resultData(size, 0);
    ret = aclrtMemcpy(resultData.data(), resultData.size() * sizeof(resultData[0]), selfRefDeviceAddr, size * sizeof(float),
                      ACL_MEMCPY_DEVICE_TO_HOST);
    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("result[%ld] is: %f\n", i, resultData[i]);
    }

    // 6. Release aclTensor and aclScalar. Modify the code based on the specific API definition.
    aclDestroyTensor(selfRef);

    // 7. Release device resources. Modify the code based on the specific API definition.
    aclrtFree(selfRefDeviceAddr);
    if (workspaceSize > 0) {
      aclrtFree(workspaceAddr);
    }
    aclrtDestroyStream(stream);
    aclrtResetDevice(deviceId);
    aclFinalize();
    return 0;
}