数据传输
AscendCL初始化与去初始化
基本原理
您必须调用aclInit接口初始化AscendCL,配置文件内容为json格式。
如果当前的默认配置已满足需求,无需修改,可向aclInit接口中传入NULL,或者可将配置文件配置为空json串(即配置文件中只有{})。向aclInit接口中传入空指针的示例如下:
<br>1<br> | <br>aclError ret = aclInit(NULL);<br> |
|---|
有初始化就有去初始化,在确定完成了AscendCL的所有调用之后,或者进程退出之前,需调用aclFinalize接口实现AscendCL去初始化。
示例代码
您可以从样例介绍中获取完整样例代码。
调用接口后,需增加异常处理的分支,并记录报错日志、提示日志,此处不一一列举。以下是关键步骤的代码示例,不可以直接拷贝编译运行,仅供参考。
<br> 1<br> 2<br> 3<br> 4<br> 5<br> 6<br> 7<br> 8<br> 9<br>10<br>11<br> | <br>// 初始化<br>// 此处的..表示相对路径,相对可执行文件所在的目录<br>// 例如,编译出来的可执行文件存放在out目录下,此处的..就表示out目录的上一级目录<br>const char *aclConfigPath = "../src/acl.json";<br>aclError ret = aclInit(aclConfigPath);<br>// ......<br>// 去初始化<br>ret = aclFinalize();<br>// ......<br> |
|---|
父主题: 基础推理应用
运行管理资源申请与释放
开发应用时,应用程序中必须包含运行管理资源申请的代码逻辑,关于运行管理资源申请的接口调用流程,请先参见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> |
|---|
父主题: 基础推理应用
模型加载
按照模型构建中的说明构建出模型后,再加载该模型,为模型执行做准备。
接口调用流程
开发应用时,如果涉及整网模型推理,则应用程序中必须包含模型加载的代码逻辑,关于模型加载的接口调用流程,请先参见AscendCL接口调用流程了解整体流程,再查看本节中的流程说明。本节描述的是整网模型加载的接口调用流程,对于算子模型加载与执行的详细说明请参见单算子调用。
AscendCL提供两套模型加载的接口,用户可根据编程习惯、使用场景选择对应的模型加载接口:
- 如图1所示,针对不同的加载方式(从文件加载、从内存加载等),只需设置接口中的配置参数,适用各种加载方式,但涉及多个接口配合使用,分别用于创建配置对象、设置对象中的属性值、加载模型。
- 如图2所示,根据不同的加载方式(从文件加载、从内存加载等)选择不同的接口,操作相对简单,但需要记住各种方式的加载接口。
图1 模型加载流程(通过接口中的配置参数区分加载方式)

图2 模型加载流程(通过不同接口区分加载方式)

