模型构建
推理应用开发视频课程
通过在线视频课程学习该功能,请参见AscendCL模型推理基础功能。
父主题: 基础推理应用
推理应用开发流程
图1 开发流程

-
准备环境。
-
创建代码目录。
在开发应用前,您需要先创建目录,存放代码文件、编译脚本、测试图片数据、模型文件等。
如下仅是示例,供参考:
├App名称├── model // 该目录下存放模型文件│ ├── xxxxxx├── data│ ├── xxx.jpg // 测试数据├── inc // 该目录下存放声明函数的头文件│ ├── xxx.h├── out // 该目录下存放输出结果├── src│ ├── xxx.json // 系统初始化的配置文件│ ├── CMakeLists.txt // 编译脚本│ ├── xxx.cpp // 实现文件 -
构建模型。
模型推理场景下,必须要有适配昇腾AI处理器的离线模型(*.om文件),请参见模型构建。
-
开发应****用。
-
AscendCL初始化,请参见AscendCL初始化与去初始化。
使用AscendCL接口开发应用时,必须先调用aclInit接口进行AscendCL初始化,否则可能会导致后续系统内部资源初始化出错,进而导致其它业务异常。
-
运行管理资源申请,请参见运行管理资源申请与释放。
-
数据传输,请参见数据传输。
-
执行模型推理。请参见模型推理。
若需要处理模型推理的结果,还需要进行数据后处理,例如对于图片分类应用,通过数据后处理从推理结果中查找最大置信度的类别标识。
模型推理结束后,需及时释放推理相关资源。
-
所有数据处理结束后,需及时释放运行管理资源,请参见运行管理资源申请与释放。
-
执行AscendCL去初始化,请参见AscendCL初始化与去初始化。
-
-
编译运行应用,包括编译代码、运行应用,请参见应用调试。
父主题: 基础推理应用
模型构建
对于开源框架的网络模型(如Caffe、TensorFlow等),不能直接在昇腾AI处理器上运行推理,需要先使用ATC(Ascend Tensor Compiler)工具将开源框架的网络模型转换为适配昇腾AI处理器的离线模型(*.om文件),模型转换的方法请参见《[转换模型](https://www.hiascend.com/document/detail/zh/Atlas200IDKA2DeveloperKit/23.0.RC2/Application Development Guide/tmuacop/tmuacop_0001.html)》。
父主题: 基础推理应用
AscendCL初始化与去初始化
基本原理
您必须调用aclInit接口初始化AscendCL,配置文件内容为json格式。
如果当前的默认配置已满足需求,无需修改,可向aclInit接口中传入NULL,或者可将配置文件配置为空json串(即配置文件中只有{})。向aclInit接口中传入空指针的示例如下:
aclError ret = aclInit(NULL);
有初始化就有去初始化,在确定完成了AscendCL的所有调用之后,或者进程退出之前,需调用aclFinalize接口实现AscendCL去初始化。
示例代码
您可以从样例介绍中获取完整样例代码。
调用接口后,需增加异常处理的分支,并记录报错日志、提示日志,此处不一一列举。以下是关键步骤的代码示例,不可以直接拷贝编译运行,仅供参考。
// 初始化
// 此处的..表示相对路径,相对可执行文件所在的目录
// 例如,编译出来的可执行文件存放在out目录下,此处的..就表示out目录的上一级目录
const char *aclConfigPath = "../src/acl.json";
aclError ret = aclInit(aclConfigPath);
// ......
// 去初始化
ret = aclFinalize();
// ......
父主题: 基础推理应用
运行管理资源申请与释放
开发应用时,应用程序中必须包含运行管理资源申请的代码逻辑,关于运行管理资源申请的接口调用流程,请先参见AscendCL接口调用流程了解整体流程,再查看本节中的资源申请、释放流程说明。
基本原理
您需要按顺序依次申请如下运行管理资源:Device、Context、Stream,确保可以使用这些资源执行运算、管理任务。所有数据处理都结束后,需要按顺序依次释放运行管理资源:Stream、Context、Device。
您需要按照Device、Context、Stream的顺序依次申请。其中,创建Context、Stream的方式分为隐式创建和显式创建,其适用场景有所不同:
-
隐式创建Context和Stream:适合简单、无复杂交互逻辑的应用,但缺点在于,在多线程编程中,每个线程都使用默认Context或默认Stream,默认Stream中任务的执行顺序取决于操作系统线程调度的顺序。
-
显式创建Context和Stream:推荐显式,适合大型、复杂交互逻辑的应用,且便于提高程序的可读性、可维护性。
-
关于单进程、单线程、单Stream场景如下所示:
-
单进程:一个应用程序对应一个进程。
-
单线程:不创建多个线程时,默认只有一个线程。
-
单Stream:整个开发的过程中使用同一个Stream。
对于同一个Stream中的异步任务,AscendCL会按照应用程序中任务的顺序执行任务,确保异步任务执行的顺序。
-
-
关于多线程、多Stream的场景请参见Stream管理。
运行管理资源申请流程
图1 运行管理资源申请流程

关键接口的说明如下:
-
申请运行管理资源时,需按顺序依次申请:Device、Context、Stream。
-
调用aclrtSetDevice接口显式****指定用于运算的Device。
- 调用aclrtCreateContext接口显式创建Context,调用aclrtCreateStream接口显式创建Stream。
- 如果不显式创建Context和Stream,您可以使用aclrtSetDevice接口隐式创建的默认Context和默认Stream,但默认Context和默认Stream存在如下限制:
- 一个Device对应一个默认Context,默认Context不能通过aclrtDestroyContext接口来释放。
- 一个Device对应一个默认Stream,默认Stream不能通过aclrtDestroyStream接口来释放。默认Stream作为接口入参时,直接传NULL。
- 默认Context、默认Stream,是在调用aclrtResetDevice接口后自动释放。
-
隐式****指定用于运算的Device。
调用aclrtCreateContext接口显式创建Context,调用aclrtCreateStream接口显式创建Stream。系统在显式创建Context时,系统内部会调用aclrtSetDevice接口指定运行的Device,Device ID通过aclrtCreateContext接口传入。
-
-
(可选)调用aclrtGetRunMode接口获取软件栈的运行模式,根据运行模式来判断后续的内存申请接口调用逻辑。
如果查询结果为ACL_HOST,则数据传输时涉及申请Host上的内存。
如果查询结果为ACL_DEVICE,则数据传输时仅需申请Device上的内存。
数据传输的详细介绍请参见数据传输。
运行管理资源释放流程
图2 运行管理资源释放流程

关键接口的说明如下:
- 释放运行管理资源时,需按顺序依次释放:Stream、Context、Device。
- 显式创建Context和Stream时,需调用aclrtDestroyStream接口释放Stream,再调用aclrtDestroyContext接口释放Context。若显式调用aclrtSetDevice接口指定运算的Device时,还需调用aclrtResetDevice接口释放Device上的资源。
- 不显式创建Context和Stream时,仅需调用aclrtResetDevice接口释放Device上的资源。
示例代码
您可以从样例介绍中获取完整样例代码。
调用接口后,需增加异常处理的分支,并记录报错日志、提示日志,此处不一一列举。以下是关键步骤的代码示例,不可以直接拷贝编译运行,仅供参考。
<br> 1<br> 2<br> 3<br> 4<br> 5<br> 6<br> 7<br> 8<br> 9<br>10<br>11<br>12<br>13<br>14<br>15<br>16<br>17<br>18<br>19<br>20<br>21<br>22<br>23<br>24<br>25<br>26<br>27<br>28<br>29<br>30<br> | <br>// 初始化变量<br>extern bool g_isDevice;<br>// =====运行管理资源申请=====<br>// 指定运算的Device<br>aclError ret = aclrtSetDevice(deviceId_);<br>// 显式创建一个Context,用于管理Stream对象<br>ret = aclrtCreateContext(&context_, deviceId_);<br>// 显式创建一个Stream<br>// 用于维护一些异步操作的执行顺序,确保按照应用程序中的代码调用顺序执行任务<br>ret = aclrtCreateStream(&stream_);<br>// 获取当前昇腾AI软件栈的运行模式,根据不同的运行模式,后续的接口调用方式不同<br>aclrtRunMode runMode;<br>ret = aclrtGetRunMode(&runMode);<br>g_isDevice = (runMode == ACL_DEVICE);<br>// =====运行管理资源申请=====<br>// ......<br>// =====运行管理资源释放=====<br>ret = aclrtDestroyStream(stream_);<br>ret = aclrtDestroyContext(context_);<br>ret = aclrtResetDevice(deviceId_);<br>// =====运行管理资源释放=====<br>// ......<br> |
|---|
父主题: 基础推理应用
数据传输
接口调用流程
数据传输的关键接口调用流程如下:
-
申请内存。
- Device上的内存,使用AscendCL提供的aclrtMalloc或aclrtMallocHost接口申请内存。如果涉及媒体数据处理(例如,图片解码、缩放等)时,需使用acldvppMalloc或hi_mpi_dvpp_malloc接口申请内存。
-
将数据读入内存。
由用户自行管理数据读入内存的实现逻辑。
-
通过内存复制实现数据传输。
数据传输可以通过内存复制的方式实现,分为同步内存复制、异步内存复制:
- 同步内存复制:调用aclrtMemcpy接口。
- 异步内存复制:调用aclrtMemcpyAsync接口,再调用aclrtSynchronizeStream接口实现Stream内任务的同步等待。
- 调用同步或异步内存复制接口时,支持以下类型的复制(可单击链接查看对应类型的内存复制示例代码):
:::note 说明 Ascend RC场景下,不涉及Host上的内存申请、Host内的数据传输、Host与Device之间的数据传输。
如果当前版本支持多种运行形态,在这种场景下,若想实现相同的应用程序可支持在多种形态下运行,申请内存的方式不同,会影响数据传输时调用的接口:
-
若应用程序中区分申请Host内存或Device内存的接口,例如使用C++标准库的接口或aclrtMallocHost接口申请Host内存、使用aclrtMalloc接口申请Device内存时:
需先调用aclrtGetRunMode接口获取软件栈的运行模式,当查询结果为ACL_HOST,则数据传输时涉及申请Host上的内存;当查询结果为ACL_DEVICE,则数据传输时不涉及申请Host上的内存,仅需申请Device上的内存。该种方式多一些代码逻辑的判断,不需要由用户处理Device上的内存对齐。在Device上运行应用的场景,该种方式少一些内存复制的步骤,性能较好。
-
若应用程序中不区分申请Host内存或Device内存的接口,统一使用aclrtMallocHost接口(该接口支持申请Host或Device内存),AscendCL内部会根据软件栈的运行模式自行判断运行时申请的是Host内存还是Device内存:
无需调用aclrtGetRunMode接口获取软件栈的运行模式。该种方式代码逻辑相比前一种简单,但需由用户处理Device上的内存对齐。 :::
一个Device内的数据传输
调用接口后,需增加异常处理的分支,并记录报错日志、提示日志,此处不一一列举。以下是关键步骤的代码示例,不可以直接拷贝编译运行,仅供参考。
<br> 1<br> 2<br> 3<br> 4<br> 5<br> 6<br> 7<br> 8<br> 9<br>10<br>11<br>12<br>13<br>14<br>15<br>16<br>17<br>18<br>19<br>20<br>21<br>22<br>23<br>24<br>25<br>26<br> | <br>// 1. 申请内存<br>uint64_t size = 1 * 1024 * 1024;<br>void* devPtrA = NULL;<br>void* devPtrB = NULL;<br>aclrtMalloc(&devPtrA, size, ACL_MEM_MALLOC_NORMAL_ONLY);<br>aclrtMalloc(&devPtrB, size, ACL_MEM_MALLOC_NORMAL_ONLY);<br>// 2. 申请内存后,可向内存中读入数据,该自定义函数ReadFile由用户实现<br>ReadFile(fileName, devPtrA, size);<br>// 3. 内存复制,可以选择同步或异步<br>// 同步内存复制,devPtrA表示Device上源内存地址指针,devPtrB表示Device上目的内存地址指针,size表示内存大小<br>aclrtMemcpy(devPtrB, size, devPtrA, size, ACL_MEMCPY_DEVICE_TO_DEVICE);<br> <br>// 异步内存复制<br>aclrtStream stream = NULL;<br>aclrtCreateStream(&stream);<br>aclrtMemcpyAsync(devPtrB, size, devPtrA, size, ACL_MEMCPY_DEVICE_TO_DEVICE, stream);<br>aclrtSynchronizeStream(stream);<br>// 4. 使用完内存中的数据后,需及时释放资源<br>aclrtDestroyStream(stream);<br>aclrtFree(devPtrA);<br>aclrtFree(devPtrB);<br>// ......<br> |
|---|
父主题: 基础推理应用
在线提单