CANN Samples(三):入门示例ACLHelloWorld

经过前两篇文章的铺垫,我们已经对CANN samples仓库的结构、版本配套以及核心工具链有了宏观的认识。现在,是时候卷起袖子,写真正能在昇腾(Ascend)设备上跑起来的代码了。

本篇文章将以inference/ACLHelloWorld为例,带你走完一个最基础的AscendCL(Ascend Computing Language)应用的完整流程。这个例子虽然简单,但它就像学习任何一门编程语言时的"Hello, World!"一样,是理解CANN应用生命周期的最短路径。

1. ACLHelloWorld:你的第一个CANN应用

ACLHelloWorld这个示例程序并不执行任何复杂的模型推理,它的核心目标只有一个:演示一个标准的AscendCL应用是如何初始化、运行和退出的。通过它,你将直观地理解CANN应用与硬件交互的基本步骤。

1.1. 准备工作:编译与运行

在深入代码之前,我们先看看如何让这个程序跑起来。假设你已经按照上一篇文章的指引,正确地搭建了合一环境

  1. 进入示例目录

    bash 复制代码
    cd ${HOME}/samples/inference/ACLHelloWorld/scripts

    这里的${HOME}/samples是你下载的samples仓库的根目录。

  2. 编译代码

    scripts目录下提供了一个方便的编译脚本sample_build.sh。它会使用CMake和g++来编译src目录下的C++代码。

    bash 复制代码
    bash sample_build.sh

    编译成功后,会在../out目录下生成一个可执行文件main

  3. 运行程序

    同样,scripts目录下也提供了运行脚本sample_run.sh

    bash 复制代码
    bash sample_run.sh

    如果一切顺利,你将会在屏幕上看到类似下面的输出:

    复制代码
    [INFO] The sample starts to run
    [INFO] Acl Init Success
    [INFO] Acl Set Device Success,Current DeviceID:0
    [INFO] Acl Create Context Success
    [INFO] Acl Create Stream Success
    [INFO] Acl Destroy Stream Success
    [INFO] Acl Destroy Context success
    [INFO] Acl Reset Device Success
    [INFO] Acl Finalize Success
    [INFO] The program runs successfully

    看到这个输出,恭喜你!你已经成功运行了你的第一个CANN应用。

2. 代码解读:AscendCL应用的生命周期

现在,让我们深入到src/main.cpp文件中,逐行解读这段代码,看看它到底做了什么。

一个AscendCL应用遵循一个非常经典且对称的资源管理模式:初始化 -> 创建资源 -> 执行业务 -> 销毁资源 -> 去初始化。这就像你早上起床后要先穿好衣服(初始化),然后使用各种工具(创建资源)完成一天的工作(执行业务),下班后把工具放回原位(销毁资源),最后脱衣睡觉(去初始化)一样,是一个有始有终的过程。

2.1. 初始化阶段

这是程序的入口,也是与昇腾硬件建立联系的第一步。

cpp 复制代码
#include "acl/acl.h"

int main(int argc, char *argv[])
{
    // ...
    const char *aclConfigPath = "../src/acl.json";
    aclError ret = aclInit(aclConfigPath);
    if (ret != ACL_ERROR_NONE) { /* 错误处理 */ }
    INFO_LOG("Acl Init Success");

    ret = aclrtSetDevice(deviceId);
    if (ret != ACL_ERROR_NONE) { /* 错误处理 */ }
    INFO_LOG("Acl Set Device Success, Current DeviceID:%d", deviceId);
    // ...
}
  1. aclInit(aclConfigPath) : 这是所有AscendCL调用的起点。它负责初始化AscendCL框架。你可以传入一个JSON配置文件的路径,用于一些高级配置,但对于大多数应用,一个空的acl.json{})就足够了。

  2. aclrtSetDevice(deviceId) : 我们的计算机上可能有多张昇腾AI加速卡,这张卡就是我们所说的Device 。这个函数的作用就是告诉AscendCL:"嘿,我接下来的所有操作,都想在deviceId这张卡上进行。" 在这个例子中,deviceId被硬编码为0,表示使用第一张卡。

2.2. 资源创建阶段

指定了Device之后,我们还需要在它上面创建两个非常重要的概念:ContextStream