关键接口的说明如下:
-
在模型加载前,需要先构建出适配昇腾AI处理器的离线模型(*.om文件),构建方式请参见模型构建。
-
当由用户管理内存时,为确保内存不浪费,在申请工作内存、权值内存前,需要调用aclmdlQuerySize接口查询模型运行时所需工作内存、权值内存的大小。
如果模型输入数据的Shape不确定,则不能调用aclmdlQuerySize接口查询内存大小,在加载模型时,就无法由用户管理内存,因此需选择由系统管理内存的模型加载接口(例如,aclmdlLoadFromFile、aclmdlLoadFromMem)。
-
支持以下方式加载模型,模型加载成功后,返回标识模型的模型ID:
- 使用aclmdlSetConfigOpt接口、aclmdlLoadWithConfig接口时,是通过配置对象中的属性来区分,在加载模型时是从文件加载,还是从内存加载,以及内存是由系统内部管理,还是由用户管理。
- 使用以下接口时,是从使用的接口上区分从文件加载,还是从内存加载,以及内存是由系统内部管理,还是由用户管理。
- aclmdlLoadFromFile:从文件加载离线模型数据,由系统内部管理内存。
- aclmdlLoadFromMem:从内存加载离线模型数据,由系统内部管理内存。
- aclmdlLoadFromFileWithMem:从文件加载离线模型数据,由用户自行管理模型运行的内存(包括工作内存和权值内存,工作内存用于模型执行过程中的临时数据,权值内存用于存放权值数据)。
- aclmdlLoadFromMemWithMem:从内存加载离线模型数据,由用户自行管理模型运行的内存(包括工作内存和权值内存)。
示例代码
模型加载成功,会返回标识模型的ID,在模型执行时需要使用该ID。
您可以从样例介绍中获取完整样例代码。
调用接口后,需增加异常处理的分支,并记录报错日志、提示日志,此处不一一列举。以下是关键步骤的代码示例,不可以直接拷贝编译运行,仅供参考。
<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> | <br>// 1.初始化变量。<br>// 此处的..表示相对路径,相对可执行文件所在的目录<br>// 例如,编译出来的可执行文件存放在out目录下,此处的..就表示out目录的上一级目录<br>const char* omModelPath = "../model/resnet50.om"<br>// ......<br>// 2.根据模型文件获取模型执行时所需的权值内存大小、工作内存大小。<br>aclError ret = aclmdlQuerySize(omModelPath, &modelMemSize_, &modelWeightSize_);<br>// 3.根据工作内存大小,申请Device上模型执行的工作内存。<br>ret = aclrtMalloc(&modelMemPtr_, modelMemSize_, ACL_MEM_MALLOC_HUGE_FIRST);<br>// 4.根据权值内存的大小,申请Device上模型执行的权值内存。<br>ret = aclrtMalloc(&modelWeightPtr_, modelWeightSize_, ACL_MEM_MALLOC_HUGE_FIRST);<br>// 5.加载离线模型文件,由用户自行管理模型运行的内存(包括权值内存、工作内存)。<br>// 模型加载成功,返回标识模型的ID。<br>ret = aclmdlLoadFromFileWithMem(modelPath, &modelId_, modelMemPtr_, modelMemSize_, modelWeightPtr_, modelWeightSize_);<br>// ......<br> |
|---|
父主题: 模型推理
模型执行
基本原理
开发应用时,如果涉及整网模型推理,则应用程序中必须包含模型执行的代码逻辑,关于模型执行的接口调用流程,请先参见AscendCL接口调用流程了解整体流程,再查看本节中的流程说明。本节描述的是整网模型执行的接口调用流程,对于算子模型加载与执行的详细说明请参见单算子调用。
- 在模型加载之后,模型执行之前,需要准备输入、输出数据结构,将输入数据传输到模型输入数据结构的对应内存中。
- 模型执行结束后,若无需使用输入数据、aclmdlDesc类型、aclmdlDataset类型、aclDataBuffer类型等相关资源,需及时释放内存、销毁对应的数据类型,防止内存异常。模型可能存在多个输入、多个输出,每个输入/输出的内存地址、内存大小用aclDataBuffer类型的数据来描述,针对每个输入/输出,需调用aclDestroyDataBuffer接口销毁相应的aclDataBuffer类型,并调用aclrtFree接口释放内存中的数据。
模型执行流程
图1 基本的模型推理流程

