1 // This file is part of OpenCV project.
2 // It is subject to the license terms in the LICENSE file found in the top-level directory
3 // of this distribution and at http://opencv.org/license.html.
5 // Copyright (C) 2018-2019 Intel Corporation
12 #include <ade/util/chain_range.hpp>
13 #include <ade/graph.hpp>
15 #include "compiler/gmodel.hpp"
16 #include "compiler/passes/passes.hpp"
20 bool is_within_same_island(const cv::gimpl::GModel::Graph &gr,
21 const ade::NodeHandle &dataNode,
22 const std::string &island)
24 // A data node is within the same island as it's reader node
25 // if and only if data object's producer island (if there's a producer)
26 // is the same as the specified one.
28 // An object may have only a single producer, but multiple consumers,
29 // and these consumers may be assigned to different Islands.
30 // Since "initIslands" traversal direction is op-to-args, i.e. reverse,
31 // a single Data object may be visited twice during Islands initialization.
33 // In this case, Data object is part of Island A if and only if:
34 // - Data object's producer is part of Island A,
35 // - AND any of Data obejct's consumers is part of Island A.
37 // Op["island0"] --> Data[ ? ] --> Op["island0"]
39 // '---------> Op["island1"]
41 // In the above example, Data object is assigned to "island0" as
42 // it is surrounded by operations assigned to "island0"
44 using namespace cv::gimpl;
46 if ( gr.metadata(dataNode).contains<Island>()
47 && gr.metadata(dataNode).get<Island>().island != island)
50 if (dataNode->inNodes().empty())
53 GAPI_Assert(dataNode->inNodes().size() == 1u);
54 const auto prod_h = dataNode->inNodes().front();
56 // FIXME: ADE should have something like get_or<> or get<>(default)
57 GAPI_Assert(gr.metadata(prod_h).get<NodeType>().t == NodeType::OP);
58 return ( gr.metadata(prod_h).contains<Island>()
59 && gr.metadata(prod_h).get<Island>().island == island)
60 && (ade::util::any_of(dataNode->outNodes(), [&](ade::NodeHandle cons_h)
62 return ( gr.metadata(cons_h).contains<Island>()
63 && gr.metadata(cons_h).get<Island>().island == island);
66 } // anonymous namespace
68 // Initially only Operations have Island tag. This pass adds Island tag
69 // to all data objects within an Island.
70 // A data object is considered within an Island if and only if
71 // its reader and writer are assigned to this Island (see above).
72 void cv::gimpl::passes::initIslands(ade::passes::PassContext &ctx)
74 GModel::Graph gr(ctx.graph);
75 for (const auto &nh : gr.nodes())
77 if (gr.metadata(nh).get<NodeType>().t == NodeType::OP)
79 if (gr.metadata(nh).contains<Island>())
81 const auto island = gr.metadata(nh).get<Island>().island;
83 // It is enough to check only input nodes
84 for (const auto &in_data_node : nh->inNodes())
86 if (is_within_same_island(gr, in_data_node, island))
88 gr.metadata(in_data_node).set(Island{island});
90 } // for(in_data_node)
91 } // if (contains<Island>)
96 // There should be no multiple (disconnected) islands with the same name.
97 // This may occur if user assigns the same islands name to multiple ranges
99 // FIXME: How it could be avoided on an earlier stage?
100 void cv::gimpl::passes::checkIslands(ade::passes::PassContext &ctx)
102 GModel::ConstGraph gr(ctx.graph);
104 // The algorithm is teh following:
106 // 1. Put all Tagged nodes (both Operations and Data) into a set
107 // 2. Initialize Visited set as (empty)
108 // 3. Initialize Traversal stack as (empty)
109 // 4. Initialize Islands map (String -> Integer) as (empty)
110 // 5. For every Tagged node from a set
111 // a. Skip if it is Visited
112 // b. For every input/output node:
113 // * if it is tagged with the same island:
114 // - add it to Traversal stack
115 // - remove from Tagged nodes if it is t
116 // c. While (stack is not empty):
117 // - Take a node from Stack
119 // d. Increment Islands map [this island] by 1
122 // If whatever Island has counter is more than 1, it is a disjoint
123 // one (i.e. there's two islands with the same name).
125 using node_set = std::unordered_set
127 , ade::HandleHasher<ade::Node>
129 node_set tagged_nodes;
130 node_set visited_tagged_nodes;
131 std::unordered_map<std::string, int> island_counters;
133 for (const auto &nh : gr.nodes())
135 if (gr.metadata(nh).contains<Island>())
137 tagged_nodes.insert(nh);
138 island_counters[gr.metadata(nh).get<Island>().island] = 0;
142 // Make a copy to allow list modifications during traversal
143 for (const auto &tagged_nh : tagged_nodes)
145 if (visited_tagged_nodes.end() != ade::util::find(visited_tagged_nodes, tagged_nh))
148 // Run the recursive traversal process as described in 5/a-d.
149 // This process is like a flood-fill traversal for island.
150 // If there's to distint successful flood-fills happened for the same island
151 // name, there are two islands with this name.
152 std::stack<ade::NodeHandle> stack;
153 stack.push(tagged_nh);
155 while (!stack.empty())
157 const auto this_nh = stack.top();
160 // Since _this_ node is visited, it is a part of processed island
161 // so mark it as visited to skip in other recursive processes
162 visited_tagged_nodes.insert(this_nh);
164 GAPI_DbgAssert(gr.metadata(this_nh).contains<Island>());
165 GAPI_DbgAssert( gr.metadata(this_nh ).get<Island>().island
166 == gr.metadata(tagged_nh).get<Island>().island);
167 const auto &this_island = gr.metadata(this_nh).get<Island>().island;
169 for (const auto neighbor_nh : ade::util::chain(this_nh->inNodes(), this_nh->outNodes()))
171 if ( gr.metadata(neighbor_nh).contains<Island>()
172 && gr.metadata(neighbor_nh).get<Island>().island == this_island
173 && !visited_tagged_nodes.count(neighbor_nh))
175 stack.push(neighbor_nh);
180 // Flood-fill is over, now increment island counter for this island
181 island_counters[gr.metadata(tagged_nh).get<Island>().island]++;
184 bool check_failed = false;
185 std::stringstream ss;
186 for (const auto &ic : island_counters)
188 GAPI_Assert(ic.second > 0);
192 ss << "\"" << ic.first << "\"(" << ic.second << ") ";
198 (std::logic_error("There are multiple distinct islands "
199 "with the same name: [" + ss.str() + "], "
200 "please check your cv::gapi::island() parameters!"));
204 void cv::gimpl::passes::checkIslandsContent(ade::passes::PassContext &ctx)
206 GModel::ConstGraph gr(ctx.graph);
207 std::unordered_map<std::string, cv::gapi::GBackend> backends_of_islands;
208 for (const auto& nh : gr.nodes())
210 if (NodeType::OP == gr.metadata(nh).get<NodeType>().t &&
211 gr.metadata(nh).contains<Island>())
213 const auto island = gr.metadata(nh).get<Island>().island;
214 auto island_backend_it = backends_of_islands.find(island);
215 const auto& op = gr.metadata(nh).get<Op>();
217 if (island_backend_it != backends_of_islands.end())
219 // Check that backend of the operation coincides with the backend of the island
220 // Backend of the island is determined by the backend of the first operation from this island
221 if (island_backend_it->second != op.backend)
223 util::throw_error(std::logic_error(island + " contains kernels " + op.k.name +
224 " with different backend"));
229 backends_of_islands.emplace(island, op.backend);