本文將介紹基于OpenVINO的異步推理隊(duì)列類 AyncInferQueue,啟動(dòng)多個(gè)(>2)推理請(qǐng)求(infer request),幫助讀者在硬件投入不變的情況下,進(jìn)一步提升 AI 推理程序的吞吐量(Throughput)。
在閱讀本文前,請(qǐng)讀者先了解使用 start_async() 和 wait() 方法實(shí)現(xiàn)基于2個(gè)推理請(qǐng)求的異步推理實(shí)現(xiàn)方式。該異步推理實(shí)現(xiàn)方式相對(duì)于同步推理方式,極大提升了 AI 推理程序的吞吐量,但從任務(wù)管理器中可以看到,AI 推理硬件的利用率還有很大的提升空間。

這意味著,AI 推理硬件還有潛力可挖,可以通過(guò)進(jìn)一步提高推理請(qǐng)求個(gè)數(shù)來(lái)提升 AI 推理硬件的利用率,從而提高 AI 推理程序的吞吐量。
1.1
推理請(qǐng)求(InferRequest)和流(stream)
OpenVINO 運(yùn)行時(shí)(Runtime)用推理請(qǐng)求(infer request)來(lái)抽象在指定計(jì)算設(shè)備上運(yùn)行已編譯模型(Compiled_Model)。從編寫程序的角度看,推理請(qǐng)求是一個(gè)類,封裝了支持推理請(qǐng)求以同步或異步方式運(yùn)行的屬性和方法。
推理請(qǐng)求(InferRequest)類的詳細(xì)定義參考:
https://github.com/openvinotoolkit/openvino/blob/master/src/inference/include/openvino/runtime/infer_request.hpp#L34
推理請(qǐng)求的個(gè)數(shù),由開發(fā)者定義;但計(jì)算設(shè)備能并行處理的推理請(qǐng)求個(gè)數(shù),由硬件本身的處理單元(Processing Unit)決定。超過(guò)計(jì)算硬件并行處理數(shù)量的推理請(qǐng)求,會(huì)被計(jì)算硬件用隊(duì)列儲(chǔ)存起來(lái),當(dāng)計(jì)算硬件空閑后,隊(duì)列中的推理請(qǐng)求將被依次取出并執(zhí)行。
OpenVINO用流(stream)來(lái)抽象計(jì)算設(shè)備能并行處理推理請(qǐng)求的能力,通過(guò)屬性:“NUM_STREAMS”,可以獲取延遲優(yōu)先或吞吐量?jī)?yōu)先模式下的計(jì)算硬件支持的最優(yōu)streams數(shù)量,如下表所示。

:上述數(shù)據(jù)在蝰蛇峽谷上測(cè)得,CPU=i7-12700H, 集成顯卡=Iris Xe, 獨(dú)立顯卡=A770m
: GPU設(shè)備沒(méi)有INFERENCE_NUM_THREADS屬性
上述數(shù)據(jù)測(cè)試的源代碼如下,歡迎各位讀者在自己的硬件平臺(tái)上測(cè)試:
from openvino.runtime import Core, get_version
core = Core()
print(get_version())
print(core.available_devices)
device = device = ['GPU.0', 'GPU.1', 'CPU', 'AUTO', 'AUTO:GPU,-CPU'][0]
cfgs = {}
cfgs['PERFORMANCE_HINT'] = ['THROUGHPUT', 'LATENCY', 'CUMULATIVE_THROUGHPUT'][0]
net = core.compile_model("model.onnx",device,cfgs)
# Get Supported properties
supported_properties = net.get_property('SUPPORTED_PROPERTIES')
print(f'Support properties for {device}:', supported_properties)
opt_nireq = net.get_property('OPTIMAL_NUMBER_OF_INFER_REQUESTS')
print(f'OPTIMAL_NUMBER_OF_INFER_REQUESTS for {device}:', opt_nireq)
nstreams = net.get_property('NUM_STREAMS')
print(f'nstreams for {device}:', nstreams)
performance_hint_num_requests = net.get_property('PERFORMANCE_HINT_NUM_REQUESTS')
print(f'performance_hint_num_requests for {device}:', performance_hint_num_requests)
if device == "CPU":
# INFERENCE_NUM_THREADS
inference_num_threads = net.get_property('INFERENCE_NUM_THREADS')
print(f'inference_num_threads for {device}:', inference_num_threads)
else:
gpu_queue_priority = net.get_property('GPU_QUEUE_PRIORITY')
print(f'GPU queue priority for {device}:', gpu_queue_priority)
向右滑動(dòng)查看完整代碼
1.1.1
CPU 的流與推理請(qǐng)求
對(duì)于 CPU 來(lái)說(shuō),一個(gè)流(stream)只能服務(wù)一個(gè)推理請(qǐng)求。通過(guò)屬性ov::range_for_streams,可以查到 CPU 支持的流數(shù)量的范圍;流的數(shù)量無(wú)需開發(fā)者使用代碼顯示設(shè)置,OpenVINO 運(yùn)行時(shí)會(huì)根據(jù)延遲優(yōu)先或吞吐量?jī)?yōu)先來(lái)自動(dòng)設(shè)置。

