Introduce a new abstraction layer over island input/output data

- Port GExecutor to this new interface
pull/16965/head
Dmitry Matveev 5 years ago committed by Ruslan Garnov
parent 277f0d270f
commit 1b5730a799
  1. 2
      modules/gapi/src/backends/fluid/gfluidbackend.hpp
  2. 29
      modules/gapi/src/compiler/gislandmodel.cpp
  3. 31
      modules/gapi/src/compiler/gislandmodel.hpp
  4. 52
      modules/gapi/src/executor/gexecutor.cpp
  5. 3
      modules/gapi/src/executor/gexecutor.hpp

@ -153,6 +153,8 @@ public:
virtual void run(std::vector<InObj> &&input_objs, virtual void run(std::vector<InObj> &&input_objs,
std::vector<OutObj> &&output_objs) override; std::vector<OutObj> &&output_objs) override;
using GIslandExecutable::run; // (IInput&, IOutput&) version
void run(std::vector<InObj> &input_objs, void run(std::vector<InObj> &input_objs,
std::vector<OutObj> &output_objs); std::vector<OutObj> &output_objs);

@ -12,6 +12,7 @@
#include <unordered_map> #include <unordered_map>
#include <ade/util/checked_cast.hpp> #include <ade/util/checked_cast.hpp>
#include <ade/util/zip_range.hpp> // zip_range, indexed
#include "api/gbackend_priv.hpp" // GBackend::Priv().compile() #include "api/gbackend_priv.hpp" // GBackend::Priv().compile()
#include "compiler/gmodel.hpp" #include "compiler/gmodel.hpp"
@ -319,5 +320,33 @@ ade::NodeHandle GIslandModel::producerOf(const ConstGraph &g, ade::NodeHandle &d
return ade::NodeHandle(); return ade::NodeHandle();
} }
void GIslandExecutable::run(GIslandExecutable::IInput &in, GIslandExecutable::IOutput &out)
{
// Default implementation: just reuse the existing old-fashioned run
// Build a single synchronous execution frame for it.
std::vector<InObj> in_objs;
std::vector<OutObj> out_objs;
const auto &in_desc = in.desc();
const auto &out_desc = out.desc();
const auto in_vector = in.get(); // FIXME: passing temporary objects to toRange() leads to issues
in_objs.reserve(in_desc.size());
out_objs.reserve(out_desc.size());
for (auto &&it: ade::util::zip(ade::util::toRange(in_desc),
ade::util::toRange(in_vector)))
{
in_objs.emplace_back(std::get<0>(it), std::get<1>(it));
}
for (auto &&it: ade::util::indexed(ade::util::toRange(out_desc)))
{
out_objs.emplace_back(ade::util::value(it),
out.get(ade::util::checked_cast<int>(ade::util::index(it))));
}
run(std::move(in_objs), std::move(out_objs));
for (auto &&it: out_objs)
{
out.post(std::move(it.second)); // report output objects as "ready" to the executor
}
}
} // namespace cv } // namespace cv
} // namespace gimpl } // namespace gimpl

@ -89,33 +89,58 @@ protected:
util::optional<std::string> m_user_tag; util::optional<std::string> m_user_tag;
}; };
// GIslandExecutable - a backend-specific thing which executes // GIslandExecutable - a backend-specific thing which executes
// contents of an Island // contents of an Island
// * Is instantiated by the last step of the Islands fusion procedure; // * Is instantiated by the last step of the Islands fusion procedure;
// * Is orchestrated by a GExecutor instance. // * Is orchestrated by a GExecutor instance.
// //
class GIslandExecutable class GIslandExecutable
{ {
public: public:
using InObj = std::pair<RcDesc, cv::GRunArg>; using InObj = std::pair<RcDesc, cv::GRunArg>;
using OutObj = std::pair<RcDesc, cv::GRunArgP>; using OutObj = std::pair<RcDesc, cv::GRunArgP>;
class IODesc;
struct IInput;
struct IOutput;
// FIXME: now run() requires full input vector to be available. // FIXME: now run() requires full input vector to be available.
// actually, parts of subgraph may execute even if there's no all data // actually, parts of subgraph may execute even if there's no all data
// slots in place. // slots in place.
// TODO: Add partial execution capabilities // TODO: Add partial execution capabilities
// TODO: This method is now obsolette and is here for backwards
// compatibility only. Use (implement) the new run instead.
virtual void run(std::vector<InObj> &&input_objs, virtual void run(std::vector<InObj> &&input_objs,
std::vector<OutObj> &&output_objs) = 0; std::vector<OutObj> &&output_objs) = 0;
// Let the island execute. I/O data is obtained from/submitted to
// in/out objects.
virtual void run(IInput &in, IOutput &out);
virtual bool canReshape() const = 0; virtual bool canReshape() const = 0;
virtual void reshape(ade::Graph& g, const GCompileArgs& args) = 0; virtual void reshape(ade::Graph& g, const GCompileArgs& args) = 0;
virtual ~GIslandExecutable() = default; virtual ~GIslandExecutable() = default;
}; };
class GIslandExecutable::IODesc {
std::vector<cv::gimpl::RcDesc> d;
public:
void set(std::vector<cv::gimpl::RcDesc> &&newd) { d = std::move(newd); }
void set(const std::vector<cv::gimpl::RcDesc> &newd) { d = newd; }
const std::vector<cv::gimpl::RcDesc> &desc() const { return d; }
};
struct GIslandExecutable::IInput: public GIslandExecutable::IODesc {
virtual ~IInput() = default;
virtual cv::GRunArgs get() = 0; // Get a new input vector (blocking)
virtual cv::GRunArgs try_get() = 0; // Get a new input vector (non-blocking)
};
struct GIslandExecutable::IOutput: public GIslandExecutable::IODesc {
virtual ~IOutput() = default;
virtual GRunArgP get(int idx) = 0; // Allocate (wrap) a new data object for output idx
virtual void post(GRunArgP&&) = 0; // Release the object back to the framework (mark available)
};
// GIslandEmitter - a backend-specific thing which feeds data into // GIslandEmitter - a backend-specific thing which feeds data into
// the pipeline. This one is just an interface, implementations are executor-defined. // the pipeline. This one is just an interface, implementations are executor-defined.
class GIslandEmitter class GIslandEmitter

