Skip to main content

多Device切换

执行固定Shape算子示例代码

前提条件

在调用AscendCL接口执行固定Shape算子前,需提前编译算子。此处借助ATC工具编译Add算子的模型文件为例:

  1. 先构造该算子的描述文件(*.json文件,描述输入输出Tensor描述、算子属性等)。

    Add算子的描述文件示例如下:

    [
    {
    "op": "Add",
    "input_desc": [
    {
    "format": "ND",
    "shape": [8, 16],
    "type": "int32"
    },
    {
    "format": "ND",
    "shape": [8, 16],
    "type": "int32"
    }
    ],
    "output_desc": [
    {
    "format": "ND",
    "shape": [8, 16],
    "type": "int32"
    }
    ]
    }
    ]
  2. 借助ATC工具,将该算子描述文件编译成单算子模型文件(*.om文件),再分别调用AscendCL接口加载om模型文件、执行算子。

    ATC工具的命令示例如下:

    atc --singleop=$HOME/singleop/add.json --output=$HOME/singleop/out/op_model --soc_version=<soc_version>

    关键参数解释如下(详细参数说明,请参见《ATC工具使用指南》。):

    • --singleop:单算子描述文件(json格式)的路径。

    • --output:存放单算子模型文件的目录。

    • --soc_version:昇腾AI处理器的版本。

      进入“CANN软件安装目录/compiler/data/platform_config”目录,".ini"文件的文件名即为昇腾AI处理器的版本,请根据实际情况选择。

示例代码

以下是单算子加载、执行关键步骤的代码示例,不可以直接拷贝编译运行,仅供参考,调用接口后,需增加异常处理的分支,并记录报错日志、提示日志,此处不一一列举。完整代码,您可以从acl_execute_add样例中查看。

<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><br>// 1.AscendCL初始化<br>aclRet = aclInit(nullptr);<br>// 2.运行管理资源申请(使用默认Context、默认Stream,默认Stream在作为其它接口入参时,可传空指针)<br>aclRet = aclrtSetDevice(0);<br>获取软件栈的运行模式,不同运行模式影响后续的接口调用流程(例如是否进行数据传输等)<br>aclrtRunMode runMode;<br>bool g_isDevice = false;<br>aclError aclRet = aclrtGetRunMode(&runMode);<br>g_isDevice = (runMode == ACL_DEVICE);<br>// 3.加载单算子模型文件(*.om文件)<br>// 该目录相对可执行文件所在的目录,例如,编译出来的可执行文件存放在out目录下,此处就表示out/op_models目录<br>aclRet = aclopSetModelDir("op_models");<br>// 4.执行算子<br>// opType表示算子类型名称,例如Add<br>// numInputs表示算子输入个数,例如Add算子是2个输入<br>// inputDesc表示算子输入tensor描述的数组,描述每个输入的format、shape、数据类型<br>// inputs表示算子输入tensor数据<br>// numOutputs表示算子输出个数,例如Add算子是1个输出<br>// outputDesc表示算子输出tensor描述的数组,描述每个输出的format、shape、数据类型<br>// outputs表示算子输出tensor数据<br>// attr表示算子属性,如果算子没有属性,也需要调用aclopCreateAttr接口创建aclopAttr类型的数据<br>// stream用于维护一些异步操作的执行顺序<br>aclopExecuteV2(opType, numInputs, inputDesc, inputs, <br> numOutputs, outputDesc, outputs, attr, nullptr);<br>// 处理执行算子后的输出数据,例如在屏幕上显示、写入文件等,由用户根据实际情况自行实现<br>// ......<br>// 阻塞应用运行,直到指定Stream中的所有任务都完成<br>aclrtSynchronizeStream(nullptr);<br>// 5. 释放运行管理资源(默认Context、Stream无需用户释放,调用aclrtResetDevice接口后自动释放)<br>aclRet = aclrtResetDevice(0);<br>// 6.AscendCL去初始化<br>aclRet = aclFinalize();<br>// ....<br>

父主题: 单算子模型执行

执行动态Shape算子示例代码

基本原理

对于支持动态Shape的算子:

示例代码

调用接口后,需增加异常处理的分支,并记录报错日志、提示日志,此处不一一列举。以下是关键步骤的代码示例,不可以直接拷贝编译运行,仅供参考。

