多模型串联推理
内存二次分配管理
用户内存管理有两种管理方式:
- 独立内存管理,根据需要单独申请所需的内存,内存不做拆分或者二次分配。
- 内存池管理内存,用户一次性申请一块较大内存,并在使用时从这块较大内存中二次分配所需内存。
在内存二次分配时,请使用如下接口从内存池申请对应内存。由于各接口对申请的内存地址、大小有约束,在内存池管理时,需要对该情况关注处理,否则容易出现内存越界。
内存管理的总体说明请参见总体说明。
| 接口 | 用途 | 输入内存/输出内存 |
|---|---|---|
| acl.rt.memcpy_async | 实现异步内存复制。 | 调用本接口进行内存复制时,源地址和目的地址都必须64字节对齐。 |
| acl.rt.malloc | 在Device上申请size大小的线性内存,该接口对用户申请的size向上对齐成32字节整数倍后再多加32字节,并通过“dev_ptr”返回已分配内存的指针地址。 | 若用户使用本接口申请大块内存并自行划分、管理内存时,每段内存需同时满足以下需求: - 内存大小向上对齐成32整数倍加32字节(m = ALIGN_UP[len, 32] + 32字节)。 - 内存起始地址需满足64字节对齐(ALIGN_UP[m, 64])。 (说明:len 表示某段内存的大小,ALIGN_UP[len, k]表示向上按k字节对齐:((len - 1) / k + 1) * k。) |
| acl.rt.malloc_host | 应用在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。) |
| acl.rt.malloc_cached | 申请Device上的内存,同步接口,该接口在任何场景下申请的内存都是支持cache缓存。在Device上申请size大小的线性内存,通过dev_ptr返回已分配内存的指针地址。 | 其它约束与acl.rt.malloc接口相同。 |
计算机视觉领域一般涉及使用媒体数据处理功能,因此会涉及以上多种内存申请接口。内存首地址涉及64字节或128字节对齐,为方便统一管理,内存首地址对齐值建议选取较大项,比如内存首地址128字节对齐。
关于媒体数据处理时自行管理内存时的典型场景如下,媒体数据处理的功能点介绍请参见媒体数据处理V1。
图1 VDEC场景

图2 JPEGD场景

父主题: 扩展更多特性
多Device场景
开发应用时,如果涉及多Device之间的任务等待,则应用程序中必须包含相关的代码逻辑。关于该场景的接口调用流程,请依次参见pyACL接口调用流程以及本节中的说明。
图1 同步等待流程_多Device场景

