Path: blob/master/modules/gapi/src/compiler/passes/islands.cpp
16345 views
// This file is part of OpenCV project.1// It is subject to the license terms in the LICENSE file found in the top-level directory2// of this distribution and at http://opencv.org/license.html.3//4// Copyright (C) 2018 Intel Corporation567#include "precomp.hpp"89#include <sstream>10#include <stack>11#include <ade/util/chain_range.hpp>12#include <ade/graph.hpp>1314#include "compiler/gmodel.hpp"15#include "compiler/passes/passes.hpp"1617namespace18{19bool is_within_same_island(const cv::gimpl::GModel::Graph &gr,20const ade::NodeHandle &dataNode,21const std::string &island)22{23// A data node is within the same island as it's reader node24// if and only if data object's producer island (if there's a producer)25// is the same as the specified one.26//27// An object may have only a single producer, but multiple consumers,28// and these consumers may be assigned to different Islands.29// Since "initIslands" traversal direction is op-to-args, i.e. reverse,30// a single Data object may be visited twice during Islands initialization.31//32// In this case, Data object is part of Island A if and only if:33// - Data object's producer is part of Island A,34// - AND any of Data obejct's consumers is part of Island A.35//36// Op["island0"] --> Data[ ? ] --> Op["island0"]37// :38// '---------> Op["island1"]39//40// In the above example, Data object is assigned to "island0" as41// it is surrounded by operations assigned to "island0"4243using namespace cv::gimpl;4445if ( gr.metadata(dataNode).contains<Island>()46&& gr.metadata(dataNode).get<Island>().island != island)47return false;4849if (dataNode->inNodes().empty())50return false;5152GAPI_Assert(dataNode->inNodes().size() == 1u);53const auto prod_h = dataNode->inNodes().front();5455// FIXME: ADE should have something like get_or<> or get<>(default)56GAPI_Assert(gr.metadata(prod_h).get<NodeType>().t == NodeType::OP);57return ( gr.metadata(prod_h).contains<Island>()58&& gr.metadata(prod_h).get<Island>().island == island)59&& (ade::util::any_of(dataNode->outNodes(), [&](ade::NodeHandle cons_h)60{61return ( gr.metadata(cons_h).contains<Island>()62&& gr.metadata(cons_h).get<Island>().island == island);63}));64}65} // anonymous namespace6667// Initially only Operations have Island tag. This pass adds Island tag68// to all data objects within an Island.69// A data object is considered within an Island if and only if70// its reader and writer are assigned to this Island (see above).71void cv::gimpl::passes::initIslands(ade::passes::PassContext &ctx)72{73GModel::Graph gr(ctx.graph);74for (const auto &nh : gr.nodes())75{76if (gr.metadata(nh).get<NodeType>().t == NodeType::OP)77{78if (gr.metadata(nh).contains<Island>())79{80const auto island = gr.metadata(nh).get<Island>().island;8182// It is enough to check only input nodes83for (const auto &in_data_node : nh->inNodes())84{85if (is_within_same_island(gr, in_data_node, island))86{87gr.metadata(in_data_node).set(Island{island});88}89} // for(in_data_node)90} // if (contains<Island>)91} // if (OP)92} // for (nodes)93}9495// There should be no multiple (disconnected) islands with the same name.96// This may occur if user assigns the same islands name to multiple ranges97// in the graph.98// FIXME: How it could be avoided on an earlier stage?99void cv::gimpl::passes::checkIslands(ade::passes::PassContext &ctx)100{101GModel::ConstGraph gr(ctx.graph);102103// The algorithm is teh following:104//105// 1. Put all Tagged nodes (both Operations and Data) into a set106// 2. Initialize Visited set as (empty)107// 3. Initialize Traversal stack as (empty)108// 4. Initialize Islands map (String -> Integer) as (empty)109// 5. For every Tagged node from a set110// a. Skip if it is Visited111// b. For every input/output node:112// * if it is tagged with the same island:113// - add it to Traversal stack114// - remove from Tagged nodes if it is t115// c. While (stack is not empty):116// - Take a node from Stack117// - Repeat (b)118// d. Increment Islands map [this island] by 1119//120//121// If whatever Island has counter is more than 1, it is a disjoint122// one (i.e. there's two islands with the same name).123124using node_set = std::unordered_set125< ade::NodeHandle126, ade::HandleHasher<ade::Node>127>;128node_set tagged_nodes;129node_set visited_tagged_nodes;130std::unordered_map<std::string, int> island_counters;131132for (const auto &nh : gr.nodes())133{134if (gr.metadata(nh).contains<Island>())135{136tagged_nodes.insert(nh);137island_counters[gr.metadata(nh).get<Island>().island] = 0;138}139}140141// Make a copy to allow list modifications during traversal142for (const auto &tagged_nh : tagged_nodes)143{144if (visited_tagged_nodes.end() != ade::util::find(visited_tagged_nodes, tagged_nh))145continue;146147// Run the recursive traversal process as described in 5/a-d.148// This process is like a flood-fill traversal for island.149// If there's to distint successful flood-fills happened for the same island150// name, there are two islands with this name.151std::stack<ade::NodeHandle> stack;152stack.push(tagged_nh);153154while (!stack.empty())155{156const auto this_nh = stack.top();157stack.pop();158159// Since _this_ node is visited, it is a part of processed island160// so mark it as visited to skip in other recursive processes161visited_tagged_nodes.insert(this_nh);162163GAPI_DbgAssert(gr.metadata(this_nh).contains<Island>());164GAPI_DbgAssert( gr.metadata(this_nh ).get<Island>().island165== gr.metadata(tagged_nh).get<Island>().island);166const auto &this_island = gr.metadata(this_nh).get<Island>().island;167168for (const auto neighbor_nh : ade::util::chain(this_nh->inNodes(), this_nh->outNodes()))169{170if ( gr.metadata(neighbor_nh).contains<Island>()171&& gr.metadata(neighbor_nh).get<Island>().island == this_island172&& !visited_tagged_nodes.count(neighbor_nh))173{174stack.push(neighbor_nh);175}176} // for (neighbor)177} // while (stack)178179// Flood-fill is over, now increment island counter for this island180island_counters[gr.metadata(tagged_nh).get<Island>().island]++;181} // for(tagged)182183bool check_failed = false;184std::stringstream ss;185for (const auto &ic : island_counters)186{187GAPI_Assert(ic.second > 0);188if (ic.second > 1)189{190check_failed = true;191ss << "\"" << ic.first << "\"(" << ic.second << ") ";192}193}194if (check_failed)195{196util::throw_error197(std::logic_error("There are multiple distinct islands "198"with the same name: [" + ss.str() + "], "199"please check your cv::gapi::island() parameters!"));200}201}202203void cv::gimpl::passes::checkIslandsContent(ade::passes::PassContext &ctx)204{205GModel::ConstGraph gr(ctx.graph);206std::unordered_map<std::string, cv::gapi::GBackend> backends_of_islands;207for (const auto& nh : gr.nodes())208{209if (NodeType::OP == gr.metadata(nh).get<NodeType>().t &&210gr.metadata(nh).contains<Island>())211{212const auto island = gr.metadata(nh).get<Island>().island;213auto island_backend_it = backends_of_islands.find(island);214const auto& op = gr.metadata(nh).get<Op>();215216if (island_backend_it != backends_of_islands.end())217{218// Check that backend of the operation coincides with the backend of the island219// Backend of the island is determined by the backend of the first operation from this island220if (island_backend_it->second != op.backend)221{222util::throw_error(std::logic_error(island + " contains kernels " + op.k.name +223" with different backend"));224}225}226else227{228backends_of_islands.emplace(island, op.backend);229}230}231}232}233234235