<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>cpp<br>// ......<br>const char *opType;<br>int numInputs;<br>aclTensorDesc *inputDesc[2];<br>aclDataBuffer *inputs[2];<br>int numOutputs;<br>aclTensorDesc *outputDesc[1];<br>aclopAttr *attr;<br>aclError ret = aclopInferShape(opType, numInputs, inputDesc, inputs,numOutputs, outputDesc, attr);<br>std::vector<std::vector<int64_t>> tensorDims; // inferShape之后的输出tensor的Shape<br>// 循环算子的每一个输出,推导或预估Shape值:<br>for (int index = 0; index < numOutputs; ++index) &#123;<br> std::vector<int64_t> dimSize; // 表示执行算子时,输出的shape<br> size_t dimNums = aclGetTensorDescNumDims(outputDesc[index]);<br> // 表示动态Shape场景下维度个数未知,该场景预留<br> if (dimNums == ACL_UNKNOWN_RANK) &#123;<br> // 由用户预估最大Shape值max shape<br> dimSize.push_back(max_shape);<br> &#125; else &#123;<br> for (size_t i = 0; i < dimNums; ++i) &#123;<br> int64_t dim;<br> ret = aclGetTensorDescDimV2(outputDesc[index], i, &dim);<br> // 表示动态Shape场景下维度值是动态的<br> if(dim == -1) &#123;<br> int64_t dimRange[2];<br> // 获取Shape范围,使用该范围中的Shape最大值来构造输出tensorDesc,作为aclopExecuteV2的输入<br> ret = aclGetTensorDescDimRange(outputDesc[index], i, 2, dimRange);<br> dim = dimRange[1];<br> &#125;<br> dimSize.push_back(dim);<br> &#125;<br> &#125;<br> tensorDims.push_back(dimSize);<br>&#125;<br>// 构造算子输入tensorDesc和输入tensor,作为aclOpExecuteV2的输入<br>aclTensorDesc *inputDescNew[2];<br>aclDataBuffer *inputsNew[2];<br>aclDataBuffer *outputsNew[1];<br>// 以上给出了执行算子时输出的shape, 根据tensorDims中的dims构造输出tensorDesc(即outputDescNew参数值), 用于调用aclopExecuteV2<br>ret = aclopExecuteV2(opType, numInputs, inputDescNew, inputsNew, numOutputs, outputDescNew, outputsNew, attr, stream);<br>// 针对上面用户预估Shape值以及使用Shape范围中的最大Shape的场景,在算子执行结束后,需增加下面的调用,获取准确的shape:<br>// for 循环每一个输出的tensorDesc<br>std::vector<std::vector<int64_t>> outTensorDims; // 准确的输出tensorShape<br>for (int index = 0; index < numOutputs; ++index) &#123;<br> std::vector<int64_t> dimSize;<br> int dimNums = aclGetTensorDescNumDims(outputDescNew[index]);<br> for (int i = 0; i < dimNums; i++)&#123;<br> int64_t dim;<br> ret = aclGetTensorDescDimV2(outputDescNew[index], i, &dim);<br> dimSize.push_back(dim);<br> &#125;<br> outTensorDims.push_back(dimSize);<br>&#125;<br>// ......<br>

父主题: 单算子模型执行

内存二次分配管理

用户内存管理有两种管理方式:

  • 独立内存管理,根据需要单独申请所需的内存,内存不做拆分或者二次分配。
  • 内存池管理内存,用户一次性申请一块较大内存,并在使用时从这块较大内存中二次分配所需内存。

在内存二次分配时,使用如下接口从内存池申请对应内存,由于接口对申请的内存地址、大小有约束,在内存池管理时,需要关注,否则容易出现内存越界。

内存管理的总体说明请参见总体说明

