aclnnScatter&aclnnInplaceScatter
Supported Products
| Product | Supported |
|---|---|
| √ | |
| √ | |
| × | |
| × | |
| √ |
Function
Description: Replaces, accumulates, or multiplies the values in the tensor src to the tensor self based on the given axis and direction and the corresponding position relationship.
Example: For a three-dimensional tensor, self is updated based on the following rules:
self[index[i][j][k]][j][k] += src[i][j][k] # if dim == 0 && reduction == 1 self[i][index[i][j][k]][k] *= src[i][j][k] # if dim == 1 && reduction == 2 self[i][j][index[i][j][k]] = src[i][j][k] # if dim == 2 && reduction == 0The following conditions must be met during computation:
- self, index, and src must have the same number of dimensions.
- For each dimension d, there is a restriction that index.size(d) <= src.size(d).
- For each dimension d, if d != dim, there is a restriction that index.size(d) <= self.size(d).
- The value of dim must be within the range of [–(number of dimensions of self), (number of dimensions of self) – 1].
- The number of dimensions of self must be less than or equal to eight.
- The corresponding dim value in index must be within the range of [0, self.size(dim) – 1].
Prototype
aclnnScatter and aclnnInplaceScatter implement the same function in different ways. Select a proper operator based on your requirements.
- aclnnScatter: An output tensor object needs to be created to store the computation result.
- aclnnInplaceScatter: No output tensor object needs to be created, and the computation result is stored in the memory of the input tensor.
Each operator has two-phase API calls. First, aclnnScatterGetWorkspaceSize or aclnnInplaceScatterGetWorkspaceSize is called to obtain the workspace size required for computation and the executor that contains the operator computation process. Then, aclnnScatter or aclnnInplaceScatter is called to perform computation.
aclnnStatus aclnnScatterGetWorkspaceSize(const aclTensor* self, int64_t dim, const aclTensor* index, const aclTensor* src, int64_t reduce, aclTensor* out, uint64_t* workspaceSize, aclOpExecutor** executor)aclnnStatus aclnnScatter(void* workspace, uint64_t workspaceSize, aclOpExecutor* executor, const aclrtStream stream)aclnnStatus aclnnInplaceScatterGetWorkspaceSize(aclTensor* selfRef, int64_t dim, const aclTensor* index, const aclTensor* src, int64_t reduce, uint64_t* workspaceSize, aclOpExecutor** executor)aclnnStatus aclnnInplaceScatter(void* workspace, uint64_t workspaceSize, aclOpExecutor* executor, aclrtStream stream)
aclnnScatterGetWorkspaceSize
Parameters:
self (aclTensor*, compute input):
selfin the formula, aclTensor on the device. The number of dimensions of self must be the same as those of index and src. The shape supports zero to eight dimensions. The data type of self must be the same as that of src. Non-contiguous tensors are supported. The data format can be ND.Atlas training series products : The data type can be UINT8, INT8, INT16, INT32, INT64, BOOL, FLOAT16, FLOAT32, DOUBLE, COMPLEX64, or COMPLEX128.Atlas A2 training products/Atlas A2 inference products ,Atlas A3 training series products/Atlas A3 inference series products : The data type can be UINT8, INT8, INT16, INT32, INT64, BOOL, FLOAT16, FLOAT32, DOUBLE, COMPLEX64, COMPLEX128, or BFLOAT16.
dim (int64_t, compute input): dimension used for scattering. The data type can be INT64. The value range is [–(number of dimensions of self), (number of dimensions of self) – 1].
index (aclTensor*, compute input):
indexin the formula, aclTensor on the device. It indicates the index tensor. The data type can be INT32 or INT64. The number of dimensions of index must be the same as those of self and src. The shape supports zero to eight dimensions. Non-contiguous tensors are supported. The data format can be ND.src (aclTensor*, compute input):
srcin the formula, aclTensor on the device. The number of dimensions of src must be the same as those of self and index. The shape supports zero to eight dimensions. The data type of src must be the same as that of self. Non-contiguous tensors are supported. The data format can be ND.Atlas training series products : The data type can be UINT8, INT8, INT16, INT32, INT64, BOOL, FLOAT16, FLOAT32, DOUBLE, COMPLEX64, or COMPLEX128.Atlas A2 training products/Atlas A2 inference products ,Atlas A3 training series products/Atlas A3 inference series products : The data type can be UINT8, INT8, INT16, INT32, INT64, BOOL, FLOAT16, FLOAT32, DOUBLE, COMPLEX64, COMPLEX128, or BFLOAT16.
reduce (int64_t, compute input): integer on the host, used to select a reduction to apply. The options and corresponding integer values are add (1), mul (2), and none (0). The specific operations are defined as follows: 0: replaces the value at a position in src to the corresponding position in out according to index. 1: accumulates the value at a position in src to the corresponding position in out according to index. 2: multiplies the value at a position in src to the corresponding position in out according to index.
out (aclTensor*, compute output): The data type is the same as that of self. The shape must be the same as that of self. The data format can be ND.
Atlas training series products : The data type can be UINT8, INT8, INT16, INT32, INT64, BOOL, FLOAT16, FLOAT32, DOUBLE, COMPLEX64, or COMPLEX128.Atlas A2 training products/Atlas A2 inference products ,Atlas A3 training series products/Atlas A3 inference series products : The data type can be UINT8, INT8, INT16, INT32, INT64, BOOL, FLOAT16, FLOAT32, DOUBLE, COMPLEX64, COMPLEX128, or BFLOAT16.
workspaceSize (uint64_t*, output): size of the workspace to be allocated on the device.
executor (aclOpExecutor**, output): operator executor, containing the operator computation process.
Returns:
aclnnStatus: status code. For details, see aclnn Return Codes.
The first-phase API implements input parameter verification. The following errors may be thrown:
161001 (ACLNN_ERR_PARAM_NULLPTR): 1. The passed self, index, src, or out is a null pointer.
161002 (ACLNN_ERR_PARAM_INVALID): 1. The data type of self, index, src, or out is not supported.
2. The data types of self, src, and out are inconsistent.
3. The numbers of dimensions of self, index, and src are inconsistent.
4. The shapes of self and out are inconsistent.
5. The shape of self, index, or src does not meet the following requirements:
For each dimension d, there is a restriction that index.size(d) <= src.size(d).
For each dimension d, if d != dim, there is a restriction that index.size(d) <= self.size(d).
6. The value of dim is not in the range of [–(number of dimensions of self), (number of dimensions of self) – 1].
7. The number of dimensions of self is greater than eight.aclnnScatter
Parameters:
workspace (void*, input): address of the workspace to be allocated on the device.
workspaceSize (uint64_t, input): size of the workspace to be allocated on the device, which is obtained by the first-phase API aclnnScatterGetWorkspaceSize.
executor (aclOpExecutor*, input): operator executor, containing the operator computation process.
stream (aclrtStream, input): stream for executing the task.
Returns:
aclnnStatus: status code. For details, see aclnn Return Codes.
aclnnInplaceScatterGetWorkspaceSize
Parameters:
- selfRef (aclTensor*, compute input | compute output): target tensor of scatter,
selfin the formula, aclTensor on the device. The shape supports zero to eight dimensions, and the number of dimensions of the shape must be the same as those of index and src. The data type is the same as that of src. Empty tensors and non-contiguous tensors are supported. The data format can be ND.Atlas training series products : The data type can be UINT8, INT8, INT16, INT32, INT64, BOOL, FLOAT16, FLOAT32, DOUBLE, COMPLEX64, or COMPLEX128.Atlas A2 training products/Atlas A2 inference products ,Atlas A3 training series products/Atlas A3 inference series products : The data type can be UINT8, INT8, INT16, INT32, INT64, BOOL, FLOAT16, FLOAT32, DOUBLE, COMPLEX64, COMPLEX128, or BFLOAT16.
- dim (int64_t, compute input): dimension along which the scattering operation is performed. The data type is INT64. It is an integer on the host. The value range is [–(number of dimensions of selfRef), (number of dimensions of selfRef) – 1].
- index (aclTensor*, compute input): index tensor, specifying the indexes of the src tensor distributed to the self tensor, aclTensor on the device. The data type can be INT32 or INT64. The number of dimensions of index must be the same as those of selfRef and src. The shape supports zero to eight dimensions. For each dimension d, make sure index.size(d) <= src.size(d). If d!= dim, make sure index.size(d) <= selfRef.size(d). Empty tensors and non-contiguous tensors are supported. The data format can be ND.
- src (aclTensor*, compute input): <idp:inline displayname="code" id="code56285812499">src in the formula, aclTensor on the device. It is a source tensor. Its values are distributed to self based on the position specified by the index tensor. src must have the same number of dimensions as selfRef and index, and the shape supports zero to eight dimensions. The data type of src must be the same as that of selfRef. Empty tensors and non-contiguous tensors are supported. The data format can be ND.
Atlas training series products : The data type can be UINT8, INT8, INT16, INT32, INT64, BOOL, FLOAT16, FLOAT, DOUBLE, COMPLEX64, or COMPLEX128.Atlas A2 training products/Atlas A2 inference products ,Atlas A3 training series products/Atlas A3 inference series products : The data type can be UINT8, INT8, INT16, INT32, INT64, BOOL, FLOAT16, FLOAT, DOUBLE, COMPLEX64, COMPLEX128, or BFLOAT16.
- reduce (int64_t, compute input): used to select a reduction to apply. The options and corresponding integer values are add (1), mul (2), and none (0). The specific operations are defined as follows: 0: replaces the value at a position in src to the corresponding position in selfRef according to index. 1: accumulates the value at a position in src to the corresponding position in selfRef according to index. 2: multiplies the value at a position in src to the corresponding position in selfRef according to index.
- workspaceSize (uint64_t*, output): size of the workspace to be allocated on the device.
- executor (aclOpExecutor**, output): operator executor, containing the operator computation process.
- selfRef (aclTensor*, compute input | compute output): target tensor of scatter,
Returns
aclnnStatus: status code. For details, see aclnn Return Codes.
The first-phase API implements input parameter verification. The following errors may be thrown:
161001 (ACLNN_ERR_PARAM_NULLPTR): 1. The passed selfRef, index, or src is a null pointer.
161002 (ACLNN_ERR_PARAM_INVALID): 1. The data type of selfRef, index, or src is not supported.
2. The data types of selfRef and src are inconsistent.
3. The numbers of dimensions of selfRef, index, and src are inconsistent.
4. The shape of selfRef, index, or src does not meet the following requirements:
For each dimension d, there is a restriction that index.size(d) <= src.size(d).
For each dimension d, if d != dim, there is a restriction that index.size(d) <= selfRef.size(d).
5. The value of dim is not in the range of [–(number of dimensions of selfRef), (number of dimensions of selfRef) – 1].
6. The number of dimensions of selfRef is greater than eight.aclnnInplaceScatter
Parameters:
- workspace (void*, input): address of the workspace to be allocated on the device.
- workspaceSize (uint64_t, input): size of the workspace to be allocated on the device, which is obtained by the first-phase API aclnnInplaceScatterGetWorkspaceSize.
- executor (aclOpExecutor*, input): operator executor, containing the operator computation process.
- stream (aclrtStream, input): stream for executing the task.
Returns:
aclnnStatus: status code. For details, see aclnn Return Codes.
Constraints
- Deterministic compute:
- aclnnScatter&aclnnInplaceScatter defaults to a deterministic implementation.
Example
The following example is for reference only. For details, see Compilation and Running Sample.
aclnnScatter sample code:
#include <iostream>
#include <vector>
#include "acl/acl.h"
#include "aclnnop/aclnn_scatter.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) {
// (Fixed writing) Initialize resources.
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 memory on the device.
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 the data on the host to the memory on the device.
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. (Fixed writing) Initialize the device and stream. For details, see the ACL API manual.
// Set the device ID in use.
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 based on the API.
int64_t dim = 1;
int64_t reduce = 1;
std::vector<int64_t> selfShape = {3, 4};
std::vector<int64_t> indexShape = {2, 3};
std::vector<int64_t> srcShape = {2, 3};
std::vector<int64_t> outShape = {3, 4};
void* selfDeviceAddr = nullptr;
void* indexDeviceAddr = nullptr;
void* srcDeviceAddr = nullptr;
void* outDeviceAddr = nullptr;
aclTensor* self = nullptr;
aclTensor* index = nullptr;
aclTensor* src = nullptr;
aclTensor* out = nullptr;
std::vector<float> selfHostData = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
std::vector<int64_t> indexHostData = {0, 0, 2, 1, 0, 2};
std::vector<float> srcHostData = {-1, -2, -3, -4, -5, -6};
std::vector<float> outHostData = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
// Create a self aclTensor.
ret = CreateAclTensor(selfHostData, selfShape, &selfDeviceAddr, aclDataType::ACL_FLOAT, &self);
CHECK_RET(ret == ACL_SUCCESS, return ret);
// Create an index aclTensor.
ret = CreateAclTensor(indexHostData, indexShape, &indexDeviceAddr, aclDataType::ACL_INT64, &index);
CHECK_RET(ret == ACL_SUCCESS, return ret);
// Create an src aclTensor.
ret = CreateAclTensor(srcHostData, srcShape, &srcDeviceAddr, aclDataType::ACL_FLOAT, &src);
CHECK_RET(ret == ACL_SUCCESS, return ret);
// Create an out aclTensor.
ret = CreateAclTensor(outHostData, outShape, &outDeviceAddr, aclDataType::ACL_FLOAT, &out);
CHECK_RET(ret == ACL_SUCCESS, return ret);
// 3. Call the CANN operator library API, which needs to be replaced with the actual API.
uint64_t workspaceSize = 0;
aclOpExecutor* executor;
// Call the first-phase API of aclnnScatter.
ret = aclnnScatterGetWorkspaceSize(self, dim, index, src, reduce, out, &workspaceSize, &executor);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnScatterGetWorkspaceSize failed. ERROR: %d\n", ret); return ret);
// Allocate device memory based on 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 aclnnScatter.
ret = aclnnScatter(workspaceAddr, workspaceSize, executor, stream);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnScatter failed. ERROR: %d\n", ret); return ret);
// 4. (Fixed writing) Wait until the task execution is 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. Modify the configuration based on the API definition.
auto size = GetShapeSize(outShape);
std::vector<float> resultData(size, 0);
ret = aclrtMemcpy(resultData.data(), resultData.size() * sizeof(resultData[0]), outDeviceAddr,
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("result[%ld] is: %f\n", i, resultData[i]);
}
// 6. Release aclTensor and aclScalar. Modify the configuration based on the API definition.
aclDestroyTensor(self);
aclDestroyTensor(index);
aclDestroyTensor(src);
aclDestroyTensor(out);
// 7. Release device resources. Modify the configuration based on the API definition.
aclrtFree(selfDeviceAddr);
aclrtFree(indexDeviceAddr);
aclrtFree(srcDeviceAddr);
aclrtFree(outDeviceAddr);
if (workspaceSize > 0) {
aclrtFree(workspaceAddr);
}
aclrtDestroyStream(stream);
aclrtResetDevice(deviceId);
aclFinalize();
return 0;
}aclnnInplaceScatter sample code:
#include <iostream>
#include <vector>
#include "acl/acl.h"
#include "aclnnop/aclnn_scatter.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) {
// (Fixed writing) Initialize resources.
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 memory on the device.
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 the data on the host to the memory on the device.
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. (Fixed writing) Initialize the device and stream. For details, see the ACL API manual.
// Set the device ID in use.
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 based on the API.
int64_t dim = 1;
int64_t reduce = 1;
std::vector<int64_t> selfRefShape = {3, 4};
std::vector<int64_t> indexShape = {2, 3};
std::vector<int64_t> srcShape = {2, 3};
void* selfRefDeviceAddr = nullptr;
void* indexDeviceAddr = nullptr;
void* srcDeviceAddr = nullptr;
aclTensor* selfRef = nullptr;
aclTensor* index = nullptr;
aclTensor* src = nullptr;
std::vector<float> selfRefHostData = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
std::vector<int64_t> indexHostData = {0, 0, 2, 1, 0, 2};
std::vector<float> srcHostData = {-1, -2, -3, -4, -5, -6};
// Create a selfRef aclTensor.
ret = CreateAclTensor(selfRefHostData, selfRefShape, &selfRefDeviceAddr, aclDataType::ACL_FLOAT, &selfRef);
CHECK_RET(ret == ACL_SUCCESS, return ret);
// Create an index aclTensor.
ret = CreateAclTensor(indexHostData, indexShape, &indexDeviceAddr, aclDataType::ACL_INT64, &index);
CHECK_RET(ret == ACL_SUCCESS, return ret);
// Create an src aclTensor.
ret = CreateAclTensor(srcHostData, srcShape, &srcDeviceAddr, aclDataType::ACL_FLOAT, &src);
CHECK_RET(ret == ACL_SUCCESS, return ret);
// 3. Call the CANN operator library API, which needs to be replaced with the actual API.
uint64_t workspaceSize = 0;
aclOpExecutor* executor;
// Call the first-phase API of aclnnInplaceScatter.
ret = aclnnInplaceScatterGetWorkspaceSize(selfRef, dim, index, src, reduce, &workspaceSize, &executor);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnInplaceScatterGetWorkspaceSize failed. ERROR: %d\n", ret); return ret);
// Allocate device memory based on 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 aclnnInplaceScatter.
ret = aclnnInplaceScatter(workspaceAddr, workspaceSize, executor, stream);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnInplaceScatter failed. ERROR: %d\n", ret); return ret);
// 4. (Fixed writing) Wait until the task execution is 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. Modify the configuration based on the API definition.
auto size = GetShapeSize(selfRefShape);
std::vector<float> resultData(size, 0);
ret = aclrtMemcpy(resultData.data(), resultData.size() * sizeof(resultData[0]), selfRefDeviceAddr,
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("result[%ld] is: %f\n", i, resultData[i]);
}
// 6. Release aclTensor and aclScalar. Modify the configuration based on the API definition.
aclDestroyTensor(selfRef);
aclDestroyTensor(index);
aclDestroyTensor(src);
// 7. Release device resources. Modify the configuration based on the API definition.
aclrtFree(selfRefDeviceAddr);
aclrtFree(indexDeviceAddr);
aclrtFree(srcDeviceAddr);
if (workspaceSize > 0) {
aclrtFree(workspaceAddr);
}
aclrtDestroyStream(stream);
aclrtResetDevice(deviceId);
aclFinalize();
return 0;
}