@ -122,6 +122,29 @@ void cv::gimpl::GExecutor::initResource(const ade::NodeHandle &orig_nh)
} }
} }
class cv::gimpl::GExecutor::Input final: public cv::gimpl::GIslandExecutable::IInput
{
cv::gimpl::Mag &mag;
virtual cv::GRunArgs get() override
{
cv::GRunArgs res;
for (const auto &rc : desc()) { res.emplace_back(magazine::getArg(mag, rc)); }
return res;
}
virtual cv::GRunArgs try_get() override { return get(); }
public:
Input(cv::gimpl::Mag &m, const std::vector<RcDesc> &rcs) : mag(m) { set(rcs); }
};
class cv::gimpl::GExecutor::Output final: public cv::gimpl::GIslandExecutable::IOutput
{
cv::gimpl::Mag &mag;
virtual GRunArgP get(int idx) override { return magazine::getObjPtr(mag, desc()[idx]); }
virtual void post(GRunArgP&&) override { } // Do nothing here
public:
Output(cv::gimpl::Mag &m, const std::vector<RcDesc> &rcs) : mag(m) { set(rcs); }
};
void cv::gimpl::GExecutor::run(cv::gimpl::GRuntimeArgs &&args) void cv::gimpl::GExecutor::run(cv::gimpl::GRuntimeArgs &&args)
{ {
// (2) // (2)
@ -145,7 +168,8 @@ void cv::gimpl::GExecutor::run(cv::gimpl::GRuntimeArgs &&args)
namespace util = ade::util; namespace util = ade::util;
// ensure that output Mat parameters are correctly allocated // ensure that output Mat parameters are correctly allocated
for (auto index : util::iota(proto.out_nhs.size()) ) //FIXME: avoid copy of NodeHandle and GRunRsltComp ? // FIXME: avoid copy of NodeHandle and GRunRsltComp ?
for (auto index : util::iota(proto.out_nhs.size()))
{ {
auto& nh = proto.out_nhs.at(index); auto& nh = proto.out_nhs.at(index);
const Data &d = m_gm.metadata(nh).get<Data>(); const Data &d = m_gm.metadata(nh).get<Data>();
@ -162,8 +186,9 @@ void cv::gimpl::GExecutor::run(cv::gimpl::GRuntimeArgs &&args)
}; };
#if !defined(GAPI_STANDALONE) #if !defined(GAPI_STANDALONE)
// Building as part of OpenCV - follow OpenCV behavior // Building as part of OpenCV - follow OpenCV behavior In
// In the case of cv::Mat if output buffer is not enough to hold the result, reallocate it // the case of cv::Mat if output buffer is not enough to
// hold the result, reallocate it
if (cv::util::holds_alternative<cv::Mat*>(args.outObjs.at(index))) if (cv::util::holds_alternative<cv::Mat*>(args.outObjs.at(index)))
{ {
auto& out_mat = *get<cv::Mat*>(args.outObjs.at(index)); auto& out_mat = *get<cv::Mat*>(args.outObjs.at(index));
@ -204,24 +229,9 @@ void cv::gimpl::GExecutor::run(cv::gimpl::GRuntimeArgs &&args)
for (auto &op : m_ops) for (auto &op : m_ops)
{ {
// (5) // (5)
using InObj = GIslandExecutable::InObj; Input i{m_res, op.in_objects};
using OutObj = GIslandExecutable::OutObj; Output o{m_res, op.out_objects};
std::vector<InObj> in_objs; op.isl_exec->run(i, o);
std::vector<OutObj> out_objs;
in_objs.reserve (op.in_objects.size());
out_objs.reserve(op.out_objects.size());
for (const auto &rc : op.in_objects)
{
in_objs.emplace_back(InObj{rc, magazine::getArg(m_res, rc)});
}
for (const auto &rc : op.out_objects)
{
out_objs.emplace_back(OutObj{rc, magazine::getObjPtr(m_res, rc)});
}
// (6)
op.isl_exec->run(std::move(in_objs), std::move(out_objs));
} }
// (7) // (7)

@ -77,6 +77,9 @@ protected:
}; };
std::vector<DataDesc> m_slots; std::vector<DataDesc> m_slots;
class Input;
class Output;
Mag m_res; Mag m_res;
void initResource(const ade::NodeHandle &orig_nh); // FIXME: shouldn't it be RcDesc? void initResource(const ade::NodeHandle &orig_nh); // FIXME: shouldn't it be RcDesc?

Loading…
Cancel
Save