接口用途输入内存/输出内存
aclrtMemcpyAsync实现异步内存复制。- 调用本接口进行内存复制时,源地址和目的地址都必须64字节对齐。
aclrtMalloc在Device上申请size大小的线性内存,该接口对用户申请的size向上对齐成32字节整数倍后再多加32字节,并通过*devPtr返回已分配内存的指针。同步接口。- 若用户使用本接口申请大块内存并自行划分、管理内存时,建议使用aclrtMallocAlign32接口,该接口相比aclrtMalloc接口,只会对用户申请的size向上对齐成32字节整数倍,不会再多加32字节。 不管是aclrtMalloc接口,还是aclrtMallocAlign32接口,每段内存需同时满足以下需求:
- 内存大小向上对齐成32整数倍+32字节(m=ALIGN_UP[len,32]+32字节);
- 内存起始地址需满足64字节对齐(ALIGN_UP[m,64])。(说明:len表示某段内存的大小,ALIGN_UP[len,k]表示向上按k字节对齐:((len-1)/k+1)*k。)
acldvppMalloc该接口主要用于分配内存给Device侧媒体数据处理时使用,申请的大页内存满足数据处理的要求(例如,内存首地址128字节对齐),同步接口。 媒体数据处理各功能的详细介绍请参见媒体数据处理V1媒体数据处理的输出作为模型推理的输入时,若用户使用本接口申请大块内存并自行划分、管理内存时,每段内存需同时满足以下要求:
- 内存大小向上对齐成32整数倍+32字节(m=ALIGN_UP[len,32]+32字节);
- 内存起始地址需满足128字节对齐(ALIGN_UP[m,128])。
(说明:len表示某段内存的大小,ALIGN_UP[len,k]表示向上按k字节对齐:((len-1)/k+1)*k。)
hi_mpi_dvpp_malloc申请Device上的内存,申请的内存满足媒体数据处理的要求(例如,内存首地址128字节对齐)。 媒体数据处理各功能的详细介绍请参见媒体数据处理V2媒体数据处理的输出作为模型推理的输入时,若用户使用本接口申请大块内存并自行划分、管理时,每段内存需同时满足以下要求:
- 内存大小向上对齐成32整数倍+32字节(m=ALIGN_UP[len,32]+32字节);
- 内存起始地址需满足128字节对齐(ALIGN_UP[m,128])。
(说明:len表示某段内存的大小,ALIGN_UP[len,k]表示向上按k字节对齐:((len-1)/k+1)*k。)
aclrtMallocHost应用在Host上运行时,调用该接口申请的是Host内存,由系统保证内存首地址64字节对齐。应用在Device上运行时,调用该接口申请的是Device内存,且Device上的内存按普通页申请,如需首地址64字节对齐,需要用户自行处理对齐。同步接口。- 若用户使用本接口申请大块内存并自行划分、管理内存时,每段内存需同时满足以下需求:
- 内存大小向上对齐成32整数倍+32字节(m=ALIGN_UP[len,32]+32字节);
- 内存起始地址需满足64字节对齐(ALIGN_UP[m,64])。(说明:len表示某段内存的大小,ALIGN_UP[len,k]表示向上按k字节对齐:((len-1)/k+1)*k。)
aclrtMallocCached在Device上申请size大小的线性内存,通过*devPtr返回已分配内存的指针,该接口在任何场景下申请的内存都是支持cache缓存。同步接口。其它约束与aclrtMalloc接口相同。

在计算机视觉领域,一般涉及使用媒体数据处理功能,因此会涉及以上多种内存申请接口,内存首地址涉及64字节或128字节对齐,为方便统一管理,内存首地址对齐值建议选取较大的,比如内存首地址128字节对齐。

关于媒体数据处理时自行管理内存时的典型场景如下,媒体数据处理的功能点介绍请参见媒体数据处理V1

图1 VDEC场景

图2 JPEGD场景

父主题: 扩展更多特性

多Device切换

开发应用时,如果涉及多Device之间的任务等待,则应用程序中必须包含相关的代码逻辑,关于该场景的接口调用流程,请依次参见AscendCL接口调用流程以及本节中的说明。

图1 同步等待流程_多Device场景

父主题: 扩展更多特性

多模型串联推理

多模型推理的基本流程与单模型类似,请参见基础推理应用

多模型推理与单模型推理在AscendCL接口使用上的不同点如下:

  • 关于模型加载,如果涉及多个模型,需调用多次模型加载接口。模型加载请参见模型加载

  • 关于模型执行,如果涉及多个模型,需调用多次模型执行接口。模型执行请参见模型执行

    例如,调用aclmdlExecute接口实现同步模型推理。

父主题: 扩展更多特性

多Batch模型推理

多Batch推理的基本流程与单Batch类似,请参见基础推理应用

多Batch推理与单Batch推理的不同点在于:

  • 多Batch场景下,在构建模型时,使用ATC工具的input_shape参数需根据实际的Batch数设置batch size值,详细说明请参见《ATC工具使用指南》。

  • 在推理前,需要编写一段代码,实现逻辑为:等输入数据满足多Batch(例如:8Batch)的要求,申请Device上的内存存放多Batch的数据,作为模型推理的输入。如果最后循环遍历所有的输入数据后,仍不满足多Batch的要求,则直接将剩余数据作为模型推理的输入。

调用接口后,需增加异常处理的分支,并记录报错日志、提示日志,此处不一一列举。以下是关键步骤的代码示例,不可以直接拷贝编译运行,仅供参考,此处以8Batch为例:

