Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DRAFT][CPU] New Plugin properties infrastructure #28246

Open
wants to merge 18 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
update behavior for set/get property. Add help message
Signed-off-by: Vladimir Paramuzov <vladimir.paramuzov@intel.com>
  • Loading branch information
vladimir-paramuzov committed Dec 23, 2024
commit 3ffe4170dd6968a7bbd55b6af4a04b6a2a7298ef
36 changes: 28 additions & 8 deletions src/inference/dev_api/openvino/runtime/plugin_config.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,26 @@

#define GET_EXCEPT_LAST(...) EXPAND(GET_EXCEPT_LAST_IMPL(COUNT(__VA_ARGS__), __VA_ARGS__))

#define GET_LAST_IMPL(N, ...) CAT(GET_LAST_IMPL_, N)(__VA_ARGS__)
#define GET_LAST_IMPL_0(_0, ...) _0
#define GET_LAST_IMPL_1(_0, _1, ...) _1
#define GET_LAST_IMPL_2(_0, _1, _2, ...) _2
#define GET_LAST_IMPL_3(_0, _1, _2, _3, ...) _3
#define GET_LAST_IMPL_4(_0, _1, _2, _3, _4, ...) _4
#define GET_LAST_IMPL_5(_0, _1, _2, _3, _4, _5, ...) _5
#define GET_LAST_IMPL_6(_0, _1, _2, _3, _4, _5, _6, ...) _6

#define GET_LAST(...) GET_LAST_IMPL(COUNT(__VA_ARGS__), _, __VA_ARGS__ ,,,,,,,,,,,)

#define OV_CONFIG_DECLARE_OPTION(PropertyNamespace, PropertyVar, Visibility, ...) \
ConfigOption<decltype(PropertyNamespace::PropertyVar)::value_type, Visibility> m_ ## PropertyVar{GET_EXCEPT_LAST(__VA_ARGS__)};

#define OV_CONFIG_OPTION_MAPPING(PropertyNamespace, PropertyVar, ...) \
m_options_map[PropertyNamespace::PropertyVar.name()] = & m_ ## PropertyVar;

