aclnnScatterValue&aclnnInplaceScatterValue
Applicable Products
| Product | Supported |
|---|---|
| √ | |
| √ | |
| √ | |
| × | |
| × | |
| × |
Function
Operator function: Fills the values from the scalar value into the tensor self one by one according to the specified axis, direction, and corresponding positional relationship. The value is broadcast into a tensor src with the same shape as the tensor index for the Scatter computation.
Example:
For a 3D tensor, self is updated according to the following rules:
self[index[i][j][k]][j][k] = value # If dim == 0 self[i][index[i][j][k]][k] = value # If dim == 1 self[i][j][index[i][j][k]] = value # If dim == 2
The following requirements must be met during computation:
self and index must have the same number of dimensions.
For each dimension d, if d != dim, the constraint index.size(d) <= self.size(d) applies.
The value of dim must be in the range [-number of dimensions of self, number of dimensions of self - 1].
The number of dimensions of self must be less than or equal to 8.
The values of index in the dimension dim must be in the range [0, self.size(dim)-1].
Function Prototype
aclnnScatterValue and aclnnInplaceScatterValue implement the same function. The differences in usage are as follows. Select the appropriate operator based on your actual scenario.
aclnnScatterValue: A new output tensor object needs to be created to store the computation result.
aclnnInplaceScatterValue: No need to create a new output tensor object. The computation result is stored directly in the memory of the input tensor.
Each operator is divided into a two-phase API. You must first call the "aclnnScatterValueGetWorkspaceSize" or "aclnnInplaceScatterValueGetWorkspaceSize" API to obtain the workspace size required for computation and the executor that contains the operator computation process, and then call the "aclnnScatterValue" or "aclnnInplaceScatterValue" API to execute the computation.
aclnnStatus aclnnScatterValueGetWorkspaceSize(const aclTensor *self, int64_t dim, const aclTensor *index, const aclScalar *value, int64_t reduce, aclTensor *out, uint64_t *workspaceSize, aclOpExecutor **executor)aclnnStatus aclnnScatterValue(void *workspace, uint64_t workspaceSize, aclOpExecutor *executor, const aclrtStream stream)aclnnStatus aclnnInplaceScatterValueGetWorkspaceSize(aclTensor *selfRef, int64_t dim, const aclTensor *index, const aclScalar *value, int64_t reduce, uint64_t *workspaceSize, aclOpExecutor **executor)aclnnStatus aclnnInplaceScatterValue(void *workspace, uint64_t workspaceSize, aclOpExecutor *executor, aclrtStream stream)
aclnnScatterValueGetWorkspaceSize
Parameters:
self(aclTensor*, computational input):
selfin the formula, a device-side aclTensor. The target tensor for scatter. The number of dimensions of self must be the same as that of index, and the shape supports 0–8 dimensions. Supports non-contiguous tensor. The data format supports ND.Atlas A2 training products/Atlas A2 inference products andAtlas A3 training products/Atlas A3 inference products : Supports UINT8, INT8, INT16, INT32, INT64, BOOL, FLOAT16, FLOAT32, DOUBLE, COMPLEX64, COMPLEX128, and BFLOAT16 data types.
dim(int64_t, computational input): The dimension along which to scatter, with the data type INT64. The range is [-number of dimensions of self, number of dimensions of self - 1].
index(aclTensor*, computational input):
indexin the formula, a device-side aclTensor. The index tensor, supporting INT32 and INT64 data types. The number of dimensions of index must be the same as that of self, and the shape supports 0–8 dimensions. Supports non-contiguous tensor. The data format supports ND.value(aclScalar*, computational input): aclScalar on the host side. When value is COMPLEX, self must also be a COMPLEX tensor. There are no other data type restrictions.
reduce(int64_t, computational input): Selects the reduction operation to apply. The available operation options and their corresponding int values are (add, 1), (mul, 2), and (none, 0). The specific operation meanings are as follows:
0: Indicates a replace operation, which replaces the corresponding position in out with value according to index. 1: Indicates an add operation, which adds value to the corresponding position in out according to index. 2: Indicates a multiply operation, which multiplies value into the corresponding position in out according to index.
out(aclTensor*, computational output): The output result of scatter will be stored in this tensor. The data format, data type, and shape must be the same as self.
- Atlas A2 training products/Atlas A2 inference products and Atlas A3 training products/Atlas A3 inference products: Supports UINT8, INT8, INT16, INT32, INT64, BOOL, FLOAT16, FLOAT32, DOUBLE, COMPLEX64, COMPLEX128, and BFLOAT16 data types.
workspaceSize (uint64_t*, output parameter): Returns the workspace size to be allocated on the Device side.
executor (aclOpExecutor, output parameter): Returns the op executor, which includes the operator computation flow.
Return value
aclnnStatus: return code. For details, see aclnn Return Code.
The first-phase API completes input validation. The following scenarios trigger an error: Returns 161001 (ACLNN_ERR_PARAM_NULLPTR): 1. The passed self, index, value, or out is a null pointer. Returns 161002 (ACLNN_ERR_PARAM_INVALID): 1. The data type of self, index, value, or out is not within the supported range. 2. The data types of self and out are different. 3. The numbers of dimensions of self and index are inconsistent. 4. The shapes of self and out are inconsistent. 5. The shapes of self and index do not meet the following restrictions: 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 within [-number of dimensions of self, number of dimensions of self - 1]. 7. The number of dimensions of self exceeds 8. 8. When value is COMPLEX, the data type of self is not COMPLEX.
aclnnScatterValue
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 aclnnScatterValueGetWorkspaceSize.
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.
aclnnInplaceScatterValueGetWorkspaceSize
Parameters:
selfRef(aclTensor*, computational input|computational output):
selfin the formula, a device-side aclTensor. The target tensor for scatter. The number of dimensions of selfRef must be the same as that of index, and the shape supports 0 to 8 dimensions. Supports empty tensors and non-contiguous tensors. The data format supports ND.Atlas A2 training products/Atlas A2 inference products andAtlas A3 training products/Atlas A3 inference products : Supports UINT8, INT8, INT16, INT32, INT64, BOOL, FLOAT16, FLOAT, DOUBLE, COMPLEX64, COMPLEX128, and BFLOAT16 data types.
dim(int64_t, computational input): The dimension along which to scatter, with the data type INT64. The range is [-number of dimensions of selfRef, number of dimensions of selfRef - 1].
index(aclTensor*, computational input):
indexin the formula, a device-side aclTensor. The index tensor, supporting INT32 and INT64 data types. The number of dimensions of index must be the same as that of selfRef, and the shape supports 0 to 8 dimensions. For each dimension d, if d != dim, ensure that index.size(d) <= selfRef.size(d). Supports empty tensors and non-contiguous tensors. The data format supports ND.value (aclScalar*, computational input): When value is COMPLEX, selfRef must also be a COMPLEX tensor. There are no other data type restrictions.
reduce (int64_t, computational input): Selects the reduction operation to apply. The available operation options and their corresponding int values are (add, 1), (mul, 2), (none, 0). The specific operation meanings are as follows:
0: Indicates a replace operation, which replaces the value at the corresponding position in selfRef according to index. 1: Indicates an add operation, which adds the value to the corresponding position in selfRef according to index. 2: Indicates a multiply operation, which multiplies the value at the corresponding position in selfRef according to index.
workspaceSize (uint64_t*, output parameter): 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. The following scenarios trigger an error: Returns 161001 (ACLNN_ERR_PARAM_NULLPTR): 1. The passed selfRef, index, or value is a null pointer. Returns 161002 (ACLNN_ERR_PARAM_INVALID): 1. The data type of selfRef, index, or value is not within the supported range 2. The numbers of dimensions of selfRef and index are inconsistent 3. The shapes of selfRef and index do not meet the following restrictions: For each dimension d, if d != dim, there is a restriction that index.size(d) <= selfRef.size(d). 4. The value of dim is not within [-number of dimensions of selfRef, number of dimensions of selfRef - 1] 5. The number of dimensions of selfRef exceeds 8 6. When value is COMPLEX, the data type of selfRef is not COMPLEX
aclnnInplaceScatterValue
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 aclnnInplaceScatterValueGetWorkspaceSize.
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:
- aclnnScatterValue&aclnnInplaceScatterValue 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.
aclnnScatterValue 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) {
// 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 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 data from the host side to the 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 the aclCreateTensor API 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. For details, see the ACL API manual.
// Set 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. Custom construction is required 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> outShape = {3, 4};
void* selfDeviceAddr = nullptr;
void* indexDeviceAddr = nullptr;
void* outDeviceAddr = nullptr;
aclTensor* self = nullptr;
aclTensor* index = nullptr;
aclScalar* value = 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> outHostData = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
float Value = 1.2f;
// Create the self aclTensor.
ret = CreateAclTensor(selfHostData, selfShape, &selfDeviceAddr, aclDataType::ACL_FLOAT, &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 value aclScalar.
value = aclCreateScalar(&Value, aclDataType::ACL_FLOAT);
CHECK_RET(value != nullptr, return ret);
// Create the 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. Replace with the specific API name.
uint64_t workspaceSize = 0;
aclOpExecutor* executor;
// Call the first-phase API of aclnnScatterValue.
ret = aclnnScatterValueGetWorkspaceSize(self, dim, index, value, reduce, out, &workspaceSize, &executor);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnScatterValueGetWorkspaceSize 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 aclnnScatterValue.
ret = aclnnScatterValue(workspaceAddr, workspaceSize, executor, stream);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnScatterValue 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 device-side memory to host-side memory. Modify according to the specific 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. Destroy aclTensor and aclScalar. Modify according to the specific API definition.
aclDestroyTensor(self);
aclDestroyTensor(index);
aclDestroyScalar(value);
aclDestroyTensor(out);
// 7. Release device resources. Modify according to the specific API definition.
aclrtFree(selfDeviceAddr);
aclrtFree(indexDeviceAddr);
aclrtFree(outDeviceAddr);
if (workspaceSize > 0) {
aclrtFree(workspaceAddr);
}
aclrtDestroyStream(stream);
aclrtResetDevice(deviceId);
aclFinalize();
return 0;
}aclnnInplaceScatterValue 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) {
// Boilerplate for 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-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);
// Compute 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 the aclCreateTensor API 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. See the ACL API manual.
// Enter the 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 the input and output. Customize the construction 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};
void* selfRefDeviceAddr = nullptr;
void* indexDeviceAddr = nullptr;
aclTensor* selfRef = nullptr;
aclTensor* index = nullptr;
aclScalar* value = 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};
float Value = 1.2f;
// Create the selfRef aclTensor.
ret = CreateAclTensor(selfRefHostData, selfRefShape, &selfRefDeviceAddr, aclDataType::ACL_FLOAT, &selfRef);
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 value aclScalar.
value = aclCreateScalar(&Value, aclDataType::ACL_FLOAT);
CHECK_RET(value != nullptr, 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 aclnnInplaceScatterValue.
ret = aclnnInplaceScatterValueGetWorkspaceSize(selfRef, dim, index, value, reduce, &workspaceSize, &executor);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnInplaceScatterValueGetWorkspaceSize 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 aclnnInplaceScatterValue.
ret = aclnnInplaceScatterValue(workspaceAddr, workspaceSize, executor, stream);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnInplaceScatterValue failed. ERROR: %d\n", ret); return ret);
// 4. (Boilerplate) Synchronously 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 memory to the host side. Modify this based on the specific 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 this based on the specific API definition.
aclDestroyTensor(selfRef);
aclDestroyTensor(index);
aclDestroyScalar(value);
// 7. Release device resources. Modify based on the specific API definition.
aclrtFree(selfRefDeviceAddr);
aclrtFree(indexDeviceAddr);
if (workspaceSize > 0) {
aclrtFree(workspaceAddr);
}
aclrtDestroyStream(stream);
aclrtResetDevice(deviceId);
aclFinalize();
return 0;
}