aclnnTopk
Applicable Products
| Product | Supported |
|---|---|
| √ | |
| √ | |
| √ | |
| × | |
| √ | |
| √ |
Function
- Operator function: returns the k extreme values and their indices of the input tensor along a specified dimension.
Function Prototype
Each operator is divided into a two-phase API. The "aclnnTopkGetWorkspaceSize" API must be called first to obtain the workspace size required for computation and the executor that encapsulates the operator computation flow. Then, the "aclnnTopk" API is called to perform the computation.
aclnnStatus aclnnTopkGetWorkspaceSize(
const aclTensor* self,
int64_t k,
int64_t dim,
bool largest,
bool sorted,
aclTensor* valuesOut,
aclTensor* indicesOut,
uint64_t* workspaceSize,
aclOpExecutor** executor)aclnnStatus aclnnTopk(
void* workspace,
uint64_t workspaceSize,
aclOpExecutor* executor,
const aclrtStream stream)aclnnTopkGetWorkspaceSize
Parameters:
Parameter Input/Output Description Instruction Data Type Data Format Dimension (Shape) Non-contiguous Tensor self (aclTensor*) Input Input tensor. Supports empty tensors. INT8, UINT8, INT16, INT32, INT64, FLOAT16, FLOAT32, DOUBLE, BFLOAT16, UINT16, UINT32, UINT64 ND 1-8 √ k (int64_t) Input Specifies the number of extreme values to output on the computation dimension. Value range: [0, self.size(dim)]. - - - - dim (int64_t) Input Specifies the dimension along which to compute. Value range: [-self.dim(), self.dim()). - - - - largest (bool) Input Specifies the sorting order of results along the computation dimension. True indicates descending order (largest to smallest), and False indicates ascending order (smallest to largest). - - - - sorted (bool) Input Indicates whether the output results are sorted. True indicates that the output results are sorted, and False indicates that the output results are not sorted. Note: Currently, this parameter only supports True. - - - - valuesOut (aclTensor*) Output Output tensor containing extreme values. The data type is the same as self. INT8, UINT8, INT16, INT32, INT64, FLOAT16, FLOAT32, DOUBLE, BFLOAT16, UINT16, UINT32, UINT64 ND 1-8 √ indicesOut (aclTensor*) Output Output tensor that contains indices. The data type supports INT64. INT64 ND 1-8 √ workspaceSize (uint64_t*) Output Returns the workspace size to be applied for on the Device side. - - - - - executor (aclOpExecutor**) Output Returns the operator executor, which contains the operator computation process. - - - - - Return value
aclnnStatus: return code. For details, see aclnn Return Codes.
The first-phase API completes input parameter verification. Errors are reported in the following case: <table style="undefined;table-layout: fixed; width: 1150px"><colgroup> <col style="width: 300px"> <col style="width: 134px"> <col style="width: 716px"> </colgroup> <thead> <tr> <th>Return Value</th> <th>Error Code</th> <th>Description</th> </tr></thead> <tbody> <tr> <td>ACLNN_ERR_PARAM_NULLPTR</td> <td>161001</td> <td>The input self, valuesOut, or indicesOut is a null pointer.</td> </tr> <tr> <td rowspan="3">ACLNN_ERR_PARAM_INVALID</td> <td rowspan="3">161002</td> <td>The data type and format of self, valuesOut, or indicesOut are not supported.</td> </tr> <tr> <td>dim is not in the valid dimension range of the input self.</td> </tr> <tr> <td>k is smaller than 0 or greater than the size of dim of the input self.</td> </tr> </tbody> </table>
aclnnTopk
Parameters
Parameter Input/Output Description workspace Input Workspace memory address requested on the device workspaceSize Input Size of the workspace requested on the device, obtained via the first-phase API aclnnTopkGetWorkspaceSize. executor Input op executor, which includes the operation computation process. stream Input Stream that executes the task. Return Value
aclnnStatus: status code. For details, see aclnn Return Codes.
Constraints
- Deterministic computation
- aclnnTopk defaults to a deterministic implementation.
Example
The following sample code is for reference only. For the compilation and running process, see Compile and Run Samples。
#include <iostream>
#include <vector>
#include "acl/acl.h"
#include "aclnnop/aclnn_topk.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;
}
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 on-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 on-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);
// 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 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> selfShape = {2, 4};
std::vector<int64_t> outShape = {2, 2};
void* selfDeviceAddr = nullptr;
void* outDeviceAddr = nullptr;
void* indicesDeviceAddr = nullptr;
aclTensor* self = nullptr;
aclTensor* out = nullptr;
aclTensor* indices = nullptr;
std::vector<float> selfHostData = {0, 1, 2.5, 3, 4, 5.5, 6, 7};
std::vector<float> outHostData = {0, 1, 2, 3.1};
std::vector<int64_t> indicesHostData = {0, 1, 2, 3};
// Create the self aclTensor.
ret = CreateAclTensor(selfHostData, selfShape, &selfDeviceAddr, aclDataType::ACL_FLOAT, &self);
CHECK_RET(ret == ACL_SUCCESS, return ret);
// Create the out aclTensor.
ret = CreateAclTensor(outHostData, outShape, &outDeviceAddr, aclDataType::ACL_FLOAT, &out);
CHECK_RET(ret == ACL_SUCCESS, return ret);
// Create the indices aclTensor.
ret = CreateAclTensor(indicesHostData, outShape, &indicesDeviceAddr, aclDataType::ACL_INT64, &indices);
CHECK_RET(ret == ACL_SUCCESS, return ret);
// 3. Call the CANN operator library API. Replace with the specific API name.
int64_t k = 2;
int64_t dim = 1;
bool largest = true;
bool sorted = true;
uint64_t workspaceSize = 0;
aclOpExecutor* executor;
// Call the first-phase API of aclnnTopk.
ret = aclnnTopkGetWorkspaceSize(self, k, dim, largest, sorted, out, indices, &workspaceSize, &executor);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnTopkGetWorkspaceSize 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 aclnnTopk.
ret = aclnnTopk(workspaceAddr, workspaceSize, executor, stream);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnTopk failed. ERROR: %d\n", ret); return ret);
// 4. (Boilerplate) Synchronize and wait for task completion.
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 on-device memory to the host side. Modify based on the specific API interface definition.
auto outSize = GetShapeSize(outShape);
std::vector<float> outData(outSize, 0);
ret = aclrtMemcpy(outData.data(), outData.size() * sizeof(outData[0]), outDeviceAddr,
outSize * sizeof(outData[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 ret);
for (int64_t i = 0; i < outSize; i++) {
LOG_PRINT("out[%ld] is: %f\n", i, outData[i]);
}
auto indicesSize = GetShapeSize(outShape);
std::vector<int64_t> indicesData(indicesSize, 0);
ret = aclrtMemcpy(indicesData.data(), indicesData.size() * sizeof(indicesData[0]), indicesDeviceAddr,
indicesSize * sizeof(indicesData[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 ret);
for (int64_t i = 0; i < indicesSize; i++) {
LOG_PRINT("indices[%ld] is: %ld\n", i, indicesData[i]);
}
// 6. Release aclTensor and aclScalar. Modify based on the specific API interface definition.
aclDestroyTensor(self);
aclDestroyTensor(out);
aclDestroyTensor(indices);
// 7. Release device resources. Modify based on the specific API definition.
aclrtFree(selfDeviceAddr);
aclrtFree(outDeviceAddr);
aclrtFree(indicesDeviceAddr);
if (workspaceSize > 0) {
aclrtFree(workspaceAddr);
}
aclrtDestroyStream(stream);
aclrtResetDevice(deviceId);
aclFinalize();
return 0;
}