<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>cpp<br>uint32_t batchSize = 8;<br>uint32_t deviceNum = 1;<br>uint32_t deviceId = 0;<br>// 获取模型第一个输入的大小<br>uint32_t modelInputSize = aclmdlGetInputSizeByIndex(modelDesc, 0);<br>// 获取每个Batch输入数据的大小<br>uint32_t singleBuffSize = modelInputSize / batchSize;<br>// 定义该变量,用于累加batch size是否达到8Batch<br>uint32_t cnt = 0;<br>// 定义该变量,用于描述每个文件读入内存时的位置偏移<br>uint32_t pos = 0;<br>void* p_batchDst = NULL;<br>std::vector<std::string>inferFile_vec;<br>for (int i = 0; i < files.size(); ++i) <br> &#123;<br> // 每8个文件,申请一次Device上的内存,存放8Batch的输入数据 <br> if (cnt % batchSize == 0)<br> &#123;<br> pos = 0;<br> inferFile_vec.clear();<br> // 申请Device上的内存<br> ret = aclrtMalloc(&p_batchDst, modelInputSize, ACL_MEM_MALLOC_NORMAL_ONLY);<br> &#125;<br> // TODO: 从某个目录下读入文件,计算文件大小fileSize<br> <br> // 根据文件大小,申请内存,存放文件数据<br> ret = aclrtMallocHost(&p_imgBuf, fileSize);<br> // 将数据传输到Device的内存<br> ret = aclrtMemcpy((uint8_t *)p_batchDst + pos, fileSize, p_imgBuf, fileSize, ACL_MEMCPY_HOST_TO_DEVICE);<br> pos += fileSize;<br> // 及时释放不使用的内存<br> aclrtFreeHost(p_imgBuf);<br> // 将第i个文件存入vector中,同时cnt+1<br> inferFile_vec.push_back(files[i]);<br> cnt++;<br> // 每8Batch的输入数据送给模型推理进行推理<br> if (cnt % batchSize == 0)<br> &#123;<br> // TODO: 创建aclmdlDataset、aclDataBuffer类型的数据,用于描述模型的输入、输出数据<br> // TODO: 调用aclmdlExecute接口执行模型推理<br> // TODO: 推理结束后,调用aclrtFree接口释放Device上的内存<br> &#125;<br> &#125;<br>// 如果最后循环遍历所有的输入数据后,仍不满足多Batch的要求,则直接将剩余数据作为模型推理的输入。<br>if (cnt % batchSize != 0)<br> &#123;<br> // TODO: 创建aclmdlDataset、aclDataBuffer类型的数据,用于描述模型的输入、输出数据<br> // TODO: 调用aclmdlExecute接口执行模型推理<br> // TODO: 推理结束后,调用aclrtFree接口释放Device上的内存<br> &#125;<br>

父主题: 扩展更多特性

队列方式模型推理

关于模型推理场景下的主要接口调用流程,请参见AscendCL接口调用流程

基本原理

:::note 说明 如果涉及多线程,当模型有多个输入时,多个输入数据的入队任务(即调用acltdtEnqueueData接口)必须在同一个线程中;当模型有多个输出时,多个输出数据的出队任务(即调用acltdtDequeueData接口)必须在同一个线程中。 :::

示例代码

调用接口后,需增加异常处理的分支,并记录报错日志、提示日志,此处不一一列举。以下是关键步骤的代码示例,不可以直接拷贝编译运行,仅供参考。

