[HETERO] Avoid unexpected dynamism (#18862)

* [HETERO] Add ConstantFolding in compile modelto avoid unexpected dynamism after model split. Add new property, which shows number of subgraphs

* Remove check for dynamic subgraph
This commit is contained in:
Nadezhda Ageeva 2023-08-09 17:22:20 +04:00 committed by GitHub
parent 19b3f062af
commit 81645aaeda
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 135 additions and 22 deletions

View File

@ -11,10 +11,13 @@
#include "ie_plugin_config.hpp" #include "ie_plugin_config.hpp"
#include "itt.hpp" #include "itt.hpp"
#include "openvino/op/util/op_types.hpp" #include "openvino/op/util/op_types.hpp"
#include "openvino/pass/constant_folding.hpp"
#include "openvino/pass/manager.hpp"
#include "openvino/runtime/internal_properties.hpp" #include "openvino/runtime/internal_properties.hpp"
#include "openvino/runtime/properties.hpp" #include "openvino/runtime/properties.hpp"
#include "openvino/util/common_util.hpp" #include "openvino/util/common_util.hpp"
#include "plugin.hpp" #include "plugin.hpp"
#include "properties.hpp"
#include "xml_parse_utils.h" #include "xml_parse_utils.h"
template <typename T> template <typename T>
@ -55,6 +58,14 @@ ov::hetero::CompiledModel::CompiledModel(const std::shared_ptr<ov::Model>& model
if (std::getenv("OPENVINO_HETERO_VISUALIZE")) if (std::getenv("OPENVINO_HETERO_VISUALIZE"))
dumpDotFile = true; dumpDotFile = true;
// Calling of ConstantFolding in HETERO plugin is required because
// in some cases topology split is happening after constant subgraph.
// It may cause replacement of Constant by Parameter in such operations
// like Reshape/Transpose/Gather and lead to unexpected dynamism or exception
ov::pass::Manager manager;
manager.register_pass<ov::pass::ConstantFolding>();
manager.run_passes(model);
ov::SupportedOpsMap queryNetworkResult; ov::SupportedOpsMap queryNetworkResult;
auto orderedOps = model->get_ordered_ops(); auto orderedOps = model->get_ordered_ops();
@ -664,7 +675,8 @@ ov::Any ov::hetero::CompiledModel::get_property(const std::string& name) const {
std::vector<ov::PropertyName> ro_properties{ov::model_name, std::vector<ov::PropertyName> ro_properties{ov::model_name,
ov::optimal_number_of_infer_requests, ov::optimal_number_of_infer_requests,
ov::execution_devices, ov::execution_devices,
ov::loaded_from_cache}; ov::loaded_from_cache,
ov::hetero::number_of_submodels};
return ro_properties; return ro_properties;
}; };
const auto& to_string_vector = [](const std::vector<ov::PropertyName>& properties) { const auto& to_string_vector = [](const std::vector<ov::PropertyName>& properties) {
@ -723,6 +735,8 @@ ov::Any ov::hetero::CompiledModel::get_property(const std::string& name) const {
device_names.push_back(comp_model_desc.device); device_names.push_back(comp_model_desc.device);
} }
return decltype(ov::execution_devices)::value_type{device_names}; return decltype(ov::execution_devices)::value_type{device_names};
} else if (ov::hetero::number_of_submodels == name) {
return decltype(ov::hetero::number_of_submodels)::value_type{m_compiled_submodels.size()};
} }
return m_cfg.get(name); return m_cfg.get(name);
OPENVINO_SUPPRESS_DEPRECATED_END OPENVINO_SUPPRESS_DEPRECATED_END

View File

@ -13,5 +13,10 @@ namespace hetero {
*/ */
static constexpr Property<std::string, PropertyMutability::RO> caching_device_properties{"CACHING_DEVICE_PROPERTIES"}; static constexpr Property<std::string, PropertyMutability::RO> caching_device_properties{"CACHING_DEVICE_PROPERTIES"};
/**
* @brief Read-only property showing number of compiled submodels
*/
static constexpr Property<size_t, PropertyMutability::RO> number_of_submodels{"HETERO_NUMBER_OF_SUBMODELS"};
} // namespace hetero } // namespace hetero
} // namespace ov } // namespace ov