1.1.2
GPU 的流與推理請(qǐng)求
對(duì)于 GPU 來(lái)說(shuō),一個(gè)流(stream)可以同時(shí)服務(wù)兩個(gè)推理請(qǐng)求。通過(guò)屬性 ov::range_for_streams,可以查到 GPU 支持的流數(shù)量的范圍:[1, 2];流的數(shù)量無(wú)需開發(fā)者使用代碼顯示設(shè)置,OpenVINO 運(yùn)行時(shí)會(huì)根據(jù)延遲優(yōu)先或吞吐量?jī)?yōu)先來(lái)自動(dòng)設(shè)置。

參考代碼:
https://www.jianshu.com/p/1748444e6a50
1.2
AsyncInferQueue類
OpenVINO運(yùn)行時(shí)(Runtime)提供 AsyncInferQueue 類來(lái)抽象并管理異步推理請(qǐng)求池,其常用方法和屬性有:
__init__(self, compiled_model, jobs = 0):創(chuàng)建AsyncInferQueue對(duì)象
set_callback(func_name):為推理請(qǐng)求池中所有的推理請(qǐng)求設(shè)置統(tǒng)一的回調(diào)函數(shù)
start_async(inputs, userdata = None):異步啟動(dòng)推理請(qǐng)求
wait_all():等待所有的推理請(qǐng)求執(zhí)行完畢
1.2.1
基于AsyncInferQueue類的
異步推理范例程序
基于 AsyncInferQueue 類 YOLOv5 模型的異步推理范例程序的核心代碼部分如下所示:
完整范例代碼請(qǐng)下載:yolov5_async_infer_queue.py
https://gitee.com/ppov-nuc/yolov5_infer/blob/main/yolov5_async_infer_queue.py
運(yùn)行代碼前,請(qǐng)參考運(yùn)行環(huán)境搭建流程。
...
def preprocess(frame):
# Preprocess the frame
letterbox_im, _, _= letterbox(frame, auto=False) # preprocess frame by letterbox
im = letterbox_im.transpose((2, 0, 1))[::-1] # HWC to CHW, BGR to RGB
im = np.float32(im) / 255.0 # 0 - 255 to 0.0 - 1.0
blob = im[None] # expand for batch dim
return blob, letterbox_im.shape[:-1], frame.shape[:-1]
def postprocess(ireq: InferRequest, user_data: tuple):
result = ireq.results[ireq.model_outputs[0]]
dets = non_max_suppression(torch.tensor(result))[0].numpy()
bboxes, scores, class_ids= dets[:,:4], dets[:,4], dets[:,5]
# rescale the coordinates
bboxes = scale_coords(user_data[1], bboxes, user_data[2]).astype(int)
print(user_data[0]," "+f"{ireq.latency:.3f}"+" ", class_ids)
return
# Step1:Initialize OpenVINO Runtime Core
core = Core()
# Step2: Build compiled model
device = device = ['GPU.0', 'GPU.1', 'CPU', 'AUTO', 'AUTO:GPU,-CPU'][0]
cfgs = {}
cfgs['PERFORMANCE_HINT'] = ['THROUGHPUT', 'LATENCY', 'CUMULATIVE_THROUGHPUT'][0]
net = core.compile_model("yolov5s.xml",device,cfgs)
output_node = net.outputs[0]
b,n,input_h,input_w = net.inputs[0].shape
# Step3: Initialize InferQueue
ireqs = AsyncInferQueue(net)
print('Number of infer requests in InferQueue:', len(ireqs))
# Step3.1: Set unified callback on all InferRequests from queue's pool
ireqs.set_callback(postprocess)
# Step4: Read the images
image_folder = "./data/images/"
image_files= os.listdir(image_folder)
print(image_files)
frames = []
for image_file in image_files:
frame = cv2.imread(os.path.join(image_folder, image_file))
frames.append(frame)
# 4.1 Warm up
for id, _ in enumerate(ireqs):
# Preprocess the frame
start = perf_counter()
blob, letterbox_shape, frame_shape = preprocess(frames[id % 4])
end = perf_counter()
print(f"Preprocess {id}: {(end-start):.4f}.")
# Run asynchronous inference using the next available InferRequest from the pool
ireqs.start_async({0:blob},(id, letterbox_shape, frame_shape))
ireqs.wait_all()
# Step5: Benchmark the Async Infer
start = perf_counter()
in_fly = set()
latencies = []
niter = 16
for i in range(niter):
# Preprocess the frame
blob, letterbox_shape, frame_shape = preprocess(frames[i % 4])
idle_id = ireqs.get_idle_request_id()
if idle_id in in_fly:
latencies.append(ireqs[idle_id].latency)
else:
in_fly.add(idle_id)
# Run asynchronous inference using the next available InferRequest from the pool
ireqs.start_async({0:blob},(i, letterbox_shape, frame_shape) )
ireqs.wait_all()
向右滑動(dòng)查看完整代碼
運(yùn)行結(jié)果如下所示,與基于單個(gè)推理請(qǐng)求的start_async()+wait()實(shí)現(xiàn)方式相比,基于 AsyncInferQueue 類的 YOLOv5 模型的異步推理程序的吞吐量明顯得到提升。

