aclnnTfScatterAdd
Supported Products
| Product | Supported |
|---|---|
| √ | |
| √ | |
| × | |
| × | |
| × |
Function
- Description: Implements the functions compatible with tf.compat.v1.scatter_add and tf.compat.v1.scatter_nd_add. Adds the values in tensor updates to the slices of tensor varRef based on the specified index tensor indices. If more than one updates value is written to the same slice of varRef, these values are accumulated at this slice. The rules are as follows:
Or:
Prototype
Each operator has two-phase API calls. First, aclnnTfScatterAddGetWorkspaceSize is called to obtain the workspace size required for computation and the executor that contains the operator computation process. Then, aclnnTfScatterAdd is called to perform computation.
aclnnStatus aclnnTfScatterAddGetWorkspaceSize(aclTensor *varRef, const aclTensor *indices, const aclTensor *updates, uint64_t *workspaceSize, aclOpExecutor **executor)aclnnStatus aclnnTfScatterAdd(void *workspace, uint64_t workspaceSize, aclOpExecutor *executor, aclrtStream stream)
aclnnTfScatterAddGetWorkspaceSize
Parameters
- varRef (aclTensor*, compute input/output): input
varRefin the formula, initial tensor to be updated, aclTensor on the device. Non-contiguous tensors are supported. The data format can be ND, and one to eight dimensions are supported. The data type must be the same as that of updates. The data type can be FLOAT32, FLOAT16, BFLOAT16, INT32, INT8, or UINT8. - indices (aclTensor*, compute input): input
indicesin the formula, index position to be updated, aclTensor on the device. Non-contiguous tensors are supported. The data format can be ND, and one to eight dimensions are supported. An out-of-bounds index is not supported in indices. If an out-of-bounds index occurs, varRef is not updated. The data type can be INT32 or INT64. - updates (aclTensor*, compute input): input
updatesin the formula, updated value to be added tovarRef, aclTensor on the device. Non-contiguous tensors are supported. The data format can be ND, and one to eight dimensions are supported. The data type must be the same as that of varRef. The data type can be FLOAT32, FLOAT16, BFLOAT16, INT32, INT8, or UINT8. - workspaceSize (uint64_t *, compute input): size of the workspace required to be allocated on the device.
- executor (uint64_t *, output parameter): operator executor, containing the operator computation process.
- varRef (aclTensor*, compute input/output): input
Returns
aclnnStatus status code. For details, see aclnn Return Codes.
The first-phase API call completes input parameter verification. The possible error codes and causes are as follows: - 161001 (ACLNN_ERR_PARAM_NULLPTR): 1. The passed varRef, indices, or updates is a null pointer. - 161002 (ACLNN_ERR_PARAM_INVALID): 1. The data type of varRef, indices, or updates is not supported. 2. The dtypes of varRef and updates are inconsistent. 3. varRef is an empty tensor, and indices is not an empty tensor. 4. The shape of updates does not meet the corresponding constraints.
aclnnTfScatterAdd
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 aclnnTfScatterAddGetWorkspaceSize.
- 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:
- aclnnTfScatterAdd defaults to a non-deterministic implementation. It can be configured to a deterministic implementation via deterministic computation settings.
- One of the following constraints must be met:
- updates.shape = indices.shape + varRef.shape[1:]
- indices.shape[-1] <= varRef.shape.rank and updates.shape = indices.shape[:-1] + varRef.shape[indices.shape[-1]:]
Example
The following example is for reference only. For details, see Compilation and Running Sample.
#include <iostream>
#include <vector>
#include "acl/acl.h"
#include "aclnnop/aclnn_tf_scatter_add.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 definition.
std::vector<int64_t> varRefShape = {3, 2};
std::vector<int64_t> indicesShape = {2, 3};
std::vector<int64_t> updatesShape = {2, 3, 2};
void* varRefDeviceAddr = nullptr;
void* indicesDeviceAddr = nullptr;
void* updatesDeviceAddr = nullptr;
aclTensor* varRef = nullptr;
aclTensor* indices = nullptr;
aclTensor* updates = nullptr;
std::vector<float> varRefHostData = {1, 2, 3, 4, 5, 6};
std::vector<int64_t> indicesHostData = {0, 2};
std::vector<float> updatesHostData = {10, 20, 30, 40};
// Create a varRef aclTensor.
ret = CreateAclTensor(varRefHostData, varRefShape, &varRefDeviceAddr, aclDataType::ACL_FLOAT, &varRef);
CHECK_RET(ret == ACL_SUCCESS, return ret);
// Create an indices aclTensor.
ret = CreateAclTensor(indicesHostData, indicesShape, &indicesDeviceAddr, aclDataType::ACL_INT64, &indices);
CHECK_RET(ret == ACL_SUCCESS, return ret);
// Create an updates aclTensor.
ret = CreateAclTensor(updatesHostData, updatesShape, &updatesDeviceAddr, aclDataType::ACL_FLOAT, &updates);
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 aclnnTfScatterAdd.
ret = aclnnTfScatterAddGetWorkspaceSize(varRef, indices, updates, &workspaceSize, &executor);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnTfScatterAddGetWorkspaceSize 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 aclnnTfScatterAdd.
ret = aclnnTfScatterAdd(workspaceAddr, workspaceSize, executor, stream);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnTfScatterAdd 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(varRefShape);
std::vector<float> resultData(size, 0);
ret = aclrtMemcpy(resultData.data(), resultData.size() * sizeof(resultData[0]), varRefDeviceAddr,
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(varRef);
aclDestroyTensor(indices);
aclDestroyTensor(updates);
// 7. Release device resources. Set the parameters based on the API definition.
aclrtFree(varRefDeviceAddr);
aclrtFree(indicesDeviceAddr);
aclrtFree(updatesDeviceAddr);
if (workspaceSize > 0) {
aclrtFree(workspaceAddr);
}
aclrtDestroyStream(stream);
aclrtResetDevice(deviceId);
aclFinalize();
return 0;
}