最近讀到這么一篇文章 Automatic Multi-Device Inference with Intel Distribution of OpenVINO toolkit ,號稱使用CPU/GPU協同運算做推理,可以大幅度提高推理能力。
以mobilenet-ssd為例,文中附上了一個性能資料對比

CPU/GPU一起推理后的性能相對只用CPU推理,性能提高了到了0.79/0.64=1.234 也就是提高了大約23.4%
下面我們來實測一下
所謂的混合模式,實際就是在step 7的時候,呼叫ie.LoadNetwork時引數device_name傳進去的引數為"MULTI:CPU, GPU", 這樣LoadNetwork會把模型資料同時加載進CPU, GPU或者其他指定的硬體里。
// ----------------- 7. Loading the model to the device --------------------------------------------------------
next_step();
std::map<std::string, std::string> config = {{ CONFIG_KEY(PERF_COUNT), perf_counts ? CONFIG_VALUE(YES) :
CONFIG_VALUE(NO) }};
startTime = Time::now();
ExecutableNetwork exeNetwork = ie.LoadNetwork(cnnNetwork, device_name, config);
duration_ms = float_to_string(get_total_ms_time());
slog::info << "Load network took " << duration_ms << " ms" << slog::endl;
if (statistics)
statistics->addParameters(StatisticsReport::Category::EXECUTION_RESULTS,
{
{"load network time (ms)", duration_ms}
});
首先是FP32模型, 當Batch size =1時
benchmark_app設定CPU_THROUGHPUT_STREAMS = CPU_THROUGHPUT_AUTO,GPU_THROUGHPUT_STREAMS = GPU_THROUGHPUT_AUTO時,得到openvino建議的CPU的nstream數為4, GPU的nstream數為2, 下面對應的number of ireq并發數為8 , 即同時并發8個推理請求

此時FPS為102, 相對與CPU的97FPS, 性能提升并不大,同時可以看到統計8路infer request的latency時間明顯不一樣,有4路比較慢,有4路比較快,速度基本相差1倍左右,應該是CPU和GPU推理的速度不同;同時也可以看出,8個infer request推理的時間也是動態變化的,并不是某個infer request句柄就是固定對應的CPU推理,某個句柄就是固定對應的GPU推理。
接下來是FP16模型, 當Batch size =1時
benchmark_app設定CPU_THROUGHPUT_STREAMS = CPU_THROUGHPUT_AUTO,GPU_THROUGHPUT_STREAMS = GPU_THROUGHPUT_AUTO時,得到openvino建議的CPU的nstream數為4, GPU的nstream數為2, 下面對應的number of ireq并發數為8 , 即同時并發8個推理請求

114FPS, 好于FP32模型推理,應該是GPU部分節省了一部分記憶體帶寬的開銷導致性能提升。但是和純GPU FP16模型推理的133FPS還差很多。
為了找原因,祭出一個免費大殺器 Intel Graphics Performance Analyzers, 這個工具可以實時看到CPU,GPU里各種資源占用的情況。先看看默認的CPU_THROUGHPUT_STREAMS = CPU_THROUGHPUT_AUTO,GPU_THROUGHPUT_STREAMS = GPU_THROUGHPUT_AUTO模式, 這里的cpu:4 gpu:4指的是cpu有4路并發,gpu有4路并發,一共8路

可以看到推理時 CPU已經100%耗干了,但是GPU的頻率還在500Hz左右晃悠,同時占用率也就在50%左右。說明推理計算的主力還在CPU這邊,GPU只是抽空來干點活,不知道是記憶體帶寬不夠還是沒有CPU資源給GPU喂資料了,導致基本半閑置狀態。所謂的性能提升從CPU FP32 97FPS到Multi FP32 102FPS, 也就是GPU幫忙干了點活;同時Multi FP16的114FPS 高于Multi FP32的102FPS, 也就是因為GPU在FP16計算的效率更高而已。
再試試把并發數降下來,從1路推理,到2路推理并發再到4路推理并發,GPA輸出如下

非常有趣,可以看到隨著并發數的增加,CPU的占用率逐漸升高,GPU一直在閑置
再增加并發數