<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>117<br>118<br>119<br>120<br>121<br>122<br>123<br>124<br>cpp<br>#include "acl/acl.h"<br>// ......<br>// 1. AscendCL初始化<br>// 此处的..表示相对路径,相对可执行文件所在的目录<br>// 例如,编译出来的可执行文件存放在out目录下,此处的..就表示out目录的上一级目录<br>const char *aclConfigPath = "../src/acl.json";<br>aclInit(aclConfigPath);<br>// 2. 申请运行管理资源<br>extern bool g_isDevice;<br>aclrtSetDevice(deviceId_);<br>aclrtCreateContext(&context_, deviceId_);<br>aclrtCreateStream(&stream_);<br>// 获取当前昇腾AI软件栈的运行模式,根据不同的运行模式,后续的内存申请、内存复制等接口调用方式不同<br>aclrtRunMode runMode;<br>aclError ret = aclrtGetRunMode(&runMode);<br>g_isDevice = (runMode == ACL_DEVICE);<br>// 3. 加载并执行模型<br>// 此处的..表示相对路径,相对可执行文件所在的目录<br>// 例如,编译出来的可执行文件存放在out目录下,此处的..就表示out目录的上一级目录<br>const char* omModelPath = "../model/resnet50.om"<br>// 3.1 创建模型的输入队列,如果模型有多个输入,则创建多个输入队列,此处以一个输入为例<br>acltdtQueueAttr *attr = acltdtCreateQueueAttr();<br>uint32_t *inputQueueList = new (nothrow) uint32_t[num];<br>int32_t inputNum = 1;<br>for (int n = 0; n < inputNum; n++) &#123;<br> uint32_t inputQid;<br> ret = acltdtCreateQueue(attr, &inputQid);<br> inputQueueList[n] = inputQid;<br> &#125;<br>// 3.2 创建模型的输出队列,如果模型有多个输出,则创建多个输出队列,此处以一个输出为例<br>uint32_t *outputQueueList = new (nothrow) uint32_t[num];<br>int32_t outputNum = 1;<br>for (int n = 0; n < outputNum; n++) &#123;<br> uint32_t outputQid;<br> ret = acltdtCreateQueue(attr, &outputQid);<br> outputQueueList[n] = outputQid;<br> &#125;<br>// 3.3 加载模型<br>uint32_t modelId;<br>ret= aclmdlLoadFromFileWithQ(modelPath, &modelId, <br> inputQueueList, inputNum, outputQueueList, outputNum);<br>// 3.4 根据模型的ID,获取该模型的描述信息<br>aclmdlDesc *modelDesc = aclmdlCreateDesc();<br>ret = aclmdlGetDesc(modelDesc, modelId);<br>// 3.5 获取模型的输入内存大小,如果模型有多个输入,则需要获取每个输入的内存大小,此处以一个输入为例<br>size_t inputSize = aclmdlGetInputSizeByIndex(modelDesc, 0);<br>// 3.6 加载测试图片数据,进行推理,并对推理结果数据进行后处理<br>string testFile[] = &#123;<br> "../data/dog1_1024_683.bin",<br> "../data/dog2_1024_683.bin"<br>&#125;;<br>for (size_t index = 0; index < sizeof(testFile) / sizeof(testFile[0]); ++index) &#123;<br> uint32_t devBufferSize;<br> void *picDevBuffer = nullptr;<br> // 自定义函数ReadBinFile,根据昇腾AI软件栈的运行模式,申请对应的内存,再调用C++标准库中的函数将图片数据读入内存<br> ret = Utils::ReadBinFile(testFile[index], picDevBuffer, devBufferSize);<br> // 将模型输入数据传入队列中,执行模型推理,-1是表示阻塞程序直到输入数据入队完成<br> ret = acltdtEnqueueData(inputQid, picDevBuffer, devBufferSize, nullptr, 0, -1, 0);<br> // 获取每个输出的大小<br> size_t dataSize = aclmdlGetOutputSizeByIndex(modelDesc, 0);<br> void *data = nullptr;<br> size_t retDataSize = 0;<br> // 为模型输出数据申请内存<br> if (!g_isDevice) &#123;<br> aclError aclRet = aclrtMallocHost(&data, dataSize);<br> &#125; else &#123;<br> aclError aclRet = aclrtMalloc(&data, dataSize, ACL_MEM_MALLOC_NORMAL_ONLY);<br> &#125;<br> // 等待模型推理执行完毕,从输出内存中获取结果数据,-1是表示阻塞程序直到推理输出数据入队完成<br> ret = acltdtDequeueData(outputQid, data, dataSize, &retDataSize, nullptr, 0, -1);<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) &#123;<br> resultMap[*outData] = j;<br> outData++;<br> &#125;<br> int cnt = 0;<br> for (auto it = resultMap.begin(); it != resultMap.end(); ++it) &#123;<br> // print top 5<br> if (++cnt > 5) &#123;<br> break;<br> &#125;<br> INFO_LOG("top %d: index[%d] value[%lf]", cnt, it->second, it->first);<br> &#125;<br> if (!g_isDevice) &#123;<br> aclError aclRet = aclrtFreeHost(picDevBuffer);<br> aclrtFreeHost(data);<br> &#125; else &#123;<br> aclError aclRet = aclrtFree(picDevBuffer);<br> aclrtFree(data); <br> &#125;<br>&#125;<br>// 4. 卸载模型,并释放模型推理相关资源<br>aclmdlUnload(modelId);<br>aclmdlDestroyDesc(modelDesc);<br>acltdtDestroyQueue(inputQid);<br>acltdtDestroyQueue(outputQid);<br>acltdtDestroyQueueAttr(attr);<br>// 6. 释放运行管理资源<br>aclrtDestroyStream(stream_);<br>aclrtDestroyContext(context_);<br>aclrtResetDevice(deviceId_);<br>// 7. AscendCL去初始化<br>aclFinalize();<br>// ......<br>

父主题: 扩展更多特性

在线提单