diff options
author | Alexey Suhov <alexey.suhov@intel.com> | 2019-10-02 17:30:49 +0300 |
---|---|---|
committer | Alexey Suhov <alexey.suhov@intel.com> | 2019-10-02 17:30:49 +0300 |
commit | 2c83de45b9c148c94f582861198d5dfe40b4e65e (patch) | |
tree | 85aa192e301c183520d6c233bc58caf02e247485 /tools/benchmark/utils | |
parent | c37d4661a27afb408a45f7752acea968032afcc0 (diff) | |
download | dldt-2c83de45b9c148c94f582861198d5dfe40b4e65e.tar.gz dldt-2c83de45b9c148c94f582861198d5dfe40b4e65e.tar.bz2 dldt-2c83de45b9c148c94f582861198d5dfe40b4e65e.zip |
publish master branch
Diffstat (limited to 'tools/benchmark/utils')
-rw-r--r-- | tools/benchmark/utils/__init__.py | 15 | ||||
-rw-r--r-- | tools/benchmark/utils/constants.py | 53 | ||||
-rw-r--r-- | tools/benchmark/utils/infer_request_wrap.py | 82 | ||||
-rw-r--r-- | tools/benchmark/utils/inputs_filling.py | 189 | ||||
-rw-r--r-- | tools/benchmark/utils/logging.py | 21 | ||||
-rw-r--r-- | tools/benchmark/utils/progress_bar.py | 65 | ||||
-rw-r--r-- | tools/benchmark/utils/statistics_report.py | 119 | ||||
-rw-r--r-- | tools/benchmark/utils/utils.py | 248 |
8 files changed, 792 insertions, 0 deletions
diff --git a/tools/benchmark/utils/__init__.py b/tools/benchmark/utils/__init__.py new file mode 100644 index 000000000..30917612e --- /dev/null +++ b/tools/benchmark/utils/__init__.py @@ -0,0 +1,15 @@ +""" + Copyright (C) 2018-2019 Intel Corporation + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +""" diff --git a/tools/benchmark/utils/constants.py b/tools/benchmark/utils/constants.py new file mode 100644 index 000000000..8ad915bcc --- /dev/null +++ b/tools/benchmark/utils/constants.py @@ -0,0 +1,53 @@ +""" + Copyright (C) 2018-2019 Intel Corporation + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an 'AS IS' BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +""" + +VPU_DEVICE_NAME = 'VPU' +MYRIAD_DEVICE_NAME = 'MYRIAD' +HDDL_DEVICE_NAME = 'HDDL' +FPGA_DEVICE_NAME = 'FPGA' +CPU_DEVICE_NAME = 'CPU' +GPU_DEVICE_NAME = 'GPU' +HETERO_DEVICE_NAME = 'HETERO' +MULTI_DEVICE_NAME = 'MULTI' +UNKNOWN_DEVICE_TYPE = 'UNKNOWN' + +XML_EXTENSION = '.xml' +BIN_EXTENSION = '.bin' + +XML_EXTENSION_PATTERN = '*' + XML_EXTENSION + +IMAGE_EXTENSIONS = ['JPEG', 'JPG', 'PNG', 'BMP'] +BINARY_EXTENSIONS = ['BIN'] + +DEVICE_DURATION_IN_SECS = { + CPU_DEVICE_NAME: 60, + GPU_DEVICE_NAME: 60, + VPU_DEVICE_NAME: 60, + MYRIAD_DEVICE_NAME: 60, + HDDL_DEVICE_NAME: 60, + FPGA_DEVICE_NAME: 120, + UNKNOWN_DEVICE_TYPE: 120 +} + +DEVICE_NIREQ_ASYNC = { + CPU_DEVICE_NAME: 2, + GPU_DEVICE_NAME: 2, + VPU_DEVICE_NAME: 4, + MYRIAD_DEVICE_NAME: 4, + HDDL_DEVICE_NAME: 100, + FPGA_DEVICE_NAME: 3, + UNKNOWN_DEVICE_TYPE: 1 +} diff --git a/tools/benchmark/utils/infer_request_wrap.py b/tools/benchmark/utils/infer_request_wrap.py new file mode 100644 index 000000000..37a757def --- /dev/null +++ b/tools/benchmark/utils/infer_request_wrap.py @@ -0,0 +1,82 @@ +""" + Copyright (C) 2018-2019 Intel Corporation + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +""" + +from datetime import datetime +import threading + + +class InferReqWrap: + def __init__(self, request, req_id, callback_queue): + self.req_id = req_id + self.request = request + self.request.set_completion_callback(self.callback, self.req_id) + self.callbackQueue = callback_queue + + def callback(self, status_code, user_data): + if user_data != self.req_id: + print('Request ID {} does not correspond to user data {}'.format(self.req_id, user_data)) + elif status_code: + print('Request {} failed with status code {}'.format(self.req_id, status_code)) + self.callbackQueue(self.req_id, self.request.latency) + + def start_async(self, input_data): + self.request.async_infer(input_data) + + def infer(self, input_data): + self.request.infer(input_data) + self.callbackQueue(self.req_id, self.request.latency) + + +class InferRequestsQueue: + def __init__(self, requests): + self.idleIds = [] + self.requests = [] + self.times = [] + for req_id in range(len(requests)): + self.requests.append(InferReqWrap(requests[req_id], req_id, self.put_idle_request)) + self.idleIds.append(req_id) + self.startTime = datetime.max + self.endTime = datetime.min + self.cv = threading.Condition() + + def reset_times(self): + self.times.clear() + + def get_duration_in_seconds(self): + return (self.endTime - self.startTime).total_seconds() + + def put_idle_request(self, req_id, latency): + self.cv.acquire() + self.times.append(latency) + self.idleIds.append(req_id) + self.endTime = max(self.endTime, datetime.now()) + self.cv.notify() + self.cv.release() + + def get_idle_request(self): + self.cv.acquire() + while len(self.idleIds) == 0: + self.cv.wait() + req_id = self.idleIds.pop() + self.startTime = min(datetime.now(), self.startTime) + self.cv.release() + return self.requests[req_id] + + def wait_all(self): + self.cv.acquire() + while len(self.idleIds) != len(self.requests): + self.cv.wait() + self.cv.release() diff --git a/tools/benchmark/utils/inputs_filling.py b/tools/benchmark/utils/inputs_filling.py new file mode 100644 index 000000000..8dcbee369 --- /dev/null +++ b/tools/benchmark/utils/inputs_filling.py @@ -0,0 +1,189 @@ +""" + Copyright (C) 2018-2019 Intel Corporation + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +""" + +import os +import cv2 +import numpy as np + +from glob import glob + +from .constants import IMAGE_EXTENSIONS, BINARY_EXTENSIONS +from .logging import logger + + +def is_image(blob): + if blob.layout != "NCHW": + return False + channels = blob.shape[1] + return channels == 3 + + +def is_image_info(blob): + if blob.layout != "NC": + return False + channels = blob.shape[1] + return channels >= 2 + + +def get_inputs(path_to_input, batch_size, input_info, requests): + input_image_sizes = {} + for key in input_info.keys(): + if is_image(input_info[key]): + input_image_sizes[key] = (input_info[key].shape[2], input_info[key].shape[3]) + logger.info("Network input '{}' precision {}, dimensions ({}): {}".format(key, + input_info[key].precision, + input_info[key].layout, + " ".join(str(x) for x in + input_info[key].shape))) + + images_count = len(input_image_sizes.keys()) + binaries_count = len(input_info) - images_count + + image_files = list() + binary_files = list() + + if path_to_input: + image_files = get_files_by_extensions(path_to_input, IMAGE_EXTENSIONS) + image_files.sort() + binary_files = get_files_by_extensions(path_to_input, BINARY_EXTENSIONS) + binary_files.sort() + + if (len(image_files) == 0) and (len(binary_files) == 0): + logger.warn("No input files were given: all inputs will be filled with random values!") + else: + binary_to_be_used = binaries_count * batch_size * len(requests) + if binary_to_be_used > 0 and len(binary_files) == 0: + logger.warn("No supported binary inputs found! Please check your file extensions: {}".format( + ",".join(BINARY_EXTENSIONS))) + elif binary_to_be_used > len(binary_files): + logger.warn( + "Some binary input files will be duplicated: {} files are required, but only {} were provided".format( + binary_to_be_used, len(binary_files))) + elif binary_to_be_used < len(binary_files): + logger.warn( + "Some binary input files will be ignored: only {} files are required from {}".format(binary_to_be_used, + len(binary_files))) + + images_to_be_used = images_count * batch_size * len(requests) + if images_to_be_used > 0 and len(image_files) == 0: + logger.warn("No supported image inputs found! Please check your file extensions: {}".format( + ",".join(IMAGE_EXTENSIONS))) + elif images_to_be_used > len(image_files): + logger.warn( + "Some image input files will be duplicated: {} files are required, but only {} were provided".format( + images_to_be_used, len(image_files))) + elif images_to_be_used < len(image_files): + logger.warn( + "Some image input files will be ignored: only {} files are required from {}".format(images_to_be_used, + len(image_files))) + + requests_input_data = [] + for request_id in range(0, len(requests)): + logger.info("Infer Request {} filling".format(request_id)) + input_data = {} + keys = list(input_info.keys()) + for key in keys: + if is_image(input_info[key]): + # input is image + if (len(image_files) > 0): + input_data[key] = fill_blob_with_image(image_files, request_id, batch_size, keys.index(key), + len(keys), input_info[key].shape) + continue + + # input is binary + if (len(binary_files) > 0): + input_data[key] = fill_blob_with_binary(binary_files, input_info[key].shape) + continue + + # most likely input is image info + if is_image_info(input_info[key]) and len(input_image_sizes) == 1: + image_size = input_image_sizes[list(input_image_sizes.keys()).pop()] + logger.info("Fill input '" + key + "' with image size " + str(image_size[0]) + "x" + + str(image_size[1])) + input_data[key] = fill_blob_with_image_info(image_size, input_info[key].shape) + continue + + # fill with random data + logger.info("Fill input '{}' with random values ({} is expected)".format(key, "image" if is_image( + input_info[key]) else "some binary data")) + input_data[key] = fill_blob_with_random(input_info[key].precision, input_info[key].shape) + + requests_input_data.append(input_data) + + return requests_input_data + + +def get_files_by_extensions(path_to_input, extensions): + input_files = list() + if os.path.isfile(path_to_input): + input_files.append(path_to_input) + else: + path = os.path.join(path_to_input, '*') + files = glob(path, recursive=True) + for file in files: + file_extension = file.rsplit('.').pop().upper() + if file_extension in extensions: + input_files.append(file) + return input_files + + +def fill_blob_with_image(image_paths, request_id, batch_size, input_id, input_size, shape): + images = np.ndarray(shape) + image_index = request_id * batch_size * input_size + input_id + for b in range(batch_size): + image_index %= len(image_paths) + image_filename = image_paths[image_index] + logger.info('Prepare image {}'.format(image_filename)) + image = cv2.imread(image_filename) + + new_im_size = tuple(shape[2:]) + if image.shape[:-1] != new_im_size: + logger.warn("Image is resized from ({}) to ({})".format(image.shape[:-1], new_im_size)) + image = cv2.resize(image, new_im_size) + + image = image.transpose((2, 1, 0)) + images[b] = image + + image_index += input_size + return images + + +def fill_blob_with_image_info(image_size, shape): + im_info = np.ndarray(shape) + for b in range(shape[0]): + for i in range(shape[1]): + im_info[b][i] = image_size[i] if i in [0, 1] else 1 + + return im_info + + +def fill_blob_with_random(precision, shape): + if precision == "FP32": + return np.random.rand(*shape).astype(np.float32) + elif precision == "FP16": + return np.random.rand(*shape).astype(np.float16) + elif precision == "I32": + return np.random.rand(*shape).astype(np.int32) + elif precision == "U8": + return np.random.rand(*shape).astype(np.uint8) + elif precision == "I8": + return np.random.rand(*shape).astype(np.int8) + elif precision == "U16": + return np.random.rand(*shape).astype(np.uint16) + elif precision == "I16": + return np.random.rand(*shape).astype(np.int16) + else: + raise Exception("Input precision is not supported: " + precision) diff --git a/tools/benchmark/utils/logging.py b/tools/benchmark/utils/logging.py new file mode 100644 index 000000000..8adf13884 --- /dev/null +++ b/tools/benchmark/utils/logging.py @@ -0,0 +1,21 @@ +""" + Copyright (C) 2018-2019 Intel Corporation + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +""" + +import logging +import sys + +logging.basicConfig(format="[ %(levelname)s ] %(message)s", level=logging.INFO, stream=sys.stdout) +logger = logging.getLogger('BenchmarkApp') diff --git a/tools/benchmark/utils/progress_bar.py b/tools/benchmark/utils/progress_bar.py new file mode 100644 index 000000000..1f44efc8e --- /dev/null +++ b/tools/benchmark/utils/progress_bar.py @@ -0,0 +1,65 @@ +""" + Copyright (C) 2018-2019 Intel Corporation + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +""" + +from progress.bar import Bar + + +class ProgressBar: + def __init__(self, total_num, stream_output=False, progress_enabled=False): + self.stream_output = stream_output + self.is_finished = True + self.progress_enabled = progress_enabled + self.percent_to_update = 1 + self.cur_progress = 0 + self.total_num = total_num + self.reset(total_num) + + def add_progress(self, num): + self.is_finished = False + if self.progress_enabled: + self.cur_progress += num + total_progress = self.bar.max + if self.cur_progress > total_progress: + self.cur_progress = total_progress + + prev_progress = self.bar.index + prev_percent = 100 * prev_progress / total_progress + cur_percent = 100 * self.cur_progress / total_progress + if prev_progress == 0 or \ + self.cur_progress == total_progress or \ + prev_percent + self.percent_to_update <= cur_percent: + self.bar.next(self.cur_progress - self.bar.index) + if self.stream_output: + print() + + def finish(self, num=0): + if num: + self.add_progress(num) + + self.is_finished = True + if self.progress_enabled: + self.bar.finish() + print() + + def reset(self, total_num): + if self.progress_enabled: + self.bar = Bar('Progress:', max=total_num, fill='.', suffix='%(percent).d%%') + + def new_bar(self, total_num): + if self.is_finished: + self.reset(total_num) + else: + raise Exception('Cannot create a new bar. Current bar is still in progress') diff --git a/tools/benchmark/utils/statistics_report.py b/tools/benchmark/utils/statistics_report.py new file mode 100644 index 000000000..daa0490ea --- /dev/null +++ b/tools/benchmark/utils/statistics_report.py @@ -0,0 +1,119 @@ +""" + Copyright (C) 2018-2019 Intel Corporation + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +""" +import os +import sys +from enum import Enum + +from .logging import logger + +## statistics reports types +noCntReport = 'no_counters' +averageCntReport = 'average_counters' +detailedCntReport = 'detailed_counters' + +## Responsible for collecting of statistics and dumping to .csv file +class StatisticsReport: + class Config(): + def __init__(self, report_type, report_folder): + self.report_type = report_type + self.report_folder = report_folder + + class Category(Enum): + COMMAND_LINE_PARAMETERS = 0, + RUNTIME_CONFIG = 1, + EXECUTION_RESULTS = 2 + + def __init__(self, config): + self.config = config + self.parameters = {} + self.csv_separator = ';' + + def add_parameters(self, category, parameters): + if category not in self.parameters.keys(): + self.parameters[category] = parameters + else: + self.parameters[category].extend(parameters) + + def dump(self): + def dump_parameters(f, parameters): + for k, v in parameters: + f.write('{}{}{}\n'.format(k, self.csv_separator, v)) + + with open(os.path.join(self.config.report_folder, 'benchmark_report.csv'), 'w') as f: + if self.Category.COMMAND_LINE_PARAMETERS in self.parameters.keys(): + f.write('Command line parameters\n') + dump_parameters(f, self.parameters[self.Category.COMMAND_LINE_PARAMETERS]) + f.write('\n') + + if self.Category.RUNTIME_CONFIG in self.parameters.keys(): + f.write('Configuration setup\n') + dump_parameters(f, self.parameters[self.Category.RUNTIME_CONFIG]) + f.write('\n') + + if self.Category.EXECUTION_RESULTS in self.parameters.keys(): + f.write('Execution results\n') + dump_parameters(f, self.parameters[self.Category.EXECUTION_RESULTS]) + f.write('\n') + + logger.info("Statistics report is stored to {}".format(f.name)) + + def dump_performance_counters_request(self, f, perf_counts): + total = 0 + total_cpu = 0 + f.write(self.csv_separator.join(['layerName', 'execStatus', 'layerType', 'execType', 'realTime (ms)', 'cpuTime (ms)\n'])) + for k, v in sorted(perf_counts.items(), key=lambda x: x[1]['execution_index']): + f.write(self.csv_separator.join([k, v['status'], v['layer_type'], v['exec_type'], str(v['real_time']/1000.0), str(v['cpu_time']/1000.0)])) + f.write('\n') + total += v['real_time'] + total_cpu += v['cpu_time'] + f.write(self.csv_separator.join(['Total','','','',str(total/1000.0),str(total_cpu/1000.0)])) + f.write('\n\n') + + def dump_performance_counters(self, perf_counts): + if self.config.report_type == '' or self.config.report_type == noCntReport: + logger.info("Statistics collecting for performance counters was not requested. No reports are dumped.") + return + + if not perf_counts: + logger.info('Peformance counters are empty. No reports are dumped.') + return + + filename = os.path.join(self.config.report_folder, 'benchmark_{}_report.csv'.format(self.config.report_type)) + with open(filename, 'w') as f: + if self.config.report_type == detailedCntReport: + for pc in perf_counts: + self.dump_performance_counters_request(f, pc) + elif self.config.report_type == averageCntReport: + def get_average_performance_counters(perf_counts): + performance_counters_avg = {} + ## iterate over each processed infer request and handle its PM data + for i in range(0, len(perf_counts)): + ## iterate over each layer from sorted vector and add required PM data to the per-layer maps + for k in perf_counts[0].keys(): + if k not in performance_counters_avg.keys(): + performance_counters_avg[k] = perf_counts[i][k] + else: + performance_counters_avg[k]['real_time'] += perf_counts[i][k]['real_time'] + performance_counters_avg[k]['cpu_time'] += perf_counts[i][k]['cpu_time'] + for _, v in performance_counters_avg.items(): + v['real_time'] /= len(perf_counts) + v['cpu_time'] /= len(perf_counts) + return performance_counters_avg + self.dump_performance_counters_request(f, get_average_performance_counters(perf_counts)) + else: + raise Exception('PM data can only be collected for average or detailed report types') + + logger.info('Pefromance counters report is stored to {}'.format(filename)) diff --git a/tools/benchmark/utils/utils.py b/tools/benchmark/utils/utils.py new file mode 100644 index 000000000..8fe49b669 --- /dev/null +++ b/tools/benchmark/utils/utils.py @@ -0,0 +1,248 @@ +""" + Copyright (C) 2018-2019 Intel Corporation + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +""" +import os + +from openvino.inference_engine import IENetwork + +from .constants import DEVICE_DURATION_IN_SECS, UNKNOWN_DEVICE_TYPE, DEVICE_NIREQ_ASYNC, BIN_EXTENSION, \ + CPU_DEVICE_NAME, GPU_DEVICE_NAME +from .inputs_filling import is_image +from .logging import logger + + +def static_vars(**kwargs): + def decorate(func): + for k in kwargs: + setattr(func, k, kwargs[k]) + return func + + return decorate + + +@static_vars(step_id=0) +def next_step(additional_info=''): + step_names = { + 1: "Parsing and validating input arguments", + 2: "Loading Inference Engine", + 3: "Reading the Intermediate Representation network", + 4: "Resizing network to match image sizes and given batch", + 5: "Configuring input of the model", + 6: "Setting device configuration", + 7: "Loading the model to the device", + 8: "Setting optimal runtime parameters", + 9: "Creating infer requests and filling input blobs with images", + 10: "Measuring performance", + 11: "Dumping statistics report", + } + + next_step.step_id += 1 + if next_step.step_id not in step_names.keys(): + raise Exception('Step ID {} is out of total steps number '.format(next_step.step_id, str(len(step_names)))) + + step_info_template = '[Step {}/{}] {}' + step_name = step_names[next_step.step_id] + (' ({})'.format(additional_info) if additional_info else '') + step_info_template = step_info_template.format(next_step.step_id, len(step_names), step_name) + print(step_info_template) + + +def read_network(path_to_model: str): + xml_filename = os.path.abspath(path_to_model) + head, tail = os.path.splitext(xml_filename) + bin_filename = os.path.abspath(head + BIN_EXTENSION) + + ie_network = IENetwork(xml_filename, bin_filename) + + input_info = ie_network.inputs + + if not input_info: + raise AttributeError('No inputs info is provided') + + return ie_network + + +def config_network_inputs(ie_network: IENetwork): + input_info = ie_network.inputs + + for key in input_info.keys(): + if is_image(input_info[key]): + # Set the precision of input data provided by the user + # Should be called before load of the network to the plugin + input_info[key].precision = 'U8' + + +def get_number_iterations(number_iterations: int, nireq: int, api_type: str): + niter = number_iterations + + if api_type == 'async' and niter: + niter = int((niter + nireq - 1) / nireq) * nireq + if number_iterations != niter: + logger.warn('Number of iterations was aligned by request number ' + 'from {} to {} using number of requests {}'.format(number_iterations, niter, nireq)) + + return niter + + +def get_duration_seconds(time, number_iterations, device): + if time: + # time limit + return time + + if not number_iterations: + return get_duration_in_secs(device) + return 0 + + +def get_duration_in_milliseconds(duration): + return duration * 1000 + + +def get_duration_in_secs(target_device): + duration = 0 + for device in DEVICE_DURATION_IN_SECS: + if device in target_device: + duration = max(duration, DEVICE_DURATION_IN_SECS[device]) + + if duration == 0: + duration = DEVICE_DURATION_IN_SECS[UNKNOWN_DEVICE_TYPE] + logger.warn('Default duration {} seconds is used for unknown device {}'.format(duration, target_device)) + + return duration + + +def get_nireq(target_device): + nireq = 0 + for device in DEVICE_NIREQ_ASYNC: + if device in target_device: + nireq = max(nireq, DEVICE_NIREQ_ASYNC[device]) + + if nireq == 0: + nireq = DEVICE_NIREQ_ASYNC[UNKNOWN_DEVICE_TYPE] + logger.warn('Default number of requests {} is used for unknown device {}'.format(nireq, target_device)) + + return nireq + + +def parse_devices(device_string): + devices = device_string + if ':' in devices: + devices = devices.partition(':')[2] + return [d[:d.index('(')] if '(' in d else d for d in devices.split(',')] + + +def parse_value_per_device(devices, values_string): + # Format: <device1>:<value1>,<device2>:<value2> or just <value> + result = {} + if not values_string: + return result + device_value_strings = values_string.upper().split(',') + for device_value_string in device_value_strings: + device_value_vec = device_value_string.split(':') + if len(device_value_vec) == 2: + for device in devices: + if device == device_value_vec[0]: + value = int(device_value_vec[1]) + result[device_value_vec[0]] = value + break + elif len(device_value_vec) == 1: + value = int(device_value_vec[0]) + for device in devices: + result[device] = value + elif not device_value_vec: + raise Exception('Unknown string format: ' + values_string) + return result + + +def process_help_inference_string(benchmark_app): + output_string = 'Start inference {}ronously'.format(benchmark_app.api_type) + if benchmark_app.api_type == 'async': + output_string += ', {} inference requests'.format(benchmark_app.nireq) + + device_ss = '' + if CPU_DEVICE_NAME in benchmark_app.device: + device_ss += str(benchmark_app.ie.get_config(CPU_DEVICE_NAME, 'CPU_THROUGHPUT_STREAMS')) + device_ss += ' streams for {}'.format(CPU_DEVICE_NAME) + if GPU_DEVICE_NAME in benchmark_app.device: + device_ss += ', ' if device_ss else '' + device_ss += str(benchmark_app.ie.get_config(GPU_DEVICE_NAME, 'GPU_THROUGHPUT_STREAMS')) + device_ss += ' streams for {}'.format(GPU_DEVICE_NAME) + + if device_ss: + output_string += ' using ' + device_ss + + limits = '' + + if benchmark_app.niter and not benchmark_app.duration_seconds: + limits += '{} iterations'.format(benchmark_app.niter) + + if benchmark_app.duration_seconds: + limits += '{} ms duration'.format(get_duration_in_milliseconds(benchmark_app.duration_seconds)) + if limits: + output_string += ', limits: ' + limits + + return output_string + + +def dump_exec_graph(exe_network, exec_graph_path): + try: + exec_graph_info = exe_network.get_exec_graph_info() + exec_graph_info.serialize(exec_graph_path) + logger.info('Executable graph is stored to {}'.format(exec_graph_path)) + del exec_graph_info + except Exception as e: + logger.exception(e) + + +def print_perf_counters(perf_counts_list): + for ni in range(len(perf_counts_list)): + perf_counts = perf_counts_list[ni] + total_time = 0 + total_time_cpu = 0 + logger.info("Performance counts for {}-th infer request".format(ni)) + for layer, stats in sorted(perf_counts.items(), key=lambda x: x[1]['execution_index']): + max_layer_name = 30 + print("{:<30}{:<15}{:<30}{:<20}{:<20}{:<20}".format( + layer[:max_layer_name - 4] + '...' if (len(layer) >= max_layer_name) else layer, + stats['status'], + 'layerType: ' + str(stats['layer_type']), + 'realTime: ' + str(stats['real_time']), + 'cpu: ' + str(stats['cpu_time']), + 'execType: ' + str(stats['exec_type']))) + total_time += stats['real_time'] + total_time_cpu += stats['cpu_time'] + print('Total time: {} microseconds'.format(total_time)) + print('Total CPU time: {} microseconds\n'.format(total_time_cpu)) + +def get_command_line_arguments(argv): + parameters = [] + arg_name = '' + arg_value = '' + for arg in argv[1:]: + if '=' in arg: + arg_name, arg_value = arg.split('=') + parameters.append((arg_name, arg_value)) + arg_name = '' + arg_value = '' + else: + if arg[0] == '-': + if arg_name is not '': + parameters.append((arg_name, arg_value)) + arg_value = '' + arg_name = arg + else: + arg_value = arg + if arg_name is not '': + parameters.append((arg_name, arg_value)) + return parameters |