aclnnInplaceRemainderTensorScalar
aclnnInplaceRandom
接口原型
每个算子有两段接口,必须先调用“aclnnXxxGetWorkspaceSize”接口获取入参并根据计算流程计算所需workspace大小,再调用“aclnnXxx”接口执行计算。两段式接口如下:
- 第一段接口:aclnnStatus aclnnInplaceRandomGetWorkspaceSize(const aclTensor *selfRef, int64_t from, int64_t to, int64_t seed, int64_t offset, uint64_t *workspaceSize, aclOpExecutor **executor)
- 第二段接口:aclnnStatus aclnnInplaceRandom(void *workspace, uint64_t workspaceSize, aclOpExecutor *executor, const aclrtStream stream)
功能描述
算子功能:从[from, to-1]的离散均匀分布中,按种子(seed)随机采样数值填充selfRef张量。
aclnnInplaceRandomGetWorkspaceSize
-
接口定义:
aclnnStatus aclnnInplaceRandomGetWorkspaceSize(const aclTensor *selfRef, int64_t from, int64_t to, int64_t seed, int64_t offset, uint64_t *workspaceSize, aclOpExecutor **executor)
-
参数说明:
- selfRef:Device侧的aclTensor,数据类型支持FLOAT、FLOAT16、DOUBLE、INT32、INT64、INT16、INT8、UINT8、BOOL。支持非连续的Tensor,数据格式支持ND。
- from:Host侧的整形,离散均匀分布取值的左边界,from值需要在selfRef的数据类型取值范围内。
- to:Host侧的整形,离散均匀分布取值的右边界,to值需要在selfRef的数据类型取值范围内。
- seed:随机数生成器的种子,它影响生成的随机数序列。
- offset:随机数生成器的偏移量,它影响生成的随机数序列的位置。设置偏移量后,生成的随机数序列会从指定位置开始。
- workspaceSize:返回用户需要在Device侧申请的workspace大小。
- executor:返回op执行器,包含了算子计算流程。
-
返回值:
返回aclnnStatus状态码,具体参见aclnn返回码。
:::note 说明 第一段接口完成入参校验,出现以下场景时报错:
- 返回161001(ACLNN_ERR_PARAM_NULLPTR):传入的selfRef是空指针。
- 返回161002(ACLNN_ERR_PARAM_INVALID):
- selfRef的数据类型和数据格式不在支持的范围内。
- 参数from取值≥to取值。
- from或者(to-1)取值超出selfRef数据类型的取值范围。 :::
aclnnInplaceRandom
-
接口定义:
aclnnStatus aclnnInplaceRandom(void *workspace, uint64_t workspaceSize, aclOpExecutor *executor, const aclrtStream stream)
-
参数说明:
- workspace:在Device侧申请的workspace内存起址。
- workspaceSize:在Device侧申请的workspace大小,由第一段接口aclnnInplaceRandomGetWorkspaceSize获取。
- executor:op执行器,包含了算子计算流程。
- stream:指定执行任务的AscendCL stream流。
-
返回值:
返回aclnnStatus状态码,具体参见aclnn返回码。
调用示例
#include <vector>
#include "acl/acl.h"
#include "aclnnop/aclnn_random.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, aclrtContext* context, aclrtStream* stream) {
// 固定写法,AscendCL初始化
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 = aclrtCreateContext(context, deviceId);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtCreateContext failed. ERROR: %d\n", ret); return ret);
ret = aclrtSetCurrentContext(*context);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtSetCurrentContext 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);
// 调用aclrtMalloc申请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);
// 调用aclrtMemcpy将Host侧数据拷贝到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);
// 计算连续tensor的strides
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];
}
// 调用aclCreateTensor接口创建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. (固定写法)device/context/stream初始化,参考AscendCL对外接口列表
// 根据自己的实际device填写deviceId
int32_t deviceId = 0;
aclrtContext context;
aclrtStream stream;
auto ret = Init(deviceId, &context, &stream);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("Init acl failed. ERROR: %d\n", ret); return ret);
// 2. 构造输入与输出,需要根据API的接口自定义构造
std::vector<int64_t> selfShape = {4, 2};
void* selfDeviceAddr = nullptr;
aclTensor* self = nullptr;
std::vector<float> selfHostData = {1,2,3,4,5,6,7,8};
int64_t from = 0;
int64_t to = 10;
int64_t seed = 1234;
int64_t offset = 0;
// 创建self aclTensor
ret = CreateAclTensor(selfHostData, selfShape, &selfDeviceAddr, aclDataType::ACL_FLOAT, &self);
CHECK_RET(ret == ACL_SUCCESS, return ret);
// 3. 调用CANN算子库API,需要修改为具体的API名称
uint64_t workspaceSize = 0;
aclOpExecutor* executor;
// 调用aclnnInplaceRandom第一段接口
ret = aclnnInplaceRandomGetWorkspaceSize(self, from, to, seed, offset, &workspaceSize, &executor);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnInplaceRandomGetWorkspaceSize failed. ERROR: %d\n", ret); return ret);
// 根据第一段接口计算出的workspaceSize申请device内存
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);
}
// 调用aclnnInplaceRandom第二段接口
ret = aclnnInplaceRandom(workspaceAddr, workspaceSize, executor, stream);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnInplaceRandom failed. ERROR: %d\n", ret); return ret);
// 4. (固定写法)同步等待任务执行结束
ret = aclrtSynchronizeStream(stream);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtSynchronizeStream failed. ERROR: %d\n", ret); return ret);
// 5. 获取输出的值,将device侧内存上的结果拷贝至Host侧,需要根据具体API的接口定义修改
auto size = GetShapeSize(selfShape);
std::vector<float> resultData(size, 0);
ret = aclrtMemcpy(resultData.data(), resultData.size() * sizeof(resultData[0]), selfDeviceAddr,
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. 释放aclTensor和aclScalar,需要根据具体API的接口定义修改
aclDestroyTensor(self);
return 0;
}
父主题: NN类算子接口
aclnnInplaceReciprocal
接口原型
每个算子有两段接口,必须先调用“aclnnXxxGetWorkspaceSize”接口获取入参并根据计算流程计算所需workspace大小,再调用“aclnnXxx”接口执行计算。两段式接口如下:
- 第一段接口:aclnnStatus aclnnInplaceReciprocalGetWorkspaceSize(const aclTensor *selfRef, uint64_t *workspaceSize, aclOpExecutor **executor)
- 第二段接口:aclnnStatus aclnnInplaceReciprocal(void *workspace, uint64_t workspaceSize, aclOpExecutor *executor, aclrtStream stream)
功能描述
-
算子功能:计算张量每个元素的倒数,并返回一个新张量。
-
计算公式:
-
示例:
x = tensor([1.00, 2.00, 3.00, 4.00])// 经过reciprocal计算后x = tensor([1.00, 0.50, 0.33, 0.25])
aclnnInplaceReciprocalGetWorkspaceSize
-
接口定义:
aclnnStatus aclnnInplaceReciprocalGetWorkspaceSize(const aclTensor *selfRef, uint64_t *workspaceSize, aclOpExecutor **executor)
-
参数说明:
- selfRef:Device侧的aclTensor,数据类型支持FLOAT16、FLOAT32、BFLOAT16(仅Atlas A2训练系列产品支持)。支持非连续的Tensor,支持空Tensor传入,数据格式支持ND。
- workspaceSize:返回用户需要在Device侧申请的workspace大小。
- executor:返回op执行器,包含了算子计算流程。
-
返回值:
返回aclnnStatus状态码,具体参见aclnn返回码。
:::note 说明 第一段接口完成入参校验,出现以下场景时报错:
- 返回161001(ACLNN_ERR_PARAM_NULLPTR):传入的selfRef是空指针。
- 返回161002(ACLNN_ERR_PARAM_INVALID):
- selfRef的数据类型和数据格式不在支持的范围内。
- selfRef的维度超过8维。 :::
aclnnInplaceReciprocal
-
接口定义:
aclnnStatus aclnnInplaceReciprocal(void *workspace, uint64_t workspaceSize, aclOpExecutor *executor, aclrtStream stream)
-
参数说明:
- workspace:在Device侧申请的workspace内存起址。
- workspaceSize:在Device侧申请的workspace大小,由第一段接口aclnnInplaceReciprocalGetWorkspaceSize获取。
- executor:op执行器,包含了算子计算流程。
- stream:指定执行任务的AscendCL stream流。
-
返回值:
返回aclnnStatus状态码,具体参见aclnn返回码。
调用示例
#include <iostream>
#include <vector>
#include "acl/acl.h"
#include "aclnnop/aclnn_reciprocal.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, aclrtContext* context, aclrtStream* stream) {
// 固定写法,AscendCL初始化
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 = aclrtCreateContext(context, deviceId);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtCreateContext failed. ERROR: %d\n", ret); return ret);
ret = aclrtSetCurrentContext(*context);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtSetCurrentContext 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);
// 调用aclrtMalloc申请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);
// 调用aclrtMemcpy将Host侧数据拷贝到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);
// 计算连续tensor的strides
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];
}
// 调用aclCreateTensor接口创建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. (固定写法)device/context/stream初始化,参考AscendCL对外接口列表
// 根据自己的实际device填写deviceId
int32_t deviceId = 0;
aclrtContext context;
aclrtStream stream;
auto ret = Init(deviceId, &context, &stream);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("Init acl failed. ERROR: %d\n", ret); return ret);
// 2. 构造输入与输出,需要根据API的接口自定义构造
std::vector<int64_t> selfRefShape = {1, 2, 4};
void* selfRefDeviceAddr = nullptr;
aclTensor* selfRef = nullptr;
std::vector<float> selfRefHostData = {0, 1, 2, 3, 4, 5, 6, 7};
// 创建selfRef aclTensor
ret = CreateAclTensor(selfRefHostData, selfRefShape, &selfRefDeviceAddr, aclDataType::ACL_FLOAT, &selfRef);
CHECK_RET(ret == ACL_SUCCESS, return ret);
// 3. 调用CANN算子库API,需要修改为具体的API名称
uint64_t workspaceSize = 0;
aclOpExecutor* executor;
// 调用aclnnInplaceReciprocal第一段接口
ret = aclnnInplaceReciprocalGetWorkspaceSize(selfRef, &workspaceSize, &executor);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnInplaceReciprocalGetWorkspaceSize failed. ERROR: %d\n", ret); return ret);
// 根据第一段接口计算出的workspaceSize申请device内存
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);
}
// 调用aclnnInplaceReciprocal第二段接口
ret = aclnnInplaceReciprocal(workspaceAddr, workspaceSize, executor, stream);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnInplaceReciprocal failed. ERROR: %d\n", ret); return ret);
// 4. (固定写法)同步等待任务执行结束
ret = aclrtSynchronizeStream(stream);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtSynchronizeStream failed. ERROR: %d\n", ret); return ret);
// 5. 获取输出的值,将device侧内存上的结果拷贝至Host侧,需要根据具体API的接口定义修改
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. 释放aclTensor和aclScalar,需要根据具体API的接口定义修改
aclDestroyTensor(selfRef);
return 0;
}
父主题: NN类算子接口
aclnnInplaceRemainderTensorScalar
接口原型
每个算子有两段接口,必须先调用“aclnnXxxGetWorkspaceSize”接口获取入参并根据计算流程计算所需workspace大小,再调用“aclnnXxx”接口执行计算。两段式接口如下:
- **第一段接口:**aclnnStatus aclnnInplaceRemainderTensorScalarGetWorkspaceSize(aclTensor *selfRef, const aclScalar *other, uint64_t *workspaceSize, aclOpExecutor **executor)
- **第二段接口:**aclnnStatus aclnnInplaceRemainderTensorScalar(void *workspace, uint64_t workspaceSize, aclOpExecutor *executor, aclrtStream stream)
功能描述
-
算子功能:将selfRef(张量)中每个元素都转换为除以other(标量)后得到的余数。该结果与除数other同符号,并且该结果的绝对值是小于other的绝对值。
-
计算公式:
-
示例:
selfRef = tensor([[-1, -2], [-3, -4]]).type(torch.int32)other = 3.5 # floatselfRef = remainder(selfRef, other)# selfRef的值# tensor([[2.5000, 1.5000],# [0.5000, 3.0000]])# 对于元素selfRef中的-1来说,计算结果为 -1 - floor(-1 / 3.5) * 3.5 = 2.5# 可以看到,最终结果2.5的绝对值小于other 3.5。
aclnnInplaceRemainderTensorScalarGetWorkspaceSize
-
接口定义:
aclnnStatus aclnnInplaceRemainderTensorScalarGetWorkspaceSize(aclTensor *selfRef, const aclScalar *other, uint64_t *workspaceSize, aclOpExecutor **executor)
-
参数说明:
- selfRef:Device侧的aclTensor,输入/输出张量,数据类型与other的数据类型需满足数据类型推导规则,推导后的数据类型支持INT32、INT64、FLOAT16、FLOAT、DOUBLE,该数据类型必须能转换为selfRef的数据类型。支持非连续的Tensor,数据格式支持ND。
- other:Host侧的aclScalar,数据类型与selfRef的数据类型需满足数据类型推导规则,且推导出的数据类型必须能转换为selfRef的数据类型。
- workspaceSize:返回用户需要在Device侧申请的workspace大小。
- executor:返回op执行器,包含了算子计算流程。
-
返回值:
返回aclnnStatus状态码,具体参见aclnn返回码。
:::note 说明 第一段接口完成入参校验,出现以下场景时报错:
- 返回161001(ACLNN_ERR_PARAM_NULLPTR):传入的selfRef、other是空指针。
- 返回161002(ACLNN_ERR_PARAM_INVALID):
- selfRef与other不能推导出数据类型。
- selfRef与other推导出的数据类型不属于支持的数据类型。
- selfRef和other推导出的数据类型无法转换为selfRef的类型。
- selfRef的维度数大于8维。 :::
aclnnInplaceRemainderTensorScalar
-
接口定义:
aclnnStatus aclnnInplaceRemainderTensorScalar(void *workspace, uint64_t workspaceSize, aclOpExecutor *executor, aclrtStream stream)
-
参数说明:
- workspace:在Device侧申请的workspace内存起址。
- workspaceSize:在Device侧申请的workspace大小,由第一段接口aclnnInplaceRemainderTensorScalarGetWorkspaceSize获取。
- executor:op执行器,包含了算子计算流程。
- stream:指定执行任务的AscendCL stream流。
-
返回值:
返回aclnnStatus状态码,具体参见aclnn返回码。
调用示例
#include <iostream>
#include <vector>
#include "acl/acl.h"
#include "aclnnop/aclnn_remainder.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, aclrtContext* context, aclrtStream* stream) {
// 固定写法,AscendCL初始化
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 = aclrtCreateContext(context, deviceId);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtCreateContext failed. ERROR: %d\n", ret); return ret);
ret = aclrtSetCurrentContext(*context);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtSetCurrentContext 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);
// 调用aclrtMalloc申请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);
// 调用aclrtMemcpy将Host侧数据拷贝到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);
// 计算连续tensor的strides
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];
}
// 调用aclCreateTensor接口创建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. (固定写法)device/context/stream初始化,参考AscendCL对外接口列表
// 根据自己的实际device填写deviceId
int32_t deviceId = 0;
aclrtContext context;
aclrtStream stream;
auto ret = Init(deviceId, &context, &stream);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("Init acl failed. ERROR: %d\n", ret); return ret);
// 2. 构造输入与输出,需要根据API的接口自定义构造
std::vector<int64_t> selfRefShape = {3, 3};
void* selfRefDeviceAddr = nullptr;
aclTensor* selfRef = nullptr;
aclScalar* other = nullptr;
std::vector<int64_t> selfRefHostData = {0, 1, 2, 3, 4, 5, 6, 7, 8};
int64_t Other = 3;
// 创建self aclTensor
ret = CreateAclTensor(selfRefHostData, selfRefShape, &selfRefDeviceAddr, aclDataType::ACL_INT64, &selfRef);
CHECK_RET(ret == ACL_SUCCESS, return ret);
// 创建other aclScalar
other = aclCreateScalar(&Other, aclDataType::ACL_INT64);
CHECK_RET(other != nullptr, return ret);
// 3. 调用CANN算子库API,需要修改为具体的API名称
uint64_t workspaceSize = 0;
aclOpExecutor* executor;
// 调用aclnnInplaceRemainderTensorScalar第一段接口
ret = aclnnInplaceRemainderTensorScalarGetWorkspaceSize(selfRef, other, &workspaceSize, &executor);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnInplaceRemainderTensorScalarGetWorkspaceSize failed. ERROR: %d\n", ret); return ret);
// 根据第一段接口计算出的workspaceSize申请device内存
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);
}
// 调用aclnnInplaceRemainderTensorScalar第二段接口
ret = aclnnInplaceRemainderTensorScalar(workspaceAddr, workspaceSize, executor, stream);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnInplaceRemainderTensorScalar failed. ERROR: %d\n", ret); return ret);
// 4. (固定写法)同步等待任务执行结束
ret = aclrtSynchronizeStream(stream);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtSynchronizeStream failed. ERROR: %d\n", ret); return ret);
// 5. 获取输出的值,将device侧内存上的结果拷贝至Host侧,需要根据具体API的接口定义修改
auto size = GetShapeSize(selfRefShape);
std::vector<int64_t> 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: %ld\n", i, resultData[i]);
}
// 6. 释放aclTensor和aclScalar,需要根据具体API的接口定义修改
aclDestroyTensor(selfRef);
aclDestroyScalar(other);
return 0;
}
父主题: NN类算子接口
aclnnInplaceRemainderTensorTensor
接口原型
每个算子有两段接口,必须先调用“aclnnXxxGetWorkspaceSize”接口获取入参并根据计算流程计算所需workspace大小,再调用“aclnnXxx”接口执行计算。两段式接口如下:
- **第一段接口:**aclnnStatus aclnnInplaceRemainderTensorTensorGetWorkspaceSize(aclTensor* selfRef, const aclTensor *other, uint64_t *workspaceSize, aclOpExecutor **executor)
- **第二段接口:**aclnnStatus aclnnInplaceRemainderTensorTensor(void *workspace, uint64_t workspaceSize, aclOpExecutor *executor, aclrtStream stream)
功能描述
-
算子功能:将selfRef(张量)broadcast成和other(张量)一样的shape后,将其每个元素都转换为除以other对应元素后得到的余数。该结果与除数other同符号,并且该结果的绝对值是小于other的绝对值。
-
计算公式:
-
示例:
selfRef = tensor([[-1, -2], [-3, -4]]).type(torch.float16)other = tensor([-3, -3]).type(torch.int64)selfRef = remainder(selfRef, other)# selfRef的值# tensor([[-1., -2.],# [-0., -1.]], dtype=torch.float16)# 首先是将other broadcast成和selfRef一致的shape,成为 [[-3, -3], [-3, -3]],然后再进行计算。# 对于元素selfRef中的-3来说,计算结果为 (-3) % (-3) = 0# 可以看到,最终结果0的绝对值小于原来的-3的绝对值。
aclnnInplaceRemainderTensorTensorGetWorkspaceSize
-
接口定义:
aclnnStatus aclnnInplaceRemainderTensorTensorGetWorkspaceSize(aclTensor* selfRef, const aclTensor *other, uint64_t *workspaceSize, aclOpExecutor **executor)
-
参数说明:
- selfRef:Device侧的aclTensor,数据类型与other的数据类型需满足数据类型推导规则,且推导出的数据类型支持INT32、INT64、FLOAT16、FLOAT、DOUBLE,且需要是推导之后可转换为selfRef的数据类型。shape需要与other满足broadcast关系,且shape与最终broadcast后的shape一致。支持非连续的Tensor,数据格式支持ND。
- other:Device侧的aclTensor, 数据类型与selfRef的数据类型需满足数据类型推导规则,且推导出的数据类型支持INT32、INT64、FLOAT16、FLOAT、DOUBLE。shape需要与selfRef满足broadcast关系,支持非连续的Tensor,数据格式支持ND。
- workspaceSize:返回用户需要在Device侧申请的workspace大小。
- executor:返回op执行器,包含了算子计算流程。
-
返回值:
返回aclnnStatus状态码,具体参见aclnn返回码。
:::note 说明 第一段接口完成入参校验,出现以下场景时报错:
- 返回161001(ACLNN_ERR_PARAM_NULLPTR):传入的selfRef、other是空指针。
- 返回161002(ACLNN_ERR_PARAM_INVALID):
- selfRef和other无法做数据类型推导。
- selfRef和other推导出的数据类型不属于支持的数据类型。
- selfRef和other推导出的数据类型无法转换为selfRef的类型。
- selfRef和other的shape无法做broadcast。
- selfRef和other broadcast以后的shape与selfRef的shape不一致。
- selfRef、other的维度数大于8维。 :::
aclnnInplaceRemainderTensorTensor
-
接口定义:
aclnnStatus aclnnInplaceRemainderTensorTensor(void *workspace, uint64_t workspaceSize, aclOpExecutor *executor, aclrtStream stream)
-
参数说明:
- workspace:在Device侧申请的workspace内存起址。
- workspaceSize:在Device侧申请的workspace大小,由第一段接口aclnnInplaceRemainderTensorTensorGetWorkspaceSize获取。
- executor:op执行器,包含了算子计算流程。
- stream:指定执行任务的AscendCL stream流。
-
返回值:
返回aclnnStatus状态码,具体参见aclnn返回码。
调用示例
#include <iostream>
#include <vector>
#include "acl/acl.h"
#include "aclnnop/aclnn_remainder.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, aclrtContext* context, aclrtStream* stream) {
// 固定写法,AscendCL初始化
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 = aclrtCreateContext(context, deviceId);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtCreateContext failed. ERROR: %d\n", ret); return ret);
ret = aclrtSetCurrentContext(*context);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtSetCurrentContext 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);
// 调用aclrtMalloc申请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);
// 调用aclrtMemcpy将Host侧数据拷贝到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);
// 计算连续tensor的strides
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];
}
// 调用aclCreateTensor接口创建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. (固定写法)device/context/stream初始化,参考AscendCL对外接口列表
// 根据自己的实际device填写deviceId
int32_t deviceId = 0;
aclrtContext context;
aclrtStream stream;
auto ret = Init(deviceId, &context, &stream);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("Init acl failed. ERROR: %d\n", ret); return ret);
// 2. 构造输入与输出,需要根据API的接口自定义构造
std::vector<int64_t> selfRefShape = {3, 3};
std::vector<int64_t> otherShape = {3, 3};
void* selfRefDeviceAddr = nullptr;
void* otherDeviceAddr = nullptr;
aclTensor* selfRef = nullptr;
aclTensor* other = nullptr;
std::vector<int64_t> selfRefHostData = {1, 2, 3, 4, 5, 6, 7, 8, 9};
std::vector<int64_t> otherHostData = {0, 1, 2, 3, 4, 5, 6, 7, 8};
// 创建selfRef aclTensor
selfRef = CreateAclTensor(selfRefHostData, selfRefShape, &selfRefDeviceAddr, aclDataType::ACL_INT64, &selfRef);
CHECK_RET(selfRef != nullptr, return ret);
// 创建other aclTensor
ret = CreateAclTensor(otherHostData, otherShape, &otherDeviceAddr, aclDataType::ACL_INT64, &other);
CHECK_RET(ret == ACL_SUCCESS, return ret);
// 3. 调用CANN算子库API,需要修改为具体的API名称
uint64_t workspaceSize = 0;
aclOpExecutor* executor;
// 调用aclnnInplaceRemainderTensorTensor第一段接口
ret = aclnnInplaceRemainderTensorTensorGetWorkspaceSize(selfRef, other, &workspaceSize, &executor);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnInplaceRemainderTensorTensorGetWorkspaceSize failed. ERROR: %d\n", ret); return ret);
// 根据第一段接口计算出的workspaceSize申请device内存
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);
}
// 调用aclnnInplaceRemainderTensorTensor第二段接口
ret = aclnnInplaceRemainderTensorTensor(workspaceAddr, workspaceSize, executor, stream);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnInplaceRemainderTensorTensor failed. ERROR: %d\n", ret); return ret);
// 4. (固定写法)同步等待任务执行结束
ret = aclrtSynchronizeStream(stream);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtSynchronizeStream failed. ERROR: %d\n", ret); return ret);
// 5. 获取输出的值,将device侧内存上的结果拷贝至Host侧,需要根据具体API的接口定义修改
auto size = GetShapeSize(selfRefShape);
std::vector<int64_t> 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: %ld\n", i, resultData[i]);
}
// 6. 释放aclTensor和aclScalar,需要根据具体API的接口定义修改
aclDestroyTensor(selfRef);
aclDestroyTensor(other);
return 0;
}
父主题: NN类算子接口
aclnnInplaceRenorm
接口原型
每个算子有两段接口,必须先调用“aclnnXxxGetWorkspaceSize”接口获取入参并根据计算流程计算所需workspace大小,再调用“aclnnXxx”接口执行计算。两段式接口如下:
- **第一段接口:**aclnnStatus aclnnInplaceRenormGetWorkspaceSize(aclTensor *selfRef, const aclScalar *p, int64_t dim, const aclScalar maxNorm, uint64_t *workspaceSize, aclOpExecutor **executor)
- **第二段接口:**aclnnStatus aclnnInplaceRenorm(void *workspace, uint64_t workspaceSize, aclOpExecutor *executor, aclrtStream stream)
功能描述
-
算子功能:返回一个张量,其中输入张量selfRef沿维度dim的每个子张量都经过归一化,使得子张量的p范数低于maxNorm值。
-
计算公式:

