aclnnScatterList
Applicable Products
| Product | Supported |
|---|---|
| × | |
| √ | |
| √ | |
| × | |
| × | |
| × |
Function
Description: Applies sparse updates to a variable reference tensor list, scattering values from updates to the corresponding dimension of var based on indices.
Formula:
Where indices is the index tensor, updates is the value tensor to be updated, var is the tensor list to be updated, and the scatter dimension is specified by the axis parameter.
Function Prototype
Each operator is divided into a two-phase API. The "aclnnScatterListGetWorkspaceSize" API must be called first to obtain the workspace size required for computation and the executor that encapsulates the operator computation flow. Then, the "aclnnScatterList" API is called to perform the computation.
aclnnStatus aclnnScatterListGetWorkspaceSize(
aclTensorList* var,
const aclTensor* indices,
const aclTensor* updates,
const aclTensor* mask,
char* reduce,
int64_t axis,
uint64_t* workspaceSize,
aclOpExecutor** executor)aclnnStatus aclnnScatterList(
void* workspace,
uint64_t workspaceSize,
aclOpExecutor* executor,
aclrtStream stream)aclnnScatterListGetWorkspaceSize
Parameters:
Parameter Input/Output Description Instruction Data Type Data Format Shape Non-contiguous Tensor var (aclTensorList*) Input/Output List of tensors to be updated. The shape of each tensor in the tensor list and updates satisfy the broadcast relationship.
The data type of each tensor must be the same as the data type of updates.INT8, INT16, INT32, UINT8, UINT16, UINT32, FLOAT16, BFLOAT16, FLOAT ND 0-8 √ indices (aclTensor*) Input Index tensor. - INT32, INT64 ND 0-8 √ updates (aclTensor*) Input The value tensor to be updated to var. The data type must be the same as the tensor data type in var.
The shape must satisfy the broadcast relationship with the shape of each tensor in var.INT8, INT16, INT32, UINT8, UINT16, UINT32, FLOAT16, BFLOAT16, FLOAT ND 0-8 √ mask (aclTensor*) Optional parameter. Mask tensor. - UINT8 ND 0-8 √ reduce (const char*) Attribute Type of the reduction operation to apply. Currently, only "update" is supported. string - - - axis (int64_t) Attribute Specifies the dimension along which to scatter. The default value is -2. int64_t - - - workspaceSize (uint64_t*) Output Returns the workspace size to be allocated on the Device side. - - - - - executor (aclOpExecutor**) Output Returns the op executor, which contains the operator computation flow. - - - - - 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 Indicates that the input tensor or tensorList is a null pointer. ACLNN_ERR_PARAM_INVALID 161002 The data type of the tensor in var is not within the supported range. The data type of indices is not within the supported range. The reduce parameter is not within the supported range. The data type of the tensor in var is inconsistent with that of updates.
aclnnScatterList
Parameters:
Parameter Input/Output Description workspace Input Specifies the workspace memory address applied for on the Device side. workspaceSize Input Specifies the workspace size applied for on the Device side, which is obtained through the first-phase API aclnnScatterListGetWorkspaceSize. executor Input Operator executor that 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:
- aclnnScatterList defaults to a non-deterministic implementation. You can enable deterministic computation through aclrtCtxSetSysParamOpt.
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_scatter_list.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("result[%ld] is: %f\n", i, resultData[i]);
}
}
int Init(int32_t deviceId, aclrtStream* stream) {
// Boilerplate: 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 aclCreateTensor.
*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 documentation.
// Set deviceId based on your 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 inputs and outputs based on the API.
std::vector<int64_t> varShape = {5, 3, 4};
std::vector<int64_t> indicesShape = {1, 2};
std::vector<int64_t> updatesShape = {1, 5, 3, 4};
void* varDeviceAddr = nullptr;
void* indicesDeviceAddr = nullptr;
void* updatesDeviceAddr = nullptr;
aclTensor* varTensor = nullptr;
aclTensor* indices = nullptr;
aclTensor* updates = nullptr;
std::vector<float> varHostData(5 * 3 * 4, 0);
std::vector<int64_t> indicesHostData = {0, 1};
std::vector<float> updatesHostData(1 * 5 * 3 * 4, 10);
ret = CreateAclTensor(varHostData, varShape, &varDeviceAddr, aclDataType::ACL_FLOAT, &varTensor);
CHECK_RET(ret == ACL_SUCCESS, return ret);
aclTensor* varArray[] = {varTensor};
auto varTensorList = aclCreateTensorList(varArray, 1);
ret = CreateAclTensor(indicesHostData, indicesShape, &indicesDeviceAddr, aclDataType::ACL_INT64, &indices);
CHECK_RET(ret == ACL_SUCCESS, return ret);
ret = CreateAclTensor(updatesHostData, updatesShape, &updatesDeviceAddr, aclDataType::ACL_FLOAT, &updates);
CHECK_RET(ret == ACL_SUCCESS, return ret);
// 3. Call the CANN operator library API.
uint64_t workspaceSize = 0;
aclOpExecutor* executor;
// Call the first-phase API of aclnnScatterList.
ret = aclnnScatterListGetWorkspaceSize(varTensorList, indices, updates, nullptr, "update", -2, &workspaceSize, &executor);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnScatterListGetWorkspaceSize failed. ERROR: %d\n", ret); return ret);
// Allocate 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 aclnnScatterList.
ret = aclnnScatterList(workspaceAddr, workspaceSize, executor, stream);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnScatterList failed. ERROR: %d\n", ret); return ret);
// 4. (Boilerplate) Wait for the task execution to finish synchronously.
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.
PrintOutResult(varShape, &varDeviceAddr);
// 6. Release the aclTensor.
aclDestroyTensorList(varTensorList);
aclDestroyTensor(indices);
aclDestroyTensor(updates);
// 7. Release the device resources.
aclrtFree(varDeviceAddr);
aclrtFree(indicesDeviceAddr);
aclrtFree(updatesDeviceAddr);
if (workspaceSize > 0) {
aclrtFree(workspaceAddr);
}
aclrtDestroyStream(stream);
aclrtResetDevice(deviceId);
aclFinalize();
return 0;
}