當并發數從5開始,GPU開始參與到推理計算里。
所以可以基本認為,使用MULTI:CPU, GPU引數,并且全部使用XXX_THROUGHPUT_AUTO引數時,推理引擎的調度邏輯是先把CPU的資源跑滿(并發<=4路)再開始使用GPU資源(并發>4路)
這在實際使用中是一個很麻煩的事情,我們沒法知道當前創建的多個infer request的句柄哪個是對應CPU,哪個是對應GPU,完全是IE引擎自己控制
for (int i = 0; i < nireq; i++)
{
inferRequest[i] = exeNetwork.CreateInferRequest();
}
所以只能想辦法減少CPU infer request的數量,比如通過手工設定cpu_nstream的數量,先試試cpu nireq = 1, gpu nireq =4, FP16模型
// ----------------- 6. Setting device configuration -----------------------------------------------------------
ie.SetConfig({ { CONFIG_KEY(CPU_BIND_THREAD), "NO" } }, "CPU");
std::cout << " CPU_BIND_THREAD :" << ie.GetConfig("CPU", CONFIG_KEY(CPU_BIND_THREAD)).as<std::string>() << std::endl;
// for CPU execution, more throughput-oriented execution via streams
//ie.SetConfig({ { CONFIG_KEY(CPU_THROUGHPUT_STREAMS),"CPU_THROUGHPUT_AUTO" } }, "CPU");
ie.SetConfig({ { CONFIG_KEY(CPU_THROUGHPUT_STREAMS),std::to_string(1) } }, "CPU");
cpu_nstreams = std::stoi(ie.GetConfig("CPU", CONFIG_KEY(CPU_THROUGHPUT_STREAMS)).as<std::string>());
std::cout << "CPU_THROUGHPUT_AUTO: Number of CPU streams = " << cpu_nstreams << std::endl;
//for GPU inference
//ie.SetConfig({ { CONFIG_KEY(GPU_THROUGHPUT_STREAMS),"GPU_THROUGHPUT_AUTO" } }, "GPU");
ie.SetConfig({ { CONFIG_KEY(GPU_THROUGHPUT_STREAMS),"4" } }, "GPU");
gpu_nstreams = std::stoi(ie.GetConfig("GPU", CONFIG_KEY(GPU_THROUGHPUT_STREAMS)).as<std::string>());
std::cout << "GPU_THROUGHPUT_AUTO: Number of GPU streams = " << gpu_nstreams << std::endl;
ie.SetConfig({ { CLDNN_CONFIG_KEY(PLUGIN_THROTTLE), "1" } }, "GPU");
std::cout << "CLDNN_CONFIG_KEY(PLUGIN_THROTTLE), 1" << std::endl;
GPA輸出

效果不是一般的好,GPU和CPU都跑滿,FPS到了139
再增大cpu_nireq到2, gpu_nireq還是4路,總共6路并發

效果還不如上面的5路并發,可能是CPU只能做效率低下的FP32的推理,過多的CPU推理反而會拉低FPS, 同時可以看到紅框部分有一個明顯的幀率下降,這時候可以聽到CPU的風扇開始狂響,應該是開始過熱了。
經過反復測驗并發數,在我這個4核CPU的筆記本上,測到了一個最高的值

FP16, CPU 1路并發, GPU 2路并發,最高可以到150FPS。看來堆數量不是重點,簡單高效才是好
另外一個提高效率的方法是讓IE引擎優先調度GPU資源,然后再CPU,具體的方法是呼叫ie.LoadNetwork時引數device_name傳進去的引數為"MULTI:GPU, CPU" 并發數量上還是多GPU辦事,少CPU填縫的原則

簡單總結一下,OpenVINO的CPU/GPU混合推理
資源分配上優先使用GPU,CPU只用來做一些錦上添花的補充
盡量少用CPU,以免造成CPU過熱引起的性能下降
盡量使用FP16模型,因為GPU喜歡
合理使用MULTI:CPU,GPU 和 MULTI:GPU,CPU引數來改變調度優先級
混合推理時,每路推理的完成時間會很不一樣,有可能出現嚴重的后發先至或者先發后至。所以處理推理結果時候會更麻煩
在目前我看到的使用集成顯卡的處理器上,盡量不要嘗試CPU/GPU混合推理,首先是CPU/GPU是共享記憶體帶寬,對于那些記憶體帶寬密集型的模型性能提升不大,有2輛拉貨的車,但是是單車道馬路,只能大家排隊一個一個走;其次是CPU和集成顯卡共享TDP,處理器本身的TDP要被平均分開給CPU和集成顯卡,一個硬體用的功耗大了,另一個硬體勢必分配的功耗就會低,實際上有勁使不上。
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/135582.html
標籤:英特爾技術
上一篇:基于openvino 2019R3的推理性能優化的學習與分析 (三) 基于CPU的推理(inference)性能分析
下一篇:基于openvino 2019R3的推理性能優化的學習與分析 (六) 基于CPU的INT8推理(inference)性能分析
