diff --git a/src/ngraph/descriptor/output.cpp b/src/ngraph/descriptor/output.cpp
index 3d7ee6af3ecda89c49726ec62bd348a283f73f05..bf1814678586da515c21ff24ed828cf2cd2561d1 100644
--- a/src/ngraph/descriptor/output.cpp
+++ b/src/ngraph/descriptor/output.cpp
@@ -14,8 +14,10 @@
 // limitations under the License.
 //*****************************************************************************
 
-#include "ngraph/descriptor/output.hpp"
+#include <algorithm>
+
 #include "ngraph/descriptor/input.hpp"
+#include "ngraph/descriptor/output.hpp"
 #include "ngraph/node.hpp"
 
 using namespace std;
@@ -31,12 +33,20 @@ descriptor::Output::Output(Node* node, size_t index, const shared_ptr<Tensor>& t
 // Add an input to the vector of inputs that use this output.
 void descriptor::Output::add_input(Input* input)
 {
-    m_inputs.insert(input);
+    // Keep the inputs in insertion order to keep sorts deterministic
+    if (find(m_inputs.begin(), m_inputs.end(), input) == m_inputs.end())
+    {
+        m_inputs.push_back(input);
+    }
 }
 
 void descriptor::Output::remove_input(Input* input)
 {
-    m_inputs.erase(input);
+    auto it = find(m_inputs.begin(), m_inputs.end(), input);
+    if (it != m_inputs.end())
+    {
+        m_inputs.erase(it);
+    }
 }
 
 shared_ptr<Node> descriptor::Output::get_node() const
diff --git a/src/ngraph/descriptor/output.hpp b/src/ngraph/descriptor/output.hpp
index 712518ad94733f75f4383f8c3d66044545aebd23..952b2a5a70f0a9c0ed769faf10782d920594eec1 100644
--- a/src/ngraph/descriptor/output.hpp
+++ b/src/ngraph/descriptor/output.hpp
@@ -17,7 +17,7 @@
 #pragma once
 
 #include <memory>
-#include <set>
+#include <vector>
 
 #include "ngraph/descriptor/input.hpp"
 #include "ngraph/descriptor/tensor.hpp"
@@ -48,7 +48,7 @@ namespace ngraph
             void set_tensor_ptr(const std::shared_ptr<Tensor>& tensor) { m_tensor = tensor; }
             void add_input(Input* input);
             void remove_input(Input* input);
-            const std::set<Input*>& get_inputs() const { return m_inputs; }
+            const std::vector<Input*>& get_inputs() const { return m_inputs; }
             Tensor& get_tensor() const;
 
             /// \return the shape of the output
@@ -64,7 +64,7 @@ namespace ngraph
             Node* m_node;
             size_t m_index;
             std::shared_ptr<Tensor> m_tensor;
-            std::set<Input*> m_inputs;
+            std::vector<Input*> m_inputs;
 
         private:
             Output(const Output&) = delete;
diff --git a/src/ngraph/function.cpp b/src/ngraph/function.cpp
index 1982695a4eaa744ba7624e838d6c3af0eed6ac04..42d1b556ee0810c5a0d24d3220e7028dc1742773 100644
--- a/src/ngraph/function.cpp
+++ b/src/ngraph/function.cpp
@@ -97,7 +97,48 @@ void Function::init()
 
 std::list<shared_ptr<Node>> Function::get_ordered_ops(bool include_control_deps) const
 {
-    return topological_sort(get_ops(include_control_deps), include_control_deps);
+    NodeVector nodes;
+    for (auto& r : get_results())
+    {
+        nodes.push_back(r);
+    }
+    for (auto& param : get_parameters())
+    {
+        nodes.push_back(param);
+    }
+
+    return topological_sort(nodes, include_control_deps);
+}
+
+void Function::map_unordered_ops(std::function<void(Node*)> f) const
+{
+    std::unordered_set<Node*> unordered_ops;
+    std::stack<Node*, std::vector<Node*>> remaining_ops;
+    for (auto& r : get_results())
+    {
+        remaining_ops.push(r.get());
+    }
+    for (auto& param : get_parameters())
+    {
+        remaining_ops.push(param.get());
+    }
+    while (remaining_ops.size() > 0)
+    {
+        Node* op = remaining_ops.top();
+        remaining_ops.pop();
+        if (unordered_ops.insert(op).second)
+        {
+            f(op);
+            for (size_t i = 0; i < op->get_input_size(); ++i)
+            {
+                remaining_ops.push(op->input(i).get_source_output().get_node());
+            }
+            for (auto& cdep : op->get_control_dependencies())
+            {
+                remaining_ops.push(cdep.get());
+            }
+        }
+    }
 }
 
 const std::string& Function::get_friendly_name() const
diff --git a/src/ngraph/function.hpp b/src/ngraph/function.hpp
index 5517deff3a417ab7f10445c880618cd99b75228f..50358db16cd331203105f24560001b776a22e269 100644
--- a/src/ngraph/function.hpp
+++ b/src/ngraph/function.hpp
@@ -88,6 +88,8 @@ namespace ngraph
 
         std::list<std::shared_ptr<Node>> get_ops(bool include_control_deps = true) const;
         std::list<std::shared_ptr<Node>> get_ordered_ops(bool include_control_deps = true) const;
+        void map_unordered_ops(std::function<void(Node*)> f) const;
+
         friend std::ostream& operator<<(std::ostream&, const Function&);
         size_t get_instance_id() { return m_instance_id; }
         size_t get_temporary_pool_size();
diff --git a/src/ngraph/graph_util.cpp b/src/ngraph/graph_util.cpp
index 384014819692b592384f269b987c187682962539..8f9e3b213494fec57d9691e7f921fcaa2659bdb6 100644
--- a/src/ngraph/graph_util.cpp
+++ b/src/ngraph/graph_util.cpp
@@ -14,7 +14,6 @@
 // limitations under the License.
 //*****************************************************************************
 
-#include <deque>
 #include <unordered_map>
 #include <unordered_set>
 #include <vector>
@@ -69,42 +68,34 @@ void ngraph::traverse_nodes(const NodeVector& subgraph_results,
                             bool include_control_deps,
                             const NodeVector& subgraph_params)
 {
-    std::unordered_set<std::shared_ptr<Node>> instances_seen{subgraph_params.begin(),
-                                                             subgraph_params.end()};
-    std::deque<std::shared_ptr<Node>> stack;
-
-    for (auto r : subgraph_results)
+    std::unordered_set<Node*> instances_seen;
+    std::stack<Node*, std::vector<Node*>> stack;
+    for (auto& node_ptr : subgraph_params)
     {
-        if (instances_seen.count(r) == 0)
-        {
-            stack.push_front(r);
-        }
+        instances_seen.insert(node_ptr.get());
+    }
+    for (auto& node_ptr : subgraph_results)
+    {
+        stack.push(node_ptr.get());
     }
 
     while (stack.size() > 0)
     {
-        std::shared_ptr<Node> n = stack.front();
-        if (instances_seen.count(n) == 0)
-        {
-            instances_seen.insert(n);
-            f(n);
-        }
-        stack.pop_front();
-        for (auto arg : n->get_arguments())
+        Node* n = stack.top();
+        stack.pop();
+        if (instances_seen.insert(n).second)
         {
-            if (instances_seen.count(arg) == 0)
+            f(n->shared_from_this());
+            for (auto& arg : n->get_arguments())
             {
-                stack.push_front(arg);
+                stack.push(arg.get());
             }
-        }
 
-        if (include_control_deps)
-        {
-            for (auto cdep : n->get_control_dependencies())
+            if (include_control_deps)
             {
-                if (instances_seen.count(cdep) == 0)
+                for (auto& cdep : n->get_control_dependencies())
                 {
-                    stack.push_front(cdep);
+                    stack.push(cdep.get());
                 }
             }
         }
@@ -182,25 +173,25 @@ void ngraph::replace_node(std::shared_ptr<Node> target, std::shared_ptr<Node> re
 bool ngraph::is_post_dominated(Node* X, Node* Y)
 {
     std::unordered_set<Node*> visited;
-    std::deque<Node*> stack;
-    stack.push_front(X);
+    std::stack<Node*, std::vector<Node*>> stack;
+    stack.push(X);
 
     while (stack.size() > 0)
     {
-        ngraph::Node* curr = stack.front();
+        ngraph::Node* curr = stack.top();
         visited.insert(curr);
         if (curr->is_output())
         {
             return false;
         }
-        stack.pop_front();
+        stack.pop();
         if (curr != Y)
         {
             for (const auto& next : curr->get_users())
             {
                 if (visited.count(next.get()) == 0)
                 {
-                    stack.push_front(next.get());
+                    stack.push(next.get());
                 }
             }
         }
@@ -503,12 +494,12 @@ NodeVector ngraph::extract_subgraph(const NodeVector& results, const NodeVector&
 bool ngraph::is_used(Node* node)
 {
     std::unordered_set<Node*> instances_seen;
-    std::deque<Node*> stack;
-    stack.push_front(node);
+    std::stack<Node*, std::vector<Node*>> stack;
+    stack.push(node);
 
     while (stack.size() > 0)
     {
-        ngraph::Node* n = stack.front();
+        ngraph::Node* n = stack.top();
         if (instances_seen.count(n) == 0)
         {
             if (n->is_output())
@@ -517,12 +508,12 @@ bool ngraph::is_used(Node* node)
             }
             instances_seen.insert(n);
         }
-        stack.pop_front();
+        stack.pop();
         for (const auto& arg : n->get_users())
         {
             if (instances_seen.count(arg.get()) == 0)
             {
-                stack.push_front(arg.get());
+                stack.push(arg.get());
             }
         }
     }
diff --git a/src/ngraph/graph_util.hpp b/src/ngraph/graph_util.hpp
index 6b07be67bc46bc4cdb52cd93de5ea7e37dea659a..276403f5a5987c8b3f9ff1b46a69afab5a04c972 100644
--- a/src/ngraph/graph_util.hpp
+++ b/src/ngraph/graph_util.hpp
@@ -20,6 +20,7 @@
 #include <functional>
 #include <list>
 #include <memory>
+#include <stack>
 #include <string>
 #include <unordered_map>
 #include <unordered_set>
@@ -81,154 +82,131 @@ namespace ngraph
 
     NodeVector find_common_args(std::shared_ptr<Node> target, std::shared_ptr<Node> replacement);
 
+    /// Topological sort of nodes needed to compute root_nodes
     template <typename T>
-    std::list<std::shared_ptr<Node>> topological_sort(const T& nodes,
+    std::list<std::shared_ptr<Node>> topological_sort(T root_nodes,
                                                       bool include_control_deps = false)
     {
-        std::deque<ngraph::Node*> independent_nodes;
-        std::unordered_map<const ngraph::Node*, size_t> node_dependency_count;
-        std::unordered_map<ngraph::Node*, std::shared_ptr<ngraph::Node>> node_map;
-        std::unordered_map<ngraph::Node*, std::set<Node*>> control_deps_users;
+        std::stack<Node*, std::vector<Node*>> nodes_to_do;
+        std::unordered_set<Node*> nodes_done;
+        std::list<std::shared_ptr<Node>> result;
 
-        for (auto node : nodes)
+        for (auto& node : root_nodes)
         {
-            //build an equivalent of node->get_users() but for control dependencies
-            size_t control_deps_count = 0;
-            if (include_control_deps)
-            {
-                for (auto cd : node->get_control_dependencies())
-                {
-                    control_deps_count++;
-                    control_deps_users[cd.get()].insert(node.get());
-                }
-            }
-
-            node_map[node.get()] = node;
-            size_t deps_count = node->get_input_size() + control_deps_count;
-            node_dependency_count[node.get()] = deps_count;
-            if (deps_count == 0)
-            {
-                independent_nodes.push_back(node.get());
-            }
+            nodes_to_do.push(node.get());
         }
-
-        std::list<std::shared_ptr<ngraph::Node>> result_list;
-        while (independent_nodes.size() > 0)
+        while (nodes_to_do.size() > 0)
         {
-            auto independent_node = independent_nodes.front();
-            result_list.push_back(node_map[independent_node]);
-            independent_nodes.pop_front();
-
-            for (const std::shared_ptr<Node>& user : independent_node->get_users())
+            Node* node = nodes_to_do.top();
+            if (nodes_done.count(node) == 0)
             {
-                if (--node_dependency_count[user.get()] == 0)
+                bool can_add = true;
+                size_t arg_count = node->get_input_size();
+                for (size_t i = 0; i < arg_count; ++i)
                 {
-                    independent_nodes.push_back(user.get());
+                    Node* dep = node->input(arg_count - i - 1).get_source_output().get_node();
+                    if (nodes_done.count(dep) == 0)
+                    {
+                        can_add = false;
+                        nodes_to_do.push(dep);
+                    }
                 }
-            }
-
-            if (include_control_deps)
-            {
-                auto cdit = control_deps_users.find(independent_node);
-                if (cdit != control_deps_users.end())
-                    for (auto cd_user : cdit->second)
+                if (include_control_deps)
+                {
+                    for (auto& depptr : node->get_control_dependencies())
                     {
-                        node_dependency_count[cd_user] -= 1;
-                        size_t count = node_dependency_count[cd_user];
-                        if (count == 0)
+                        Node* dep = depptr.get();
+                        if (nodes_done.count(dep) == 0)
                         {
-                            independent_nodes.push_back(cd_user);
+                            can_add = false;
+                            nodes_to_do.push(dep);
                         }
                     }
+                }
+                if (can_add)
+                {
+                    result.push_back(node->shared_from_this());
+                    nodes_to_do.pop();
+                    nodes_done.insert(node);
+                }
+            }
+            else
+            {
+                nodes_to_do.pop();
             }
         }
-
-        NGRAPH_CHECK(nodes.size() == result_list.size());
-        return result_list;
+        return result;
     }
 
-    // For cases, where `nodes` is a subset of the entire graph
+    /// Topological sort of just nodes
     template <typename T>
-    std::list<std::shared_ptr<Node>> subgraph_topological_sort(const T& nodes,
+    std::list<std::shared_ptr<Node>> subgraph_topological_sort(T nodes,
                                                                bool include_control_deps = false)
     {
-        std::deque<ngraph::Node*> independent_nodes;
-        std::unordered_map<const ngraph::Node*, size_t> node_dependency_count;
-        std::unordered_map<ngraph::Node*, std::shared_ptr<ngraph::Node>> node_map;
-        std::unordered_map<ngraph::Node*, std::set<Node*>> control_deps_users;
-        std::unordered_set<std::shared_ptr<ngraph::Node>> nodes_set(nodes.begin(), nodes.end());
+        std::stack<Node*, std::vector<Node*>> nodes_to_do;
+        std::unordered_set<Node*> nodes_done;
+        std::unordered_set<Node*> nodes_to_emit;
+        std::list<std::shared_ptr<Node>> result;
 
-        for (auto node : nodes)
+        for (auto& node : nodes)
+        {
+            nodes_to_emit.insert(node.get());
+            nodes_to_do.push(node.get());
+        }
+        // NB: Some centos versions implement std::list::size() by counting elements
+        size_t nodes_remaining = nodes_to_emit.size();
+        while (nodes_to_do.size() > 0 && nodes_remaining > 0)
         {
-            //build an equivalent of node->get_users() but for control dependencies
-            size_t deps_count = 0;
-            if (include_control_deps)
+            Node* node = nodes_to_do.top();
+            if (nodes_done.count(node) == 0)
             {
-                for (auto cd : node->get_control_dependencies())
+                bool can_add = true;
+                size_t arg_count = node->get_input_size();
+                for (size_t i = 0; i < arg_count; ++i)
                 {
-                    if (nodes_set.count(cd) != 0)
+                    Node* dep = node->input(arg_count - i - 1).get_source_output().get_node();
+                    if (nodes_done.count(dep) == 0)
                     {
-                        control_deps_users[cd.get()].insert(node.get());
-                        deps_count++;
+                        can_add = false;
+                        nodes_to_do.push(dep);
                     }
                 }
-            }
-
-            node_map[node.get()] = node;
-            for (auto arg : node->get_arguments())
-            {
-                if (nodes_set.count(arg) != 0)
+                if (include_control_deps)
                 {
-                    deps_count++;
+                    for (auto& depptr : node->get_control_dependencies())
+                    {
+                        Node* dep = depptr.get();
+                        if (nodes_done.count(dep) == 0)
+                        {
+                            can_add = false;
+                            nodes_to_do.push(dep);
+                        }
+                    }
                 }
-            }
-
-            node_dependency_count[node.get()] = deps_count;
-            if (deps_count == 0)
-            {
-                independent_nodes.push_back(node.get());
-            }
-        }
-
-        std::list<std::shared_ptr<ngraph::Node>> result_list;
-        while (independent_nodes.size() > 0)
-        {
-            auto independent_node = independent_nodes.front();
-            result_list.push_back(node_map[independent_node]);
-            independent_nodes.pop_front();
-
-            for (const std::shared_ptr<Node>& user : independent_node->get_users())
-            {
-                if (--node_dependency_count[user.get()] == 0)
+                if (can_add)
                 {
-                    independent_nodes.push_back(user.get());
+                    if (nodes_to_emit.count(node) != 0)
+                    {
+                        result.push_back(node->shared_from_this());
+                        nodes_remaining--;
+                    }
+                    nodes_to_do.pop();
+                    nodes_done.insert(node);
                 }
             }
 
-            if (include_control_deps)
+            else
             {
-                auto cdit = control_deps_users.find(independent_node);
-                if (cdit != control_deps_users.end())
-                    for (auto cd_user : cdit->second)
-                    {
-                        node_dependency_count[cd_user] -= 1;
-                        size_t count = node_dependency_count[cd_user];
-                        if (count == 0)
-                        {
-                            independent_nodes.push_back(cd_user);
-                        }
-                    }
+                nodes_to_do.pop();
             }
         }
-
-        NGRAPH_CHECK(nodes.size() == result_list.size());
-        return result_list;
+        return result;
     }
 
     template <typename T>
     void validate_nodes_and_infer_types(const T& nodes)
     {
-        for (auto node : subgraph_topological_sort(nodes))
+        for (auto& node : subgraph_topological_sort(nodes))
         {
             node->revalidate_and_infer_types();
         }
diff --git a/src/ngraph/node.cpp b/src/ngraph/node.cpp
index 5acc23e32fa710907548ba33bd02bbfa953c5376..f8456ef1fbe992d79aa0d5e3dc9371d5167f4aa3 100644
--- a/src/ngraph/node.cpp
+++ b/src/ngraph/node.cpp
@@ -329,14 +329,18 @@ NodeVector Node::get_arguments() const
     return result;
 }
 
-const std::set<std::shared_ptr<Node>>& Node::get_control_dependencies() const
+const std::vector<std::shared_ptr<Node>>& Node::get_control_dependencies() const
 {
     return m_control_dependencies;
 }
 
 void Node::add_control_dependency(std::shared_ptr<Node> node)
 {
-    m_control_dependencies.insert(node);
+    if (find(m_control_dependencies.begin(), m_control_dependencies.end(), node) ==
+        m_control_dependencies.end())
+    {
+        m_control_dependencies.push_back(node);
+    }
 }
 
 namespace ngraph
@@ -444,7 +448,7 @@ shared_ptr<descriptor::Tensor> Node::get_output_tensor_ptr() const
     return m_outputs.at(0).get_tensor_ptr();
 }
 
-const std::set<descriptor::Input*>& Node::get_output_inputs(size_t i) const
+const std::vector<descriptor::Input*>& Node::get_output_inputs(size_t i) const
 {
     return m_outputs.at(i).get_inputs();
 }
diff --git a/src/ngraph/node.hpp b/src/ngraph/node.hpp
index e8f1a6dbe71ea96cc6a9681c8f41ffdee78558a7..e03a7540bfe38d114ae7cc613a5c7252f8da5345 100644
--- a/src/ngraph/node.hpp
+++ b/src/ngraph/node.hpp
@@ -236,13 +236,17 @@ namespace ngraph
             NGRAPH_DEPRECATED("use outputs() instead");
 
         /// Get control dependencies registered on the node
-        const std::set<std::shared_ptr<Node>>& get_control_dependencies() const;
+        const std::vector<std::shared_ptr<Node>>& get_control_dependencies() const;
 
         void add_control_dependency(std::shared_ptr<Node> node);
 
         void remove_control_dependency(std::shared_ptr<Node> node)
         {
-            m_control_dependencies.erase(node);
+            auto it = find(m_control_dependencies.begin(), m_control_dependencies.end(), node);
+            if (it != m_control_dependencies.end())
+            {
+                m_control_dependencies.erase(it);
+            }
         }
 
         /// Returns the number of outputs from the node.
@@ -295,7 +299,7 @@ namespace ngraph
             "output, or update calling code not to assume only one output");
 
         /// Returns the set of inputs using output i
-        const std::set<descriptor::Input*>& get_output_inputs(size_t i) const
+        const std::vector<descriptor::Input*>& get_output_inputs(size_t i) const
             NGRAPH_DEPRECATED("use node->output(i).get_target_inputs() instead");
 
         /// Returns the number of inputs for the op
@@ -390,7 +394,7 @@ namespace ngraph
         descriptor::Input& get_input_descriptor(size_t position);
         descriptor::Output& get_output_descriptor(size_t position);
 
-        std::set<std::shared_ptr<Node>> m_control_dependencies;
+        std::vector<std::shared_ptr<Node>> m_control_dependencies;
         const std::string m_node_type;
         size_t m_instance_id{m_next_instance_id.fetch_add(1)};
         std::string m_friendly_name;
diff --git a/test/control_dependencies.cpp b/test/control_dependencies.cpp
index 135d2f74ff240f17a6ac3003121b58bad29c43b4..54e05b3a5400fec263ffb6218b9e2da268a9c742 100644
--- a/test/control_dependencies.cpp
+++ b/test/control_dependencies.cpp
@@ -87,8 +87,7 @@ TEST(control_dependencies, cdep_ops)
         make_shared<ControlDependencyOp>(NodeVector{A}, std::set<std::shared_ptr<Node>>{absn});
 
     auto f = make_shared<Function>(cdop, ParameterVector{A, B});
-    auto nodes = f->get_ordered_ops(true);
-    ASSERT_EQ(nodes.back()->get_argument(0), cdop);
+    test_ordered_ops(f, NodeVector{absn});
 }
 
 TEST(control_dependencies, two_cdep_ops)
@@ -102,8 +101,7 @@ TEST(control_dependencies, two_cdep_ops)
                                                  std::set<std::shared_ptr<Node>>{absn, absn_c});
 
     auto f = make_shared<Function>(cdop, ParameterVector{A, B, C});
-    auto nodes = f->get_ordered_ops(true);
-    ASSERT_EQ(nodes.back()->get_argument(0), cdop);
+    test_ordered_ops(f, NodeVector{absn, absn_c});
 }
 
 TEST(control_dependencies, two_cdep_ops_op_on_top)
@@ -117,8 +115,7 @@ TEST(control_dependencies, two_cdep_ops_op_on_top)
     auto absn_cdop = make_shared<op::Abs>(cdop);
 
     auto f = make_shared<Function>(absn_cdop, ParameterVector{A, B});
-    auto nodes = f->get_ordered_ops(true);
-    ASSERT_EQ(nodes.back()->get_argument(0), absn_cdop);
+    test_ordered_ops(f, NodeVector{absn, absn_b});
 }
 
 TEST(control_dependencies, clone_function_cdop)
@@ -129,6 +126,7 @@ TEST(control_dependencies, clone_function_cdop)
         make_shared<ControlDependencyOp>(NodeVector{A}, std::set<std::shared_ptr<Node>>{absn});
 
     auto f = make_shared<Function>(cdop, ParameterVector{A});
+    test_ordered_ops(f, NodeVector{absn});
     auto clone = ngraph::clone_function(*f.get());
     auto matcher = std::make_shared<pattern::Matcher>(cdop);
     auto cdop_clone = clone->get_results().at(0)->get_argument(0);
diff --git a/test/util.cpp b/test/util.cpp
index 3db992d557dc67cb2bc9e083650752b06ccd2c7e..67b73d45cb6ee98a821e3b415b4f16d923a7e8cc 100644
--- a/test/util.cpp
+++ b/test/util.cpp
@@ -540,7 +540,6 @@ TEST(util, enum_mask_operators)
 
 TEST(graph, huge)
 {
-    Function* f;
     std::vector<std::weak_ptr<Node>> weak_nodes;
     {
         auto param = make_shared<op::Parameter>(element::f32, Shape{3, 3});
@@ -549,16 +548,12 @@ TEST(graph, huge)
         {
             n = make_shared<op::Negative>(n);
         }
-        f = new Function(NodeVector{n}, ParameterVector{param});
-        for (auto node : f->get_ops())
-        {
-            weak_nodes.push_back(node);
-        }
+        auto f = make_shared<Function>(NodeVector{n}, ParameterVector{param});
+        f->map_unordered_ops(
+            [&weak_nodes](Node* node) { weak_nodes.push_back(node->shared_from_this()); });
     }
 
-    delete f;
-
-    for (auto weak_node : weak_nodes)
+    for (auto& weak_node : weak_nodes)
     {
         EXPECT_TRUE(weak_node.expired());
     }
diff --git a/test/util/test_tools.cpp b/test/util/test_tools.cpp
index 63a08f303217449380536a422c7aa0061c5828b2..1c9ca782229fabecfefa756b21c07de5fe8e3637 100644
--- a/test/util/test_tools.cpp
+++ b/test/util/test_tools.cpp
@@ -313,3 +313,44 @@ std::shared_ptr<Function> make_function_from_file(const std::string& file_name)
     return func;
 }
 #endif
+
+::testing::AssertionResult test_ordered_ops(shared_ptr<Function> f, const NodeVector& required_ops)
+{
+    unordered_set<Node*> seen;
+    for (auto& node_ptr : f->get_ordered_ops())
+    {
+        Node* node = node_ptr.get();
+        if (seen.count(node) > 0)
+        {
+            return ::testing::AssertionFailure() << "Duplication in ordered ops";
+        }
+        size_t arg_count = node->get_input_size();
+        for (size_t i = 0; i < arg_count; ++i)
+        {
+            Node* dep = node->input(i).get_source_output().get_node();
+            if (seen.count(dep) == 0)
+            {
+                return ::testing::AssertionFailure() << "Argument " << *dep
+                                                     << " does not occur before op" << *node;
+            }
+        }
+        for (auto& dep_ptr : node->get_control_dependencies())
+        {
+            if (seen.count(dep_ptr.get()) == 0)
+            {
+                return ::testing::AssertionFailure() << "Control dependency " << *dep_ptr
+                                                     << " does not occur before op" << *node;
+            }
+        }
+        seen.insert(node);
+    }
+    for (auto& node_ptr : required_ops)
+    {
+        if (seen.count(node_ptr.get()) == 0)
+        {
+            return ::testing::AssertionFailure() << "Required op " << *node_ptr
+                                                 << "does not occur in ordered ops";
+        }
+    }
+    return ::testing::AssertionSuccess();
+}
diff --git a/test/util/test_tools.hpp b/test/util/test_tools.hpp
index b3e36800dce7ca716c977db66ece5b172b872f0f..bcc720d1d425611756120cb61ff5dad0cc7fda5b 100644
--- a/test/util/test_tools.hpp
+++ b/test/util/test_tools.hpp
@@ -25,6 +25,8 @@
 #include <random>
 #include <vector>
 
+#include "gtest/gtest.h"
+
 #include "ngraph/descriptor/layout/tensor_layout.hpp"
 #include "ngraph/file_util.hpp"
 #include "ngraph/log.hpp"
@@ -277,3 +279,6 @@ std::vector<T> read_binary_file(const std::string& path)
     inputs_fs.read(reinterpret_cast<char*>(file_content.data()), size);
     return file_content;
 }
+
+testing::AssertionResult test_ordered_ops(std::shared_ptr<ngraph::Function> f,
+                                          const ngraph::NodeVector& required_ops);