下载
中文
注册

aclnnAddcmul/aclnnInplaceAddcmul

接口原型

  • aclnnAddcmul和aclnnInplaceAddcmul实现相同的功能,其使用区别如下,请根据自身实际场景选择合适的算子。
    • aclnnAddcmul:需新建一个输出张量对象存储计算结果。
    • aclnnInplaceAddcmul:无需新建输出张量对象,直接在输入张量的内存中存储计算结果。
  • 每个算子分为两段接口,必须先调用“aclnnXxxGetWorkspaceSize”接口获取入参并根据计算流程计算所需workspace大小,再调用“aclnnXxx”接口执行计算。
  • aclnnAddcmul两段式接口如下:
    • 第一段接口:aclnnStatus aclnnAddcmulGetWorkspaceSize(const aclTensor *self, const aclTensor *tensor1, const aclTensor *tensor2, const aclScalar *value, const aclTensor *out, uint64_t *workspaceSize, aclOpExecutor** executor)
    • 第二段接口:aclnnStatus aclnnAddcmul(void *workspace, uint64_t workspaceSize, aclOpExecutor **executor, const aclrtStream stream)
  • aclnnInplaceAddcmul两段式接口如下:
    • 第一段接口:aclnnStatus aclnnInplaceAddcmulGetWorkspaceSize(const aclTensor *self, const aclTensor *tensor1, const aclTensor *tensor2, const aclScalar *value, uint64_t *workspaceSize, aclOpExecutor **executor)
    • 第二段接口:aclnnStatus aclnnInplaceAddcmul(void *workspace, uint64_t workspaceSize, aclOpExecutor **executor, const aclrtStream stream)

功能描述

  • 算子功能:张量tensor1和张量tensor2逐元素相乘,结果乘以标量value后将其添加到输入张量self中。
  • 计算公式:

aclnnAddcmulGetWorkspaceSize

  • 接口定义:

    aclnnStatus aclnnAddcmulGetWorkspaceSize(const aclTensor *self, const aclTensor *tensor1, const aclTensor *tensor2, const aclScalar *value, const aclTensor *out, uint64_t *workspaceSize, aclOpExecutor** executor)

  • 参数说明:
    • self:Device侧的aclTensor,数据类型支持FLOAT、FLOAT16、INT32、INT64、INT16、INT8、UINT8、BFLOAT16(仅Atlas A2训练系列产品支持),且数据类型需要与tensor1、tensor2相乘的结果构成互推导关系,shape需要与tensor1、tensor2满足broadcast关系。支持非连续的Tensor,数据格式支持ND,且数据格式需要与tensor1、tensor2一致。
    • tensor1:Device侧的aclTensor,数据类型支持FLOAT、FLOAT16、INT32、INT64、INT16、INT8、UINT8、BFLOAT16(仅Atlas A2训练系列产品支持),且数据类型需要与self、tensor2构成互推导关系,shape需要与self、tensor2满足broadcast关系。支持非连续的Tensor,数据格式支持ND,且数据格式需要与self、tensor2一致。
    • tensor2:Device侧的aclTensor,数据类型支持FLOAT、FLOAT16、INT32、INT64、INT16、INT8、UINT8、BFLOAT16(仅Atlas A2训练系列产品支持),且数据类型需要与self、tensor1构成互推导关系,shape需要与self、tensor1满足broadcast关系。支持非连续的Tensor,数据格式支持ND,且数据格式需要与self、tensor1一致。
    • value:Host侧的aclScalar,数据类型支持FLOAT、FLOAT16、DOUBLE、INT32、INT64、INT8、UINT8、BOOL、BFLOAT16(仅Atlas A2训练系列产品支持),数据类型需要可转换成self与tensor1、tensor2推导后的数据类型。
    • out :Device侧的aclTensor,数据类型支持FLOAT、FLOAT16、INT32、INT64、INT16、INT8、UINT8、BFLOAT16(仅Atlas A2训练系列产品支持),且数据类型需要是self与tensor1、tensor2推导之后可转换的数据类型,shape需要是self与tensor1、tensor2 broadcast之后的shape,支持非连续的Tensor,数据格式支持ND,且数据格式需要与self一致。
    • workspaceSize:返回用户需要在Device侧申请的workspace大小。
    • executor:返回op执行器,包含了算子计算流程。
  • 返回值:

    返回aclnnStatus状态码,具体参见aclnn返回码

    第一段接口完成入参校验,出现以下场景时报错:

    • 返回161001(ACLNN_ERR_PARAM_NULLPTR):传入的self、tensor1、tensor2、value或out是空指针。
    • 返回161002(ACLNN_ERR_PARAM_INVALID):
      • self和tensor1、tensor2的数据类型和数据格式不在支持的范围内。
      • self和tensor1、tensor2无法做数据类型推导。
      • 推导出的数据类型无法转换为指定输出out的类型。
      • self和tensor1、tensor2的shape无法做broadcast。