关键接口的说明如下:
-
调用aclmdlCreateDesc接口创建描述模型基本信息的数据类型。
-
调用aclmdlGetDesc接口根据模型加载中返回的模型ID获取模型基本信息。
-
准备模型执行的输入、输出数据结构,具体流程,请参见准备模型执行的输入/输出数据结构。
如果模型的输入涉及动态Batch、动态分辨率、动态AIPP、动态维度(ND格式)等特性,请参见模型动态Shape输入推理、模型动态AIPP推理。
-
执行模型推理。
对于固定的多Batch场景,需要满足batch size后,才能将输入数据发送给模型进行推理。不满足batch size时,用户需根据自己的实际场景处理。
当前系统支持模型的同步推理和异步推理:
-
同步推理
调用aclmdlExecute接口执行同步推理。
-
异步推理
调用aclmdlExecuteAsync接口执行异步推理。
但对于异步接口,还需调用aclrtSynchronizeStream接口阻塞应用程序运行,直到指定Stream中的所有任务都完成。
异步推理的详细介绍,请参见异步模型推理。
-
-
获取模型推理的结果,用于后续处理。
- 对于同步推理,直接获取模型推理的输出数据即可。
- 对于异步推理,在实现Callback功能时,在回调函数内获取模型推理的结果,供后续使用。
-
释放内存。
调用aclrtFree接口释放Device上的内存。
-
释放相关数据类型的数据。
在模型推理结束后,需依次调用aclDestroyDataBuffer接口、aclmdlDestroyDataset接口及时释放描述模型输入、输出数据类型的数据。如果存在多个输入、输出,需调用多次aclDestroyDataBuffer接口。
准备模型执行的输入/输出数据结构
AscendCL提供了以下数据类型来描述模型、描述其输入输出以及存放数据的内存,在模型执行前,需要构造好这些数据类型,作为模型执行的输入:
-
使用aclmdlDesc类型的数据描述模型基本信息(例如输入/输出的个数、名称、数据类型、Format、维度信息等)。
模型加载成功后,用户可根据模型的ID,调用aclmdlGetDesc接口获取该模型的描述信息,进而从模型的描述信息中获取模型输入/输出的个数、内存大小、维度信息、Format、数据类型等信息,可参见aclmdlDesc类型下的操作接口。
-
使用aclmdlDataset类型的数据描述模型的输入/输出数据,模型可能存在多个输入、多个输出。
调用aclmdlDataset类型下的操作接口添加aclDataBuffer类型的数据、获取aclDataBuffer的个数等。
-
每个输入/输出的内存地址、内存大小用aclDataBuffer类型的数据来描述。
调用aclDataBuffer类型下的操作接口获取内存地址、内存大小等。
图2 aclmdlDataset类型与aclDataBuffer类型的关系

了解相关的数据类型后,可以使用这些数据类型的操作接口准备模型的输入、输出数据结构,如下图所示。
图3 模型执行的输入/输出数据结构的准备流程