#define OV_CONFIG_OPTION_HELP(PropertyNamespace, PropertyVar, Visibility, DefaultValue, ...) \
{ #PropertyNamespace "::" #PropertyVar, PropertyNamespace::PropertyVar.name(), GET_LAST(__VA_ARGS__)},

#define OV_CONFIG_RELEASE_OPTION(PropertyNamespace, PropertyVar, ...) \
OV_CONFIG_OPTION(PropertyNamespace, PropertyVar, OptionVisibility::RELEASE, __VA_ARGS__)

Expand Down Expand Up @@ -159,18 +173,12 @@ class OPENVINO_RUNTIME_API PluginConfig {

void set_property(const ov::AnyMap& properties);
Any get_property(const std::string& name) const;
void set_user_property(const ov::AnyMap& properties);

template <typename... Properties>
util::EnableIfAllStringAny<void, Properties...> set_property(Properties&&... properties) {
set_property(ov::AnyMap{std::forward<Properties>(properties)...});
}

template <typename... Properties>
util::EnableIfAllStringAny<void, Properties...> set_user_property(Properties&&... properties) {
set_user_property(ov::AnyMap{std::forward<Properties>(properties)...});
}

template <typename T, PropertyMutability mutability>
T get_property(const ov::Property<T, mutability>& property) const {
if (is_set_by_user(property)) {
Expand All @@ -189,6 +197,7 @@ class OPENVINO_RUNTIME_API PluginConfig {
virtual void apply_debug_options(std::shared_ptr<IRemoteContext> context);
virtual void finalize_impl(std::shared_ptr<IRemoteContext> context) {}


template <typename T, PropertyMutability mutability>
bool is_set_by_user(const ov::Property<T, mutability>& property) const {
return m_user_properties.find(property.name()) != m_user_properties.end();
Expand All @@ -207,12 +216,13 @@ class OPENVINO_RUNTIME_API PluginConfig {
if (!is_set_by_user(property)) {
auto rt_info_val = rt_info.find(property.name());
if (rt_info_val != rt_info.end()) {
set_user_property(property(rt_info_val->second.template as<T>()));
set_property(property(rt_info_val->second.template as<T>()));
}
}
}

void set_user_property(const ov::AnyMap& properties, const std::vector<OptionVisibility>& allowed_visibility, bool throw_on_error);
ov::Any get_property(const std::string& name, const std::vector<OptionVisibility>& allowed_visibility) const;
void set_property(const ov::AnyMap& properties, const std::vector<OptionVisibility>& allowed_visibility, bool throw_on_error);

ov::AnyMap read_config_file(const std::string& filename, const std::string& target_device_name) const;
ov::AnyMap read_env(const std::vector<std::string>& prefixes) const;
Expand All @@ -223,6 +233,16 @@ class OPENVINO_RUNTIME_API PluginConfig {
// List of properties explicitly set by user via Core::set_property() or Core::compile_model() or ov::Model's runtime info
ov::AnyMap m_user_properties;
using OptionMapEntry = decltype(m_options_map)::value_type;

// property variable name, string name, default value, description
using OptionsDesc = std::vector<std::tuple<std::string, std::string, std::string>>;
static OptionsDesc m_options_desc;
virtual const OptionsDesc& get_options_desc() const { static OptionsDesc empty; return empty; }
const std::string get_help_message(const std::string& name = "") const;
void print_help() const;

private:
bool m_is_finalized = false;
};

} // namespace ov
134 changes: 119 additions & 15 deletions src/inference/src/dev/plugin_config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,52 +8,87 @@
#include "openvino/runtime/device_id_parser.hpp"
#include "openvino/util/common_util.hpp"
#include "openvino/util/env_util.hpp"
#include <cmath>
#include <fstream>
#include <iomanip>

#ifdef JSON_HEADER
# include <json.hpp>
#else
# include <nlohmann/json.hpp>
#endif

namespace ov {

void PluginConfig::set_property(const AnyMap& config) {
for (auto& kv : config) {
auto& name = kv.first;
auto& val = kv.second;
#ifdef _WIN32
#include <windows.h>
#else
#include <unistd.h>
#include <sys/ioctl.h>
#endif

auto option = get_option_ptr(name);
option->set_any(val);
namespace {
size_t get_terminal_width() {
const size_t default_width = 120;
#ifdef _WIN32
CONSOLE_SCREEN_BUFFER_INFO csbi;
if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi)) {
return csbi.srWindow.Right - csbi.srWindow.Left + 1;
} else {
return default_width;
}
#else
struct winsize w;
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) == 0) {
return w.ws_col;
} else {
return default_width;
}
#endif // _WIN32
}
}

namespace ov {

ov::Any PluginConfig::get_property(const std::string& name) const {
const static std::vector<OptionVisibility> allowed_visibility = {OptionVisibility::RELEASE, OptionVisibility::RELEASE_INTERNAL};
return get_property(name, allowed_visibility);
}

ov::Any PluginConfig::get_property(const std::string& name, const std::vector<OptionVisibility>& allowed_visibility) const {
if (m_user_properties.find(name) != m_user_properties.end()) {
return m_user_properties.at(name);
}

auto option = get_option_ptr(name);
if (std::find(allowed_visibility.begin(), allowed_visibility.end(), option->get_visibility()) == allowed_visibility.end()) {
OPENVINO_THROW("Couldn't get unknown property: ", name);
}

return option->get_any();
}

void PluginConfig::set_user_property(const AnyMap& config) {
void PluginConfig::set_property(const AnyMap& config) {
const static std::vector<OptionVisibility> allowed_visibility = {OptionVisibility::RELEASE};
const bool throw_on_error = true;
set_user_property(config, allowed_visibility, throw_on_error);
set_property(config, allowed_visibility, throw_on_error);
}

void PluginConfig::set_user_property(const ov::AnyMap& config, const std::vector<OptionVisibility>& allowed_visibility, bool throw_on_error) {
void PluginConfig::set_property(const ov::AnyMap& config, const std::vector<OptionVisibility>& allowed_visibility, bool throw_on_error) {
OPENVINO_ASSERT(!m_is_finalized, "Setting property after config finalization is prohibited");

for (auto& kv : config) {
auto& name = kv.first;
auto& val = kv.second;

auto option = get_option_ptr(name);
if (std::find(allowed_visibility.begin(), allowed_visibility.end(), option->get_visibility()) == allowed_visibility.end()) {
if (throw_on_error)
OPENVINO_THROW("Unkown property: ", name);
OPENVINO_THROW("Couldn't set unknown property: ", name);
else
continue;
}
if (!option->is_valid_value(val)) {
if (throw_on_error)
OPENVINO_THROW("Invalid value: ", val.as<std::string>(), " for property: ", name);
OPENVINO_THROW("Invalid value: ", val.as<std::string>(), " for property: ", name, "\nProperty description: ", get_help_message(name));
else
continue;
}
Expand All @@ -79,6 +114,8 @@ void PluginConfig::finalize(std::shared_ptr<IRemoteContext> context, const ov::R

// Clear properties after finalize_impl to be able to check if a property was set by user during plugin-side finalization
m_user_properties.clear();

m_is_finalized = true;
}

void PluginConfig::apply_debug_options(std::shared_ptr<IRemoteContext> context) {
Expand All @@ -95,12 +132,12 @@ void PluginConfig::apply_debug_options(std::shared_ptr<IRemoteContext> context)
if (context) {
ov::AnyMap config_properties = read_config_file("config.json", context->get_device_name());
cleanup_unsupported(config_properties);
set_user_property(config_properties, allowed_visibility, throw_on_error);
set_property(config_properties, allowed_visibility, throw_on_error);
}

ov::AnyMap env_properties = read_env({"OV_"});
cleanup_unsupported(env_properties);
set_user_property(env_properties, allowed_visibility, throw_on_error);
set_property(env_properties, allowed_visibility, throw_on_error);
}

ov::AnyMap PluginConfig::read_config_file(const std::string& filename, const std::string& target_device_name) const {
Expand Down Expand Up @@ -194,4 +231,71 @@ std::string PluginConfig::to_string() const {
return s.str();
}

void PluginConfig::print_help() const {
auto format_text = [](const std::string& cpp_name, const std::string& str_name, const std::string& desc, size_t max_name_width, size_t max_width) {
std::istringstream words(desc);
std::ostringstream formatted_text;
std::string word;
std::vector<std::string> words_vec;

while (words >> word) {
words_vec.push_back(word);
}

size_t j = 0;
size_t count_of_desc_lines = (desc.length() + max_width - 1) / max_width;
for (size_t i = 0 ; i < std::max<size_t>(2, count_of_desc_lines); i++) {
if (i == 0) {
formatted_text << std::left << std::setw(max_name_width) << cpp_name;
} else if (i == 1) {
formatted_text << std::left << std::setw(max_name_width) << str_name;
} else {
formatted_text << std::left << std::setw(max_name_width) << "";
}

formatted_text << " | ";

size_t line_length = max_name_width + 3;
for (; j < words_vec.size();) {
line_length += words_vec[j].size() + 1;
if (line_length > max_width) {
break;
} else {
formatted_text << words_vec[j] << " ";
}
j++;
}
formatted_text << "\n";
}
return formatted_text.str();
};

const auto& options_desc = get_options_desc();
std::stringstream ss;
auto max_name_length_item = std::max_element(options_desc.begin(), options_desc.end(),
[](const OptionsDesc::value_type& a, const OptionsDesc::value_type& b){
return std::get<0>(a).size() < std::get<0>(b).size();
});

const size_t max_name_width = static_cast<int>(std::get<0>(*max_name_length_item).size() + std::get<1>(*max_name_length_item).size());
const size_t terminal_width = get_terminal_width();
ss << std::left << std::setw(max_name_width) << ("Option name") << " | " << " Description " << "\n";
ss << std::left << std::setw(terminal_width) << std::setfill('-') << "" << "\n";
for (auto& kv : options_desc) {
ss << format_text(std::get<0>(kv), std::get<1>(kv), std::get<2>(kv), max_name_width, terminal_width) << "\n";
}

std::cout << ss.str();
}

const std::string PluginConfig::get_help_message(const std::string& name) const {
const auto& options_desc = get_options_desc();
auto it = std::find_if(options_desc.begin(), options_desc.end(), [&](const OptionsDesc::value_type& v) { return std::get<1>(v) == name; });
if (it != options_desc.end()) {
return std::get<2>(*it);
}

return "";
}

} // namespace ov
32 changes: 12 additions & 20 deletions src/inference/tests/unit/config_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,35 +101,27 @@ TEST(plugin_config, can_set_get_property) {
ASSERT_EQ(cfg.get_property(bool_property), true);
ASSERT_NO_THROW(cfg.set_property(bool_property(false)));
ASSERT_EQ(cfg.get_property(bool_property), false);

ASSERT_NO_THROW(cfg.set_user_property(bool_property(true)));
ASSERT_EQ(cfg.get_property(bool_property), true);
}

TEST(plugin_config, throw_for_unsupported_property) {
NotEmptyTestConfig cfg;
ASSERT_ANY_THROW(cfg.get_property(unsupported_property));
ASSERT_ANY_THROW(cfg.set_property(unsupported_property(10.0f)));
ASSERT_ANY_THROW(cfg.set_user_property(unsupported_property(10.0f)));
}

TEST(plugin_config, can_direct_access_to_properties) {
NotEmptyTestConfig cfg;
ASSERT_EQ(cfg.m_bool_property.value, cfg.get_property(bool_property));
ASSERT_NO_THROW(cfg.set_property(bool_property(false)));
ASSERT_EQ(cfg.m_bool_property.value, cfg.get_property(bool_property));
ASSERT_EQ(cfg.m_bool_property.value, false);
ASSERT_EQ(cfg.m_int_property.value, cfg.get_property(int_property));
ASSERT_NO_THROW(cfg.set_property(int_property(1)));
ASSERT_EQ(cfg.m_int_property.value, -1); // user property doesn't impact member value until finalize() is called

ASSERT_NO_THROW(cfg.set_user_property(bool_property(true)));
ASSERT_EQ(cfg.m_bool_property.value, false); // user property doesn't impact member value until finalize() is called

cfg.m_bool_property.value = true;
ASSERT_EQ(cfg.get_property(bool_property), true);
cfg.m_int_property.value = 2;
ASSERT_EQ(cfg.get_property(int_property), 1); // still 1 as user property was set previously
}

TEST(plugin_config, finalization_updates_member) {
NotEmptyTestConfig cfg;
ASSERT_NO_THROW(cfg.set_user_property(bool_property(false)));
ASSERT_NO_THROW(cfg.set_property(bool_property(false)));
ASSERT_EQ(cfg.m_bool_property.value, true); // user property doesn't impact member value until finalize() is called

cfg.finalize(nullptr, {});
Expand All @@ -146,7 +138,7 @@ TEST(plugin_config, get_property_before_finalization_returns_user_property_if_se
cfg.m_bool_property.value = false; // update member directly
ASSERT_EQ(cfg.get_property(bool_property), false); // OK, return the class member value as no user property was set

ASSERT_NO_THROW(cfg.set_user_property(bool_property(true)));
ASSERT_NO_THROW(cfg.set_property(bool_property(true)));
ASSERT_TRUE(cfg.is_set_by_user(bool_property));
ASSERT_EQ(cfg.get_property(bool_property), true); // now user property value is returned
ASSERT_EQ(cfg.m_bool_property.value, false); // but class member is not updated
Expand All @@ -159,7 +151,7 @@ TEST(plugin_config, get_property_before_finalization_returns_user_property_if_se
TEST(plugin_config, finalization_updates_dependant_properties) {
NotEmptyTestConfig cfg;

cfg.set_user_property(high_level_property("value1"));
cfg.set_property(high_level_property("value1"));
ASSERT_TRUE(cfg.is_set_by_user(high_level_property));
ASSERT_FALSE(cfg.is_set_by_user(low_level_property));

Expand Down Expand Up @@ -196,7 +188,7 @@ TEST(plugin_config, can_copy_config) {
cfg1.m_high_level_property.value = "value1";
cfg1.m_low_level_property.value = "value2";
cfg1.m_int_property.value = 1;
cfg1.set_user_property(bool_property(false));
cfg1.set_property(bool_property(false));

NotEmptyTestConfig cfg2 = cfg1;
ASSERT_EQ(cfg2.m_high_level_property.value, "value1");
Expand All @@ -211,10 +203,10 @@ TEST(plugin_config, can_copy_config) {
ASSERT_EQ(cfg2.m_int_property.value, 1);
}

TEST(plugin_config, set_user_property_throw_for_non_release_options) {
TEST(plugin_config, set_property_throw_for_non_release_options) {
NotEmptyTestConfig cfg;
ASSERT_ANY_THROW(cfg.set_user_property(release_internal_property(10)));
ASSERT_ANY_THROW(cfg.set_user_property(debug_property(10)));
ASSERT_ANY_THROW(cfg.set_property(release_internal_property(10)));
ASSERT_ANY_THROW(cfg.set_property(debug_property(10)));
}

TEST(plugin_config, visibility_is_correct) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ class Plugin : public ov::IPlugin {

bool is_metric(const std::string& name) const;
ov::Any get_metric(const std::string& name, const ov::AnyMap& arguments) const;
void set_cache_info(const std::shared_ptr<const ov::Model>& model, ExecutionConfig& properties) const;

public:
Plugin();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,11 @@ struct ExecutionConfig : public ov::PluginConfig {
#include "intel_gpu/runtime/options.inl"
#undef OV_CONFIG_OPTION

protected:
void finalize_impl(std::shared_ptr<IRemoteContext> context) override;
void apply_rt_info(std::shared_ptr<IRemoteContext> context, const ov::RTMap& rt_info) override;
const ov::PluginConfig::OptionsDesc& get_options_desc() const override;

private:
void apply_user_properties(const cldnn::device_info& info);
void apply_hints(const cldnn::device_info& info);
void apply_execution_hints(const cldnn::device_info& info);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ OV_CONFIG_RELEASE_OPTION(ov, cache_dir, "", "Directory where model cache can be
OV_CONFIG_RELEASE_OPTION(ov, num_streams, 1, "Number of streams to be used for inference")
OV_CONFIG_RELEASE_OPTION(ov, compilation_num_threads, std::max(1, static_cast<int>(std::thread::hardware_concurrency())), "Max number of CPU threads used for model compilation for the stages that supports parallelism")
OV_CONFIG_RELEASE_OPTION(ov::hint, inference_precision, ov::element::f16,
[](ov::element::Type t) { return t == ov::element::f16 || t == ov::element::f32 || t == ov::element::undefined; }, "Model floating-point inference precision")
[](ov::element::Type t) { return t == ov::element::f16 || t == ov::element::f32 || t == ov::element::undefined; }, "Model floating-point inference precision. Supported values: { f16, f32, undefined }")
OV_CONFIG_RELEASE_OPTION(ov::hint, model_priority, ov::hint::Priority::MEDIUM, "High-level hint that defines the priority of the model. It may impact number of threads used for model compilton and inference as well as device queue settings")
OV_CONFIG_RELEASE_OPTION(ov::hint, performance_mode, ov::hint::PerformanceMode::LATENCY, "High-level hint that defines target model inference mode. It may impact number of streams, auto batching, etc")
OV_CONFIG_RELEASE_OPTION(ov::hint, execution_mode, ov::hint::ExecutionMode::PERFORMANCE, "High-level hint that defines the most important metric for the model. Performance mode allows unsafe optimizations that may reduce the model accuracy")
Expand Down
Loading