- 在多Device时,利用Context切换(调用acl.rt.set_context接口)来切换Device,比使用acl.rt.set_device接口效率高。
- 调用acl.rt.synchronize_device接口等待Device上的计算任务结束。
- 模型加载与执行的流程请参见开发基础推理应用。
- 算子加载与执行的流程请参见单算子调用。
父主题: 扩展更多特性
多模型串联推理
多模型推理的基本流程与单模型类似,请参见模型推理基本场景。
多模型推理与单模型推理的不同点如下:
-
关于模型加载,如果涉及多个模型,需调用多次模型加载接口。模型加载请参见模型加载。
-
关于模型执行,如果涉及多个模型,需调用多次模型执行接口。模型执行请参见模型执行。
例如,调用acl.mdl.execute接口实现同步模型推理。
父主题: 扩展更多特性
多Batch模型推理
多Batch推理的基本流程与单Batch类似,请参见模型推理基本场景。
多Batch推理与单Batch推理的不同点在于:
- 多Batch场景下,在构建模型时,使用ATC工具的input_shape参数需设置具体的Batch数,详细说明请参见《ATC工具使用指南》。
- 在推理前,需要编写一段代码逻辑:等输入数据满足多Batch(例如:8Batch)的要求,申请Device上的内存存放多Batch的数据,作为模型推理的输入。如果最后循环遍历所有的输入数据后,仍不满足多Batch的要求,则直接将剩余数据作为模型推理的输入。
调用接口后,需增加异常处理的分支,并记录报错日志、提示日志,此处不一一列举。以下是关键步骤的代码示例,不可以直接拷贝运行,仅供参考,此处以8Batch为例:
batch_size = 8
device_num = 1
device_id = 0
## 获取模型第一个输入的大小。
model_input_size = acl.mdl.get_input_size_by_index(model_desc, 0)
## 获取每个Batch输入数据的大小。
single_buff_size = model_input_size / batch_size
## 定义该变量,用于累加Batch数是否达到8Batch。
cnt = 0
## 定义该变量,用于描述每个文件读入内存时的位置偏移。
pos = 0
infer_file_vec = []
for i in len(files_list):
# 每8个文件,申请一次Device上的内存,存放8Batch的输入数据。
if cnt % batch_size == 0:
pos = 0
infer_file_vec.clear()
# 申请Device上的内存。
p_batch_dst, ret = acl.rt.malloc(model_input_size, ACL_MEM_MALLOC_NORMAL_ONLY)
# TODO: 从某个目录下读入文件,计算文件大小file_size。
# 根据文件大小,申请Host上的内存,存放文件数据。
p_img_buf, ret = acl.rt.malloc_host(file_size)
# 将Host上的文件数据复制到Device的内存中。
ret = acl.rt.memcpy(p_batch_dst + pos, file_size, p_img_buf, file_size, ACL_MEMCPY_HOST_TO_DEVICE)
pos += file_size
# 释放Host上的内存。
acl.rt.free_host(p_img_buf)
# 将第i个文件存入vector中,同时cnt++。
infer_file_vec.append(files_list[i])
cnt++
# 每8Batch的输入数据送给模型推理进行推理。
if cnt % batch_size == 0:
# TODO: 创建aclmdlDataset、aclDataBuffer类型的数据,用于描述模型的输入、输出数据。
# TODO: 调用acl.mdl.execute接口执行模型推理。
# TODO: 推理结束后,调用acl.rt.free接口释放Device上的内存。
## 如果最后循环遍历所有的输入数据后,仍不满足多Batch的要求,则直接将剩余数据作为模型推理的输入。
if cnt % batch_size != 0:
# TODO: 创建aclmdlDataset、aclDataBuffer类型的数据,用于描述模型的输入、输出数据。
# TODO: 调用acl.mdl.execute接口执行模型推理。
# TODO: 推理结束后,调用acl.rt.free接口释放Device上的内存。
## ......
父主题: 扩展更多特性
接口调用流程
开发应用时,如果涉及异步场景下的同步等待,则应用程序中必须包含相关的代码逻辑,关于该场景的接口调用流程,请参见下图。
图1 同步等待流程_Callback场景

关键接口说明如下:
-
回调函数需由用户提前创建,用于获取并处理模型推理或算子执行的结果。
-
线程需由用户提前创建,并自定义线程函数,在线程函数内调用acl.rt.process_report接口,设置超时时间,等待acl.rt.launch_callback接口下发的回调任务执行。
-
调用acl.rt.subscribe_report接口:指定处理Stream上回调函数的线程,线程与2中创建的线程保持一致。
-
异步推理时调用acl.mdl.execute_async接口。
对于异步接口,还需调用acl.rt.synchronize_stream接口阻塞应用程序运行,直到指定Stream中的所有任务都完成。
用户可以在acl.rt.synchronize_stream接口之后一次性获取所有图片的异步推理结果,但如果图片数据量较大的情况下,需要等待的时间比较长,这时可以使用Callback功能,每隔一段时间下发一次Callback任务,获取前一段时间内的异步推理结果。
-
调用acl.rt.launch_callback接口:在Stream的任务队列中下发一个函数回调任务,系统内部在执行到该回调任务时,会在Stream上订阅的线程(通过acl.rt.subscribe_report接口订阅的线程)中执行回调函数,回调函数与1中保持一致。
每调用一次acl.rt.launch_callback接口,就会下发一个回调函数任务。
-
调用acl.rt.unsubscribe_report接口:取消线程订阅(Stream上的回调函数不再由指定线程处理)。
父主题: 模型异步推理
在线提单