View File

@ -42,6 +42,20 @@ TEST_F(HeteroTests, compile_without_device_priorities_throw) {
EXPECT_THROW(core.compile_model(model, "HETERO"), ov::Exception); EXPECT_THROW(core.compile_model(model, "HETERO"), ov::Exception);
} }
TEST_F(HeteroTests, compile_dynamic_model_fail) {
// Change device priority
core.set_property("HETERO", ov::device::priorities("MOCK0,MOCK1"));
auto model = create_model_with_subtract_reshape(true);
EXPECT_THROW(core.compile_model(model, "HETERO"), ov::Exception);
}
TEST_F(HeteroTests, compile_model_shapeof) {
// Change device priority
core.set_property("HETERO", ov::device::priorities("MOCK0,MOCK1"));
auto model = create_model_with_subtract_shapeof_reshape();
EXPECT_NO_THROW(core.compile_model(model, "HETERO"));
}
TEST_F(HeteroTests, compile_with_device_properties) { TEST_F(HeteroTests, compile_with_device_properties) {
ov::AnyMap config = {ov::device::priorities("MOCK0,MOCK1"), ov::AnyMap config = {ov::device::priorities("MOCK0,MOCK1"),
ov::device::properties("MOCK0", ov::num_streams(4), ov::enable_profiling(false)), ov::device::properties("MOCK0", ov::num_streams(4), ov::enable_profiling(false)),

View File

@ -12,6 +12,8 @@
#include "openvino/core/any.hpp" #include "openvino/core/any.hpp"
#include "openvino/core/except.hpp" #include "openvino/core/except.hpp"
#include "openvino/opsets/opset11.hpp" #include "openvino/opsets/opset11.hpp"
#include "openvino/pass/constant_folding.hpp"
#include "openvino/pass/manager.hpp"
#include "openvino/pass/serialize.hpp" #include "openvino/pass/serialize.hpp"
#include "openvino/runtime/exec_model_info.hpp" #include "openvino/runtime/exec_model_info.hpp"
#include "openvino/runtime/internal_properties.hpp" #include "openvino/runtime/internal_properties.hpp"
@ -22,6 +24,7 @@
#include "openvino/runtime/properties.hpp" #include "openvino/runtime/properties.hpp"
#include "openvino/util/file_util.hpp" #include "openvino/util/file_util.hpp"
#include "openvino/util/shared_object.hpp" #include "openvino/util/shared_object.hpp"
#include "transformations/init_node_info.hpp"
#include "transformations/rt_info/fused_names_attribute.hpp" #include "transformations/rt_info/fused_names_attribute.hpp"
namespace { namespace {
@ -67,8 +70,9 @@ ov::Tensor ov::hetero::tests::HeteroTests::create_and_fill_tensor(const ov::elem
OPENVINO_THROW("Cannot generate tensor. Unsupported element type."); OPENVINO_THROW("Cannot generate tensor. Unsupported element type.");
} }
std::shared_ptr<ov::Model> ov::hetero::tests::HeteroTests::create_model_with_subtract() { std::shared_ptr<ov::Model> ov::hetero::tests::HeteroTests::create_model_with_subtract(bool dynamic) {
auto param = std::make_shared<ov::opset11::Parameter>(ov::element::i64, ov::Shape{1, 3, 2, 2}); int64_t bs = dynamic ? -1 : 1;
auto param = std::make_shared<ov::opset11::Parameter>(ov::element::i64, ov::PartialShape{bs, 3, 2, 2});
param->set_friendly_name("input"); param->set_friendly_name("input");
auto const_value = ov::opset11::Constant::create(ov::element::i64, ov::Shape{1, 1, 1, 1}, {1}); auto const_value = ov::opset11::Constant::create(ov::element::i64, ov::Shape{1, 1, 1, 1}, {1});
const_value->set_friendly_name("const_val"); const_value->set_friendly_name("const_val");
@ -81,8 +85,9 @@ std::shared_ptr<ov::Model> ov::hetero::tests::HeteroTests::create_model_with_sub
return std::make_shared<ov::Model>(ov::ResultVector{result}, ov::ParameterVector{param}); return std::make_shared<ov::Model>(ov::ResultVector{result}, ov::ParameterVector{param});
} }
std::shared_ptr<ov::Model> ov::hetero::tests::HeteroTests::create_model_with_subtract_reshape() { std::shared_ptr<ov::Model> ov::hetero::tests::HeteroTests::create_model_with_subtract_reshape(bool dynamic) {
auto param = std::make_shared<ov::opset11::Parameter>(ov::element::i64, ov::Shape{1, 3, 2, 2}); int64_t bs = dynamic ? -1 : 1;
auto param = std::make_shared<ov::opset11::Parameter>(ov::element::i64, ov::PartialShape{bs, 3, 2, 2});
param->set_friendly_name("input"); param->set_friendly_name("input");
auto const_value = ov::opset11::Constant::create(ov::element::i64, ov::Shape{1, 1, 1, 1}, {1}); auto const_value = ov::opset11::Constant::create(ov::element::i64, ov::Shape{1, 1, 1, 1}, {1});
const_value->set_friendly_name("const_val"); const_value->set_friendly_name("const_val");
@ -99,8 +104,9 @@ std::shared_ptr<ov::Model> ov::hetero::tests::HeteroTests::create_model_with_sub
return std::make_shared<ov::Model>(ov::ResultVector{result}, ov::ParameterVector{param}); return std::make_shared<ov::Model>(ov::ResultVector{result}, ov::ParameterVector{param});
} }
std::shared_ptr<ov::Model> ov::hetero::tests::HeteroTests::create_model_with_subtract_reshape_relu() { std::shared_ptr<ov::Model> ov::hetero::tests::HeteroTests::create_model_with_subtract_reshape_relu(bool dynamic) {
auto param = std::make_shared<ov::opset11::Parameter>(ov::element::i64, ov::Shape{1, 3, 2, 2}); int64_t bs = dynamic ? -1 : 1;
auto param = std::make_shared<ov::opset11::Parameter>(ov::element::i64, ov::PartialShape{bs, 3, 2, 2});
param->set_friendly_name("input"); param->set_friendly_name("input");
auto const_value = ov::opset11::Constant::create(ov::element::i64, ov::Shape{1, 1, 1, 1}, {1}); auto const_value = ov::opset11::Constant::create(ov::element::i64, ov::Shape{1, 1, 1, 1}, {1});
const_value->set_friendly_name("const_val"); const_value->set_friendly_name("const_val");
@ -119,8 +125,9 @@ std::shared_ptr<ov::Model> ov::hetero::tests::HeteroTests::create_model_with_sub
return std::make_shared<ov::Model>(ov::ResultVector{result}, ov::ParameterVector{param}); return std::make_shared<ov::Model>(ov::ResultVector{result}, ov::ParameterVector{param});
} }
std::shared_ptr<ov::Model> ov::hetero::tests::HeteroTests::create_model_with_reshape() { std::shared_ptr<ov::Model> ov::hetero::tests::HeteroTests::create_model_with_reshape(bool dynamic) {
auto param = std::make_shared<ov::opset11::Parameter>(ov::element::i64, ov::Shape{1, 3, 2, 2}); int64_t bs = dynamic ? -1 : 1;
auto param = std::make_shared<ov::opset11::Parameter>(ov::element::i64, ov::PartialShape{bs, 3, 2, 2});
param->set_friendly_name("input"); param->set_friendly_name("input");
auto const_value = ov::opset11::Constant::create(ov::element::i64, ov::Shape{1, 1, 1, 1}, {1}); auto const_value = ov::opset11::Constant::create(ov::element::i64, ov::Shape{1, 1, 1, 1}, {1});
const_value->set_friendly_name("const_val"); const_value->set_friendly_name("const_val");
@ -135,6 +142,27 @@ std::shared_ptr<ov::Model> ov::hetero::tests::HeteroTests::create_model_with_res
return std::make_shared<ov::Model>(ov::ResultVector{result}, ov::ParameterVector{param}); return std::make_shared<ov::Model>(ov::ResultVector{result}, ov::ParameterVector{param});
} }
std::shared_ptr<ov::Model> ov::hetero::tests::HeteroTests::create_model_with_subtract_shapeof_reshape(bool dynamic) {
int64_t bs = dynamic ? -1 : 1;
auto param = std::make_shared<ov::opset11::Parameter>(ov::element::i64, ov::PartialShape{bs, 3, 2, 2});
param->set_friendly_name("input");
auto reshape_val0 = ov::opset11::Constant::create<int64_t>(ov::element::i64, ov::Shape{2}, {bs, 12});
reshape_val0->set_friendly_name("reshape_val0");
auto reshape0 = std::make_shared<ov::opset11::Reshape>(param, reshape_val0, true);
reshape0->set_friendly_name("reshape0");
auto const_value = ov::opset11::Constant::create(ov::element::i64, ov::Shape{1, 1}, {1});
const_value->set_friendly_name("const_val");
auto subtract = std::make_shared<ov::opset11::Subtract>(reshape0, const_value);
subtract->set_friendly_name("sub");
auto shape_of = std::make_shared<ov::opset11::ShapeOf>(param);
shape_of->set_friendly_name("shape_of");
auto reshape1 = std::make_shared<ov::opset11::Reshape>(subtract, shape_of, true);
reshape1->set_friendly_name("reshape1");
auto result = std::make_shared<ov::opset11::Result>(reshape1);
result->set_friendly_name("res");
return std::make_shared<ov::Model>(ov::ResultVector{result}, ov::ParameterVector{param});
}
// Mock plugins // Mock plugins
class MockCompiledModel : public ov::ICompiledModel { class MockCompiledModel : public ov::ICompiledModel {
@ -386,8 +414,11 @@ public:
class MockPluginBase : public ov::IPlugin { class MockPluginBase : public ov::IPlugin {
public: public:
MockPluginBase(const std::string& name, const std::unordered_set<std::string>& supported_ops) MockPluginBase(const std::string& name,
: m_supported_ops(supported_ops) { const std::unordered_set<std::string>& supported_ops,
bool dynamism_supported = false)
: m_supported_ops(supported_ops),
m_dynamism_supported(dynamism_supported) {
set_device_name(name); set_device_name(name);
} }
@ -501,10 +532,24 @@ public:
auto device_id = properties.count(ov::device::id.name()) auto device_id = properties.count(ov::device::id.name())
? properties.at(ov::device::id.name()).as<std::string>() ? properties.at(ov::device::id.name()).as<std::string>()
: m_default_device_id; : m_default_device_id;
for (const auto& op : model->get_ordered_ops()) {
auto supported = ov::get_supported_nodes(
model,
[&](std::shared_ptr<ov::Model>& model) {
ov::pass::Manager manager;
manager.register_pass<ov::pass::InitNodeInfo>();
manager.register_pass<ov::pass::ConstantFolding>();
manager.run_passes(model);
},
[&](const std::shared_ptr<ov::Node>& op) {
if (op->is_dynamic() && !m_dynamism_supported)
return false;
if (m_supported_ops.find(op->get_type_info().name) == m_supported_ops.end()) if (m_supported_ops.find(op->get_type_info().name) == m_supported_ops.end())
continue; return false;
res[op->get_friendly_name()] = get_device_name() + "." + device_id; return true;
});
for (auto&& op_name : supported) {
res.emplace(op_name, get_device_name() + "." + device_id);
} }
return res; return res;
} }
@ -512,6 +557,7 @@ public:
protected: protected:
std::string m_default_device_id = "0"; std::string m_default_device_id = "0";
std::unordered_set<std::string> m_supported_ops; std::unordered_set<std::string> m_supported_ops;
bool m_dynamism_supported = false;
bool m_profiling = false; bool m_profiling = false;
bool m_loaded_from_cache{false}; bool m_loaded_from_cache{false};
}; };
@ -519,7 +565,7 @@ protected:
class MockPluginReshape : public MockPluginBase { class MockPluginReshape : public MockPluginBase {
public: public:
MockPluginReshape(const std::string& name) MockPluginReshape(const std::string& name)
: MockPluginBase(name, {"Parameter", "Result", "Add", "Constant", "Reshape"}) {} : MockPluginBase(name, {"Parameter", "Result", "Add", "Constant", "Reshape"}, true) {}
const ov::Version& get_const_version() override { const ov::Version& get_const_version() override {
static const ov::Version version = {CI_BUILD_NUMBER, "openvino_mock_reshape_plugin"}; static const ov::Version version = {CI_BUILD_NUMBER, "openvino_mock_reshape_plugin"};

View File

@ -20,10 +20,11 @@ public:
void SetUp() override; void SetUp() override;
std::shared_ptr<ov::Model> create_model_with_subtract(); std::shared_ptr<ov::Model> create_model_with_subtract(bool dynamic = false);
std::shared_ptr<ov::Model> create_model_with_subtract_reshape(); std::shared_ptr<ov::Model> create_model_with_subtract_reshape(bool dynamic = false);
std::shared_ptr<ov::Model> create_model_with_subtract_reshape_relu(); std::shared_ptr<ov::Model> create_model_with_subtract_reshape_relu(bool dynamic = false);
std::shared_ptr<ov::Model> create_model_with_reshape(); std::shared_ptr<ov::Model> create_model_with_reshape(bool dynamic = false);
std::shared_ptr<ov::Model> create_model_with_subtract_shapeof_reshape(bool dynamic = false);
ov::Tensor create_and_fill_tensor(const ov::element::Type& type, const ov::Shape& shape); ov::Tensor create_and_fill_tensor(const ov::element::Type& type, const ov::Shape& shape);
private: private:

View File

@ -37,8 +37,14 @@ TEST_F(HeteroTests, query_model_on_mock1) {
EXPECT_EQ(op.second, dev_name); EXPECT_EQ(op.second, dev_name);
names.erase(op.first); names.erase(op.first);
} }
EXPECT_EQ(1, names.size()); const std::vector<std::string> unmarked_names = {"reshape_val", "reshape", "res"};
EXPECT_EQ("reshape", *names.begin()); EXPECT_EQ(unmarked_names.size(), names.size());
for (auto& name : unmarked_names) {
auto it = names.find(name);
if (it != names.end())
names.erase(it);
}
EXPECT_EQ(0, names.size());
} }
TEST_F(HeteroTests, query_model_on_mixed) { TEST_F(HeteroTests, query_model_on_mixed) {
@ -65,3 +71,30 @@ TEST_F(HeteroTests, query_model_on_mixed) {
} }
EXPECT_EQ(0, names.size()); EXPECT_EQ(0, names.size());
} }
TEST_F(HeteroTests, query_dynamic_model_on_mixed) {
const std::string dev_name0 = "MOCK0.3";
const std::string dev_name1 = "MOCK1.2";
ov::AnyMap config = {ov::device::priorities(dev_name0 + "," + dev_name1)};
const auto model = create_model_with_subtract_reshape(true);
std::set<std::string> supported_ops_mock0;
for (auto& op : core.query_model(model, dev_name0)) {
if (op.second == dev_name0)
supported_ops_mock0.insert(op.first);
}
const auto supported_ops = core.query_model(model, "HETERO", config);
std::unordered_set<std::string> names;
for (const auto& op : model->get_ops()) {
names.insert(op->get_friendly_name());
}
for (const auto& op : supported_ops) {
if (supported_ops_mock0.count(op.first))
EXPECT_EQ(op.second, dev_name0);
else
EXPECT_EQ(op.second, dev_name1);
names.erase(op.first);
}
EXPECT_EQ(1, names.size());
// fallback plugin doesn't support dynamism
ASSERT_TRUE(names.count("sub"));
}