From 45f98cece56af585c4bb2111b7d012dc4189bbfc Mon Sep 17 00:00:00 2001 From: Gleb Kazantaev Date: Fri, 28 Jan 2022 16:36:45 +0300 Subject: [PATCH] Load Time Improvements (#9946) * Fix ConstantFolding; update ric fusion * Update MarkPrecisionSensitiveDivdes pass * Move back RIC fusion; Update copy_runtime_info logic + tests * Update CF + tests * Code style fix * Code clean-up * fix cf attr propagation * Fix windows build --- .../include/transformations/utils/utils.hpp | 6 +- .../mark_precision_sensitive_divides.cpp | 38 ++-- .../mark_precision_sensitive_subgraphs.cpp | 36 ++-- .../src/transformations/utils/utils.cpp | 13 +- .../openvino/pass/constant_folding.hpp | 13 +- src/core/src/pass/constant_folding.cpp | 61 ++++-- src/core/src/rt_info.cpp | 5 +- src/core/src/validation_util.cpp | 18 -- src/core/tests/CMakeLists.txt | 1 + src/core/tests/constant_folding.cpp | 113 +++++++++- src/core/tests/copy_runtime_info.cpp | 199 ++++++++++++++++++ .../compress_float_constants_test.cpp | 28 +-- .../transformations/convert_divide.cpp | 39 +++- 13 files changed, 456 insertions(+), 114 deletions(-) create mode 100644 src/core/tests/copy_runtime_info.cpp diff --git a/src/common/transformations/include/transformations/utils/utils.hpp b/src/common/transformations/include/transformations/utils/utils.hpp index ca223755c6b..0884155ccd5 100644 --- a/src/common/transformations/include/transformations/utils/utils.hpp +++ b/src/common/transformations/include/transformations/utils/utils.hpp @@ -180,9 +180,9 @@ TRANSFORMATIONS_API std::shared_ptr clone_try_fold(const std::shared_ptr& node, - std::unordered_set>& visited, - std::function)> func); +TRANSFORMATIONS_API void visit_shape_path(ov::Node * node, + std::unordered_set& visited, + std::function func); template std::shared_ptr make_try_fold(Args&&... args) { diff --git a/src/common/transformations/src/transformations/common_optimizations/mark_precision_sensitive_divides.cpp b/src/common/transformations/src/transformations/common_optimizations/mark_precision_sensitive_divides.cpp index ed982bbba6f..afddeeec062 100644 --- a/src/common/transformations/src/transformations/common_optimizations/mark_precision_sensitive_divides.cpp +++ b/src/common/transformations/src/transformations/common_optimizations/mark_precision_sensitive_divides.cpp @@ -13,34 +13,44 @@ #include "transformations/utils/utils.hpp" bool ov::pass::MarkPrecisionSensitiveDivides::run_on_model(const std::shared_ptr& m) { - std::deque> nodes; - std::unordered_set> visited; - for (auto& r : m->get_results()) - nodes.push_back(r); - for (auto& r : m->get_sinks()) - nodes.emplace_back(r); + std::deque nodes; + std::unordered_set visited, precision_sensitive_visited; + for (auto& r : m->get_results()) { + nodes.push_back(r.get()); + visited.insert(r.get()); + } + for (auto& r : m->get_sinks()) { + nodes.emplace_back(r.get()); + visited.insert(r.get()); + } - auto markup_func = [](std::shared_ptr node) { + auto markup_func = [](Node* node) { if (ov::is_type(node) && node->get_output_element_type(0) == ngraph::element::f16) { - ov::disable_divide_conversion(node); + ov::disable_divide_conversion(node->shared_from_this()); } }; while (!nodes.empty()) { auto curr_node = nodes.front(); nodes.pop_front(); - if (visited.count(curr_node)) - continue; for (auto& input : curr_node->inputs()) { - if (ov::is_precision_sensitive(input)) - ngraph::op::util::visit_shape_path(input.get_source_output().get_node_shared_ptr(), visited, markup_func); + if (ov::is_precision_sensitive(input)) { + visited.insert(input.get_source_output().get_node()); + // visit_shape_path shouldn't depend on "visited" nodes because we can approach Divide + // earlier from some non precision sensitive path. So we use dedicated "precision_sensitive_visited" + // set for precision sensitive nodes, so they can be visited twice and finally marked-up. + ngraph::op::util::visit_shape_path(input.get_source_output().get_node(), + precision_sensitive_visited, + markup_func); + } } - visited.insert(curr_node); for (auto& input_value : curr_node->input_values()) { // continue searching - const auto& input_node = input_value.get_node_shared_ptr(); + const auto& input_node = input_value.get_node(); + if (visited.count(input_node)) continue; nodes.push_front(input_node); + visited.insert(input_node); } } return true; diff --git a/src/common/transformations/src/transformations/common_optimizations/mark_precision_sensitive_subgraphs.cpp b/src/common/transformations/src/transformations/common_optimizations/mark_precision_sensitive_subgraphs.cpp index b2a403476d9..dd1775bd951 100644 --- a/src/common/transformations/src/transformations/common_optimizations/mark_precision_sensitive_subgraphs.cpp +++ b/src/common/transformations/src/transformations/common_optimizations/mark_precision_sensitive_subgraphs.cpp @@ -18,34 +18,40 @@ using namespace std; bool ov::pass::MarkPrecisionSensitiveSubgraphs::run_on_model(const std::shared_ptr& f) { - deque> nodes; - unordered_set> visited; - for (auto& r : f->get_results()) - nodes.push_back(r); - for (auto& r : f->get_sinks()) - nodes.emplace_back(r); + deque nodes; + unordered_set visited, precision_sensitive_visited; + for (const auto& r : f->get_results()) { + nodes.push_back(r.get()); + visited.insert(r.get()); + } + for (const auto& r : f->get_sinks()) { + nodes.emplace_back(r.get()); + visited.insert(r.get()); + } - auto markup_func = [](shared_ptr node) { + auto markup_func = [](Node * node) { if (ov::is_type(node)) { - ov::disable_fp16_compression(node); + ov::disable_fp16_compression(node->shared_from_this()); } }; while (!nodes.empty()) { auto curr_node = nodes.front(); nodes.pop_front(); - if (visited.count(curr_node)) - continue; - for (auto& input : curr_node->inputs()) { - if (ov::is_precision_sensitive(input)) - ngraph::op::util::visit_shape_path(input.get_source_output().get_node_shared_ptr(), visited, markup_func); + for (const auto& input : curr_node->inputs()) { + if (ov::is_precision_sensitive(input)) { + visited.insert(input.get_source_output().get_node()); + ngraph::op::util::visit_shape_path(input.get_source_output().get_node(), + precision_sensitive_visited, markup_func); + } } - visited.insert(curr_node); for (auto& input_value : curr_node->input_values()) { // continue searching - const auto& input_node = input_value.get_node_shared_ptr(); + const auto& input_node = input_value.get_node(); + if (visited.count(input_node)) continue; nodes.push_front(input_node); + visited.insert(input_node); } } return true; diff --git a/src/common/transformations/src/transformations/utils/utils.cpp b/src/common/transformations/src/transformations/utils/utils.cpp index 00c13bea42b..83d1ed91d73 100644 --- a/src/common/transformations/src/transformations/utils/utils.cpp +++ b/src/common/transformations/src/transformations/utils/utils.cpp @@ -178,13 +178,13 @@ bool shapes_equal_except_dynamic_expected_batch(const ngraph::PartialShape& expe } } -void visit_shape_path(const std::shared_ptr& node, - std::unordered_set>& visited, - std::function)> func) { +void visit_shape_path(Node * node, + std::unordered_set& visited, + std::function func) { if (!node) return; visited.insert(node); - std::deque> nodes{node}; + std::deque nodes{node}; while (!nodes.empty()) { auto curr_node = nodes.front(); nodes.pop_front(); @@ -193,12 +193,13 @@ void visit_shape_path(const std::shared_ptr& node, continue; } - visited.insert(curr_node); func(curr_node); for (auto& input_value : curr_node->input_values()) { // continue searching - const auto& input_node = input_value.get_node_shared_ptr(); + const auto& input_node = input_value.get_node(); + if (visited.count(input_node)) continue; nodes.push_front(input_node); + visited.insert(input_node); } } } diff --git a/src/core/include/openvino/pass/constant_folding.hpp b/src/core/include/openvino/pass/constant_folding.hpp index 40c4cd1bfd8..5cce3572fea 100644 --- a/src/core/include/openvino/pass/constant_folding.hpp +++ b/src/core/include/openvino/pass/constant_folding.hpp @@ -9,6 +9,7 @@ namespace ov { namespace pass { + /** * @brief Constant folding iterates over the function and tries to evaluate nodes * with constant inputs. Such nodes are then replaced with new Constants containing @@ -19,13 +20,23 @@ public: OPENVINO_RTTI("ConstantFolding"); bool run_on_model(const std::shared_ptr& f) override; -private: +protected: void copy_runtime_info_to_target_inputs(const std::shared_ptr& node, const Output& replacement); /// \brief Folds pre-calculated output tensor values to constants in case lower and /// upper estimations are equal. Traverses graph backwards starting from the results. bool pre_calculated_values_folding(const std::shared_ptr& f); }; +/** + * @brief this method disables constant folding for given node. Under constant folding we consider ConstantFolding + * transformation, so other type of constant folding like `get_constant_from_source` doesn't work with + * this attribute. Also before using this attribute please consider two corner cases: + * 1. If for sub-graph like ShapeOf->ShapeOf we disable cf for first ShapeOf node, it doesn't spread + * to the second ShapeOf, so the entire sub-graph will be folded. (In case if first ShapeOf has exactly one + * consumer) + * 2. If node with disable_constant_folding was replaced with another node, the attribute will be lost because + * it is not copyable. + */ OPENVINO_API void disable_constant_folding(const std::shared_ptr& node); OPENVINO_API void enable_constant_folding(const std::shared_ptr& node); diff --git a/src/core/src/pass/constant_folding.cpp b/src/core/src/pass/constant_folding.cpp index f243cd0ad51..be6fcafc194 100644 --- a/src/core/src/pass/constant_folding.cpp +++ b/src/core/src/pass/constant_folding.cpp @@ -7,6 +7,8 @@ #include #include "ngraph/op/util/sub_graph_base.hpp" +#include "ngraph/opsets/opset1.hpp" +#include "ngraph/opsets/opset3.hpp" #include "ngraph/rt_info.hpp" #include "ngraph/validation_util.hpp" @@ -21,7 +23,11 @@ bool ov::pass::ConstantFolding::run_on_model(const std::shared_ptr& f } OutputVector replacements(node->get_output_size()); - if (node->constant_fold(replacements, node->input_values())) { + + // We have to check node for DisableConstantFolding because operations can override constant_folding + // method, so we can't always rely on attribute check inside default node->constant_fold method + if (node->get_rt_info().count(DisableConstantFolding::get_type_info_static()) == 0 && + node->constant_fold(replacements, node->input_values())) { NGRAPH_CHECK(replacements.size() == node->get_output_size(), "constant_fold_default returned incorrect number of replacements for ", node); @@ -66,6 +72,30 @@ void ngraph::pass::ConstantFolding::copy_runtime_info_to_target_inputs(const std } bool ngraph::pass::ConstantFolding::pre_calculated_values_folding(const std::shared_ptr& f) { + // To avoid excess graph traversals we have to manually propagate DisableConstantFolding with some + // temporary attribute which indicates that the node which is marked with this attribute can't be folded because + // it is included into not foldable sub-graph. + for (auto&& node : f->get_ordered_ops()) { + const auto& inputs = node->input_values(); + auto& rt_info = node->get_rt_info(); + bool can_be_folded = true; + if (rt_info.count(DisableConstantFolding::get_type_info_static())) { + can_be_folded = false; + } else if (is_type(node) || is_type(node)) { + // In case if node is ShapeOf operation we stop propagation of can_be_folded attribute. We have to limit + // propagation because we can't detect borders of shape_of sub-graphs, so we propagate can_be_folded + // attribute through all nodes including nodes on data path. So to limit the spread of attribute to other + // shape-of sub-graphs we do not propagate it through ShapeOf nodes. + can_be_folded = true; + } else if (std::any_of(inputs.cbegin(), inputs.cend(), [](const Output& output) { + const auto& rt_info = output.get_node()->get_rt_info(); + return rt_info.count("can_be_folded") && !rt_info.at("can_be_folded").as(); + })) { + can_be_folded = false; + } + rt_info["can_be_folded"] = can_be_folded; + } + deque> nodes; set> visited; for (auto& r : f->get_results()) @@ -81,31 +111,20 @@ bool ngraph::pass::ConstantFolding::pre_calculated_values_folding(const std::sha continue; visited.insert(curr_node); - for (auto& input_value : curr_node->input_values()) { - // Check that ConstantFolding is not disabled on this path - std::vector order; - auto status = ngraph::could_propagate(input_value, order); - if (status) { - for (const auto& node : order) { - const auto& rt_info = node->get_rt_info(); - if (rt_info.count(DisableConstantFolding::get_type_info_static())) { - status = false; - break; - } - } - } - - if (status && input_value.get_tensor().has_and_set_bound()) { - auto input_node = input_value.get_node_shared_ptr(); - auto replacement = std::make_shared(input_value.get_tensor().get_lower_value()); + for (auto& output : curr_node->input_values()) { + const auto& rt_info = output.get_node()->get_rt_info(); + auto can_be_folded = !rt_info.count("can_be_folded") || rt_info.at("can_be_folded").as(); + if (can_be_folded && output.get_tensor().has_and_set_bound()) { + auto input_node = output.get_node_shared_ptr(); + auto replacement = std::make_shared(output.get_tensor().get_lower_value()); if (replacement && !ov::is_type(input_node)) { if (input_node->get_output_size() == 1) { replacement->set_friendly_name(input_node->get_friendly_name()); } else { replacement->set_friendly_name(input_node->get_friendly_name() + "." + - std::to_string(input_value.get_index())); + std::to_string(output.get_index())); } - input_value.replace(replacement); + output.replace(replacement); // Propagate runtime info attributes to replacement consumer nodes copy_runtime_info_to_target_inputs(input_node, replacement); @@ -113,7 +132,7 @@ bool ngraph::pass::ConstantFolding::pre_calculated_values_folding(const std::sha } } else { // continue searching - const auto& input_node = input_value.get_node_shared_ptr(); + const auto& input_node = output.get_node_shared_ptr(); nodes.push_front(input_node); } } diff --git a/src/core/src/rt_info.cpp b/src/core/src/rt_info.cpp index 2e0c4f45931..80c8cfc450c 100644 --- a/src/core/src/rt_info.cpp +++ b/src/core/src/rt_info.cpp @@ -51,7 +51,9 @@ ov::Any get_opset(const ngraph::Node::RTMap& rt_info) { void assign_runtime_info(const ngraph::Node::RTMap& from, ngraph::Node::RTMap& to) { auto opset = get_opset(to); - to = from; + for (auto& item : from) { + to[item.first] = item.second; + } if (!opset.empty()) { to["opset"] = opset; } @@ -62,7 +64,6 @@ void assign_runtime_info(const ngraph::Node::RTMap& from, ngraph::Node::RTMap& t void ngraph::copy_runtime_info(std::shared_ptr from, std::shared_ptr to) { auto& attrs = to->get_rt_info(); auto opset = get_opset(attrs); - attrs.clear(); for (const auto& item : from->get_rt_info()) { bool copy = item.first != "opset"; diff --git a/src/core/src/validation_util.cpp b/src/core/src/validation_util.cpp index beb22c1772a..6fd4caa4fc2 100644 --- a/src/core/src/validation_util.cpp +++ b/src/core/src/validation_util.cpp @@ -1204,25 +1204,7 @@ void propagate_rt_info(Node* node, const Output& final_port) { if (stop_nodes.count(in.get_node())) continue; auto consumer = in.get_node()->shared_from_this(); - // FIXME: Here we have a WA in order to save some original fields - // if we have conflicts because Variant merge doesn't work. - // We can restore original fields because we don't change the operation - auto orig_rt_info = consumer->get_rt_info(); - copy_runtime_info({curr_node, consumer}, consumer); - - auto& rt_info = consumer->get_rt_info(); - for (const auto& it : orig_rt_info) { - if (rt_info.find(it.first) == rt_info.end()) { - bool copy = true; - if (it.second.is()) { - copy = it.second.as().is_copyable(); - } - if (copy) { - rt_info[it.first] = it.second; - } - } - } } } } diff --git a/src/core/tests/CMakeLists.txt b/src/core/tests/CMakeLists.txt index e76d0099782..4009b141429 100644 --- a/src/core/tests/CMakeLists.txt +++ b/src/core/tests/CMakeLists.txt @@ -48,6 +48,7 @@ set(SRC coordinate.cpp coordinate_range.cpp copy.cpp + copy_runtime_info.cpp element_type.cpp eval.cpp extension.cpp diff --git a/src/core/tests/constant_folding.cpp b/src/core/tests/constant_folding.cpp index e98e41a0da7..9f8e3395393 100644 --- a/src/core/tests/constant_folding.cpp +++ b/src/core/tests/constant_folding.cpp @@ -4,8 +4,11 @@ #include "ngraph/pass/constant_folding.hpp" +#include + #include "gtest/gtest.h" #include "ngraph/ngraph.hpp" +#include "ngraph/opsets/opset1.hpp" #include "ngraph/opsets/opset5.hpp" #include "ngraph/pass/manager.hpp" #include "util/all_close_f.hpp" @@ -3195,19 +3198,107 @@ TEST(constant_folding, constant_dyn_reshape_v1_pattern_with_zero_dims) { } TEST(constant_folding, disable_constant_folding) { - auto input = make_shared(element::f32, Shape{1, 3}); - auto constant_shape = op::Constant::create(element::i64, Shape{1}, {3}); - auto dyn_reshape = make_shared(input, constant_shape, true); - auto& rt_info = dyn_reshape->get_rt_info(); - rt_info[ov::pass::DisableConstantFolding::get_type_info_static()]; - auto f = make_shared(dyn_reshape, ParameterVector{input}); + auto data = std::make_shared(element::f16, Shape{1, 3, 22, 22}); - pass::Manager pass_manager; - pass_manager.register_pass(); - pass_manager.run_passes(f); + // In this test case following sub-graph will be consumed by Interpolate, so during shape inference Interpolate + // will request values from this sub-graph and ConstantFolding pass will try to use this pre-calculated values + // to fold it. But in our case we are disabling CF for this sub-graph first and then enable CF to check that all + // checks inside ConstantFolding transformation are working and doesn't cache anytihng. + auto gather = op::util::node_to_get_shape_value_of_indices_from_shape_source(data, {2, 3}); + auto convert = std::make_shared(gather, element::f16); + auto divide_constant = op::Constant::create(element::f16, Shape{1}, {0.5}); + auto divide = std::make_shared(convert, divide_constant); + auto convert_after = std::make_shared(divide, element::i32); - ASSERT_EQ(count_ops_of_type(f), 1); - ASSERT_EQ(count_ops_of_type(f), 1); + opset1::Interpolate::Attributes interp_attr; + interp_attr.antialias = false; + interp_attr.axes = {2, 3}; + interp_attr.mode = "nearest"; + interp_attr.pads_begin = {0, 0, 0, 0}; + interp_attr.pads_end = {0, 0, 0, 0}; + + auto interpolate = std::make_shared(data, convert_after, interp_attr); + auto f = std::make_shared(NodeVector{interpolate}, ParameterVector{data}); + + ov::disable_constant_folding(convert); + + pass::Manager m; + m.register_pass(); + m.run_passes(f); + + // Check that sub-graph on second Interpolate input wasn't folded + ASSERT_EQ(interpolate->input_value(1), convert_after->output(0)); + + ov::enable_constant_folding(convert); + + m.run_passes(f); + + // After we enabled CF the sub-graph will be folded to Constant + ASSERT_TRUE(ov::is_type(interpolate->get_input_node_shared_ptr(1))); + + // Check that DisableConstantFolding attribute wasn't propagated to some other nodes during CF + for (auto node : f->get_ordered_ops()) { + ASSERT_FALSE(ov::pass::constant_folding_is_disabled(node)); + } +} + +TEST(constant_folding, disable_constant_folding_simple) { + // This test case checks the behaviour of CF pass when output values are not precalculated + // so CF triggers another branch where it goes through nodes and trying to fold one by one. + auto data = std::make_shared(element::f32, Shape{1, 3, 22, 22}); + auto reshape = std::make_shared(op::Constant::create(element::f32, Shape{3}, {1, 2, 3}), + op::Constant::create(element::i64, Shape{3}, {3, 1, 1}), + true); + auto divide = std::make_shared(data, reshape); + auto f = std::make_shared(NodeVector{divide}, ParameterVector{data}); + + ov::disable_constant_folding(reshape); + + pass::Manager m; + m.register_pass(); + m.run_passes(f); + + // Check that Reshape is not folded + ASSERT_EQ(divide->input_value(1), reshape->output(0)); + + ov::enable_constant_folding(reshape); + + m.run_passes(f); + + // After we enabled CF the sub-graph will be folded to Constant + ASSERT_TRUE(ov::is_type(divide->get_input_node_shared_ptr(1))); + + // Check that DisableConstantFolding attribute wasn't propagated to some other nodes during CF + for (auto node : f->get_ordered_ops()) { + ASSERT_FALSE(ov::pass::constant_folding_is_disabled(node)); + } +} + +TEST(constant_folding, disable_constant_folding_check) { + auto data = std::make_shared(element::f32, Shape{1, 3, 22, 22}); + auto shapeof1 = std::make_shared(data); + auto reshape1 = std::make_shared(data, shapeof1, true); + auto shapeof2 = std::make_shared(reshape1); + auto reshape2 = std::make_shared(reshape1, shapeof2, true); + auto f = std::make_shared(NodeVector{reshape2}, ParameterVector{data}); + + ov::disable_constant_folding(shapeof1); + + class ConstantFoldingAccessor : public pass::ConstantFolding { + public: + ConstantFoldingAccessor() = default; + using ConstantFolding::pre_calculated_values_folding; + }; + + ConstantFoldingAccessor().pre_calculated_values_folding(f); + + ASSERT_TRUE(shapeof1->get_rt_info().count("can_be_folded")); + ASSERT_FALSE(shapeof1->get_rt_info().at("can_be_folded").as()); + + ASSERT_TRUE(shapeof2->get_rt_info().count("can_be_folded")); + ASSERT_TRUE(shapeof2->get_rt_info().at("can_be_folded").as()); + + ASSERT_TRUE(ov::is_type(reshape2->get_input_node_shared_ptr(1))); } TEST(constant_folding, constant_loop) { diff --git a/src/core/tests/copy_runtime_info.cpp b/src/core/tests/copy_runtime_info.cpp new file mode 100644 index 00000000000..3cd640e4f47 --- /dev/null +++ b/src/core/tests/copy_runtime_info.cpp @@ -0,0 +1,199 @@ +// Copyright (C) 2018-2022 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include +#include +#include +#include +#include +#include +#include + +#include "gtest/gtest.h" +#include "ngraph/graph_util.hpp" +#include "ngraph/log.hpp" +#include "ngraph/ngraph.hpp" +#include "ngraph/opsets/opset3.hpp" +#include "ngraph/pass/graph_rewrite.hpp" +#include "ngraph/pass/manager.hpp" + +using namespace ngraph; +using namespace std; + +class TestAttributeNoCopyable : public ov::RuntimeAttribute { +public: + OPENVINO_RTTI("TestAttributeNoCopyable"); + TestAttributeNoCopyable() = default; + bool is_copyable() const override { + return false; + } + + static void set(std::shared_ptr node) { + auto& rt_info = node->get_rt_info(); + rt_info[TestAttributeNoCopyable::get_type_info_static()] = TestAttributeNoCopyable(); + } + + static bool exists_in(std::shared_ptr node) { + const auto& rt_info = node->get_rt_info(); + return rt_info.count(TestAttributeNoCopyable::get_type_info_static()); + } +}; + +class TestAttributeCopyable : public ov::RuntimeAttribute { +public: + OPENVINO_RTTI("TestAttributeCopyable"); + TestAttributeCopyable() = default; + + static void set(std::shared_ptr node) { + auto& rt_info = node->get_rt_info(); + rt_info[TestAttributeCopyable::get_type_info_static()] = TestAttributeCopyable(); + } + + static bool exists_in(std::shared_ptr node) { + const auto& rt_info = node->get_rt_info(); + return rt_info.count(TestAttributeCopyable::get_type_info_static()); + } +}; + +class TestAttributeMergable : public ov::RuntimeAttribute { +public: + OPENVINO_RTTI("TestAttributeMergable"); + TestAttributeMergable() = default; + + static void set(std::shared_ptr node) { + auto& rt_info = node->get_rt_info(); + rt_info[TestAttributeMergable::get_type_info_static()] = TestAttributeMergable(); + } + + static bool exists_in(std::shared_ptr node) { + const auto& rt_info = node->get_rt_info(); + return rt_info.count(TestAttributeMergable::get_type_info_static()); + } + + ov::Any merge(const ngraph::NodeVector& nodes) const override { + return {TestAttributeMergable()}; + } +}; + +TEST(copy_runtime_info, node_to_node_1) { + auto a = make_shared(element::f32, Shape{1}); + auto b = make_shared(element::f32, Shape{1}); + + TestAttributeCopyable::set(a); + TestAttributeNoCopyable::set(b); + + copy_runtime_info(a, b); + + ASSERT_TRUE(TestAttributeCopyable::exists_in(a)); + ASSERT_TRUE(TestAttributeCopyable::exists_in(b)); + + ASSERT_TRUE(TestAttributeNoCopyable::exists_in(b)); + + copy_runtime_info(b, b); + ASSERT_TRUE(TestAttributeCopyable::exists_in(b)); + ASSERT_TRUE(TestAttributeNoCopyable::exists_in(b)); +} + +TEST(copy_runtime_info, node_to_node_2) { + auto a = make_shared(element::f32, Shape{1}); + auto b = make_shared(element::f32, Shape{1}); + + TestAttributeCopyable::set(a); + TestAttributeNoCopyable::set(a); + + copy_runtime_info(a, b); + + ASSERT_TRUE(TestAttributeCopyable::exists_in(a)); + ASSERT_TRUE(TestAttributeNoCopyable::exists_in(a)); + + ASSERT_TRUE(TestAttributeCopyable::exists_in(b)); + ASSERT_FALSE(TestAttributeNoCopyable::exists_in(b)); +} + +TEST(copy_runtime_info, node_to_nodes) { + auto a = make_shared(element::f32, Shape{1}); + auto b = make_shared(element::f32, Shape{1}); + auto c = make_shared(element::f32, Shape{1}); + + TestAttributeCopyable::set(a); + TestAttributeNoCopyable::set(b); + TestAttributeNoCopyable::set(c); + + copy_runtime_info(a, {b, c}); + + ASSERT_TRUE(TestAttributeCopyable::exists_in(a)); + ASSERT_TRUE(TestAttributeCopyable::exists_in(b)); + ASSERT_TRUE(TestAttributeCopyable::exists_in(c)); + + ASSERT_FALSE(TestAttributeNoCopyable::exists_in(a)); + ASSERT_TRUE(TestAttributeNoCopyable::exists_in(b)); + ASSERT_TRUE(TestAttributeNoCopyable::exists_in(c)); +} + +TEST(copy_runtime_info, nodes_to_node_1) { + auto a = make_shared(element::f32, Shape{1}); + auto b = make_shared(element::f32, Shape{1}); + auto c = make_shared(element::f32, Shape{1}); + + TestAttributeCopyable::set(a); + TestAttributeNoCopyable::set(a); + + TestAttributeCopyable::set(b); + TestAttributeNoCopyable::set(b); + + copy_runtime_info({a, b}, c); + + ASSERT_FALSE(TestAttributeCopyable::exists_in(c)); + ASSERT_FALSE(TestAttributeNoCopyable::exists_in(c)); +} + +TEST(copy_runtime_info, nodes_to_node_2) { + auto a = make_shared(element::f32, Shape{1}); + auto b = make_shared(element::f32, Shape{1}); + auto c = make_shared(element::f32, Shape{1}); + + TestAttributeMergable::set(a); + TestAttributeMergable::set(b); + TestAttributeNoCopyable::set(c); + + copy_runtime_info({a, b}, c); + + ASSERT_TRUE(TestAttributeMergable::exists_in(c)); + ASSERT_TRUE(TestAttributeNoCopyable::exists_in(c)); +} + +TEST(copy_runtime_info, nodes_to_node_3) { + auto a = make_shared(element::f32, Shape{1}); + auto b = make_shared(element::f32, Shape{1}); + + TestAttributeCopyable::set(a); + TestAttributeNoCopyable::set(b); + + copy_runtime_info({a, b}, b); + + ASSERT_TRUE(TestAttributeCopyable::exists_in(b)); + ASSERT_TRUE(TestAttributeNoCopyable::exists_in(b)); +} + +TEST(copy_runtime_info, replace_output_update_name) { + auto a = make_shared(element::f32, Shape{1}); + auto b = make_shared(a); + auto c = make_shared(b); + auto d = make_shared(c); + + TestAttributeMergable::set(b); + TestAttributeMergable::set(c); + + TestAttributeCopyable::set(c); + + TestAttributeNoCopyable::set(b); + TestAttributeNoCopyable::set(c); + + // performs copy_runtime_info like copy_runtime_info({b, c}, b); + ov::replace_output_update_name(c->output(0), b->output(0)); + + ASSERT_TRUE(TestAttributeCopyable::exists_in(b)); + ASSERT_TRUE(TestAttributeMergable::exists_in(b)); + ASSERT_TRUE(TestAttributeNoCopyable::exists_in(b)); +} diff --git a/src/tests/functional/inference_engine/transformations/compress_float_constants_test.cpp b/src/tests/functional/inference_engine/transformations/compress_float_constants_test.cpp index 819e55593ae..df643154b77 100644 --- a/src/tests/functional/inference_engine/transformations/compress_float_constants_test.cpp +++ b/src/tests/functional/inference_engine/transformations/compress_float_constants_test.cpp @@ -19,8 +19,7 @@ using namespace testing; -TEST(TransformationTests, CompressConstants_f32) { - std::shared_ptr f(nullptr), f_ref(nullptr); +TEST_F(TransformationTestsF, CompressConstants_f32) { { auto input = std::make_shared(ov::element::f32, ov::Shape{ 1, 3, 12, 12 }); auto const_weights = ov::opset8::Constant::create(ov::element::f32, @@ -49,14 +48,10 @@ TEST(TransformationTests, CompressConstants_f32) { auto resize = std::make_shared(conv, convert2, default_scales_node, axes_node, interpolate4_attr); - f = std::make_shared(ov::NodeVector{ resize }, ov::ParameterVector{ input }); + function = std::make_shared(ov::NodeVector{ resize }, ov::ParameterVector{ input }); - ov::pass::Manager manager; - manager.register_pass(); manager.register_pass(); manager.register_pass(); - manager.run_passes(f); - ASSERT_NO_THROW(check_rt_info(f)); } { @@ -88,15 +83,11 @@ TEST(TransformationTests, CompressConstants_f32) { auto resize = std::make_shared(conv, convert2, default_scales_node, axes_node, interpolate4_attr); - f_ref = std::make_shared(ov::NodeVector{ resize }, ov::ParameterVector{ input }); + function_ref = std::make_shared(ov::NodeVector{ resize }, ov::ParameterVector{ input }); } - - auto res = compare_functions(f, f_ref, true); - ASSERT_TRUE(res.first) << res.second; } -TEST(TransformationTests, CompressConstants_f64) { - std::shared_ptr f(nullptr), f_ref(nullptr); +TEST_F(TransformationTestsF, CompressConstants_f64) { { auto input = std::make_shared(ov::element::f64, ov::Shape{ 1, 3, 12, 12 }); auto const_weights = ov::opset8::Constant::create(ov::element::f64, @@ -108,14 +99,10 @@ TEST(TransformationTests, CompressConstants_f64) { ov::CoordinateDiff{ 0, 0 }, ov::CoordinateDiff{ 0, 0 }, ov::Strides{ 1, 1 }); - f = std::make_shared(ov::NodeVector{ conv }, ov::ParameterVector{ input }); + function = std::make_shared(ov::NodeVector{ conv }, ov::ParameterVector{ input }); - ov::pass::Manager manager; - manager.register_pass(); manager.register_pass(); manager.register_pass(); - manager.run_passes(f); - ASSERT_NO_THROW(check_rt_info(f)); } { @@ -130,9 +117,6 @@ TEST(TransformationTests, CompressConstants_f64) { ov::CoordinateDiff{ 0, 0 }, ov::CoordinateDiff{ 0, 0 }, ov::Strides{ 1, 1 }); - f_ref = std::make_shared(ov::NodeVector{ conv }, ov::ParameterVector{ input }); + function_ref = std::make_shared(ov::NodeVector{ conv }, ov::ParameterVector{ input }); } - - auto res = compare_functions(f, f_ref, true); - ASSERT_TRUE(res.first) << res.second; } diff --git a/src/tests/functional/inference_engine/transformations/convert_divide.cpp b/src/tests/functional/inference_engine/transformations/convert_divide.cpp index 85372456adf..95bb69b50d1 100644 --- a/src/tests/functional/inference_engine/transformations/convert_divide.cpp +++ b/src/tests/functional/inference_engine/transformations/convert_divide.cpp @@ -171,7 +171,44 @@ TEST_F(TransformationTestsF, ConvertDivideFP16ShapeOfSubgraphNegative) { function = std::make_shared(ngraph::NodeVector{interpolate}, ngraph::ParameterVector{data}); - ov::pass::MarkPrecisionSensitiveDivides().run_on_model(function); + manager.register_pass(); + manager.register_pass(); + } +} + +TEST_F(TransformationTestsF, ConvertDivideFP16ShapeOfSubgraphNegative2) { + { + // This test case checks that MarkPrecisionSensitiveDivides works correctly when Divide is included + // into precision sensitive and non precision sensitive sub-graphs. So the potential problem here is + // that MarkPrecisionSensitiveDivides could traverse graph first form "add" output so all nodes including + // Divide will be marked as visited, but Divide and other nodes must also be visited again because of + // precision sensitive Interpolate second input. And to handle this MarkPrecisionSensitiveDivides has + // special visited set for precision sensitive nodes which needs to be tested as well. So in the worst case + // we will traverse each node twice. + auto data = std::make_shared(ngraph::element::f16, ngraph::Shape{1, 3, 22, 22}); + auto gather = ngraph::op::util::node_to_get_shape_value_of_indices_from_shape_source(data, {2, 3}); + auto convert = std::make_shared(gather, ngraph::element::f16); + auto divide_constant = ngraph::opset1::Constant::create(ngraph::element::f16, ngraph::Shape{1}, {0.5}); + auto divide = std::make_shared(convert, divide_constant); + auto convert_after = std::make_shared(divide, ngraph::element::i32); + + auto data2 = std::make_shared(ngraph::element::i32, ngraph::Shape{2}); + auto add = std::make_shared(data2, convert_after); + + ngraph::opset1::Interpolate::Attributes interp_attr; + interp_attr.antialias = false; + interp_attr.axes = {2, 3}; + interp_attr.mode = "nearest"; + interp_attr.pads_begin = {0, 0, 0, 0}; + interp_attr.pads_end = {0, 0, 0, 0}; + + auto interpolate = std::make_shared(data, convert_after, interp_attr); + + // "add" node specially set as a first output, so MarkPrecisionSensitiveDivides will start graph traversal from it + // and after all nodes above are visited it will start traverse from "interpolate" + function = std::make_shared(ngraph::NodeVector{add, interpolate}, ngraph::ParameterVector{data, data2}); + + manager.register_pass(); manager.register_pass(); } }