关键说明如下:
-
模型存在多个输入、输出时,用户可调用aclmdlGetNumInputs、aclmdlGetNumOutputs接口获取输入、输出的个数。
-
模型每个输入、输出所需的内存大小,用户可调用aclmdlGetInputSizeByIndex、aclmdlGetOutputSizeByIndex接口获取。
如果模型的输入涉及动态Batch、动态分辨率、动态维度(ND格式)等特性,输入tensor数据的Shape支持多种档位,在模型执行前才能确定,因此该输入所需的内存大小建议用户调用aclmdlGetInputSizeByIndex接口获取,该接口获取的是最大档位的内存,确保内存够用。
-
模型存在多个输入、输出时,用户在向aclmdlDataset中添加aclDataBuffer时,为避免顺序出错,可以先调用aclmdlGetInputNameByIndex、aclmdlGetOutputNameByIndex接口获取输入、输出的名称,根据输入、输出名称所对应的index的顺序添加。
示例代码
此处的示例代码是处理图片分类模型的输出结果,屏显每张图片的top5置信度的类别编号。用户可根据实际需求,自行实现模型推理输出数据的处理逻辑。
您可以从样例介绍中获取完整样例代码。
调用接口后,需增加异常处理的分支,并记录报错日志、提示日志,此处不一一列举。以下是关键步骤的代码示例,不可以直接拷贝编译运行,仅供参考。
<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> 31<br> 32<br> 33<br> 34<br> 35<br> 36<br> 37<br> 38<br> 39<br> 40<br> 41<br> 42<br> 43<br> 44<br> 45<br> 46<br> 47<br> 48<br> 49<br> 50<br> 51<br> 52<br> 53<br> 54<br> 55<br> 56<br> 57<br> 58<br> 59<br> 60<br> 61<br> 62<br> 63<br> 64<br> 65<br> 66<br> 67<br> 68<br> 69<br> 70<br> 71<br> 72<br> 73<br> 74<br> 75<br> 76<br> 77<br> 78<br> 79<br> 80<br> 81<br> 82<br> 83<br> 84<br> 85<br> 86<br> 87<br> 88<br> 89<br> 90<br> 91<br> 92<br> 93<br> 94<br> 95<br> 96<br> 97<br> 98<br> 99<br>100<br>101<br>102<br>103<br>104<br>105<br>106<br>107<br>108<br>109<br>110<br>111<br>112<br>113<br>114<br>115<br>116<br> | cpp<br>// 1 根据模型的ID,获取该模型的描述信息。<br>// modelDesc_为aclmdlDesc类型。<br>modelDesc_ = aclmdlCreateDesc();<br>ret = aclmdlGetDesc(modelDesc_, modelId_);<br>// 2 准备模型推理的输入数据结构<br>// (1)申请输入内存<br>size_t modelInputSize;<br>void *modelInputBuffer = nullptr;<br>// 当前示例代码中的模型只有一个输入,所以index为0,如果模型有多个输入,则需要先调用aclmdlGetNumInputs接口获取模型输入的数量<br>modelInputSize = aclmdlGetInputSizeByIndex(modelDesc_, 0);<br>aclRet = aclrtMalloc(&modelInputBuffer, modelInputSize, ACL_MEM_MALLOC_NORMAL_ONLY);<br>// (2)准备模型的输入数据结构<br>// 创建aclmdlDataset类型的数据,描述模型推理的输入,input_为aclmdlDataset类型<br>input_ = aclmdlCreateDataset();<br>aclDataBuffer *inputData = aclCreateDataBuffer(modelInputBuffer, modelInputSize);<br>ret = aclmdlAddDatasetBuffer(input_, inputData);<br>// 3 准备模型推理的输出数据结构<br>// (1)创建aclmdlDataset类型的数据,描述模型推理的输出,output_为aclmdlDataset类型<br>output_ = aclmdlCreateDataset();<br>// (2)获取模型的输出个数.<br>size_t outputSize = aclmdlGetNumOutputs(modelDesc_);<br>// (3)循环为每个输出申请内存,并将每个输出添加到aclmdlDataset类型的数据中.<br>for (size_t i = 0; i < outputSize; ++i) {<br> size_t buffer_size = aclmdlGetOutputSizeByIndex(modelDesc_, i);<br> void *outputBuffer = nullptr;<br> aclError ret = aclrtMalloc(&outputBuffer, buffer_size, ACL_MEM_MALLOC_NORMAL_ONLY);<br> aclDataBuffer* outputData = aclCreateDataBuffer(outputBuffer, buffer_size); <br> ret = aclmdlAddDatasetBuffer(output_, outputData);<br> }<br>// 4 模型执行<br>string testFile[] = {<br> "../data/dog1_1024_683.bin",<br> "../data/dog2_1024_683.bin"<br> };<br>for (size_t index = 0; index < sizeof(testFile) / sizeof(testFile[0]); ++index) {<br> // 4.1 自定义函数ReadBinFile,调用C++标准库std::ifstream中的函数读取图片文件,输出图片文件占用的内存大小inputBuffSize以及图片文件存放在内存中的地址inputBuff<br> void *inputBuff = nullptr;<br> uint32_t inputBuffSize = 0;<br> auto ret = Utils::ReadBinFile(fileName, inputBuff, inputBuffSize);<br> <br> // 4.2 准备模型推理的输入数据<br> // 在申请运行管理资源时调用aclrtGetRunMode接口获取软件栈的运行模式<br> // 如果运行模式为ACL_DEVICE,则g_isDevice参数值为true,表示软件栈运行在Device侧,无需传输图片数据或在Device内传输数据 ;否则,需要调用内存复制接口将数据传输到Device<br> if (!g_isDevice) {<br> // if app is running in host, need copy data from host to device<br> // modelInputBuffer、modelInputSize分别表示模型推理输入数据的内存地址、内存大小,在输入/输出数据结构准备时申请该内存<br> aclError aclRet = aclrtMemcpy(modelInputBuffer, modelInputSize, inputBuff, inputBuffSize, ACL_MEMCPY_HOST_TO_DEVICE);<br> (void)aclrtFreeHost(inputBuff);<br> } else { // app is running in device<br> aclError aclRet = aclrtMemcpy(modelInputBuffer, modelInputSize, inputBuff, inputBuffSize, ACL_MEMCPY_DEVICE_TO_DEVICE);<br> (void)aclrtFree(inputBuff);<br> }<br> // 4.3 执行模型推理<br> // modelId_表示模型ID,在模型加载成功后,会返回标识模型的ID<br> // input_、output_分别表示模型推理的输入、输出数据,在准备模型推理的输入、输出数据结构时已定义<br> aclError ret = aclmdlExecute(modelId_, input_, output_)<br> <br> // 处理模型推理的输出数据,输出top5置信度的类别编号 <br> // output_表示模型执行的输出<br> for (size_t i = 0; i < aclmdlGetDatasetNumBuffers(output_); ++i) {<br> // 获取每个输出的内存地址和内存大小<br> aclDataBuffer* dataBuffer = aclmdlGetDatasetBuffer(output_, i);<br> void* data = aclGetDataBufferAddr(dataBuffer);<br> size_t len = aclGetDataBufferSizeV2(dataBuffer);<br> // 将内存中的数据转换为float类型<br> float *outData = NULL;<br> outData = reinterpret_cast<float*>(data);<br> <br> // 屏显每张图片的top5置信度的类别编号<br> map<float, int, greater<float> > resultMap;<br> for (int j = 0; j < len / sizeof(float); ++j) {<br> resultMap[*outData] = j;<br> outData++;<br> }<br> int cnt = 0;<br> for (auto it = resultMap.begin(); it != resultMap.end(); ++it) {<br> // print top 5<br> if (++cnt > 5) {<br> break;<br> }<br> INFO_LOG("top %d: index[%d] value[%lf]", cnt, it->second, it->first);<br> }<br>}<br>// 5 释放模型推理的输入、输出资源<br>// 释放输入资源,包括数据结构和内存<br>for (size_t i = 0; i < aclmdlGetDatasetNumBuffers(input_); ++i) {<br> aclDataBuffer *dataBuffer = aclmdlGetDatasetBuffer(input_, i);<br> (void)aclDestroyDataBuffer(dataBuffer);<br>}<br>(void)aclmdlDestroyDataset(input_);<br>input_ = nullptr;<br>aclrtFree(modelInputBuffer);<br>// 释放输出资源,包括数据结构和内存<br>for (size_t i = 0; i < aclmdlGetDatasetNumBuffers(output_); ++i) {<br> aclDataBuffer* dataBuffer = aclmdlGetDatasetBuffer(output_, i);<br> void* data = aclGetDataBufferAddr(dataBuffer);<br> (void)aclrtFree(data);<br> (void)aclDestroyDataBuffer(dataBuffer);<br>}<br>(void)aclmdlDestroyDataset(output_);<br>output_ = nullptr;<br> |
|---|
父主题: 模型推理
模型卸载
关于模型卸载的接口调用流程,请参见AscendCL接口调用流程。
基本原理
在模型推理结束后,还需要通过aclmdlUnload接口卸载模型,并销毁aclmdlDesc类型的模型描述信息、释放模型运行的工作内存和权值内存。
示例代码
<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> | <br>// 1. 卸载模型<br>aclError ret = aclmdlUnload(modelId_);<br>// 2. 释放模型描述信息<br>if (modelDesc_ != nullptr) {<br> (void)aclmdlDestroyDesc(modelDesc_);<br> modelDesc_ = nullptr;<br>}<br>// 3. 释放模型运行的工作内存<br>if (modelWorkPtr_ != nullptr) {<br> (void)aclrtFree(modelWorkPtr_);<br> modelWorkPtr_ = nullptr;<br> modelWorkSize_ = 0;<br>}<br>// 4. 释放模型运行的权值内存<br>if (modelWeightPtr_ != nullptr) {<br> (void)aclrtFree(modelWeightPtr_);<br> modelWeightPtr_ = nullptr;<br> modelWeightSize_ = 0;<br>}<br> |
|---|
父主题: 模型推理
在线提单