入门样例
样例功能
本样例展示了如何使用CANN的Runtime API以及算子库中的Add算子实现向量加法运算out = self + alpha * other。
Input vectors: self: [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0] other: [0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0] alpha: 1.0 Vector addition result: out: [1.5, 6.0, 4.5, 6.0, 7.5, 9.0, 10.5, 12.0]
了解关键代码逻辑
本节按照应用开发的顺序介绍样例代码的逻辑:资源初始化 → 创建Stream → 准备输入 → 调用Add算子 → 输出结果 → 释放资源→ 资源去初始化。通过本节的介绍,还可以帮助您了解CANN应用开发中关键API的功能,这些关键API的接口名前缀为acl,下文简称acl接口。
- 资源初始化。
1 2 3 4 5 6
// 初始化系统,nullptr表示采用默认配置初始化系统,调用其它acl接口前,必须先初始化,否则可能会导致后续系统内部资源初始化出错,进而导致其它业务异常 aclInit(nullptr); // 指定计算设备 int32_t deviceId = 0; aclrtSetDevice(deviceId);
- 创建Stream。
1 2 3
// Stream相当于一个任务队列,任务按照进入队列的顺序依次执行 aclrtStream stream = nullptr; aclrtCreateStream(&stream);
- 准备输入。根据Add算子的计算公式(out = self + alpha * other),self和other为相加的两个输入向量,alpha为系数,out为相加后的结果向量。因此在准备输入时,涉及创建两个输入Tensor、创建一个表示系数的Scalar、创建一个输出Tensor。在创建输入、输出Tensor时,涉及申请Device内存存放输入或输出数据。
- 定义一个创建Tensor的通用接口。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
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); // 申请Device内存 aclrtMalloc(deviceAddr, size, ACL_MEM_MALLOC_HUGE_FIRST); // 将输入数据从Host同步复制到Device aclrtMemcpy(*deviceAddr, size, hostData.data(), size, ACL_MEMCPY_HOST_TO_DEVICE); // 计算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]; } // 创建Tensor *tensor = aclCreateTensor(shape.data(), shape.size(), dataType, strides.data(), 0, aclFormat::ACL_FORMAT_ND, shape.data(), shape.size(), *deviceAddr); return 0; }
- 创建输入、输出Tensor以及一个系数Scalar。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
std::vector<int64_t> shape{8}; // 向量长度为8 void* selfDeviceAddr = nullptr; void* otherDeviceAddr = nullptr; void* outDeviceAddr = nullptr; aclDataBuffer* outDataBuffer = nullptr; aclTensor* self = nullptr; aclTensor* other = nullptr; aclTensor* out = nullptr; aclScalar* alpha = nullptr; std::vector<float> selfHostData = {1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f}; // self向量输入数据 std::vector<float> otherHostData = {0.5f, 1.0f, 1.5f, 2.0f, 2.5f, 3.0f, 3.5f, 4.0f}; // other向量输入数据 std::vector<float> outHostData(8, 0.0f); float alphaValue = 1.0f; // 系数值 // 创建输入 Tensor CreateAclTensor(selfHostData, shape, &selfDeviceAddr, aclDataType::ACL_FLOAT, &self); CreateAclTensor(otherHostData, shape, &otherDeviceAddr, aclDataType::ACL_FLOAT, &other); // 创建 alpha Scalar alpha = aclCreateScalar(&alphaValue, aclDataType::ACL_FLOAT); // 创建输出 Tensor CreateAclTensor(outHostData, shape, &outDeviceAddr, aclDataType::ACL_FLOAT, &out);
- 定义一个创建Tensor的通用接口。
- 调用Add算子。
// 调用CANN内置的算子,通常调用两段式接口 // 第一段接口aclnnAddGetWorkspaceSize,该接口内部执行入参校验、在动态Shape场景下推导输出Shape、数据切块(Tiling)以及计算执行算子所需的workspace内存大小等任务 uint64_t workspaceSize = 0; aclOpExecutor* executor = nullptr; ret = aclnnAddGetWorkspaceSize(self, other, alpha, out, &workspaceSize, &executor); // 第二段接口aclnnAdd,执行算子计算,接口内部涉及DFX(例如Dump、溢出检测等)、调用Runtime提供的LaunchKernel接口等 // workspaceAddr表示在Device侧申请的workspace内存地址 ret = aclnnAdd(workspaceAddr, workspaceSize, executor, stream); // 同步等待任务完成 aclrtSynchronizeStream(stream);
- 输出结果。将Device侧的结果数据回传到Host上。
1 2 3
// outBufferAddr表示存放Device侧结果数据的内存地址 // resultData.data()表示存放Host侧数据的内存地址 aclrtMemcpy(resultData.data(), resultData.size() * sizeof(float), outBufferAddr, size * sizeof(float), ACL_MEMCPY_DEVICE_TO_HOST);
- 释放资源。
// 销毁Tensor和Scalar aclDestroyTensor(self); aclDestroyTensor(other); aclDestroyScalar(alpha); aclDestroyTensor(out); // 释放Device内存 aclrtFree(selfDeviceAddr); aclrtFree(otherDeviceAddr); aclrtFree(outDeviceAddr); aclrtFree(workspaceAddr); // 销毁Stream aclrtDestroyStream(stream);
- 资源去初始化。
1 2 3 4
// 复位设备 aclrtResetDeviceForce(deviceId); // 去初始化 aclFinalize();
父主题: 快速入门