aclnnIndexCopy&aclnnInplaceIndexCopy
Applicable Products
| Product | Supported |
|---|---|
| √ | |
| √ | |
| √ | |
| × | |
| × | |
| × |
Function
Uses the element values in the index tensor as indices to copy elements from source to the corresponding positions in selfRef or out along the specified axis dim.
Function Prototype
aclnnIndexCopy and aclnnInplaceIndexCopy implement the same function. The differences in usage are as follows. Select the appropriate API based on your actual scenario.
aclnnIndexCopy: Requires creating a new output tensor object to store the computation result.
aclnnInplaceIndexCopy: No need to create a new output tensor object. The computation result is stored directly in the memory of the input tensor.
Each operator uses a two-phase API. You must first call the "aclnnIndexCopyGetWorkspaceSize" or "aclnnInplaceIndexCopyGetWorkspaceSize" API to obtain the workspace size required for computation and the executor that encapsulates the operator computation process, and then call the "aclnnIndexCopy" or "aclnnInplaceIndexCopy" API to perform the computation.
aclnnStatus aclnnIndexCopyGetWorkspaceSize(
aclTensor* selfRef,
int64_t dim,
const aclTensor* index,
const aclTensor* source,
aclTensor* outRef,
uint64_t* workspaceSize,
aclOpExecutor** executor)aclnnStatus aclnnIndexCopy(
void *workspace,
uint64_t workspaceSize,
aclOpExecutor *executor,
aclrtStream stream)aclnnStatus aclnnInplaceIndexCopyGetWorkspaceSize(
aclTensor* selfRef,
int64_t dim,
const aclTensor* index,
const aclTensor* source,
uint64_t* workspaceSize,
aclOpExecutor** executor)aclnnStatus aclnnInplaceIndexCopy(
void *workspace,
uint64_t workspaceSize,
aclOpExecutor *executor,
aclrtStream stream)aclnnIndexCopyGetWorkspaceSize
Parameters
Parameter Input/Output Description Instruction Data Type Data Format Dimension (shape) Non-contiguous Tensor selfRef Input Input aclTensor. - FLOAT, BFLOAT16, FLOAT16, INT32, UINT32, INT64, UINT64, INT16, INT8, UINT8, DOUBLE, BOOL, COMPLEX128, COMPLEX64 ND - √ dim Input Input aclTensor. In the range of [-selfRef.dim(), selfRef.dim() - 1]. int64 - - - index Input Input aclTensor. - The dimension must not be greater than 1, and the number of elements must be the same as the size of the dim axis in the source tensor.
- When index contains duplicate index values, the result order is not guaranteed.
- Out-of-bounds index values are not supported.
INT32, INT64 ND - √ source Input Input aclTensor. The size of the dim axis is the same as the number of elements in index, and other dimensions are the same as the shape of selfRef. Same as selfRef. ND Same as selfRef. √ outRef Output Output aclTensor. - Same as selfRef. - Same as selfRef. - workspaceSize Output Returns the workspace size to be applied for on the Device side. - - - - - executor Output Returns the operator executor, which contains the operator computation flow. - - - - - Atlas A2 training products/Atlas A2 inference products ,Atlas A3 training products/Atlas A3 inference products : UINT32 and UINT64 are not supported.
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 Value Error Code Description ACLNN_ERR_PARAM_NULLPTR 161001 The input selfRef, index, source, and out are null pointers. ACLNN_ERR_PARAM_INVALID 161002 The data types of selfRef and index are not within the supported range. The data types of selfRef and source are different. The size of source along the dim axis is not equal to the number of elements in index. The shapes of selfRef and source differ in sizes along non-dim axes. The dimension of index is greater than 1. When source is a scalar, the number of elements in index is not 1. When source and selfRef are not scalars, their dimensions differ. The shape or data type of selfRef differs from that of out. The dim value is out of range.
aclnnIndexCopy
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 aclnnIndexCopyGetWorkspaceSize. executor Input Operator executor, which 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.
aclnnInplaceIndexCopyGetWorkspaceSize
Parameters
Parameter Input/Output Description Instruction Data Type Data Format Shape Non-contiguous Tensor selfRef Input Input aclTensor. - FLOAT, BFLOAT16, FLOAT16, INT32, UINT32, INT64, UINT64, INT16, INT8, UINT8, DOUBLE, BOOL, COMPLEX128, COMPLEX64 ND - √ dim Input Input aclTensor. Within the range of [-selfRef.dim(), selfRef.dim() - 1]. int64 - - - index Input Input aclTensor. - The dimension must not be greater than 1, and the number of elements must be the same as the size of the dim axis in the source tensor.
- When index contains duplicate index values, the result is not order-preserving.
INT32, INT64 ND - √ source Input Input aclTensor. The size of the dim axis equals the number of elements in index, and other dimensions match the shape of selfRef. Same as selfRef. ND Same as selfRef. √ workspaceSize Output Returns the workspace size to be applied for on the Device side. - - - - - executor Output Returns the operator executor, which contains the operator computation process. - - - - - Atlas A2 training products/Atlas A2 inference products ,Atlas A3 training products/Atlas A3 inference products : UINT32 and UINT64 are not supported.
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 Value Error Code Description ACLNN_ERR_PARAM_NULLPTR 161001 The input selfRef, index, source, or out is a null pointer. ACLNN_ERR_PARAM_INVALID 161002 The data types of selfRef and index are not within the supported range. The data types of selfRef and source are different. The size of source along the dim axis is not equal to the number of elements in index. The shapes of selfRef and source differ along axes other than dim. When the dimension of index is greater than 1. When source is a scalar and the number of index elements is not 1. When neither source nor selfRef is a scalar and their dimensions differ. When dim is out of bounds.
aclnnInplaceIndexCopy
Parameters
Parameter Input/Output Description workspace Input Address of the workspace memory applied for on the Device side. workspaceSize Input Size of the workspace applied for on the Device side, obtained from the first-phase API aclnnInplaceIndexCopyGetWorkspaceSize. executor Input Operator executor, which contains the operator computation process. stream Input Specifies the stream for task execution. Return Value
aclnnStatus: return code. For details, see aclnn Return Code.
Constraints
Deterministic computation:
- aclnnIndexCopy&aclnnInplaceIndexCopy default 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_index_copy.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 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 the aclCreateTensor API.
*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 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> selfShape = {4, 2};
std::vector<int64_t> indexShape = {4};
std::vector<int64_t> sourceShape = {4, 2};
std::vector<int64_t> outShape = {4, 2};
void* selfDeviceAddr = nullptr;
void* indexDeviceAddr = nullptr;
void* sourceDeviceAddr = nullptr;
void* outDeviceAddr = nullptr;
aclTensor* self = nullptr;
aclTensor* index = nullptr;
aclTensor* source = nullptr;
aclTensor* out = nullptr;
int64_t dim=0;
std::vector<float> selfHostData = {0,0,0,0,0,0,0,0};
std::vector<int64_t> indexHostData = {3,2,1,0};
std::vector<float> sourceHostData={1,2,3,4,5,6,7,8};
std::vector<float> outHostData = {0,0,0,0,0,0,0,0};
// Create the self aclTensor.
ret = CreateAclTensor(selfHostData, selfShape, &selfDeviceAddr, aclDataType::ACL_INT32, &self);
CHECK_RET(ret == ACL_SUCCESS, return ret);
// Create the index aclTensor.
ret = CreateAclTensor(indexHostData, indexShape, &indexDeviceAddr, aclDataType::ACL_INT64, &index);
CHECK_RET(ret == ACL_SUCCESS, return ret);
// Create the source aclTensor.
ret = CreateAclTensor(sourceHostData, sourceShape, &sourceDeviceAddr, aclDataType::ACL_INT32, &source);
CHECK_RET(ret == ACL_SUCCESS, return ret);
// Create the out aclTensor.
ret = CreateAclTensor(outHostData, outShape, &outDeviceAddr, aclDataType::ACL_INT32, &out);
CHECK_RET(ret == ACL_SUCCESS, return ret);
// 3. Call the CANN operator library API. Replace with the actual API name.
uint64_t workspaceSize = 0;
aclOpExecutor* executor;
// Call the first-phase API of aclnnIndexCopy.
ret = aclnnIndexCopyGetWorkspaceSize(self, dim, index, source, out, &workspaceSize, &executor);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnIndexCopyGetWorkspaceSize 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 second-phase API of aclnnIndexCopy.
ret = aclnnIndexCopy(workspaceAddr, workspaceSize, executor, stream);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnIndexCopy failed. ERROR: %d\n", ret); return ret);
uint64_t inplaceWorkspaceSize = 0;
aclOpExecutor* inplaceExecutor;
// Call the first-phase API of aclnnInplaceIndexCopy.
ret = aclnnInplaceIndexCopyGetWorkspaceSize(self, dim, index, source, &inplaceWorkspaceSize, &inplaceExecutor);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnInplaceIndexCopyGetWorkspaceSize failed. ERROR: %d\n", ret); return ret);
// Apply for device memory based on the workspaceSize calculated by the first-phase API.
void* inplaceWorkspaceAddr = nullptr;
if (inplaceWorkspaceSize > 0) {
ret = aclrtMalloc(&inplaceWorkspaceAddr, inplaceWorkspaceSize, 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 aclnnInplaceIndexCopy.
ret = aclnnInplaceIndexCopy(inplaceWorkspaceAddr, inplaceWorkspaceSize, inplaceExecutor, stream);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnInplaceIndexCopy 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 side to the host side. Modify based on the specific API definition.
auto size = GetShapeSize(selfShape);
std::vector<float> resultData(size, 0);
ret = aclrtMemcpy(resultData.data(), resultData.size() * sizeof(resultData[0]), selfDeviceAddr,
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 ret);
for (int64_t i = 0; i < size; i++) {
LOG_PRINT("aclnnIndexCopy result[%ld] is: %f\n", i, resultData[i]);
}
auto inplaceSize = GetShapeSize(selfShape);
std::vector<float> inplaceResultData(inplaceSize, 0);
ret = aclrtMemcpy(inplaceResultData.data(), inplaceResultData.size() * sizeof(inplaceResultData[0]), selfDeviceAddr,
inplaceSize * sizeof(inplaceResultData[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 < inplaceSize; i++) {
LOG_PRINT("aclnnInplaceIndexCopy result[%ld] is: %f\n", i, inplaceResultData[i]);
}
// 6. Release aclTensor and aclScalar. Modify based on the specific API definition.
aclDestroyTensor(self);
aclDestroyTensor(index);
aclDestroyTensor(source);
aclDestroyTensor(out);
// 7. Release device resources. Modify based on the specific API definition.
aclrtFree(selfDeviceAddr);
aclrtFree(indexDeviceAddr);
aclrtFree(sourceDeviceAddr);
aclrtFree(outDeviceAddr);
if (workspaceSize > 0) {
aclrtFree(workspaceAddr);
}
aclrtDestroyStream(stream);
aclrtResetDevice(deviceId);
aclFinalize();
return 0;
}