aclnnAddcmul

  • 接口定义:

    aclnnStatus aclnnAddcmul(void* workspace, int64 workspaceSize, aclOpExecutor** executor, aclrtStream stream)

  • 参数说明:
    • workspace:在Device侧申请的workspace内存起址。
    • workspaceSize:在Device侧申请的workspace大小,由第一段接口aclnnAddcmulGetWorkspaceSize获取。
    • executor:op执行器,包含了算子计算流程。
    • stream:指定执行任务的AscendCL stream流。
  • 返回值:

    返回aclnnStatus状态码,具体参见aclnn返回码

aclnnInplaceAddcmulGetWorkspaceSize

  • 接口定义:

    aclnnStatus aclnnInplaceAddcmulGetWorkspaceSize(const aclTensor *self, const aclTensor *tensor1, const aclTensor *tensor2, const aclScalar *value, uint64_t *workspaceSize, aclOpExecutor **executor)

  • 参数说明:
    • self:Device侧的aclTensor,数据类型支持FLOAT、FLOAT16、INT32、INT64、INT16、INT8、UINT8、BFLOAT16(仅Atlas A2训练系列产品支持),且数据类型需要与tensor1、tensor2相乘的结果构成互推导关系,shape需要与tensor1、tensor2满足broadcast关系。支持非连续的Tensor,数据格式支持ND,且数据格式需要与tensor1、tensor2一致。
    • tensor1:Device侧的aclTensor,数据类型支持FLOAT、FLOAT16、INT32、INT64、INT16、INT8、UINT8、BFLOAT16(仅Atlas A2训练系列产品支持),且数据类型需要与self、tensor2构成互推导关系,shape需要与self、tensor2满足broadcast关系。支持非连续的Tensor,数据格式支持ND,且数据格式需要与self、tensor2一致。
    • tensor2:Device侧的aclTensor,数据类型支持FLOAT、FLOAT16、INT32、INT64、INT16、INT8、UINT8、BFLOAT16(仅Atlas A2训练系列产品支持),且数据类型需要与self、tensor1构成互推导关系,shape需要与self、tensor1满足broadcast关系。支持非连续的Tensor,数据格式支持ND,且数据格式需要与self、tensor1一致。
    • value:Host侧的aclScalar,数据类型支持FLOAT、FLOAT16、DOUBLE、INT32、INT64、INT8、UINT8、BOOL、BFLOAT16(仅Atlas A2训练系列产品支持),数据类型需要可转换成self与tensor1、tensor2推导后的数据类型。
    • workspaceSize:返回用户需要在Device侧申请的workspace大小。
    • executor:返回op执行器,包含了算子计算流程。
  • 返回值:

    返回aclnnStatus状态码,具体参见aclnn返回码

    第一段接口完成入参校验,出现以下场景时报错:

    • 返回161001(ACLNN_ERR_PARAM_NULLPTR):传入的self、tensor1、tensor2、value是空指针。
    • 返回161002(ACLNN_ERR_PARAM_INVALID):
      • self和tensor1、tensor2的数据类型和数据格式不在支持的范围内。
      • self和tensor1、tensor2无法做数据类型推导。
      • self和tensor1、tensor2的shape无法做broadcast。

