aclnnTfScatterAdd
Applicable Products
| Product | Supported |
|---|---|
| × | |
| √ | |
| √ | |
| × | |
| × | |
| × |
Function
- Operator function: Implements functionality compatible with tf.compat.v1.scatter_add and tf.compat.v1.scatter_nd_add, adding values from the tensor updates to slices of the tensor varRef according to the specified index tensor indices. If more than one updates value is assigned to the same slice of varRef, these values are accumulated on that slice. The rule is as follows:
or
Function Prototype
Each operator is divided into a two-phase API. The "aclnnTfScatterAddGetWorkspaceSize" API must be called first to obtain the workspace size required for computation and the executor that encapsulates the operator computation flow. Then, the "aclnnTfScatterAdd" API is called to perform the 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|compute output): The input
varRefin the formula, representing the initial tensor to be updated. It is an aclTensor on the Device side. Supports non-contiguous tensor. The data format supports ND. The dimension support ranges from 1 to 8 dimensions. The data type must be consistent with updates. Supported data types: FLOAT32, FLOAT16, BFLOAT16, INT32, INT8, UINT8.indices (aclTensor*, compute input): The input
indicesin the formula, representing the index positions to be updated. It is an aclTensor on the Device side. Supports non-contiguous tensor. The data format supports ND. The dimension support ranges from 1 to 8 dimensions. Out-of-bounds index data in indices is not supported. If an out-of-bounds index occurs, varRef will not be updated. Supported data types: INT32, INT64.updates (aclTensor*, compute input): The input
updatesin the formula, representing the update values to be added tovarRef. It is an aclTensor on the Device side. Supports non-contiguous tensor. The data format supports ND. The dimension support ranges from 1 to 8 dimensions. The data type must be consistent with varRef. Supported data types: FLOAT32, FLOAT16, BFLOAT16, INT32, INT8, UINT8.workspaceSize (uint64_t*, compute input): Returns the workspace size to be allocated on the Device side.
executor (aclOpExecutor**, output parameter): Returns the op executor, which includes computation.
Return Value
aclnnStatus: return code. For details, see aclnn Return Code.
The first-phase API completes input validation. If the following error codes occur, the corresponding causes are: - 161001 (ACLNN_ERR_PARAM_NULLPTR): 1. The passed varRef, indices, or updates is a null pointer. - 161002 (ACLNN_ERR_PARAM_INVALID): 1. The data types of varRef, indices, and updates are not within the supported range. 2. The dtypes of varRef and updates are inconsistent. 3. varRef is empty while indices is a non-empty tensor. 4. The shape of updates does not meet the corresponding shape constraints.
aclnnTfScatterAdd
Parameters
workspace (void *, input parameter): Device-side allocated workspace memory address.
workspaceSize (uint64_t, input parameter): Device-side allocated workspace size, obtained from the first-phase API aclnnTfScatterAddGetWorkspaceSize.
executor (aclOpExecutor *, input parameter): Op executor, which includes the computation process of the operator.
stream (aclrtStream, input parameter): Stream on which the task is executed.
Return Value
aclnnStatus: return code. For details, see aclnn Return Code.
Constraints
deterministic computation:
- aclnnTfScatterAdd defaults to a non-deterministic implementation. It can be configured as a deterministic implementation through deterministic computation.
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 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_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) {
// Boilerplate. 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 apply for device-side 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 device-side 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 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. (Boilerplate) Initialize the device/stream. Refer to the ACL API manual.
// Fill in the 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> 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 the varRef aclTensor.
ret = CreateAclTensor(varRefHostData, varRefShape, &varRefDeviceAddr, aclDataType::ACL_FLOAT, &varRef);
CHECK_RET(ret == ACL_SUCCESS, return ret);
// Create the indices aclTensor.
ret = CreateAclTensor(indicesHostData, indicesShape, &indicesDeviceAddr, aclDataType::ACL_INT64, &indices);
CHECK_RET(ret == ACL_SUCCESS, return ret);
// Create the 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. Replace with the actual API name.
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);
// Apply for 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 aclnnTfScatterAdd.
ret = aclnnTfScatterAdd(workspaceAddr, workspaceSize, executor, stream);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnTfScatterAdd failed. ERROR: %d\n", ret); return ret);
// 4. (Boilerplate) Synchronously wait for the 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 memory to the host side. Modify based on the specific 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 based on the specific API definition.
aclDestroyTensor(varRef);
aclDestroyTensor(indices);
aclDestroyTensor(updates);
// 7. Release device resources. Modify parameters based on the specific API definition.
aclrtFree(varRefDeviceAddr);
aclrtFree(indicesDeviceAddr);
aclrtFree(updatesDeviceAddr);
if (workspaceSize > 0) {
aclrtFree(workspaceAddr);
}
aclrtDestroyStream(stream);
aclrtResetDevice(deviceId);
aclFinalize();
return 0;
}