基于openvino 2019R3的推理性能优化的学习与分析 (五) 基于CPU/GPU混合运算的推理(inference)性能分析

最近读到这么一篇文章 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混合推理

  1. 资源分配上优先使用GPU,CPU只用来做一些锦上添花的补充
  2. 尽量少用CPU,以免造成CPU过热引起的性能下降
  3. 尽量使用FP16模型,因为GPU喜欢
  4. 合理使用MULTI:CPU,GPU 和 MULTI:GPU,CPU参数来改变调度优先级
  5. 混合推理时,每路推理的完成时间会很不一样,有可能出现严重的后发先至或者先发后至。所以处理推理结果时候会更麻烦
  6. 在目前我看到的使用集成显卡的处理器上,尽量不要尝试CPU/GPU混合推理,首先是CPU/GPU是共享内存带宽,对于那些内存带宽密集型的模型性能提升不大,有2辆拉货的车,但是是单车道马路,只能大家排队一个一个走;其次是CPU和集成显卡共享TDP,处理器本身的TDP要被平均分开给CPU和集成显卡,一个硬件用的功耗大了,另一个硬件势必分配的功耗就会低,实际上有劲使不上。
  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 21
    评论
OpenVINO是Intel推出的一套深度学习推理框架,支持使用CPUGPU和VPU等硬件进行推理加速。下面是使用OpenVINO进行CPU加速推理的步骤: 1. 安装OpenVINO 请参考OpenVINO官方文档进行安装。 2. 转换模型 将训练好的深度学习模型转换为OpenVINO支持的格式,使用Model Optimizer工具进行转换。例如,将TensorFlow模型转换为OpenVINO IR格式: ``` mo_tf.py --input_model model.pb --input_shape [1,224,224,3] --data_type FP16 --output_dir . --model_name model ``` 其中,model.pb为TensorFlow模型,[1,224,224,3]为输入数据的shape,FP16表示使用半精度浮点数进行推理,model为转换后的模型文件名。 3. 加载模型 使用Inference Engine API加载转换后的模型: ``` from openvino.inference_engine import IECore ie = IECore() net = ie.read_network(model='model.xml', weights='model.bin') exec_net = ie.load_network(network=net, device_name='CPU', num_requests=1) input_blob = next(iter(net.input_info)) output_blob = next(iter(net.outputs)) ``` 其中,model.xml和model.bin为转换后的模型文件名,device_name指定使用CPU进行推理。 4. 推理 使用Inference Engine API进行推理: ``` import cv2 import numpy as np img = cv2.imread('image.jpg') img = cv2.resize(img, (224, 224)) img = img.transpose((2, 0, 1)) # HWC -> CHW img = np.expand_dims(img, axis=0) res = exec_net.infer(inputs={input_blob: img}) output = res[output_blob] ``` 其中,image.jpg为输入图片,将图片resize到模型输入的大小,转换为CHW格式,并扩展一维表示batch size为1。使用exec_net.infer进行推理,输出结果保存在output中。 5. 释放资源 推理完成后,释放资源: ``` del exec_net del ie ``` 以上就是使用OpenVINO进行CPU加速推理的基本步骤。如果需要更详细的操作说明和示例代码,可以参考OpenVINO官方文档。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 21
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值