Improved properties handling between Core and plugins (#16296)

* [HETERO]: adopt setting device properties in benchmark_app/speech_sample for HETERO

Fix IEClassHeteroExecutableNetworkGetMetricTest_SUPPORTED_METRICS test

Fix NumStreamsAndDefaultPerfHintToHWTest/PerHintAndDefaultPerfHintToHWTest tests

[HETERO][MULTI][AUTO] Make ov::device::properties regular property

[PYTHON] Update python BA with device properties

Update after rebase

Update src/plugins/auto/auto_executable_network.cpp

Co-authored-by: Ilya Lavrenov <ilya.lavrenov@intel.com>

Update src/plugins/auto/multi_executable_network.cpp

Co-authored-by: Ilya Lavrenov <ilya.lavrenov@intel.com>

Fix merge conflicts, apply some review comments

* Multiple improvements

* [HETERO]: adopt setting device properties in benchmark_app/speech_sample for HETERO

Fix IEClassHeteroExecutableNetworkGetMetricTest_SUPPORTED_METRICS test

Fix NumStreamsAndDefaultPerfHintToHWTest/PerHintAndDefaultPerfHintToHWTest tests

[HETERO][MULTI][AUTO] Make ov::device::properties regular property

[PYTHON] Update python BA with device properties

Update after rebase

Update src/plugins/auto/auto_executable_network.cpp

Co-authored-by: Ilya Lavrenov <ilya.lavrenov@intel.com>

Update src/plugins/auto/multi_executable_network.cpp

Co-authored-by: Ilya Lavrenov <ilya.lavrenov@intel.com>

Fix merge conflicts, apply some review comments

* Code style, bugfix after merging improvement

* More improvements

* Even more improvements

* Commit changes in core_impl.cpp

* Added parsing of any maps

* Fixed code-style

* Fixed AB mock tests build

* Fixed comparison

* Added new AB config key

* Improvements and fixes (#147)

* Fix BA, fix GetSupportedConfig call for virtual plugins (#148)

* Fix GPU tests (#149)

* Fix BA, fix GetSupportedConfig call for virtual plugins

* Fix GPU tests

* Code style

* Improvements 10

* Fixed incorrect tests

* Revert removal cache_dir

* Revert removal cache_dir

* Fixed clean

* Supported device ID in CPU

* More fixed tests

* clang-format

* Fix legacy GPU tests (#150)

* Removed clone_map

* clang-format

* Added clone_map back

---------

Co-authored-by: Nadezhda Ageeva <nadezhda.ageeva@intel.com>
Co-authored-by: Nadezhda Ageeva <nkogteva@gmail.com>
This commit is contained in:
Ilya Lavrenov
2023-03-20 16:42:40 +04:00
committed by GitHub
parent 8b31e3aafe
commit 0c99135d44
53 changed files with 1711 additions and 840 deletions

View File

@@ -5,7 +5,7 @@ import os
import sys
from datetime import datetime
from openvino.runtime import Dimension
from openvino.runtime import Dimension,properties
from openvino.tools.benchmark.benchmark import Benchmark
from openvino.tools.benchmark.parameters import parse_args
@@ -18,7 +18,7 @@ from openvino.tools.benchmark.utils.utils import next_step, get_number_iteration
process_help_inference_string, print_perf_counters, print_perf_counters_sort, dump_exec_graph, get_duration_in_milliseconds, \
get_command_line_arguments, parse_value_per_device, parse_devices, get_inputs_info, \
print_inputs_and_outputs_info, get_network_batch_size, load_config, dump_config, get_latency_groups, \
check_for_static, can_measure_as_static, parse_value_for_virtual_device
check_for_static, can_measure_as_static, parse_value_for_virtual_device, is_virtual_device, is_virtual_device_found
from openvino.tools.benchmark.utils.statistics_report import StatisticsReport, JsonStatisticsReport, CsvStatisticsReport, \
averageCntReport, detailedCntReport
@@ -108,19 +108,23 @@ def main():
# --------------------- 3. Setting device configuration --------------------------------------------------------
next_step()
for device in devices:
supported_properties = benchmark.core.get_property(device, 'SUPPORTED_PROPERTIES')
if 'PERFORMANCE_HINT' in supported_properties:
def get_performance_hint(device) -> properties.hint.PerformanceMode:
perf_hint = properties.hint.PerformanceMode.UNDEFINED
supported_properties = benchmark.core.get_property(device, properties.supported_properties())
if properties.hint.performance_mode() in supported_properties:
if is_flag_set_in_command_line('hint'):
if args.perf_hint=='none':
logger.warning(f"No device {device} performance hint is set.")
args.perf_hint = 'UNDEFINED'
perf_hint = properties.hint.PerformanceMode.UNDEFINED
else:
perf_hint = properties.hint.PerformanceMode(args.perf_hint.upper())
else:
args.perf_hint = "THROUGHPUT" if benchmark.api_type == "async" else "LATENCY"
perf_hint = properties.hint.PerformanceMode.THROUGHPUT if benchmark.api_type == "async" else properties.hint.PerformanceMode.LATENCY
logger.warning(f"Performance hint was not explicitly specified in command line. " +
f"Device({device}) performance hint will be set to " + args.perf_hint + ".")
f"Device({device}) performance hint will be set to {perf_hint}.")
else:
logger.warning(f"Device {device} does not support performance hint property(-hint).")
return perf_hint
def get_device_type_from_name(name) :
new_name = str(name)
@@ -142,10 +146,9 @@ def main():
perf_counts = False
# check if using the virtual device
hw_devices_list = devices.copy()
if_auto = AUTO_DEVICE_NAME in devices
if_multi = MULTI_DEVICE_NAME in devices
# Remove the hardware devices if AUTO/MULTI appears in the devices list.
if if_auto or if_multi:
# Remove the hardware devices if AUTO/MULTI/HETERO appears in the devices list.
is_virtual = is_virtual_device_found(devices)
if is_virtual:
devices.clear()
# Parse out the currect virtual device as the target device.
virtual_device = device_name.partition(":")[0]
@@ -155,72 +158,89 @@ def main():
parse_value_for_virtual_device(virtual_device, device_infer_precision)
for device in devices:
supported_properties = benchmark.core.get_property(device, 'SUPPORTED_PROPERTIES')
supported_properties = benchmark.core.get_property(device, properties.supported_properties())
if device not in config.keys():
config[device] = {}
## high-level performance modes
if properties.hint.performance_mode() not in config[device].keys():
config[device][properties.hint.performance_mode()] = get_performance_hint(device)
perf_hint = config[device][properties.hint.performance_mode()]
if is_flag_set_in_command_line('nireq'):
config[device][properties.hint.num_requests()] = str(args.number_infer_requests)
## Set performance counter
if is_flag_set_in_command_line('pc'):
## set to user defined value
config[device]['PERF_COUNT'] = 'YES' if args.perf_counts else 'NO'
elif 'PERF_COUNT' in config[device].keys() and config[device]['PERF_COUNT'] == 'YES':
config[device][properties.enable_profiling()] = True if args.perf_counts else False
elif properties.enable_profiling() in config[device].keys() and config[device][properties.enable_profiling()] == True:
logger.warning(f"Performance counters for {device} device is turned on. " +
"To print results use -pc option.")
elif args.report_type in [ averageCntReport, detailedCntReport ]:
logger.warning(f"Turn on performance counters for {device} device " +
f"since report type is {args.report_type}.")
config[device]['PERF_COUNT'] = 'YES'
config[device][properties.enable_profiling()] = True
elif args.exec_graph_path is not None:
logger.warning(f"Turn on performance counters for {device} device " +
"due to execution graph dumping.")
config[device]['PERF_COUNT'] = 'YES'
config[device][properties.enable_profiling()] = True
elif is_flag_set_in_command_line('pcsort'):
## set to default value
logger.warning(f"Turn on performance counters for {device} device " +
f"since pcsort value is {args.perf_counts_sort}.")
config[device]['PERF_COUNT'] = 'YES' if args.perf_counts_sort else 'NO'
config[device][properties.enable_profiling()] = True if args.perf_counts_sort else False
else:
## set to default value
config[device]['PERF_COUNT'] = 'YES' if args.perf_counts else 'NO'
perf_counts = True if config[device]['PERF_COUNT'] == 'YES' else perf_counts
## high-level performance hints
config[device]['PERFORMANCE_HINT'] = args.perf_hint.upper()
if is_flag_set_in_command_line('nireq'):
config[device]['PERFORMANCE_HINT_NUM_REQUESTS'] = str(args.number_infer_requests)
config[device][properties.enable_profiling()] = args.perf_counts
perf_counts = True if config[device][properties.enable_profiling()] == True else perf_counts
## insert or append property into hw device properties list
def update_configs(hw_device, property_name, property_value):
is_set_streams_auto = property_name == 'NUM_STREAMS' and property_value == 'AUTO'
(key, value) = properties.device.properties({hw_device:{property_name:property_value}})
is_set_streams_auto = property_name == properties.num_streams() and property_value == properties.streams.Num.AUTO
if not is_set_streams_auto and is_load_config and is_dev_set_property[hw_device] and hw_device in config[device].keys():
# overwrite the device properties loaded from configuration file if
# 1. not setting 'NUM_STREAMS' to default value 'AUTO',
# 2. enable loading device properties from configuration file,
# 3. device properties in config[device] is loaded from configuration file, and never setting device properties before
is_dev_set_property[hw_device] = False
del config[device][hw_device]
del config[device][key]
# add property into hw device properties list.
if hw_device not in config[device].keys():
config[device][hw_device] = ' '.join([property_name, property_value])
if key not in config[device].keys():
config[device][key] = value
else:
config[device][hw_device] += " " + property_name + " " + property_value
current_config = config[device][key].get()
if hw_device not in current_config.keys():
current_config.update(value.get())
else:
current_device_config = current_config[hw_device].get()
for prop in value.get().items():
current_device_config.update(prop[1].get())
current_config[hw_device].set(current_device_config)
config[device][key].set(current_config)
def update_device_config_for_virtual_device(value, config, key):
# check if the element contains the hardware device property
if len(value.split(':')) == 1:
config[device][key] = device_infer_precision[device]
else:
# set device nstreams properties in the AUTO/MULTI plugin
value_vec = value[value.find('{') + 1:value.rfind('}')].split(',')
device_properties = {value_vec[i].split(':')[0] : value_vec[i].split(':')[1] for i in range(0, len(value_vec))}
for hw_device in device_properties.keys():
update_configs(hw_device, key, device_properties[hw_device])
## infer precision
def set_infer_precision():
key = properties.hint.inference_precision()
if device in device_infer_precision.keys():
## set to user defined value
if 'INFERENCE_PRECISION_HINT' in supported_properties:
config[device]['INFERENCE_PRECISION_HINT'] = device_infer_precision[device]
elif device in [MULTI_DEVICE_NAME, AUTO_DEVICE_NAME]:
# check if the element contains the hardware device property
value_vec = device_infer_precision[device].split(' ')
if len(value_vec) == 1:
config[device]['INFERENCE_PRECISION_HINT'] = device_infer_precision[device]
else:
# set device nstreams properties in the AUTO/MULTI plugin
device_properties = {value_vec[i]: value_vec[i + 1] for i in range(0, len(value_vec), 2)}
for hw_device in device_properties.keys():
update_configs(hw_device, "INFERENCE_PRECISION_HINT", device_properties[hw_device])
if key in supported_properties:
config[device][key] = device_infer_precision[device]
elif is_virtual_device(device):
update_device_config_for_virtual_device(device_infer_precision[device], config, key)
else:
raise Exception(f"Device {device} doesn't support config key INFERENCE_PRECISION_HINT!" \
" Please specify -infer_precision for correct devices in format" \
@@ -234,24 +254,16 @@ def main():
## set to user defined value
if key in supported_properties:
config[device][key] = device_number_streams[device]
elif "NUM_STREAMS" in supported_properties:
key = "NUM_STREAMS"
elif properties.streams.num() in supported_properties:
key = properties.streams.num()
config[device][key] = device_number_streams[device]
elif device in [MULTI_DEVICE_NAME, AUTO_DEVICE_NAME]:
# check if the element contains the hardware device property
value_vec = device_number_streams[device].split(' ')
if len(value_vec) == 1:
key = "NUM_STREAMS"
config[device][key] = device_number_streams[key]
else:
# set device nstreams properties in the AUTO/MULTI plugin
device_properties = {value_vec[i]: value_vec[i + 1] for i in range(0, len(value_vec), 2)}
for hw_device in device_properties.keys():
update_configs(hw_device, "NUM_STREAMS", device_properties[hw_device])
elif is_virtual_device(device):
key = properties.streams.num()
update_device_config_for_virtual_device(device_number_streams[device], config, key)
else:
raise Exception(f"Device {device} doesn't support config key '{key}'! " +
"Please specify -nstreams for correct devices in format <dev1>:<nstreams1>,<dev2>:<nstreams2>")
elif key not in config[device].keys() and args.api_type == "async" \
elif key not in config[device].keys() and args.api_type == "async" and key not in config[device].keys() \
and 'PERFORMANCE_HINT' in config[device].keys() and config[device]['PERFORMANCE_HINT'] == '':
## set the _AUTO value for the #streams
logger.warning(f"-nstreams default value is determined automatically for {device} device. " +
@@ -259,18 +271,18 @@ def main():
"but it still may be non-optimal for some cases, for more information look at README.")
if key in supported_properties:
config[device][key] = get_device_type_from_name(device) + "_THROUGHPUT_AUTO"
elif "NUM_STREAMS" in supported_properties:
key = "NUM_STREAMS"
elif properties.streams.Num() in supported_properties:
key = properties.streams.Num()
config[device][key] = "-1" # Set AUTO mode for streams number
elif device in [MULTI_DEVICE_NAME, AUTO_DEVICE_NAME]:
elif is_virtual_device(device):
# Set nstreams to default value auto if no nstreams specified from cmd line.
for hw_device in hw_devices_list:
hw_supported_properties = benchmark.core.get_property(hw_device, 'SUPPORTED_PROPERTIES')
hw_supported_properties = benchmark.core.get_property(hw_device, properties.supported_properties())
key = get_device_type_from_name(hw_device) + "_THROUGHPUT_STREAMS"
value = get_device_type_from_name(hw_device) + "_THROUGHPUT_AUTO"
if key not in hw_supported_properties:
key = "NUM_STREAMS"
value = "AUTO"
key = properties.streams.Num()
value = properties.streams.Num.AUTO
if key in hw_supported_properties:
update_configs(hw_device, key, value)
if key in config[device].keys():
@@ -278,15 +290,15 @@ def main():
return
def set_nthreads_pin(property_name, property_value):
if property_name == "AFFINITY":
if property_name == properties.affinity():
if property_value == "YES":
property_value = "CORE"
property_value = properties.Affinity.CORE
elif property_value == "NO":
property_value = "NONE"
property_value = properties.Affinity.NONE
if property_name in supported_properties or device_name == AUTO_DEVICE_NAME:
# create nthreads/pin primary property for HW device or AUTO if -d is AUTO directly.
config[device][property_name] = property_value
elif if_auto or if_multi:
elif is_virtual:
# Create secondary property of -nthreads/-pin only for CPU if CPU device appears in the devices
# list specified by -d.
if CPU_DEVICE_NAME in hw_devices_list:
@@ -295,36 +307,16 @@ def main():
if args.number_threads and is_flag_set_in_command_line("nthreads"):
# limit threading for CPU portion of inference
set_nthreads_pin('INFERENCE_NUM_THREADS', str(args.number_threads))
set_nthreads_pin(properties.inference_num_threads(), str(args.number_threads))
if is_flag_set_in_command_line('pin'):
## set for CPU to user defined value
set_nthreads_pin('AFFINITY', args.infer_threads_pinning)
if CPU_DEVICE_NAME in device: # CPU supports few special performance-oriented keys
## for CPU execution, more throughput-oriented execution via streams
set_throughput_streams()
set_infer_precision()
elif GPU_DEVICE_NAME in device:
## for GPU execution, more throughput-oriented execution via streams
set_throughput_streams()
set_infer_precision()
elif AUTO_DEVICE_NAME in device:
set_throughput_streams()
set_infer_precision()
if device in device_number_streams.keys():
del device_number_streams[device]
elif MULTI_DEVICE_NAME in device:
set_throughput_streams()
set_infer_precision()
if CPU_DEVICE_NAME in device and GPU_DEVICE_NAME in device:
logger.warning("Turn on GPU throttling. Multi-device execution with the CPU + GPU performs best with GPU throttling hint, " +
"which releases another CPU thread (that is otherwise used by the GPU driver for active polling)")
update_configs(GPU_DEVICE_NAME, 'GPU_PLUGIN_THROTTLE', '1')
# limit threading for CPU portion of inference
if not is_flag_set_in_command_line('pin'):
if CPU_DEVICE_NAME in config[device].keys() and 'CPU_BIND_THREAD' in config[device][CPU_DEVICE_NAME]:
logger.warning(f"Turn off threads pinning for {device} " +
"device since multi-scenario with GPU device is used.")
update_configs(CPU_DEVICE_NAME, 'CPU_BIND_THREAD', 'NO')
set_nthreads_pin(properties.affinity(), args.infer_threads_pinning)
set_throughput_streams()
set_infer_precision()
if is_virtual_device(device):
if device in device_number_streams.keys():
del device_number_streams[device]
@@ -419,9 +411,9 @@ def main():
# --------------------- 7. Loading the model to the device -------------------------------------------------
next_step()
start_time = datetime.utcnow()
compiled_model = benchmark.core.compile_model(model, benchmark.device, device_config)
duration_ms = f"{(datetime.utcnow() - start_time).total_seconds() * 1000:.2f}"
logger.info(f"Compile model took {duration_ms} ms")
if statistics:
@@ -459,11 +451,20 @@ def main():
next_step()
## actual device-deduced settings
keys = compiled_model.get_property('SUPPORTED_PROPERTIES')
keys = compiled_model.get_property(properties.supported_properties())
logger.info("Model:")
for k in keys:
if k not in ('SUPPORTED_METRICS', 'SUPPORTED_CONFIG_KEYS', 'SUPPORTED_PROPERTIES'):
logger.info(f' {k}: {compiled_model.get_property(k)}')
skip_keys = ('SUPPORTED_METRICS', 'SUPPORTED_CONFIG_KEYS', properties.supported_properties())
if k not in skip_keys:
value = compiled_model.get_property(k)
if k == properties.device.properties():
for device_key in value.keys():
logger.info(f' {device_key}:')
for k2, value2 in value.get(device_key).get().items():
if k2 not in skip_keys:
logger.info(f' {k2}: {value2}')
else:
logger.info(f' {k}: {value}')
# Update number of streams
for device in device_number_streams.keys():

View File

@@ -3,11 +3,12 @@
from collections import defaultdict
from datetime import timedelta
from openvino.runtime import Core, Model, PartialShape, Dimension, Layout, Type, serialize
import enum
from openvino.runtime import Core, Model, PartialShape, Dimension, Layout, Type, serialize, properties
from openvino.preprocess import PrePostProcessor
from .constants import DEVICE_DURATION_IN_SECS, UNKNOWN_DEVICE_TYPE, \
AUTO_DEVICE_NAME, MULTI_DEVICE_NAME
AUTO_DEVICE_NAME, MULTI_DEVICE_NAME, HETERO_DEVICE_NAME
from .logging import logger
import json
@@ -275,6 +276,12 @@ def can_measure_as_static(app_input_info):
return False
return True
meta_plugins = [ MULTI_DEVICE_NAME, HETERO_DEVICE_NAME, AUTO_DEVICE_NAME ]
def is_virtual_device(device_name) -> bool:
return device_name in meta_plugins
def is_virtual_device_found(device_names) -> bool:
return any(is_virtual_device(device_name) for device_name in device_names)
def parse_devices(device_string):
result = []
@@ -323,8 +330,8 @@ def parse_value_for_virtual_device(device, values_string):
# Remove the element that the key is virtual device MULTI
# e.g. MULTI:xxx -nstreams 2 will set nstreams 2 to xxx.
values_string.pop(device)
elif device == AUTO_DEVICE_NAME:
# Just keep the element that the key is virtual device AUTO
elif device == AUTO_DEVICE_NAME or device == HETERO_DEVICE_NAME:
# Just keep the element that the key is virtual device AUTO/HETERO
# e.g. AUTO:xxx,xxx -nstreams 2 will trigger exception that AUTO plugin didn't support nstream property.
value = values_string.get(device)
values_string.clear()
@@ -332,11 +339,15 @@ def parse_value_for_virtual_device(device, values_string):
keys = values_string.keys()
for key in list(values_string):
if device not in list(values_string):
values_string[device] = ''
values_string[device] += key + " " + values_string.get(key) + " "
values_string[device] = '{'
else:
values_string[device] += ','
values_string[device] += key + ":" + values_string.get(key)
del values_string[key]
if device in values_string.keys():
values_string[device] = values_string[device].strip()
if values_string[device] != '':
values_string[device] += '}'
return
def process_help_inference_string(benchmark_app, device_number_streams):
@@ -402,14 +413,14 @@ def print_perf_counters_sort(perf_counts_list,sort_flag="sort"):
elif sort_flag=="simple_sort":
total_detail_data = sorted(total_detail_data,key=lambda tmp_data:tmp_data[-4],reverse=True)
total_detail_data = [tmp_data for tmp_data in total_detail_data if str(tmp_data[1])!="Status.NOT_RUN"]
print_detail_result(total_detail_data)
print_detail_result(total_detail_data)
print(f'Total time: {total_time / 1000:.3f} milliseconds')
print(f'Total CPU time: {total_time_cpu / 1000:.3f} milliseconds')
print(f'Total proportion: {"%.2f"%(round(total_real_time_proportion)*100)} % \n')
return total_detail_data
def print_detail_result(result_list):
""" Print_perf_counters_sort result
""" Print_perf_counters_sort result
"""
max_print_length = 20
for tmp_result in result_list:
@@ -748,28 +759,15 @@ def show_available_devices():
def dump_config(filename, config):
properties = {}
for device in config:
properties[device] = {}
supported_properties = Core().get_property(device, 'SUPPORTED_PROPERTIES')
# check if ov::device::properties exists in the config
if device not in (AUTO_DEVICE_NAME, MULTI_DEVICE_NAME):
properties[device] = config[device]
continue
for property_name in config[device]:
property_value = config[device][property_name]
if property_name in supported_properties:
properties[device][property_name] = property_value
else:
properties[device].setdefault('DEVICE_PROPERTIES', {})
properties[device]['DEVICE_PROPERTIES'].setdefault(property_name, {})
array = property_value.split(' ')
properties_dict = {array[i]: array[i + 1] for i in range(0, len(array), 2)}
for key in properties_dict:
properties[device]['DEVICE_PROPERTIES'][property_name][key] = properties_dict[key]
json_config = {}
for device_name, device_config in config.items():
json_config[device_name] = {}
for key, value in device_config.items():
value_string = value.name if isinstance(value, properties.hint.PerformanceMode) else str(value)
json_config[device_name][key] = value_string
with open(filename, 'w') as f:
json.dump(properties, f, indent=4)
json.dump(json_config, f, indent=4)
def load_config(filename, config):
@@ -778,14 +776,4 @@ def load_config(filename, config):
for device in original_config:
config[device] = {}
for property_name in original_config[device]:
property_value = original_config[device][property_name]
if property_name != 'DEVICE_PROPERTIES':
config[device][property_name] = property_value
continue
for hw_device in property_value:
hw_device_config = property_value[hw_device]
array = ""
for key in hw_device_config:
value = hw_device_config[key]
array += key + ' ' + value + ' '
config[device][hw_device] = array.strip()
config[device][property_name] = original_config[device][property_name]