[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 "itt.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/properties.hpp"
#include "openvino/util/common_util.hpp"
#include "plugin.hpp"
#include "properties.hpp"
#include "xml_parse_utils.h"
template <typename T>
@ -55,6 +58,14 @@ ov::hetero::CompiledModel::CompiledModel(const std::shared_ptr<ov::Model>& model
if (std::getenv("OPENVINO_HETERO_VISUALIZE"))
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;
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,
ov::optimal_number_of_infer_requests,
ov::execution_devices,
ov::loaded_from_cache};
ov::loaded_from_cache,
ov::hetero::number_of_submodels};
return ro_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);
}
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);
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"};
/**
* @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 ov

View File

@ -42,6 +42,20 @@ TEST_F(HeteroTests, compile_without_device_priorities_throw) {
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) {
ov::AnyMap config = {ov::device::priorities("MOCK0,MOCK1"),
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/except.hpp"
#include "openvino/opsets/opset11.hpp"
#include "openvino/pass/constant_folding.hpp"
#include "openvino/pass/manager.hpp"
#include "openvino/pass/serialize.hpp"
#include "openvino/runtime/exec_model_info.hpp"
#include "openvino/runtime/internal_properties.hpp"
@ -22,6 +24,7 @@
#include "openvino/runtime/properties.hpp"
#include "openvino/util/file_util.hpp"
#include "openvino/util/shared_object.hpp"
#include "transformations/init_node_info.hpp"
#include "transformations/rt_info/fused_names_attribute.hpp"
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.");
}
std::shared_ptr<ov::Model> ov::hetero::tests::HeteroTests::create_model_with_subtract() {
auto param = std::make_shared<ov::opset11::Parameter>(ov::element::i64, ov::Shape{1, 3, 2, 2});
std::shared_ptr<ov::Model> ov::hetero::tests::HeteroTests::create_model_with_subtract(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 const_value = ov::opset11::Constant::create(ov::element::i64, ov::Shape{1, 1, 1, 1}, {1});
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});
}
std::shared_ptr<ov::Model> ov::hetero::tests::HeteroTests::create_model_with_subtract_reshape() {
auto param = std::make_shared<ov::opset11::Parameter>(ov::element::i64, ov::Shape{1, 3, 2, 2});
std::shared_ptr<ov::Model> ov::hetero::tests::HeteroTests::create_model_with_subtract_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 const_value = ov::opset11::Constant::create(ov::element::i64, ov::Shape{1, 1, 1, 1}, {1});
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});
}
std::shared_ptr<ov::Model> ov::hetero::tests::HeteroTests::create_model_with_subtract_reshape_relu() {
auto param = std::make_shared<ov::opset11::Parameter>(ov::element::i64, ov::Shape{1, 3, 2, 2});
std::shared_ptr<ov::Model> ov::hetero::tests::HeteroTests::create_model_with_subtract_reshape_relu(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 const_value = ov::opset11::Constant::create(ov::element::i64, ov::Shape{1, 1, 1, 1}, {1});
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});
}
std::shared_ptr<ov::Model> ov::hetero::tests::HeteroTests::create_model_with_reshape() {
auto param = std::make_shared<ov::opset11::Parameter>(ov::element::i64, ov::Shape{1, 3, 2, 2});
std::shared_ptr<ov::Model> ov::hetero::tests::HeteroTests::create_model_with_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 const_value = ov::opset11::Constant::create(ov::element::i64, ov::Shape{1, 1, 1, 1}, {1});
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});
}
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
class MockCompiledModel : public ov::ICompiledModel {
@ -386,8 +414,11 @@ public:
class MockPluginBase : public ov::IPlugin {
public:
MockPluginBase(const std::string& name, const std::unordered_set<std::string>& supported_ops)
: m_supported_ops(supported_ops) {
MockPluginBase(const std::string& name,
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);
}
@ -501,10 +532,24 @@ public:
auto device_id = properties.count(ov::device::id.name())
? properties.at(ov::device::id.name()).as<std::string>()
: 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())
continue;
res[op->get_friendly_name()] = get_device_name() + "." + device_id;
return false;
return true;
});
for (auto&& op_name : supported) {
res.emplace(op_name, get_device_name() + "." + device_id);
}
return res;
}
@ -512,6 +557,7 @@ public:
protected:
std::string m_default_device_id = "0";
std::unordered_set<std::string> m_supported_ops;
bool m_dynamism_supported = false;
bool m_profiling = false;
bool m_loaded_from_cache{false};
};
@ -519,7 +565,7 @@ protected:
class MockPluginReshape : public MockPluginBase {
public:
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 {
static const ov::Version version = {CI_BUILD_NUMBER, "openvino_mock_reshape_plugin"};

View File

@ -20,10 +20,11 @@ public:
void SetUp() override;
std::shared_ptr<ov::Model> create_model_with_subtract();
std::shared_ptr<ov::Model> create_model_with_subtract_reshape();
std::shared_ptr<ov::Model> create_model_with_subtract_reshape_relu();
std::shared_ptr<ov::Model> create_model_with_reshape();
std::shared_ptr<ov::Model> create_model_with_subtract(bool dynamic = false);
std::shared_ptr<ov::Model> create_model_with_subtract_reshape(bool dynamic = false);
std::shared_ptr<ov::Model> create_model_with_subtract_reshape_relu(bool dynamic = false);
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);
private:

View File

@ -37,8 +37,14 @@ TEST_F(HeteroTests, query_model_on_mock1) {
EXPECT_EQ(op.second, dev_name);
names.erase(op.first);
}
EXPECT_EQ(1, names.size());
EXPECT_EQ("reshape", *names.begin());
const std::vector<std::string> unmarked_names = {"reshape_val", "reshape", "res"};
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) {
@ -65,3 +71,30 @@ TEST_F(HeteroTests, query_model_on_mixed) {
}
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"));
}