cpp 复制代码
    // ...
    aclrtContext context = nullptr;
    aclrtStream stream = nullptr;

    ret = aclrtCreateContext(&context, deviceId);
    if (ret != ACL_ERROR_NONE) { /* 错误处理 */ }
    INFO_LOG("Acl Create Context Success");

    ret = aclrtCreateStream(&stream);
    if (ret != ACL_ERROR_NONE) { /* 错误处理 */ }
    INFO_LOG("Acl Create Stream Success");
    // ...
  • Context(上下文):你可以把它想象成一个"工作空间"。它管理着在同一个Device上运行的所有任务和资源。你在一个Context里分配的内存、加载的模型,都归这个Context所有。通常情况下,一个线程只使用一个Context。

  • Stream(流) :如果说Context是一个大的工作空间,那么Stream就是这个空间里的一条"流水线"。所有在同一个Stream里的任务,比如内存拷贝、模型推理等,都会按照你提交的顺序依次执行。CANN支持创建多个Stream,这使得我们可以通过并行执行不同的任务来提升应用的整体性能,这也就是所谓的"多流并发"。

2.3. 业务执行阶段

ACLHelloWorld这个简单的例子中,并没有任何实际的业务逻辑。代码在这里留了一个注释,告诉你真正的模型推理、数据处理等代码应该放在这里。

cpp 复制代码
    /*
    * 业务执行
    */

在后续的文章中,我们将会在这里填充上模型加载、内存申请、数据拷贝和模型执行等真实的代码。

2.4. 资源销毁与去初始化阶段

有借有还,再借不难。程序结束前,我们需要把自己申请的资源一一释放掉,这是一个好习惯,也能避免内存泄漏等问题。

资源的销毁顺序应该与创建的顺序严格相反

cpp 复制代码
    // ...
    ret = aclrtDestroyStream(stream);
    if (ret != ACL_ERROR_NONE) { /* ... */ }
    INFO_LOG("Acl Destroy Stream Success");

    ret = aclrtDestroyContext(context);
    if (ret != ACL_ERROR_NONE) { /* ... */ }
    INFO_LOG("Acl Destroy Context success");

    ret = aclrtResetDevice(deviceId);
    if (ret != ACL_ERROR_NONE) { /* ... */ }
    INFO_LOG("Acl Reset Device Success");

    ret = aclFinalize();
    if (ret != ACL_ERROR_NONE) { /* ... */ }
    INFO_LOG("Acl Finalize Success");
    // ...
  1. aclrtDestroyStream(stream): 销毁我们创建的Stream。
  2. aclrtDestroyContext(context): 销毁我们创建的Context。
  3. aclrtResetDevice(deviceId): 重置我们之前指定的Device,释放该Device上的所有资源。
  4. aclFinalize() : 这是aclInit的对应操作,用于去初始化整个AscendCL框架。

3. 总结

通过ACLHelloWorld这个麻雀虽小五脏俱全的例子,我们走通了一个最基本的AscendCL应用的完整生命周期。请务必牢记这个"初始化 -> 创建 -> 执行 -> 销毁 -> 去初始化"的对称结构,它将贯穿我们后续所有的CANN应用开发过程。

现在,你已经不再是一个CANN编程的门外汉了。在下一篇文章中,我们将在这个"Hello, World"的基础上,引入真正的AI模型,看看如何加载一个OM模型并执行一次完整的推理。

相关推荐
~无忧花开~4 小时前
JavaScript实现PDF本地预览技巧
开发语言·前端·javascript
小时前端5 小时前
“能说说事件循环吗?”—— 我从候选人回答中看到的浏览器与Node.js核心差异
前端·面试·浏览器
IT_陈寒5 小时前
Vite 5.0实战:10个你可能不知道的性能优化技巧与插件生态深度解析
前端·人工智能·后端
SAP庖丁解码5 小时前
【SAP Web Dispatcher负载均衡】
运维·前端·负载均衡
天蓝色的鱼鱼5 小时前
Ant Design 6.0 正式发布:前端开发者的福音与革新
前端·react.js·ant design
HIT_Weston5 小时前
38、【Ubuntu】【远程开发】拉出内网 Web 服务:构建静态网页(一)
linux·前端·ubuntu
零一科技6 小时前
Vue3拓展:自定义权限指令
前端·vue.js
im_AMBER6 小时前
AI井字棋项目开发笔记
前端·笔记·学习·算法
小时前端6 小时前
Vuex 响应式原理剖析:构建可靠的前端状态管理
前端·面试·vuex
xiaoxue..6 小时前
深入理解浏览器渲染流程:从HTML/CSS/JS到像素呈现
前端·javascript·css·html