1.3
結(jié)論
使用 OpenVINO Runtime 的 AsyncInferQueue 類,可以極大提升 AI 推理程序的吞出量。
審核編輯 :李倩
-
AI
+關(guān)注
關(guān)注
89文章
37540瀏覽量
293350 -
程序
+關(guān)注
關(guān)注
117文章
3835瀏覽量
84652 -
任務(wù)管理器
+關(guān)注
關(guān)注
0文章
15瀏覽量
7911
原文標(biāo)題:使用AsyncInferQueue進(jìn)一步提升AI推理程序的吞吐量 | 開發(fā)者實(shí)戰(zhàn)
文章出處:【微信號(hào):英特爾物聯(lián)網(wǎng),微信公眾號(hào):英特爾物聯(lián)網(wǎng)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
數(shù)據(jù)吞吐量提升!面向下一代音頻設(shè)備,藍(lán)牙HDT、星閃、Wi-Fi、UWB同臺(tái)競(jìng)技
使用羅德與施瓦茨CMX500的吞吐量應(yīng)用層測(cè)試方案
蔚來(lái)進(jìn)一步拓展其全球業(yè)務(wù)
信而泰×DeepSeek:AI推理引擎驅(qū)動(dòng)網(wǎng)絡(luò)智能診斷邁向 “自愈”時(shí)代
軟通動(dòng)力與中國(guó)聯(lián)通合作關(guān)系進(jìn)一步深化
晶圓級(jí)封裝:連接密度提升的關(guān)鍵一步
CY7C65211 作為 SPI 從機(jī)模式工作時(shí)每秒的最大吞吐量是多少?
如何在Visual Studio 2022中運(yùn)行FX3吞吐量基準(zhǔn)測(cè)試工具?
FX3進(jìn)行讀或?qū)懖僮鲿r(shí)CS信號(hào)拉低,在讀或?qū)懲瓿珊驝S置高,對(duì)吞吐量有沒(méi)有影響?
三星攜Galaxy AI和以軟件為中心的網(wǎng)絡(luò)技術(shù)亮相MWC 2025,進(jìn)一步強(qiáng)化移動(dòng)AI領(lǐng)先優(yōu)勢(shì)
迅為2K0300開發(fā)板進(jìn)一步刨析,打造HMI一體機(jī)產(chǎn)品的靈活優(yōu)勢(shì)
NVIDIA RTX AI PC如何解鎖AI智能體
ADC芯片的采樣率為100MSPS,位寬16位,那么吞吐量是多少?
亞馬遜云科技推出全新數(shù)據(jù)中心組件,支持AI創(chuàng)新并進(jìn)一步提升能效
邊緣AI如何提升日常體驗(yàn)

使用AsyncInferQueue進(jìn)一步提升AI推理程序的吞吐量
評(píng)論