Path: blob/master/modules/gapi/test/internal/gapi_int_island_fusion_tests.cpp
16339 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 "test_precomp.hpp"8#include "compiler/transactions.hpp"910#include "gapi_mock_kernels.hpp"1112#include "compiler/gmodel.hpp"13#include "compiler/gislandmodel.hpp"14#include "compiler/gcompiler.hpp"1516namespace opencv_test17{1819TEST(IslandFusion, TwoOps_OneIsland)20{21namespace J = Jupiter; // see mock_kernels.cpp2223// Define a computation:24//25// (in) -> J::Foo1 -> (tmp0) -> J::Foo2 -> (out)26// : :27// : "island0" :28// :<----------------------------->:2930cv::GMat in;31cv::GMat tmp0 = I::Foo::on(in);32cv::GMat out = I::Foo::on(tmp0);33cv::GComputation cc(in, out);3435// Prepare compilation parameters manually36const auto in_meta = cv::GMetaArg(cv::GMatDesc{CV_8U,1,cv::gapi::own::Size(32,32)});37const auto pkg = cv::gapi::kernels<J::Foo>();3839// Directly instantiate G-API graph compiler and run partial compilation40cv::gimpl::GCompiler compiler(cc, {in_meta}, cv::compile_args(pkg));41cv::gimpl::GCompiler::GPtr graph = compiler.generateGraph();42compiler.runPasses(*graph);4344// Inspect the graph and verify the islands configuration45cv::gimpl::GModel::ConstGraph gm(*graph);4647auto in_nh = cv::gimpl::GModel::dataNodeOf(gm, in);48auto tmp_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp0);49auto out_nh = cv::gimpl::GModel::dataNodeOf(gm, out);5051// in/out mats shouldn't be assigned to any Island52EXPECT_FALSE(gm.metadata(in_nh ).contains<cv::gimpl::Island>());53EXPECT_FALSE(gm.metadata(out_nh).contains<cv::gimpl::Island>());5455// Since tmp is surrounded by two J kernels, tmp should be assigned56// to island J57EXPECT_TRUE(gm.metadata(tmp_nh).contains<cv::gimpl::Island>());58}5960TEST(IslandFusion, TwoOps_TwoIslands)61{62namespace J = Jupiter; // see mock_kernels.cpp63namespace S = Saturn; // see mock_kernels.cpp6465// Define a computation:66//67// (in) -> J::Foo --> (tmp0) -> S::Bar --> (out)68// : : -> :69// : : : :70// :<-------->: :<-------->:7172cv::GMat in;73cv::GMat tmp0 = I::Foo::on(in);74cv::GMat out = I::Bar::on(tmp0, tmp0);75cv::GComputation cc(in, out);7677// Prepare compilation parameters manually78const auto in_meta = cv::GMetaArg(cv::GMatDesc{CV_8U,1,cv::gapi::own::Size(32,32)});79const auto pkg = cv::gapi::kernels<J::Foo, S::Bar>();8081// Directly instantiate G-API graph compiler and run partial compilation82cv::gimpl::GCompiler compiler(cc, {in_meta}, cv::compile_args(pkg));83cv::gimpl::GCompiler::GPtr graph = compiler.generateGraph();84compiler.runPasses(*graph);8586// Inspect the graph and verify the islands configuration87cv::gimpl::GModel::ConstGraph gm(*graph);8889auto in_nh = cv::gimpl::GModel::dataNodeOf(gm, in);90auto tmp_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp0);91auto out_nh = cv::gimpl::GModel::dataNodeOf(gm, out);9293// in/tmp/out mats shouldn't be assigned to any Island94EXPECT_FALSE(gm.metadata(in_nh ).contains<cv::gimpl::Island>());95EXPECT_FALSE(gm.metadata(out_nh).contains<cv::gimpl::Island>());96EXPECT_FALSE(gm.metadata(tmp_nh).contains<cv::gimpl::Island>());9798auto isl_model = gm.metadata().get<cv::gimpl::IslandModel>().model;99cv::gimpl::GIslandModel::ConstGraph gim(*isl_model);100101// There should be two islands in the GIslandModel102const auto is_island = [&](ade::NodeHandle nh) {103return (cv::gimpl::NodeKind::ISLAND104== gim.metadata(nh).get<cv::gimpl::NodeKind>().k);105};106const std::size_t num_isl = std::count_if(gim.nodes().begin(),107gim.nodes().end(),108is_island);109EXPECT_EQ(2u, num_isl);110111auto isl_foo_nh = cv::gimpl::GIslandModel::producerOf(gim, tmp_nh);112auto isl_bar_nh = cv::gimpl::GIslandModel::producerOf(gim, out_nh);113ASSERT_NE(nullptr, isl_foo_nh);114ASSERT_NE(nullptr, isl_bar_nh);115116// Islands should be different117auto isl_foo_obj = gim.metadata(isl_foo_nh).get<cv::gimpl::FusedIsland>().object;118auto isl_bar_obj = gim.metadata(isl_bar_nh).get<cv::gimpl::FusedIsland>().object;119EXPECT_FALSE(isl_foo_obj == isl_bar_obj);120}121122TEST(IslandFusion, ConsumerHasTwoInputs)123{124namespace J = Jupiter; // see mock_kernels.cpp125126// Define a computation: island127// ............................128// (in0) ->:J::Foo -> (tmp) -> S::Bar :--> (out)129// :....................^.....:130// |131// (in1) -----------------------`132//133134// Check that island is build correctly, when consumer has two inputs135136GMat in[2];137GMat tmp = I::Foo::on(in[0]);138GMat out = I::Bar::on(tmp, in[1]);139140cv::GComputation cc(cv::GIn(in[0], in[1]), cv::GOut(out));141142// Prepare compilation parameters manually143cv::GMetaArgs in_metas = {GMetaArg(cv::GMatDesc{CV_8U,1,cv::gapi::own::Size(32,32)}),144GMetaArg(cv::GMatDesc{CV_8U,1,cv::gapi::own::Size(32,32)})};145const auto pkg = cv::gapi::kernels<J::Foo, J::Bar>();146147// Directly instantiate G-API graph compiler and run partial compilation148cv::gimpl::GCompiler compiler(cc, std::move(in_metas), cv::compile_args(pkg));149cv::gimpl::GCompiler::GPtr graph = compiler.generateGraph();150compiler.runPasses(*graph);151152cv::gimpl::GModel::ConstGraph gm(*graph);153154auto in0_nh = cv::gimpl::GModel::dataNodeOf(gm, in[0]);155auto in1_nh = cv::gimpl::GModel::dataNodeOf(gm, in[1]);156auto tmp_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp);157auto out_nh = cv::gimpl::GModel::dataNodeOf(gm, out);158159EXPECT_FALSE(gm.metadata(in0_nh ).contains<cv::gimpl::Island>());160EXPECT_FALSE(gm.metadata(in1_nh ).contains<cv::gimpl::Island>());161EXPECT_FALSE(gm.metadata(out_nh).contains<cv::gimpl::Island>());162EXPECT_TRUE(gm.metadata(tmp_nh).contains<cv::gimpl::Island>());163164auto isl_model = gm.metadata().get<cv::gimpl::IslandModel>().model;165cv::gimpl::GIslandModel::ConstGraph gim(*isl_model);166167const auto is_island = [&](ade::NodeHandle nh) {168return (cv::gimpl::NodeKind::ISLAND169== gim.metadata(nh).get<cv::gimpl::NodeKind>().k);170};171const std::size_t num_isl = std::count_if(gim.nodes().begin(),172gim.nodes().end(),173is_island);174EXPECT_EQ(1u, num_isl);175176auto isl_nh = cv::gimpl::GIslandModel::producerOf(gim, out_nh);177auto isl_obj = gim.metadata(isl_nh).get<cv::gimpl::FusedIsland>().object;178179EXPECT_TRUE(ade::util::contains(isl_obj->contents(), tmp_nh));180181EXPECT_EQ(2u, static_cast<std::size_t>(isl_nh->inNodes().size()));182EXPECT_EQ(1u, static_cast<std::size_t>(isl_nh->outNodes().size()));183}184185TEST(IslandFusion, DataNodeUsedDifferentBackend)186{187// Define a computation:188//189// internal isl isl0190// ...........................191// (in1) -> :J::Foo--> (tmp) -> J::Foo: --> (out0)192// :............|............:193// | ........194// `---->:S::Baz: --> (out1)195// :......:196197// Check that the node was not dropped out of the island198// because it is used by the kernel from another backend199200namespace J = Jupiter;201namespace S = Saturn;202203cv::GMat in, tmp, out0;204cv::GScalar out1;205tmp = I::Foo::on(in);206out0 = I::Foo::on(tmp);207out1 = I::Baz::on(tmp);208209cv::GComputation cc(cv::GIn(in), cv::GOut(out0, out1));210211// Prepare compilation parameters manually212const auto in_meta = cv::GMetaArg(cv::GMatDesc{CV_8U,1,cv::gapi::own::Size(32,32)});213const auto pkg = cv::gapi::kernels<J::Foo, S::Baz>();214215// Directly instantiate G-API graph compiler and run partial compilation216cv::gimpl::GCompiler compiler(cc, {in_meta}, cv::compile_args(pkg));217cv::gimpl::GCompiler::GPtr graph = compiler.generateGraph();218compiler.runPasses(*graph);219220// Inspect the graph and verify the islands configuration221cv::gimpl::GModel::ConstGraph gm(*graph);222223auto in_nh = cv::gimpl::GModel::dataNodeOf(gm, in);224auto tmp_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp);225auto out0_nh = cv::gimpl::GModel::dataNodeOf(gm, out0);226auto out1_nh = cv::gimpl::GModel::dataNodeOf(gm, out1);227228EXPECT_TRUE(gm.metadata(tmp_nh).contains<cv::gimpl::Island>());229230auto isl_model = gm.metadata().get<cv::gimpl::IslandModel>().model;231cv::gimpl::GIslandModel::ConstGraph gim(*isl_model);232233auto isl_nh = cv::gimpl::GIslandModel::producerOf(gim, tmp_nh);234auto isl_obj = gim.metadata(isl_nh).get<cv::gimpl::FusedIsland>().object;235236EXPECT_TRUE(ade::util::contains(isl_obj->contents(), tmp_nh));237238EXPECT_EQ(2u, static_cast<std::size_t>(isl_nh->outNodes().size()));239EXPECT_EQ(7u, static_cast<std::size_t>(gm.nodes().size()));240EXPECT_EQ(6u, static_cast<std::size_t>(gim.nodes().size()));241}242243TEST(IslandFusion, LoopBetweenDifferentBackends)244{245// Define a computation:246//247//248// .............................249// (in) -> :J::Baz -> (tmp0) -> J::Quux: -> (out0)250// | :............|..........^....251// | ........ | | ........252// `---->:S::Foo: `----------|-------->:S::Qux:-> (out1)253// :....|.: | :....^.:254// | | |255// `-------------- (tmp1) -----------`256257// Kernels S::Foo and S::Qux cannot merge, because there will be a cycle between islands258259namespace J = Jupiter;260namespace S = Saturn;261262cv::GScalar tmp0;263cv::GMat in, tmp1, out0, out1;264265tmp0 = I::Baz::on(in);266tmp1 = I::Foo::on(in);267out1 = I::Qux::on(tmp1, tmp0);268out0 = I::Quux::on(tmp0, tmp1);269270cv::GComputation cc(cv::GIn(in), cv::GOut(out1, out0));271272// Prepare compilation parameters manually273const auto in_meta = cv::GMetaArg(cv::GMatDesc{CV_8U,1,cv::gapi::own::Size(32,32)});274const auto pkg = cv::gapi::kernels<J::Baz, J::Quux, S::Foo, S::Qux>();275276// Directly instantiate G-API graph compiler and run partial compilation277cv::gimpl::GCompiler compiler(cc, {in_meta}, cv::compile_args(pkg));278cv::gimpl::GCompiler::GPtr graph = compiler.generateGraph();279compiler.runPasses(*graph);280281cv::gimpl::GModel::ConstGraph gm(*graph);282auto isl_model = gm.metadata().get<cv::gimpl::IslandModel>().model;283cv::gimpl::GIslandModel::ConstGraph gim(*isl_model);284285auto in_nh = cv::gimpl::GModel::dataNodeOf(gm, in);286auto tmp0_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp0);287auto tmp1_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp1);288auto out0_nh = cv::gimpl::GModel::dataNodeOf(gm, out0);289auto out1_nh = cv::gimpl::GModel::dataNodeOf(gm, out1);290291EXPECT_FALSE(gm.metadata(in_nh ).contains<cv::gimpl::Island>());292EXPECT_FALSE(gm.metadata(out0_nh).contains<cv::gimpl::Island>());293EXPECT_FALSE(gm.metadata(out1_nh).contains<cv::gimpl::Island>());294// The node does not belong to the island so as not to form a cycle295EXPECT_FALSE(gm.metadata(tmp1_nh).contains<cv::gimpl::Island>());296297EXPECT_TRUE(gm.metadata(tmp0_nh).contains<cv::gimpl::Island>());298299// There should be three islands in the GIslandModel300const auto is_island = [&](ade::NodeHandle nh) {301return (cv::gimpl::NodeKind::ISLAND302== gim.metadata(nh).get<cv::gimpl::NodeKind>().k);303};304const std::size_t num_isl = std::count_if(gim.nodes().begin(),305gim.nodes().end(),306is_island);307EXPECT_EQ(3u, num_isl);308}309310TEST(IslandsFusion, PartionOverlapUserIsland)311{312// Define a computation:313//314// internal isl isl0315// ........ ........316// (in0) -> :J::Foo:--> (tmp) ->:S::Bar: --> (out)317// :......: :......:318// ^319// |320// (in1) --------------------------`321322// Check that internal islands does't overlap user island323324namespace J = Jupiter;325namespace S = Saturn;326327GMat in[2];328GMat tmp = I::Foo::on(in[0]);329GMat out = I::Bar::on(tmp, in[1]);330331cv::gapi::island("isl0", cv::GIn(tmp, in[1]), cv::GOut(out));332cv::GComputation cc(cv::GIn(in[0], in[1]), cv::GOut(out));333334// Prepare compilation parameters manually335cv::GMetaArgs in_metas = {GMetaArg(cv::GMatDesc{CV_8U,1,cv::gapi::own::Size(32,32)}),336GMetaArg(cv::GMatDesc{CV_8U,1,cv::gapi::own::Size(32,32)})};337const auto pkg = cv::gapi::kernels<J::Foo, J::Bar>();338339// Directly instantiate G-API graph compiler and run partial compilation340cv::gimpl::GCompiler compiler(cc, std::move(in_metas), cv::compile_args(pkg));341cv::gimpl::GCompiler::GPtr graph = compiler.generateGraph();342compiler.runPasses(*graph);343344cv::gimpl::GModel::ConstGraph gm(*graph);345auto isl_model = gm.metadata().get<cv::gimpl::IslandModel>().model;346cv::gimpl::GIslandModel::ConstGraph gim(*isl_model);347348auto in0_nh = cv::gimpl::GModel::dataNodeOf(gm, in[0]);349auto in1_nh = cv::gimpl::GModel::dataNodeOf(gm, in[1]);350auto tmp_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp);351auto out_nh = cv::gimpl::GModel::dataNodeOf(gm, out);352353auto foo_nh = cv::gimpl::GIslandModel::producerOf(gim, tmp_nh);354auto foo_obj = gim.metadata(foo_nh).get<cv::gimpl::FusedIsland>().object;355356auto bar_nh = cv::gimpl::GIslandModel::producerOf(gim, out_nh);357auto bar_obj = gim.metadata(bar_nh).get<cv::gimpl::FusedIsland>().object;358359EXPECT_FALSE(gm.metadata(in0_nh ).contains<cv::gimpl::Island>());360EXPECT_FALSE(gm.metadata(in1_nh ).contains<cv::gimpl::Island>());361EXPECT_FALSE(gm.metadata(out_nh).contains<cv::gimpl::Island>());362EXPECT_FALSE(gm.metadata(tmp_nh).contains<cv::gimpl::Island>());363EXPECT_FALSE(foo_obj->is_user_specified());364EXPECT_TRUE(bar_obj->is_user_specified());365}366367TEST(IslandsFusion, DISABLED_IslandContainsDifferentBackends)368{369// Define a computation:370//371// isl0372// ............................373// (in0) -> :J::Foo:--> (tmp) -> S::Bar: --> (out)374// :..........................:375// ^376// |377// (in1) --------------------------`378379// Try create island contains different backends380381namespace J = Jupiter;382namespace S = Saturn;383384GMat in[2];385GMat tmp = I::Foo::on(in[0]);386GMat out = I::Bar::on(tmp, in[1]);387388cv::gapi::island("isl0", cv::GIn(in[0], in[1]), cv::GOut(out));389cv::GComputation cc(cv::GIn(in[0], in[1]), cv::GOut(out));390391// Prepare compilation parameters manually392cv::GMetaArgs in_metas = {GMetaArg(cv::GMatDesc{CV_8U,1,cv::gapi::own::Size(32,32)}),393GMetaArg(cv::GMatDesc{CV_8U,1,cv::gapi::own::Size(32,32)})};394const auto pkg = cv::gapi::kernels<J::Foo, S::Bar>();395396// Directly instantiate G-API graph compiler and run partial compilation397cv::gimpl::GCompiler compiler(cc, std::move(in_metas), cv::compile_args(pkg));398cv::gimpl::GCompiler::GPtr graph = compiler.generateGraph();399EXPECT_ANY_THROW(compiler.runPasses(*graph));400}401402TEST(IslandFusion, WithLoop)403{404namespace J = Jupiter; // see mock_kernels.cpp405406// Define a computation:407//408// (in) -> J::Foo --> (tmp0) -> J::Foo --> (tmp1) -> J::Qux -> (out)409// : ^410// '--> J::Baz --> (scl0) --'411//412// The whole thing should be merged to a single island413// There's a cycle warning if Foo/Foo/Qux are merged first414// Then this island both produces data for Baz and consumes data415// from Baz. This is a cycle and it should be avoided by the merging code.416//417cv::GMat in;418cv::GMat tmp0 = I::Foo::on(in);419cv::GMat tmp1 = I::Foo::on(tmp0);420cv::GScalar scl0 = I::Baz::on(tmp0);421cv::GMat out = I::Qux::on(tmp1, scl0);422cv::GComputation cc(in, out);423424// Prepare compilation parameters manually425const auto in_meta = cv::GMetaArg(cv::GMatDesc{CV_8U,1,cv::gapi::own::Size(32,32)});426const auto pkg = cv::gapi::kernels<J::Foo, J::Baz, J::Qux>();427428// Directly instantiate G-API graph compiler and run partial compilation429cv::gimpl::GCompiler compiler(cc, {in_meta}, cv::compile_args(pkg));430cv::gimpl::GCompiler::GPtr graph = compiler.generateGraph();431compiler.runPasses(*graph);432433// Inspect the graph and verify the islands configuration434cv::gimpl::GModel::ConstGraph gm(*graph);435436auto in_nh = cv::gimpl::GModel::dataNodeOf(gm, in);437auto tmp0_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp0);438auto tmp1_nh = cv::gimpl::GModel::dataNodeOf(gm, tmp1);439auto scl0_nh = cv::gimpl::GModel::dataNodeOf(gm, scl0);440auto out_nh = cv::gimpl::GModel::dataNodeOf(gm, out);441442// in/out mats shouldn't be assigned to any Island443EXPECT_FALSE(gm.metadata(in_nh ).contains<cv::gimpl::Island>());444EXPECT_FALSE(gm.metadata(out_nh).contains<cv::gimpl::Island>());445446// tmp0/tmp1/scl should be assigned to island447EXPECT_TRUE(gm.metadata(tmp0_nh).contains<cv::gimpl::Island>());448EXPECT_TRUE(gm.metadata(tmp1_nh).contains<cv::gimpl::Island>());449EXPECT_TRUE(gm.metadata(scl0_nh).contains<cv::gimpl::Island>());450451// Check that there's a single island object and it contains all452// that data object handles453454cv::gimpl::GModel::ConstGraph cg(*graph);455auto isl_model = cg.metadata().get<cv::gimpl::IslandModel>().model;456cv::gimpl::GIslandModel::ConstGraph gim(*isl_model);457458const auto is_island = [&](ade::NodeHandle nh) {459return (cv::gimpl::NodeKind::ISLAND460== gim.metadata(nh).get<cv::gimpl::NodeKind>().k);461};462const std::size_t num_isl = std::count_if(gim.nodes().begin(),463gim.nodes().end(),464is_island);465EXPECT_EQ(1u, num_isl);466467auto isl_nh = cv::gimpl::GIslandModel::producerOf(gim, out_nh);468auto isl_obj = gim.metadata(isl_nh).get<cv::gimpl::FusedIsland>().object;469EXPECT_TRUE(ade::util::contains(isl_obj->contents(), tmp0_nh));470EXPECT_TRUE(ade::util::contains(isl_obj->contents(), tmp1_nh));471EXPECT_TRUE(ade::util::contains(isl_obj->contents(), scl0_nh));472}473474TEST(IslandFusion, Regression_ShouldFuseAll)475{476// Initially the merge procedure didn't work as expected and477// stopped fusion even if it could be continued (e.g. full478// GModel graph could be fused into a single GIsland node).479// Example of this is custom RGB 2 YUV pipeline as shown below:480481cv::GMat r, g, b;482cv::GMat y = 0.299f*r + 0.587f*g + 0.114f*b;483cv::GMat u = 0.492f*(b - y);484cv::GMat v = 0.877f*(r - y);485486cv::GComputation customCvt({r, g, b}, {y, u, v});487488const auto in_meta = cv::GMetaArg(cv::GMatDesc{CV_8U,1,cv::Size(32,32)});489490// Directly instantiate G-API graph compiler and run partial compilation491cv::gimpl::GCompiler compiler(customCvt, {in_meta,in_meta,in_meta}, cv::compile_args());492cv::gimpl::GCompiler::GPtr graph = compiler.generateGraph();493compiler.runPasses(*graph);494495cv::gimpl::GModel::ConstGraph cg(*graph);496auto isl_model = cg.metadata().get<cv::gimpl::IslandModel>().model;497cv::gimpl::GIslandModel::ConstGraph gim(*isl_model);498499std::vector<ade::NodeHandle> data_nhs;500std::vector<ade::NodeHandle> isl_nhs;501for (auto &&nh : gim.nodes())502{503if (gim.metadata(nh).contains<cv::gimpl::FusedIsland>())504isl_nhs.push_back(std::move(nh));505else if (gim.metadata(nh).contains<cv::gimpl::DataSlot>())506data_nhs.push_back(std::move(nh));507else FAIL() << "GIslandModel node with unexpected metadata type";508}509510EXPECT_EQ(6u, data_nhs.size()); // 3 input nodes + 3 output nodes511EXPECT_EQ(1u, isl_nhs.size()); // 1 island512}513514// FIXME: add more tests on mixed (hetero) graphs515// ADE-222, ADE-223516517// FIXME: add test on combination of user-specified island518// which should be heterogeneous (based on kernel availability)519// but as we don't support this, compilation should fail520521// FIXME: add tests on automatic inferred islands which are522// connected via 1) gmat 2) gscalar 3) garray,523// check the case with executor524// check the case when this 1/2/3 interim object is also gcomputation output525526} // namespace opencv_test527528529