aclnnInplaceAddcmul

  • 接口定义:

    aclnnStatus aclnnInplaceAddcmul(void *workspace, uint64_t workspaceSize, aclOpExecutor *executor, aclrtStream stream)

  • 参数说明:
    • workspace:在Device侧申请的workspace内存起址。
    • workspaceSize:在Device侧申请的workspace大小,由第一段接口aclnnInplaceAddcmulGetWorkspaceSize获取。
    • executor:op执行器,包含了算子计算流程。
    • stream:指定执行任务的AscendCL stream流。
  • 返回值:

    返回aclnnStatus状态码,具体参见aclnn返回码

调用示例

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
#include <iostream>
#include <vector>
#include "acl/acl.h"
#include "aclnnop/aclnn_addcmul.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 shape_size = 1;
  for (auto i : shape) {
    shape_size *= i;
  }
  return shape_size;
}

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根据自己的需要处理
  CHECK_RET(ret == 0, LOG_PRINT("Init acl failed. ERROR: %d\n", ret); return ret);

  // 2. 构造输入与输出,需要根据API的接口自定义构造
  std::vector<int64_t> selfShape = {4, 2};
  std::vector<int64_t> other1Shape = {4, 2};
  std::vector<int64_t> other2Shape = {4, 2};
  std::vector<int64_t> outShape = {4, 2};
  void* selfDeviceAddr = nullptr;
  void* other1DeviceAddr = nullptr;
  void* other2DeviceAddr = nullptr;
  void* outDeviceAddr = nullptr;
  aclTensor* self = nullptr;
  aclTensor* other1 = nullptr;
  aclTensor* other2 = nullptr;
  aclScalar* value= nullptr;
  aclTensor* out = nullptr;
  std::vector<float> selfHostData = {0, 1, 2, 3, 4, 5, 6, 7};
  std::vector<float> other1HostData = {1, 1, 1, 2, 2, 2, 3, 3};
  std::vector<float> other2HostData = {1, 1, 1, 2, 2, 2, 3, 3};
  std::vector<float> outHostData = {0, 0, 0, 0, 0, 0, 0, 0};
  float valueNum = 1.2f;
  // 创建self aclTensor
  ret = CreateAclTensor(selfHostData, selfShape, &selfDeviceAddr, aclDataType::ACL_FLOAT, &self);
  CHECK_RET(ret == ACL_SUCCESS, return ret);
  // 创建other1 aclTensor
  ret = CreateAclTensor(other1HostData, other1Shape, &other1DeviceAddr, aclDataType::ACL_FLOAT, &other1);
  CHECK_RET(ret == ACL_SUCCESS, return ret);
  // 创建other2 aclTensor
  ret = CreateAclTensor(other2HostData, other2Shape, &other2DeviceAddr, aclDataType::ACL_FLOAT, &other2);
  CHECK_RET(ret == ACL_SUCCESS, return ret);
  // 创建value aclScalar
  value = aclCreateScalar(&valueNum, aclDataType::ACL_FLOAT);
  CHECK_RET(value != nullptr, return ret);
  // 创建out aclTensor
  ret = CreateAclTensor(outHostData, outShape, &outDeviceAddr, aclDataType::ACL_FLOAT, &out);
  CHECK_RET(ret == ACL_SUCCESS, return ret);

  // 3.调用CANN算子库API,需要修改为具体的算子接口
  uint64_t workspaceSize = 0;
  aclOpExecutor* executor;
  // 调用aclnnAddcmul第一段接口
  ret = aclnnAddcmulGetWorkspaceSize(self, other1, other2, value, out, &workspaceSize, &executor);
  CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnAddcmulGetWorkspaceSize 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;);
  }
  // 调用aclnnAddcmul第二段接口
  ret = aclnnAddcmul(workspaceAddr, workspaceSize, executor, stream);
  CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnAddcmul 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(outShape);
  std::vector<float> resultData(size, 0);
  ret = aclrtMemcpy(resultData.data(), resultData.size() * sizeof(resultData[0]), outDeviceAddr, size * sizeof(float),
                    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);
  aclDestroyTensor(other1);
  aclDestroyTensor(other2);
  aclDestroyScalar(value);
  aclDestroyTensor(out);
  return 0;
}