2 Copyright (C) 2018-2019 Intel Corporation
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
8 http://www.apache.org/licenses/LICENSE-2.0
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
16 from datetime import datetime
17 from statistics import median
18 from openvino.inference_engine import IENetwork, IECore, get_version
20 from .utils.constants import CPU_DEVICE_NAME, MULTI_DEVICE_NAME, GPU_DEVICE_NAME, MYRIAD_DEVICE_NAME
21 from .utils.logging import logger
22 from .utils.utils import get_duration_seconds, parse_value_per_device, parse_devices
27 def __init__(self, device: str, number_infer_requests, number_iterations, duration_seconds, api_type):
28 self.device = device.upper()
30 self.nireq = number_infer_requests
31 self.niter = number_iterations
32 self.duration_seconds = get_duration_seconds(duration_seconds, self.niter, self.device)
33 self.api_type = api_type
34 self.device_number_streams = {}
39 def add_extension(self, path_to_extension: str=None, path_to_cldnn_config: str=None):
40 if GPU_DEVICE_NAME in self.device:
41 if path_to_cldnn_config:
42 self.ie.set_config({'CONFIG_FILE': path_to_cldnn_config}, GPU_DEVICE_NAME)
43 logger.info('GPU extensions is loaded {}'.format(path_to_cldnn_config))
44 if CPU_DEVICE_NAME in self.device or MYRIAD_DEVICE_NAME in self.device:
46 self.ie.add_extension(extension_path=path_to_extension, device_name=CPU_DEVICE_NAME)
47 logger.info('CPU extensions is loaded {}'.format(path_to_extension))
49 def get_version_info(self) -> str:
50 logger.info('InferenceEngine:\n{: <9}{:.<24} {}'.format('', 'API version', get_version()))
51 version_string = 'Device info\n'
52 for device, version in self.ie.get_versions(self.device).items():
53 version_string += '{: <9}{}\n'.format('', device)
54 version_string += '{: <9}{:.<24}{} {}.{}\n'.format('', version.description, ' version', version.major,
56 version_string += '{: <9}{:.<24} {}\n'.format('', 'Build', version.build_number)
60 def reshape(ie_network: IENetwork, batch_size: int):
62 for input_layer_name, input_layer in ie_network.inputs.items():
63 shape = input_layer.shape
64 layout = input_layer.layout
67 batch_index = layout.index('N')
69 batch_index = 1 if layout == 'C' else -1
71 if batch_index != -1 and shape[batch_index] != batch_size:
72 shape[batch_index] = batch_size
73 new_shapes[input_layer_name] = shape
76 logger.info('Resizing network to batch = {}'.format(batch_size))
77 ie_network.reshape(new_shapes)
79 def set_config(self, number_streams: int, api_type: str = 'async',
80 number_threads: int = None, infer_threads_pinning: int = None):
81 devices = parse_devices(self.device)
82 self.device_number_streams = parse_value_per_device(devices, number_streams)
83 for device in devices:
84 if device == CPU_DEVICE_NAME: # CPU supports few special performance-oriented keys
85 # limit threading for CPU portion of inference
87 self.ie.set_config({'CPU_THREADS_NUM': str(number_threads)}, device)
89 if MULTI_DEVICE_NAME in self.device and GPU_DEVICE_NAME in self.device:
90 self.ie.set_config({'CPU_BIND_THREAD': 'NO'}, CPU_DEVICE_NAME)
92 # pin threads for CPU portion of inference
93 self.ie.set_config({'CPU_BIND_THREAD': infer_threads_pinning}, device)
95 # for CPU execution, more throughput-oriented execution via streams
96 # for pure CPU execution, more throughput-oriented execution via streams
97 if api_type == 'async':
98 cpu_throughput = {'CPU_THROUGHPUT_STREAMS': 'CPU_THROUGHPUT_AUTO'}
99 if device in self.device_number_streams.keys():
100 cpu_throughput['CPU_THROUGHPUT_STREAMS'] = str(self.device_number_streams.get(device))
101 self.ie.set_config(cpu_throughput, device)
102 self.device_number_streams[device] = self.ie.get_config(device, 'CPU_THROUGHPUT_STREAMS')
104 elif device == GPU_DEVICE_NAME:
105 if api_type == 'async':
106 gpu_throughput = {'GPU_THROUGHPUT_STREAMS': 'GPU_THROUGHPUT_AUTO'}
107 if device in self.device_number_streams.keys():
108 gpu_throughput['GPU_THROUGHPUT_STREAMS'] = str(self.device_number_streams.get(device))
109 self.ie.set_config(gpu_throughput, device)
110 self.device_number_streams[device] = self.ie.get_config(device, 'GPU_THROUGHPUT_STREAMS')
112 if MULTI_DEVICE_NAME in self.device and CPU_DEVICE_NAME in self.device:
113 # multi-device execution with the CPU+GPU performs best with GPU trottling hint,
114 # which releases another CPU thread (that is otherwise used by the GPU driver for active polling)
115 self.ie.set_config({'CLDNN_PLUGIN_THROTTLE': '1'}, device)
117 elif device == MYRIAD_DEVICE_NAME:
118 self.ie.set_config({'LOG_LEVEL': 'LOG_INFO',
119 'VPU_LOG_LEVEL': 'LOG_WARNING'}, MYRIAD_DEVICE_NAME)
121 def load_network(self, ie_network: IENetwork, perf_counts: bool, number_infer_requests: int = None):
122 config = {'PERF_COUNT': ('YES' if perf_counts else 'NO')}
124 exe_network = self.ie.load_network(ie_network,
127 num_requests=number_infer_requests or 0)
131 def infer(self, request_queue, requests_input_data, batch_size, progress_bar):
133 # warming up - out of scope
134 infer_request = request_queue.get_idle_request()
135 if not infer_request:
136 raise Exception('No idle Infer Requests!')
138 if self.api_type == 'sync':
139 infer_request.infer(requests_input_data[infer_request.req_id])
141 infer_request.start_async(requests_input_data[infer_request.req_id])
143 request_queue.wait_all()
144 request_queue.reset_times()
146 start_time = datetime.now()
147 exec_time = (datetime.now() - start_time).total_seconds()
150 # Start inference & calculate performance
151 # to align number if iterations to guarantee that last infer requests are executed in the same conditions **/
152 while (self.niter and iteration < self.niter) or \
153 (self.duration_seconds and exec_time < self.duration_seconds) or \
154 (self.api_type == 'async' and iteration % self.nireq):
155 infer_request = request_queue.get_idle_request()
156 if not infer_request:
157 raise Exception('No idle Infer Requests!')
159 if self.api_type == 'sync':
160 infer_request.infer(requests_input_data[infer_request.req_id])
162 infer_request.start_async(requests_input_data[infer_request.req_id])
165 exec_time = (datetime.now() - start_time).total_seconds()
167 if self.duration_seconds:
168 # calculate how many progress intervals are covered by current iteration.
169 # depends on the current iteration time and time of each progress interval.
170 # Previously covered progress intervals must be skipped.
171 progress_interval_time = self.duration_seconds / progress_bar.total_num
172 new_progress = int(exec_time / progress_interval_time - progress_count)
173 progress_bar.add_progress(new_progress)
174 progress_count += new_progress
176 progress_bar.add_progress(1)
178 # wait the latest inference executions
179 request_queue.wait_all()
181 total_duration_sec = request_queue.get_duration_in_seconds()
182 times = request_queue.times
184 latency_ms = median(times)
185 fps = batch_size * 1000 / latency_ms
186 if self.api_type == 'async':
187 fps = batch_size * iteration / total_duration_sec
188 progress_bar.finish()
189 return fps, latency_ms, total_duration_sec, iteration