其中i维dim确定的某维度张量切片为
-
示例:
selfRef = tensor([[1., 1., 1.],[2., 2., 2.],[3., 3., 3.]])# 这里p=1,dim=0,maxnorm=5torch.renorm(selfRef, 1, 0, 5)# 因为dim=0,所以以行(第0维)维单位进行判断计算;# 第一行子张量的范数是1+1+1=3,小于5,因此该子张量不变;# 第二行子张量的范数是2+2+2=6,大于5,因此该子张量进行计算,(2/6)*5=1.6667;# 第三行子张量的范数是3+3+3=9,大于5,因此该子张量进行计算,(3/9)*5=1.6667;tensor([[ 1.0000, 1.0000, 1.0000],[ 1.6667, 1.6667, 1.6667],[ 1.6667, 1.6667, 1.6667]])# 若p=2,则第一行子张量的范数计算时变更为(1+1+1)开平方根(即1.73),# 同理第二行变为(2*2+2*2+2*2)开平方根(即3.46)、第三行变为(3*3+3*3+3*3)开平方根(即5.19)
aclnnInplaceRenormGetWorkspaceSize
-
接口定义:
aclnnStatus aclnnInplaceRenormGetWorkspaceSize(aclTensor *selfRef, const aclScalar *p, int64_t dim, const aclScalar maxNorm, uint64_t *workspaceSize, aclOpExecutor **executor)
-
参数说明:
- selfRef:Device侧的aclTensor,输入/输出张量,数据类型支持FLOAT、FLOAT16,支持非连续的Tensor,支持空Tensor传入,数据格式支持ND,shape维度不超过8。
- p:Host侧的aclScalar,表示范数,取值仅支持0.0f、1.0f、2.0f、3.0f,数据类型支持FLOAT。
- dim:Host侧的整型,指定求norm的维度方向,取值范围为[-selfRef.dim(), selfRef.dim()),数据类型支持INT64。
- maxNorm:Host侧的aclScalar,表示最大允许的归一化值,取值要求≥0,数据类型支持FLOAT。如果对应维度的p范数(由p值确定)大于maxNorm,则将该维度的值关于p范数归一化并乘上maxNorm;如果对应维度的p范数(由p值确定)小于等于maxNorm,则该维度张量保持不变输出。
- workspaceSize:返回用户需要在Device侧申请的workspace大小。
- executor:返回op执行器,包含了算子计算流程。
-
返回值:
返回aclnnStatus状态码,具体参见aclnn返回码。
:::note 说明 第一段接口完成入参校验,出现以下场景时报错:
- 返回161001(ACLNN_ERR_PARAM_NULLPTR):传入的selfRef、p、maxNorm是空指针。
- 返回161002(ACLNN_ERR_PARAM_INVALID):
- selfRef和out的数据类型或数据格式不在支持的范围之内。
- p值不等于0.0f、1.0f、2.0f、3.0f。
- dim的值不在支持的范围内。
- maxNorm取值小于0。
- selfRef的维度超过8。 :::
aclnnInplaceRenorm
-
接口定义:
aclnnStatus aclnnInplaceRenorm(void *workspace, uint64_t workspaceSize, aclOpExecutor *executor, aclrtStream stream)
-
参数说明:
- workspace:在Device侧申请的workspace内存起址。
- workspaceSize:在Device侧申请的workspace大小,由第一段接口aclnnInplaceRenormGetWorkspaceSize获取。
- executor:op执行器,包含了算子计算流程。
- stream:指定执行任务的AscendCL stream流。
-
返回值:
返回aclnnStatus状态码,具体参见aclnn返回码。
调用示例
#include <iostream>
#include <vector>
#include "acl/acl.h"
#include "aclnnop/aclnn_renorm.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, aclrtContext* context, aclrtStream* stream) {
// 固定写法,AscendCL初始化
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 = aclrtCreateContext(context, deviceId);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtCreateContext failed. ERROR: %d\n", ret); return ret);
ret = aclrtSetCurrentContext(*context);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtSetCurrentContext 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);
// 调用aclrtMalloc申请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);
// 调用aclrtMemcpy将Host侧数据拷贝到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);
// 计算连续tensor的strides
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];
}
// 调用aclCreateTensor接口创建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. (固定写法)device/context/stream初始化,参考AscendCL对外接口列表
// 根据自己的实际device填写deviceId
int32_t deviceId = 0;
aclrtContext context;
aclrtStream stream;
auto ret = Init(deviceId, &context, &stream);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("Init acl failed. ERROR: %d\n", ret); return ret);
// 2. 构造输入与输出,需要根据API的接口自定义构造
std::vector<int64_t> selfRefShape = {3, 3};
void* selfRefDeviceAddr = nullptr;
aclTensor* selfRef = nullptr;
aclScalar* p = nullptr;
aclScalar* maxNorm = nullptr;
std::vector<float> selfRefHostData = {1, 1, 1, 2, 2, 2, 3, 3, 3};
int64_t dim = -1;
float pValue = 1.0f;
float maxNormValue = 5.0f;
// 创建selfRef aclTensor
ret = CreateAclTensor(selfRefHostData, selfRefShape, &selfRefDeviceAddr, aclDataType::ACL_FLOAT, &selfRef);
CHECK_RET(ret == ACL_SUCCESS, return ret);
// 创建p aclScalar
p = aclCreateScalar(&pValue,aclDataType::ACL_FLOAT);
CHECK_RET(p != nullptr, return ret);
// 创建maxNorm aclScalar
maxNorm = aclCreateScalar(&maxNormValue,aclDataType::ACL_FLOAT);
CHECK_RET(maxNorm != nullptr, return ret);
// 3. 调用CANN算子库API,需要修改为具体的API名称
uint64_t workspaceSize = 0;
aclOpExecutor* executor;
// 调用aclnnInplaceRenorm第一段接口
ret = aclnnInplaceRenormGetWorkspaceSize(selfRef, p, dim, maxNorm, &workspaceSize, &executor);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnInplaceRenormGetWorkspaceSize failed. ERROR: %d\n", ret); return ret);
// 根据第一段接口计算出的workspaceSize申请device内存
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);
}
// 调用aclnnInplaceRenorm第二段接口
ret = aclnnInplaceRenorm(workspaceAddr, workspaceSize, executor, stream);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnInplaceRenorm failed. ERROR: %d\n", ret); return ret);
// 4. (固定写法)同步等待任务执行结束
ret = aclrtSynchronizeStream(stream);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtSynchronizeStream failed. ERROR: %d\n", ret); return ret);
// 5. 获取输出的值,将device侧内存上的结果拷贝至Host侧,需要根据具体API的接口定义修改
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. 释放aclTensor和aclScalar,需要根据具体API的接口定义修改
aclDestroyTensor(selfRef);
aclDestroyScalar(p);
aclDestroyScalar(maxNorm);
return 0;
}
父主题: NN类算子接口
aclnnInplaceScatter
接口原型
每个算子有两段接口,必须先调用“aclnnXxxGetWorkspaceSize”接口获取入参并根据计算流程计算所需workspace大小,再调用“aclnnXxx”接口执行计算。两段式接口如下:
- **第一段接口:**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)
功能描述
-
算子功能:将源张量src中的值按指定轴方向和对应的位置关系逐个填入张量selfRef中。
-
示例:
对于一个3D tensor, selfRef会按照如下规则进行更新:
selfRef[index[i][j][k]][j][k] = src[i][j][k] # 如果dim == 0selfRef[i][index[i][j][k]][k] = src[i][j][k] # 如果dim == 1selfRef[i][j][index[i][j][k]] = src[i][j][k] # 如果dim == 2在计算时需要满足以下要求:
- selfRef、index和src的维度数量必须相同。
- 对于每一个维度d,需满足index.size(d)≤src.size(d)。
- 对于每一个维度d,如果d!=dim,需满足index.size(d)≤selfRef.size(d)。
- dim的值大小必须在[-selfRef的维度数量, selfRef的维度数量-1]之间。
- selfRef的维度数应该≤8。
- index中对应维度dim的值必须在[0, selfRef.size(dim)-1]之间。
aclnnInplaceScatterGetWorkspaceSize
-
接口定义:
aclnnStatus aclnnInplaceScatterGetWorkspaceSize(aclTensor *selfRef, int64_t dim, const aclTensor *index, const aclTensor *src, int64_t reduce, uint64_t *workspaceSize, aclOpExecutor **executor)
-
参数说明:
- selfRef:Device侧的aclTensor,输入/输出张量,数据类型支持UINT8、INT8、INT16、INT32、INT64、BOOL、FLOAT16、FLOAT、DOUBLE、COMPLEX64、COMPLEX128。selfRef的维度数量需要与index、src相同,并且不超过8。selfRef的数据类型需要与src一致。支持空Tensor,支持非连续的Tensor。数据格式支持ND。
- dim:指定scatter的维度,数据类型为INT64,取值范围为[-selfRef.dim(), selfRef.dim()-1]。
- index:Device侧的aclTensor,数据类型支持INT32、INT64。index的维度数量需要与selfRef、src相同。支持空Tensor,支持非连续的Tensor。数据格式支持ND。
- src:Device侧的aclTensor,数据类型支持UINT8、INT8、INT16、INT32、INT64、BOOL、FLOAT16、FLOAT、DOUBLE、COMPLEX64、COMPLEX128。src的维度数量需要与selfRef、index相同。src的数据类型需要与selfRef一致。支持空Tensor,支持非连续的Tensor。数据格式支持ND。
- reduce:选择应用的reduction操作。可选的操作选项以及对应的int值为 (add, 1), (mul, 2),(none, 0):
- 0:表示替换操作,将src中对应位置的值按照index替换到selfRef的对应位置。
- 1:表示累加操作,将src中对应位置的值按照index累加到selfRef的对应位置。
- 2:表示累乘操作,将src中对应位置的值按照index累乘到selfRef的对应位置。
- workspaceSize:返回用户需要在Device侧申请的workspace大小。
- executor:返回op执行器,包含了算子计算流程。
-
返回值:
返回aclnnStatus状态码,具体参见aclnn返回码。
:::note 说明 第一段接口完成入参校验,出现以下场景时报错:
- 返回161001(ACLNN_ERR_PARAM_NULLPTR):传入的selfRef、index、src是空指针。
- 返回161002(ACLNN_ERR_PARAM_INVALID):
- selfRef、index、src的数据类型不在支持范围内。
- selfRef、src的数据类型不一样。
- selfRef、index、src的维度数不一致。
- selfRef、index、src的shape不满足限制:对于每一个维度d,需满足index.size(d)≤src.size(d);若d!=dim,需满足index.size(d)<=selfRef.size(d)。
- dim值不在支持的范围内。
- selfRef的维度数超过8。 :::
aclnnInplaceScatter
-
接口定义:
aclnnStatus aclnnInplaceScatter(void *workspace, uint64_t workspaceSize, aclOpExecutor *executor, aclrtStream stream)
-
参数说明:
- workspace:在Device侧申请的workspace内存起址。
- workspaceSize:在Device侧申请的workspace大小,由第一段接口aclnnInplaceScatterGetWorkspaceSize获取。
- executor:op执行器,包含了算子计算流程。
- stream:指定执行任务的AscendCL stream流。
-
返回值:
返回aclnnStatus状态码,具体参见aclnn返回码。
调用示例
#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, aclrtContext* context, aclrtStream* stream) {
// 固定写法,AscendCL初始化
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 = aclrtCreateContext(context, deviceId);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtCreateContext failed. ERROR: %d\n", ret); return ret);
ret = aclrtSetCurrentContext(*context);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtSetCurrentContext 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);
// 调用aclrtMalloc申请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);
// 调用aclrtMemcpy将Host侧数据拷贝到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);
// 计算连续tensor的strides
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];
}
// 调用aclCreateTensor接口创建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. (固定写法)device/context/stream初始化,参考AscendCL对外接口列表
// 根据自己的实际device填写deviceId
int32_t deviceId = 0;
aclrtContext context;
aclrtStream stream;
auto ret = Init(deviceId, &context, &stream);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("Init acl failed. ERROR: %d\n", ret); return ret);
// 2. 构造输入与输出,需要根据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};
// 创建selfRef aclTensor
ret = CreateAclTensor(selfRefHostData, selfRefShape, &selfRefDeviceAddr, aclDataType::ACL_FLOAT, &selfRef);
CHECK_RET(ret == ACL_SUCCESS, return ret);
// 创建index aclTensor
ret = CreateAclTensor(indexHostData, indexShape, &indexDeviceAddr, aclDataType::ACL_INT64, &index);
CHECK_RET(ret == ACL_SUCCESS, return ret);
// 创建src aclTensor
ret = CreateAclTensor(srcHostData, srcShape, &srcDeviceAddr, aclDataType::ACL_FLOAT, &src);
CHECK_RET(ret == ACL_SUCCESS, return ret);
// 3. 调用CANN算子库API,需要修改为具体的API名称
uint64_t workspaceSize = 0;
aclOpExecutor* executor;
// 调用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);
// 根据第一段接口计算出的workspaceSize申请device内存
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);
}
// 调用aclnnInplaceScatter第二段接口
ret = aclnnInplaceScatter(workspaceAddr, workspaceSize, executor, stream);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnInplaceScatter failed. ERROR: %d\n", ret); return ret);
// 4. (固定写法)同步等待任务执行结束
ret = aclrtSynchronizeStream(stream);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtSynchronizeStream failed. ERROR: %d\n", ret); return ret);
// 5. 获取输出的值,将device侧内存上的结果拷贝至Host侧,需要根据具体API的接口定义修改
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. 释放aclTensor和aclScalar,需要根据具体API的接口定义修改
aclDestroyTensor(selfRef);
aclDestroyTensor(index);
aclDestroyTensor(src);
return 0;
}
父主题: NN类算子接口
在线提单