From a311ac7f84d6219ca651cc0b9b934f1f4833777d Mon Sep 17 00:00:00 2001 From: Joseph Schuchart Date: Thu, 24 Mar 2022 16:55:45 -0400 Subject: [PATCH] Implement TT policies to control process mapping, priorities, and inlining Policies replace keymap and priomaps and can be directly inlined if so provided. Users of ttg::make_tt can use ttg::make_policy to define policies with custom lambdas mapping keys to integers. Statically set policies cannot be changed at runtime, all defaulted policies can be set. Similarly, ttg::make_static_policy provides policies with statically defaulted mapping functions, which cannot be changed at runtime. This change does not implement task inlining but provides the policy to control future implementations. Signed-off-by: Joseph Schuchart --- examples/madness/mrattg.cc | 17 +- examples/madness/mrattg_streaming.cc | 53 ++- examples/spmm/spmm.cc | 9 +- tests/unit/tt.cc | 113 +++++ ttg/CMakeLists.txt | 1 + ttg/ttg/base/keymap.h | 14 + ttg/ttg/madness/fwd.h | 5 +- ttg/ttg/madness/ttg.h | 141 +++--- ttg/ttg/make_tt.h | 147 ++++-- ttg/ttg/parsec/fwd.h | 5 +- ttg/ttg/parsec/ttg.h | 216 +++++---- ttg/ttg/policies.h | 655 +++++++++++++++++++++++++++ ttg/ttg/reduce.h | 2 +- ttg/ttg/util/meta.h | 36 ++ 14 files changed, 1188 insertions(+), 226 deletions(-) create mode 100644 ttg/ttg/policies.h diff --git a/examples/madness/mrattg.cc b/examples/madness/mrattg.cc index 47bce1ce9..b86e1afd0 100644 --- a/examples/madness/mrattg.cc +++ b/examples/madness/mrattg.cc @@ -136,7 +136,8 @@ namespace detail { using compress_out_type = std::tuple>; using compress_in_type = std::tuple; template - using compwrap_type = ttg::CallableWrapTT, compress_out_type, Rin, Rin>; + using compwrap_type = ttg::CallableWrapTT, compress_out_type, + ttg::TTPolicyBase>, Rin, Rin>; }; template @@ -146,7 +147,8 @@ namespace detail { using compress_out_type = std::tuple>; using compress_in_type = std::tuple; template - using compwrap_type = ttg::CallableWrapTT, compress_out_type, Rin, Rin, Rin, Rin>; + using compwrap_type = ttg::CallableWrapTT, compress_out_type, + ttg::TTPolicyBase>, Rin, Rin, Rin, Rin>; }; template @@ -157,7 +159,8 @@ namespace detail { using compress_in_type = std::tuple; template using compwrap_type = - ttg::CallableWrapTT, compress_out_type, Rin, Rin, Rin, Rin, Rin, Rin, Rin, Rin>; + ttg::CallableWrapTT, compress_out_type, + ttg::TTPolicyBase>, Rin, Rin, Rin, Rin, Rin, Rin, Rin, Rin>; }; }; // namespace detail @@ -237,7 +240,7 @@ auto make_compress(rnodeEdge& in, cnodeEdge& out, const using sendfuncT = decltype(&send_leaves_up); using sendwrapT = ttg::CallableWrapTT, typename ::detail::tree_types::compress_out_type, - FunctionReconstructedNode>; + ttg::TTPolicyBase>, FunctionReconstructedNode>; using compfuncT = decltype(&do_compress); using compwrapT = typename ::detail::tree_types::template compwrap_type; @@ -250,8 +253,10 @@ auto make_compress(rnodeEdge& in, cnodeEdge& out, const outnames.push_back("output"); auto s = - std::unique_ptr(new sendwrapT(&send_leaves_up, "send_leaves_up", {"input"}, outnames)); - auto c = std::unique_ptr(new compwrapT(&do_compress, "do_compress", innames, outnames)); + std::unique_ptr(new sendwrapT(&send_leaves_up, ttg::make_policy>(), + "send_leaves_up", {"input"}, outnames)); + auto c = std::unique_ptr(new compwrapT(&do_compress, ttg::make_policy>(), + "do_compress", innames, outnames)); in.set_out(s->template in<0>()); // Connect input to s out.set_in(s->template out()); // Connect s result to output diff --git a/examples/madness/mrattg_streaming.cc b/examples/madness/mrattg_streaming.cc index 4df95c74e..6d54b09f5 100644 --- a/examples/madness/mrattg_streaming.cc +++ b/examples/madness/mrattg_streaming.cc @@ -72,7 +72,7 @@ class LevelPmapX { LevelPmapX(size_t nproc) : nproc(nproc) {} /// Find the owner of a given key - HashValue operator()(const Key<3>& key) const { + int operator()(const Key<3>& key) const { Level n = key.level(); if (n == 0) return 0; madness::hashT hash; @@ -112,7 +112,7 @@ class PartitionPmap { } /// Find the owner of a given key - HashValue operator()(const Key& key) const { + int operator()(const Key& key) const { HashValue hash; if (key.level() <= target_level) { hash = key.hash(); @@ -313,12 +313,14 @@ auto make_start(const ctlEdge& ctl) { /// Constructs an operator that adaptively projects the provided function into the basis /// Returns an std::unique_ptr to the object -template +template >> auto make_project(functorT& f, const T thresh, /// should be scalar value not complex ctlEdge& ctl, rnodeEdge& result, - const std::string& name = "project") { + const std::string& name = "project", + PmapT&& pmap = PmapT()) { auto F = [f, thresh](const Key& key, std::tuple, rnodeOut>& out) { FunctionReconstructedNodeWrap node(key); // Our eventual result @@ -348,7 +350,8 @@ auto make_project(functorT& f, ttg::send<1>(key, std::move(node), out); // always produce a result }; ctlEdge refine("refine"); - return ttg::make_tt(F, edges(fuse(refine, ctl)), ttg::edges(refine, result), name, {"control"}, {"refine", "result"}); + return ttg::make_tt(F, edges(fuse(refine, ctl)), ttg::edges(refine, result), + ttg::make_static_policy(pmap), name, {"control"}, {"refine", "result"}); } namespace detail { @@ -471,13 +474,20 @@ std::string int2bitstring(size_t i, size_t width) { } /// Make a composite operator that implements compression for a single function -template -auto make_compress(rnodeEdge& in, cnodeEdge& out, const std::string& name = "compress") { +template >> +auto make_compress(rnodeEdge& in, cnodeEdge& out, + PmapT&& pmap, const std::string& name = "compress") { rnodeEdge children1("children1"), children2("children2"); - return std::make_tuple(ttg::make_tt(&send_leaves_up, edges(in), edges(children1, out), "send_leaves_up", {"input"}, {"children1", "output"}), - ttg::make_tt(&reduce_leaves, edges(children1), edges(children2), "reduce_leaves", {"children1"}, {"children2"}), - ttg::make_tt(&do_compress, edges(children2), edges(children1,out), "do_compress", {"children2"}, {"recur","output"})); + return std::make_tuple(ttg::make_tt(&send_leaves_up, edges(in), edges(children1, out), + ttg::make_static_policy(pmap), + "send_leaves_up", {"input"}, {"children1", "output"}), + ttg::make_tt(&reduce_leaves, edges(children1), edges(children2), + ttg::make_static_policy(pmap), + "reduce_leaves", {"children1"}, {"children2"}), + ttg::make_tt(&do_compress, edges(children2), edges(children1,out), + ttg::make_static_policy(pmap), + "do_compress", {"children2"}, {"recur","output"})); } template @@ -519,11 +529,13 @@ void do_reconstruct(const Key& key, ttg::broadcast<1>(bcast_keys[1], std::move(r), out); } -template -auto make_reconstruct(const cnodeEdge& in, rnodeEdge& out, const std::string& name = "reconstruct") { +template >> +auto make_reconstruct(const cnodeEdge& in, rnodeEdge& out, + PmapT&& pmap = PmapT(), const std::string& name = "reconstruct") { ttg::Edge,FixedTensor> S("S"); // passes scaling functions down - auto s = ttg::make_tt_tpl(&do_reconstruct, ttg::edges(in, S), ttg::edges(S, out), name, {"input", "s"}, {"s", "output"}); + auto s = ttg::make_tt_tpl(&do_reconstruct, ttg::edges(in, S), ttg::edges(S, out), + ttg::make_static_policy(pmap), name, {"input", "s"}, {"s", "output"}); if (ttg::default_execution_context().rank() == 0) { s->template in<1>()->send(Key{0,{0}}, FixedTensor()); // Prime the flow of scaling functions @@ -764,16 +776,12 @@ void test2(size_t nfunc, T thresh = 1e-6) { rnodeEdge a("a"), c("c"); cnodeEdge b("b"); - auto p1 = make_project(ff, T(thresh), ctl, a, "project A"); - p1->set_keymap(pmap); + auto p1 = make_project(ff, T(thresh), ctl, a, "project A", pmap); - auto compress = make_compress(a, b); - std::get<0>(compress)->set_keymap(pmap); - std::get<1>(compress)->set_keymap(pmap); - std::get<2>(compress)->set_keymap(pmap); + auto compress = make_compress(a, b, pmap); auto &reduce_leaves_op = std::get<1>(compress); - reduce_leaves_op->template set_input_reducer<0>([](FunctionReconstructedNodeWrap &node, + reduce_leaves_op->template set_input_reducer<0>([](FunctionReconstructedNodeWrap &node, const FunctionReconstructedNodeWrap &another) { //Update self values into the array. @@ -786,8 +794,7 @@ void test2(size_t nfunc, T thresh = 1e-6) { }); reduce_leaves_op->template set_static_argstream_size<0>(1 << NDIM); - auto recon = make_reconstruct(b,c); - recon->set_keymap(pmap); + auto recon = make_reconstruct(b,c,pmap); //auto printer = make_printer(a,"projected ", true); // auto printer2 = make_printer(b,"compressed ", false); @@ -814,7 +821,7 @@ void test2(size_t nfunc, T thresh = 1e-6) { //std::cout << "==== begin dot ====\n"; //std::cout << Dot()(start.get()) << std::endl; //std::cout << "==== end dot ====\n"; - beg = std::chrono::high_resolution_clock::now(); + beg = std::chrono::high_resolution_clock::now(); // This kicks off the entire computation start->invoke(Key(0, {0})); } diff --git a/examples/spmm/spmm.cc b/examples/spmm/spmm.cc index 216ba029b..339e56f79 100644 --- a/examples/spmm/spmm.cc +++ b/examples/spmm/spmm.cc @@ -202,7 +202,7 @@ class Read_SpMatrix : public TT, std::tuple, Blk>>, Read_SpMat auto rank = ttg::default_execution_context().rank(); for (int k = 0; k < matrix_.outerSize(); ++k) { for (typename SpMatrix::InnerIterator it(matrix_, k); it; ++it) { - if (rank == this->get_keymap()(Key<2>({it.row(), it.col()}))) + if (rank == this->procmap(Key<2>({it.row(), it.col()}))) ::send<0>(Key<2>({it.row(), it.col()}), it.value(), out); } } @@ -340,9 +340,8 @@ class SpMM { if (k >= b_rowidx_to_colidx_.size()) return; auto world = default_execution_context(); std::vector procmap(world.size()); - auto keymap = baseT::get_keymap(); for (auto &j : b_rowidx_to_colidx_[k]) { - const long p = keymap(Key<2>({i, j})); + const long p = this->procmap(Key<2>({i, j})); if (!procmap[p]) { ttg::trace("Broadcasting A[", i, "][", k, "] to proc ", p); ikp_keys.emplace_back(Key<3>({i, k, p})); @@ -413,9 +412,9 @@ class SpMM { auto world = default_execution_context(); std::vector procmap(world.size()); for (auto &i : a_colidx_to_rowidx_[k]) { - long p = baseT::get_keymap()(Key<2>({i, j})); + long p = baseT::procmap(Key<2>({i, j})); if (!procmap[p]) { - ttg::trace("Broadcasting B[", k, "][", j, "] to proc ", p); + ttg::trace("Broadcasting A[", k, "][", j, "] to proc ", p); kjp_keys.emplace_back(Key<3>({k, j, p})); procmap[p] = true; } diff --git a/tests/unit/tt.cc b/tests/unit/tt.cc index 597f58b5b..f3d85f0af 100644 --- a/tests/unit/tt.cc +++ b/tests/unit/tt.cc @@ -145,6 +145,35 @@ namespace tt_i_iv { }; } // namespace tt_i_iv +// {task_id,data} = {int, int, void} +namespace tt_i_i_p { + + struct Policy : public ttg::TTPolicyBase { + + Policy() : TTPolicyBase() {} + + int procmap(const int&) const { return 0; } + + }; + + class tt : public ttg::TT, tt, ttg::typelist, Policy> { + using baseT = typename tt::ttT; + + public: + tt(const typename baseT::input_edges_type &inedges, const typename baseT::output_edges_type &outedges, + const std::string &name) + : baseT(inedges, outedges, name, {"int"}, {}, Policy()) {} + + static constexpr const bool have_cuda_op = false; + + void op(const int &key, const baseT::input_refs_tuple_type &data, baseT::output_terminals_type &outs) {} + + ~tt() {} + }; +} // namespace tt_i_iv + + + TEST_CASE("TemplateTask", "[core]") { SECTION("constructors") { { // void task id, void data @@ -249,5 +278,89 @@ TEST_CASE("TemplateTask", "[core]") { }, ttg::edges(in), ttg::edges())); } + { // nonvoid task id, nonvoid data, w/ policies + ttg::Edge in; + CHECK_NOTHROW(std::make_unique(ttg::edges(in), ttg::edges(), "")); + CHECK_NOTHROW( + ttg::make_tt([](const int &key, const int &datum, std::tuple<> &outs) {}, ttg::edges(in), ttg::edges(), + tt_i_i_p::Policy())); + + auto tt = ttg::make_tt([](const int &key, const int &datum, std::tuple<> &outs) {}, ttg::edges(in), ttg::edges(), + tt_i_i_p::Policy()); + auto policy = tt->get_policy(); + auto procmap = tt->get_procmap(); + } + } + + SECTION("policies") { + { // default policy + ttg::Edge in; + auto tt = ttg::make_tt([](const int &key, const int &datum, std::tuple<> &outs) {}, ttg::edges(in), ttg::edges()); + auto policy = tt->get_policy(); + CHECK(tt->procmap(0) == 0); + CHECK(tt->get_procmap()(0) == 0); + CHECK(policy.procmap(0) == 0); + } + { // custom procmap + ttg::Edge in; + auto tt = ttg::make_tt([](const int &key, const int &datum, std::tuple<> &outs) {}, ttg::edges(in), ttg::edges(), + ttg::make_policy([](const int& key){ return key*1000; })); + auto policy = tt->get_policy(); + CHECK(tt->procmap(1) == 1000); + CHECK(tt->get_procmap()(2) == 2000); + CHECK(policy.procmap(3) == 3000); + tt->set_priomap([](const int&){ return 0; }); + } + { // custom all maps + ttg::Edge in; + auto tt = ttg::make_tt([](const int &key, const int &datum, std::tuple<> &outs) {}, ttg::edges(in), ttg::edges(), + ttg::make_policy([](const int& key){ return key*1000; }, + [](const int& key){ return key*1000; }, + [](const int& key){ return key*1000; })); + auto policy = tt->get_policy(); + CHECK(policy.procmap(1) == 1000); + CHECK(tt->procmap(2) == 2000); + CHECK(tt->get_procmap()(3) == 3000); + + CHECK(tt->priomap(1) == 1000); + CHECK(tt->get_priomap()(2) == 2000); + + CHECK(tt->inlinemap(1) == 1000); + CHECK(tt->get_inlinemap()(2) == 2000); + } + { // custom all maps static + ttg::Edge in; + auto tt = ttg::make_tt([](const int &key, const int &datum, std::tuple<> &outs) {}, ttg::edges(in), ttg::edges(), + ttg::make_static_policy([](const int& key){ return key*1000; }, + [](const int& key){ return key*1000; }, + [](const int& key){ return key*1000; })); + auto policy = tt->get_policy(); + CHECK(policy.procmap(1) == 1000); + CHECK(tt->procmap(2) == 2000); + CHECK(tt->get_procmap()(3) == 3000); + + CHECK(tt->priomap(1) == 1000); + CHECK(tt->get_priomap()(2) == 2000); + + CHECK(tt->inlinemap(1) == 1000); + CHECK(tt->get_inlinemap()(2) == 2000); + } + { // custom all maps static, void key + ttg::Edge in; + auto tt = ttg::make_tt([](const int &datum, std::tuple<> &outs) {}, ttg::edges(in), ttg::edges(), + ttg::make_static_policy([](){ return 1000; }, + [](){ return 2000; }, + [](){ return 3000; })); + auto policy = tt->get_policy(); + CHECK(policy.procmap() == 1000); + CHECK(tt->procmap() == 1000); + CHECK(tt->get_procmap()() == 1000); + + CHECK(tt->priomap() == 2000); + CHECK(tt->get_priomap()() == 2000); + + CHECK(tt->inlinemap() == 3000); + CHECK(tt->get_inlinemap()() == 3000); + } } } diff --git a/ttg/CMakeLists.txt b/ttg/CMakeLists.txt index d87c94b08..0fa1270a5 100644 --- a/ttg/CMakeLists.txt +++ b/ttg/CMakeLists.txt @@ -36,6 +36,7 @@ set(ttg-impl-headers ${CMAKE_CURRENT_SOURCE_DIR}/ttg/func.h ${CMAKE_CURRENT_SOURCE_DIR}/ttg/fwd.h ${CMAKE_CURRENT_SOURCE_DIR}/ttg/impl_selector.h + ${CMAKE_CURRENT_SOURCE_DIR}/ttg/policies.h ${CMAKE_CURRENT_SOURCE_DIR}/ttg/tt.h ${CMAKE_CURRENT_SOURCE_DIR}/ttg/reduce.h ${CMAKE_CURRENT_SOURCE_DIR}/ttg/run.h diff --git a/ttg/ttg/base/keymap.h b/ttg/ttg/base/keymap.h index 8997dd80d..a4227b0b1 100644 --- a/ttg/ttg/base/keymap.h +++ b/ttg/ttg/base/keymap.h @@ -50,6 +50,20 @@ namespace ttg { operator()() const { return 0; } }; + + /// the default inline map implementation + template + struct default_inlinemap_impl { + default_inlinemap_impl() = default; + + template + std::enable_if_t,int> + operator()(const Key &key) const { return 0; } + template + std::enable_if_t,int> + operator()() const { return 0; } + }; + } // namespace detail } // namespace ttg diff --git a/ttg/ttg/madness/fwd.h b/ttg/ttg/madness/fwd.h index 4e6744754..4dfe6229d 100644 --- a/ttg/ttg/madness/fwd.h +++ b/ttg/ttg/madness/fwd.h @@ -3,12 +3,15 @@ #include "ttg/fwd.h" #include "ttg/util/typelist.h" +#include "ttg/policies.h" #include namespace ttg_madness { - template > + template , + typename policiesT = ttg::TTPolicyBase> class TT; /// \internal the OG name diff --git a/ttg/ttg/madness/ttg.h b/ttg/ttg/madness/ttg.h index 5a59782e8..b7238a7ca 100644 --- a/ttg/ttg/madness/ttg.h +++ b/ttg/ttg/madness/ttg.h @@ -14,6 +14,7 @@ #include "ttg/base/tt.h" #include "ttg/func.h" #include "ttg/runtimes.h" +#include "ttg/policies.h" #include "ttg/tt.h" #include "ttg/util/bug.h" #include "ttg/util/env.h" @@ -177,8 +178,11 @@ namespace ttg_madness { /// values /// flowing into this TT; a const type indicates nonmutating (read-only) use, nonconst type /// indicates mutating use (e.g. the corresponding input can be used as scratch, moved-from, etc.) - template - class TT : public ttg::TTBase, public ::madness::WorldObject> { + template + class TT + : public ttg::TTBase + , public ::madness::WorldObject> + , public ttg::detail::TTPolicyWrapper { static_assert(ttg::meta::is_typelist_v, "The fourth template for ttg::TT must be a ttg::typelist containing the input types"); using input_tuple_type = ttg::meta::typelist_to_tuple_t; @@ -186,6 +190,8 @@ namespace ttg_madness { using actual_input_tuple_type = std::conditional_t, ttg::meta::typelist_to_tuple_t, std::tuple>; + using PolicyWrapper = typename ttg::detail::TTPolicyWrapper; + public: using ttT = TT; /// preconditions @@ -193,8 +199,6 @@ namespace ttg_madness { private: ttg::World world; - ttg::meta::detail::keymap_t keymap; - ttg::meta::detail::keymap_t priomap; // For now use same type for unary/streaming input terminals, and stream reducers assigned at runtime ttg::meta::detail::input_reducers_t input_reducers; //!< Reducers for the input terminals (empty = expect single value) @@ -351,9 +355,9 @@ namespace ttg_madness { int owner; if constexpr (!ttg::meta::is_void_v) { - owner = keymap(key); + owner = this->keymap(key); } else { - owner = keymap(); + owner = this->keymap(); } if (owner != world.rank()) { @@ -553,7 +557,7 @@ namespace ttg_madness { assert(size > 0 && "TT::set_argstream_size(size) called with size=0"); // body - const auto owner = keymap(); + const auto owner = this->keymap(); if (owner != world.rank()) { ttg::trace(world.rank(), ":", get_name(), " : forwarding stream size for terminal ", i); worldobjT::send(owner, &ttT::template set_argstream_size, size); @@ -633,7 +637,7 @@ namespace ttg_madness { assert(size > 0 && "TT::set_argstream_size(key,size) called with size=0"); // body - const auto owner = keymap(key); + const auto owner = this->keymap(key); if (owner != world.rank()) { ttg::trace(world.rank(), ":", get_name(), " : ", key, ": forwarding stream size for terminal ", i); worldobjT::send(owner, &ttT::template set_argstream_size, key, size); @@ -688,7 +692,7 @@ namespace ttg_madness { assert(std::get(input_reducers) && "TT::finalize_argstream called on nonstreaming input terminal"); // body - const auto owner = keymap(key); + const auto owner = this->keymap(key); if (owner != world.rank()) { ttg::trace(world.rank(), ":", get_name(), " : ", key, ": forwarding stream finalize for terminal ", i); worldobjT::send(owner, &ttT::template finalize_argstream, key); @@ -737,7 +741,7 @@ namespace ttg_madness { assert(std::get(input_reducers) && "TT::finalize_argstream called on nonstreaming input terminal"); // body - const int owner = keymap(); + const int owner = this->keymap(); if (owner != world.rank()) { ttg::trace(world.rank(), ":", get_name(), " : forwarding stream finalize for terminal ", i); worldobjT::send(owner, &ttT::template finalize_argstream); @@ -879,19 +883,15 @@ namespace ttg_madness { } public: - template , - typename priomapT = ttg::detail::default_priomap> + + template TT(const std::string &name, const std::vector &innames, const std::vector &outnames, - ttg::World world, keymapT &&keymap_ = keymapT(), priomapT &&priomap_ = priomapT()) + ttg::World world, policyT_&& policy) : ttg::TTBase(name, numinedges, numouts) + , PolicyWrapper(world, std::forward(policy)) , static_streamsize() , worldobjT(world.impl().impl()) - , world(world) - // if using default keymap, rebind to the given world - , keymap(std::is_same_v> - ? decltype(keymap)(ttg::detail::default_keymap(world)) - : decltype(keymap)(std::forward(keymap_))) - , priomap(decltype(keymap)(std::forward(priomap_))) { + , world(world) { // Cannot call these in base constructor since terminals not yet constructed if (innames.size() != numinedges) { ttg::print_error(world.rank(), ":", get_name(), "#input_names", innames.size(), "!= #input_terminals", @@ -906,27 +906,52 @@ namespace ttg_madness { register_input_callbacks(std::make_index_sequence{}); } - template , - typename priomapT = ttg::detail::default_priomap> + template , policyT>>> + TT(const std::string &name, const std::vector &innames, const std::vector &outnames, + policyT_&& policy) + : TT(name, innames, outnames, ttg::default_execution_context(), std::forward(policy)) + {} + + template >> + TT(const std::string &name, const std::vector &innames, const std::vector &outnames) + : TT(name, innames, outnames, ttg::default_execution_context(), policyT()) + {} + + template >>> TT(const std::string &name, const std::vector &innames, const std::vector &outnames, - keymapT &&keymap = keymapT(ttg::default_execution_context()), priomapT &&priomap = priomapT()) - : TT(name, innames, outnames, ttg::default_execution_context(), std::forward(keymap), - std::forward(priomap)) {} + ttg::meta::detail::keymap_t keymap) + : TT(name, innames, outnames, ttg::default_execution_context(), + policyT(std::move(keymap))) {} + + template >>> + TT(const input_edges_type &inedges, const output_edges_type &outedges, const std::string &name, + const std::vector &innames, const std::vector &outnames, ttg::World world, + ttg::meta::detail::keymap_t keymap) + : TT(inedges, outedges, name, innames, outnames, world, + policyT(std::move(keymap))) + { } - template , - typename priomapT = ttg::detail::default_priomap> + template , policyT>>> + TT(const input_edges_type &inedges, const output_edges_type &outedges, const std::string &name, + const std::vector &innames, const std::vector &outnames, + policyT_&& policy) + : TT(inedges, outedges, name, innames, outnames, ttg::default_execution_context(), std::forward(policy)) + { } + + template TT(const input_edges_type &inedges, const output_edges_type &outedges, const std::string &name, const std::vector &innames, const std::vector &outnames, ttg::World world, - keymapT &&keymap_ = keymapT(), priomapT &&priomap_ = priomapT()) + policyT_&& policy) : ttg::TTBase(name, numinedges, numouts) + , PolicyWrapper(world, std::forward(policy)) , static_streamsize() , worldobjT(ttg::default_execution_context().impl().impl()) - , world(ttg::default_execution_context()) - // if using default keymap, rebind to the given world - , keymap(std::is_same_v> - ? decltype(keymap)(ttg::detail::default_keymap(world)) - : decltype(keymap)(std::forward(keymap_))) - , priomap(decltype(keymap)(std::forward(priomap_))) { + , world(ttg::default_execution_context()) { // Cannot call in base constructor since terminals not yet constructed if (innames.size() != numinedges) { ttg::print_error(world.rank(), ":", get_name(), "#input_names", innames.size(), "!= #input_terminals", @@ -944,13 +969,21 @@ namespace ttg_madness { connect_my_outputs_to_outgoing_edge_inputs(std::make_index_sequence{}, outedges); } - template , - typename priomapT = ttg::detail::default_priomap> + template >>> TT(const input_edges_type &inedges, const output_edges_type &outedges, const std::string &name, const std::vector &innames, const std::vector &outnames, - keymapT &&keymap = keymapT(ttg::default_execution_context()), priomapT &&priomap = priomapT()) + ttg::meta::detail::keymap_t keymap) + : TT(inedges, outedges, name, innames, outnames, ttg::default_execution_context(), + policyT_(std::move(keymap))) {} + + template >> + TT(const input_edges_type &inedges, const output_edges_type &outedges, const std::string &name, + const std::vector &innames, const std::vector &outnames) : TT(inedges, outedges, name, innames, outnames, ttg::default_execution_context(), - std::forward(keymap), std::forward(priomap)) {} + policyT_()) {} + // Destructor checks for unexecuted tasks virtual ~TT() { @@ -988,21 +1021,6 @@ namespace ttg_madness { set_static_argstream_size(size); } - template - void set_keymap(Keymap &&km) { - keymap = km; - } - - auto get_priomap(void) const { return priomap; } - - /// Set the priority map, mapping a Key to an integral value. - /// Higher values indicate higher priority. The default priority is 0, higher - /// values are treated as high priority tasks in the MADNESS backend. - template - void set_priomap(Priomap &&pm) { - priomap = pm; - } - /// implementation of TTBase::make_executable() void make_executable() override { this->process_pending(); @@ -1078,25 +1096,6 @@ namespace ttg_madness { else TTBase::invoke(); } - - /// keymap accessor - /// @return the keymap - const decltype(keymap) &get_keymap() const { return keymap; } - - /// computes the owner of key @c key - /// @param[in] key the key - /// @return the owner of @c key - template - std::enable_if_t, int> owner(const Key &key) const { - return keymap(key); - } - - /// computes the owner of void key - /// @return the owner of void key - template - std::enable_if_t, int> owner() const { - return keymap(); - } }; #include "ttg/make_tt.h" diff --git a/ttg/ttg/make_tt.h b/ttg/ttg/make_tt.h index f93be9d53..eddad8c3f 100644 --- a/ttg/ttg/make_tt.h +++ b/ttg/ttg/make_tt.h @@ -29,11 +29,11 @@ namespace detail { // case 2 (keyT == void): void op(std::tuple&&, std::tuple&) // template + typename policyT, typename... input_valuesT> class CallableWrapTT : public TT, - ttg::typelist> { + CallableWrapTT, + ttg::typelist, policyT> { using baseT = typename CallableWrapTT::ttT; using input_values_tuple_type = typename baseT::input_values_tuple_type; @@ -74,16 +74,19 @@ class CallableWrapTT } public: - template + template CallableWrapTT(funcT_ &&f, const input_edges_type &inedges, const output_edges_type &outedges, + policyT_ &&policy, const std::string &name, const std::vector &innames, const std::vector &outnames) - : baseT(inedges, outedges, name, innames, outnames), func(std::forward(f)) {} + : baseT(inedges, outedges, name, innames, outnames, std::forward(policy)) + , func(std::forward(f)) {} - template - CallableWrapTT(funcT_ &&f, const std::string &name, const std::vector &innames, + template + CallableWrapTT(funcT_ &&f, policyT_ &&policy, const std::string &name, const std::vector &innames, const std::vector &outnames) - : baseT(name, innames, outnames), func(std::forward(f)) {} + : baseT(name, innames, outnames, std::forward(policy)) + , func(std::forward(f)) {} template std::enable_if_t && !ttg::meta::is_empty_tuple_v && @@ -115,23 +118,23 @@ class CallableWrapTT }; template + typename policyT, typename input_values_tupleT> struct CallableWrapTTUnwrapTypelist; template + typename policyT, typename... input_valuesT> struct CallableWrapTTUnwrapTypelist> { + policyT, std::tuple> { using type = CallableWrapTT...>; + policyT, std::remove_reference_t...>; }; template + typename policyT, typename... input_valuesT> struct CallableWrapTTUnwrapTypelist> { + policyT, ttg::meta::typelist> { using type = CallableWrapTT...>; + policyT, std::remove_reference_t...>; }; // Class to wrap a callable with signature @@ -140,11 +143,11 @@ struct CallableWrapTTUnwrapTypelist&) // template + typename policyT, typename... input_valuesT> class CallableWrapTTArgs : public TT, - ttg::typelist> { + CallableWrapTTArgs, + ttg::typelist, policyT> { using baseT = typename CallableWrapTTArgs::ttT; using input_values_tuple_type = typename baseT::input_values_tuple_type; @@ -206,16 +209,20 @@ class CallableWrapTTArgs } public: - template + template CallableWrapTTArgs(funcT_ &&f, const input_edges_type &inedges, const typename baseT::output_edges_type &outedges, + policyT_&& policy, const std::string &name, const std::vector &innames, const std::vector &outnames) - : baseT(inedges, outedges, name, innames, outnames), func(std::forward(f)) {} + : baseT(inedges, outedges, name, innames, outnames, std::forward(policy)) + , func(std::forward(f)) {} - template - CallableWrapTTArgs(funcT_ &&f, const std::string &name, const std::vector &innames, + template + CallableWrapTTArgs(funcT_ &&f, policyT_&& policy, + const std::string &name, const std::vector &innames, const std::vector &outnames) - : baseT(name, innames, outnames), func(std::forward(f)) {} + : baseT(name, innames, outnames, std::forward(policy)) + , func(std::forward(f)) {} template std::enable_if_t && @@ -252,23 +259,23 @@ class CallableWrapTTArgs }; template + typename policyT, typename input_values_typelistT> struct CallableWrapTTArgsAsTypelist; template -struct CallableWrapTTArgsAsTypelist +struct CallableWrapTTArgsAsTypelist> { - using type = CallableWrapTTArgs...>; }; template + typename policyT, typename... input_valuesT> struct CallableWrapTTArgsAsTypelist> { + policyT, ttg::meta::typelist> { using type = CallableWrapTTArgs...>; + policyT, std::remove_reference_t...>; }; /// @brief Factory function to assist in wrapping a callable with signature @@ -277,11 +284,13 @@ struct CallableWrapTTArgsAsTypelist==true`, the signature /// must be `void(const std::tuple&, std::tuple&)`, /// else `void(const keyT&, const std::tuple&, std::tuple&)` /// @param[in] inedges a tuple of input edges /// @param[in] outedges a tuple of output edges +/// @param[in] policy a policy controlling aspects of the TT's execution /// @param[in] name a string label for the resulting TT /// @param[in] name a string label for the resulting TT /// @param[in] innames string labels for the respective input terminals of the resulting TT @@ -289,9 +298,11 @@ struct CallableWrapTTArgsAsTypelist +template>> auto make_tt_tpl(funcT &&func, const std::tuple...> &inedges, - const std::tuple &outedges, const std::string &name = "wrapper", + const std::tuple &outedges, + policyT&& policy, const std::string &name = "wrapper", const std::vector &innames = std::vector(sizeof...(input_edge_valuesT), "input"), const std::vector &outnames = std::vector(sizeof...(output_edgesT), @@ -342,13 +353,41 @@ auto make_tt_tpl(funcT &&func, const std::tuple; using decayed_input_args_t = ttg::meta::decayed_typelist_t; using wrapT = - typename CallableWrapTTUnwrapTypelist::type; + typename CallableWrapTTUnwrapTypelist::type; static_assert(std::is_same_v>, "ttg::make_tt_tpl(func, inedges, outedges): inedges value types do not match argument types of func"); auto input_edges = detail::edge_base_tuple(inedges); - return std::make_unique(std::forward(func), input_edges, outedges, name, innames, outnames); + return std::make_unique(std::forward(func), inedges, outedges, std::forward(policy), name, innames, outnames); +} + +/// @brief Factory function to assist in wrapping a callable with signature +/// +/// @tparam keyT a task ID type +/// @tparam funcT a callable type +/// @tparam input_edge_valuesT a pack of types of input data +/// @tparam output_edgesT a pack of types of output edges +/// @param[in] func a callable object; if `ttg::meta::is_void_v==true`, the signature +/// must be `void(const std::tuple&, std::tuple&)`, +/// else `void(const keyT&, const std::tuple&, std::tuple&)` +/// @param[in] inedges a tuple of input edges +/// @param[in] outedges a tuple of output edges +/// @param[in] name a string label for the resulting TT +/// @param[in] name a string label for the resulting TT +/// @param[in] innames string labels for the respective input terminals of the resulting TT +/// @param[in] outnames string labels for the respective output terminals of the resulting TT +/// +/// @internal To be able to handle generic callables the input edges are used to determine the trial set of +/// argument types. +template +auto make_tt_tpl(funcT &&func, const std::tuple...> &inedges, + const std::tuple &outedges, const std::string &name = "wrapper", + const std::vector &innames = std::vector( + std::tuple_size...>>::value, "input"), + const std::vector &outnames = + std::vector(std::tuple_size>::value, "output")) { + return make_tt_tpl(std::forward(func), inedges, outedges, ttg::TTPolicyBase(), name, innames, outnames); } /// @brief Factory function to assist in wrapping a callable with signature @@ -357,11 +396,13 @@ auto make_tt_tpl(funcT &&func, const std::tuple==true`, the signature /// must be `void(input_valuesT&&..., std::tuple&)`, /// else `void(const keyT&, input_valuesT&&..., std::tuple&)` /// @param[in] inedges a tuple of input edges /// @param[in] outedges a tuple of output edges +/// @param[in] policy a policy controlling aspects of the TT's execution /// @param[in] name a string label for the resulting TT /// @param[in] name a string label for the resulting TT /// @param[in] innames string labels for the respective input terminals of the resulting TT @@ -369,9 +410,11 @@ auto make_tt_tpl(funcT &&func, const std::tuple +template >> auto make_tt(funcT &&func, const std::tuple...> &inedges, - const std::tuple &outedges, const std::string &name = "wrapper", + const std::tuple &outedges, policyT&& policy, + const std::string &name = "wrapper", const std::vector &innames = std::vector(sizeof...(input_edge_valuesT), "input"), const std::vector &outnames = std::vector(sizeof...(output_edgesT), "output")) { // ensure input types do not contain Void @@ -421,11 +464,39 @@ auto make_tt(funcT &&func, const std::tuple. // 3. full_input_args_t = edge-types with non-void types replaced by input_args_t using full_input_args_t = ttg::meta::replace_nonvoid_t; using wrapT = typename CallableWrapTTArgsAsTypelist::type; + policyT, full_input_args_t>::type; auto input_edges = detail::edge_base_tuple(inedges); - return std::make_unique(std::forward(func), input_edges, outedges, name, innames, outnames); + return std::make_unique(std::forward(func), inedges, outedges, std::forward(policy), name, innames, outnames); +} + +/// @brief Factory function to assist in wrapping a callable with signature +/// +/// @tparam keyT a task ID type +/// @tparam funcT a callable type +/// @tparam input_edge_valuesT a pack of types of input data +/// @tparam output_edgesT a pack of types of output edges +/// @param[in] func a callable object; if `ttg::meta::is_void_v==true`, the signature +/// must be `void(input_valuesT&&..., std::tuple&)`, +/// else `void(const keyT&, input_valuesT&&..., std::tuple&)` +/// @param[in] inedges a tuple of input edges +/// @param[in] outedges a tuple of output edges +/// @param[in] name a string label for the resulting TT +/// @param[in] name a string label for the resulting TT +/// @param[in] innames string labels for the respective input terminals of the resulting TT +/// @param[in] outnames string labels for the respective output terminals of the resulting TT +/// +/// @internal To be able to handle generic callables the input edges are used to determine the trial set of +/// argument types. +template +auto make_tt(funcT &&func, const std::tuple...> &inedges, + const std::tuple &outedges, const std::string &name = "wrapper", + const std::vector &innames = std::vector( + std::tuple_size...>>::value, "input"), + const std::vector &outnames = + std::vector(std::tuple_size>::value, "output")) { + return make_tt(std::forward(func), inedges, outedges, ttg::TTPolicyBase(), name, innames, outnames); } template diff --git a/ttg/ttg/parsec/fwd.h b/ttg/ttg/parsec/fwd.h index d4cf48051..3a372470e 100644 --- a/ttg/ttg/parsec/fwd.h +++ b/ttg/ttg/parsec/fwd.h @@ -3,12 +3,15 @@ #include "ttg/fwd.h" #include "ttg/util/typelist.h" +#include "ttg/policies.h" #include namespace ttg_parsec { - template > + template , + typename policiesT = ttg::TTPolicyBase> class TT; /// \internal the OG name diff --git a/ttg/ttg/parsec/ttg.h b/ttg/ttg/parsec/ttg.h index f62e71a55..d77f7554e 100644 --- a/ttg/ttg/parsec/ttg.h +++ b/ttg/ttg/parsec/ttg.h @@ -750,8 +750,10 @@ namespace ttg_parsec { }; } // namespace detail - template - class TT : public ttg::TTBase, detail::ParsecTTBase { + template + class TT : public ttg::TTBase + , detail::ParsecTTBase + , public ttg::detail::TTPolicyWrapper { private: /// preconditions static_assert(ttg::meta::is_typelist_v, @@ -765,6 +767,10 @@ namespace ttg_parsec { static_assert((ttg::meta::none_has_reference_v), "Input typelist cannot contain reference types"); static_assert(ttg::meta::is_none_Void_v, "ttg::Void is for internal use only, do not use it"); + using PolicyWrapper = typename ttg::detail::TTPolicyWrapper; + static_assert(ttg::detail::is_policy_v, + "The policyT template argument must implement procmap(), priomap(), and inlinemap()"); + parsec_mempool_t mempools; // check for a non-type member named have_cuda_op @@ -857,8 +863,6 @@ namespace ttg_parsec { make_finalize_argstream_fcts(std::make_index_sequence{}); ttg::World world; - ttg::meta::detail::keymap_t keymap; - ttg::meta::detail::keymap_t priomap; // For now use same type for unary/streaming input terminals, and stream reducers assigned at runtime ttg::meta::detail::input_reducers_t input_reducers; //!< Reducers for the input terminals (empty = expect single value) @@ -1073,7 +1077,7 @@ namespace ttg_parsec { for (int k = 0; k < num_keys; ++k) { keyT key; pos = unpack(key, msg->bytes, pos); - assert(keymap(key) == rank); + assert(this->keymap(key) == rank); keylist.push_back(std::move(key)); } // case 1 @@ -1193,11 +1197,11 @@ namespace ttg_parsec { auto rank = world.rank(); keyT key; pos = unpack(key, msg->bytes, pos); - assert(keymap(key) == rank); + assert(this->keymap(key) == rank); finalize_argstream(key); } else { auto rank = world.rank(); - assert(keymap() == rank); + assert(this->keymap() == rank); finalize_argstream(); } } @@ -1212,13 +1216,13 @@ namespace ttg_parsec { auto rank = world.rank(); keyT key; pos = unpack(key, msg->bytes, pos); - assert(keymap(key) == rank); + assert(this->keymap(key) == rank); std::size_t argstream_size; pos = unpack(argstream_size, msg->bytes, pos); set_argstream_size(key, argstream_size); } else { auto rank = world.rank(); - assert(keymap() == rank); + assert(this->keymap() == rank); std::size_t argstream_size; pos = unpack(argstream_size, msg->bytes, pos); set_argstream_size(argstream_size); @@ -1264,11 +1268,11 @@ namespace ttg_parsec { char *taskobj = (char *)parsec_thread_mempool_allocate(mempool); int32_t priority; if constexpr (!keyT_is_Void) { - priority = priomap(key); + priority = this->priomap(key); /* placement-new the task */ newtask = new (taskobj) task_t(key, mempool, &this->self, world_impl.taskpool(), this, priority); } else { - priority = priomap(); + priority = this->priomap(); /* placement-new the task */ newtask = new (taskobj) task_t(mempool, &this->self, world_impl.taskpool(), this, priority); } @@ -1306,7 +1310,7 @@ namespace ttg_parsec { parsec_key_t hk = 0; if constexpr (!keyT_is_Void) { hk = reinterpret_cast(&key); - assert(keymap(key) == world.rank()); + assert(this->keymap(key) == world.rank()); } task_t *task; @@ -1462,9 +1466,9 @@ namespace ttg_parsec { int owner; if constexpr (!ttg::meta::is_void_v) - owner = keymap(key); + owner = this->keymap(key); else - owner = keymap(); + owner = this->keymap(); if (owner == world.rank()) { if constexpr (!ttg::meta::is_void_v) set_arg_local(key, std::forward(value)); @@ -1590,7 +1594,7 @@ namespace ttg_parsec { int rank = world.rank(); bool have_remote = keylist.end() != std::find_if(keylist.begin(), keylist.end(), - [&](const Key &key) { return keymap(key) != rank; }); + [&](const Key &key) { return this->keymap(key) != rank; }); if (have_remote) { std::vector keylist_sorted(keylist.begin(), keylist.end()); @@ -1601,8 +1605,8 @@ namespace ttg_parsec { /* sort the input key list by owner and check whether there are remote keys */ std::sort(keylist_sorted.begin(), keylist_sorted.end(), [&](const Key &a, const Key &b) mutable { - int rank_a = keymap(a); - int rank_b = keymap(b); + int rank_a = this->keymap(a); + int rank_b = this->keymap(b); return rank_a < rank_b; }); @@ -1615,12 +1619,12 @@ namespace ttg_parsec { parsec_taskpool_t *tp = world_impl.taskpool(); for (auto it = keylist_sorted.begin(); it < keylist_sorted.end(); /* increment inline */) { - auto owner = keymap(*it); + auto owner = this->keymap(*it); if (owner == rank) { /* make sure we don't lose local keys */ local_begin = it; local_end = - std::find_if_not(++it, keylist_sorted.end(), [&](const Key &key) { return keymap(key) == rank; }); + std::find_if_not(++it, keylist_sorted.end(), [&](const Key &key) { return this->keymap(key) == rank; }); it = local_end; continue; } @@ -1632,7 +1636,7 @@ namespace ttg_parsec { ++num_keys; pos = pack(*it, msg->bytes, pos); ++it; - } while (it < keylist_sorted.end() && keymap(*it) == owner); + } while (it < keylist_sorted.end() && this->keymap(*it) == owner); msg->tt_id.num_keys = num_keys; /* TODO: use RMA to transfer the value */ @@ -1661,7 +1665,7 @@ namespace ttg_parsec { auto world = ttg_default_execution_context(); int rank = world.rank(); bool have_remote = keylist.end() != std::find_if(keylist.begin(), keylist.end(), - [&](const Key &key) { return keymap(key) != rank; }); + [&](const Key &key) { return this->keymap(key) != rank; }); if (have_remote) { using decvalueT = std::decay_t; @@ -1669,8 +1673,8 @@ namespace ttg_parsec { /* sort the input key list by owner and check whether there are remote keys */ std::vector keylist_sorted(keylist.begin(), keylist.end()); std::sort(keylist_sorted.begin(), keylist_sorted.end(), [&](const Key &a, const Key &b) mutable { - int rank_a = keymap(a); - int rank_b = keymap(b); + int rank_a = this->keymap(a); + int rank_b = this->keymap(b); return rank_a < rank_b; }); @@ -1713,12 +1717,12 @@ namespace ttg_parsec { parsec_taskpool_t *tp = world_impl.taskpool(); for (auto it = keylist_sorted.begin(); it < keylist_sorted.end(); /* increment done inline */) { - auto owner = keymap(*it); + auto owner = this->keymap(*it); if (owner == rank) { local_begin = it; /* find first non-local key */ local_end = - std::find_if_not(++it, keylist_sorted.end(), [&](const Key &key) { return keymap(key) == rank; }); + std::find_if_not(++it, keylist_sorted.end(), [&](const Key &key) { return this->keymap(key) == rank; }); it = local_end; continue; } @@ -1731,7 +1735,7 @@ namespace ttg_parsec { ++num_keys; pos = pack(*it, msg->bytes, pos); ++it; - } while (it < keylist_sorted.end() && keymap(*it) == owner); + } while (it < keylist_sorted.end() && this->keymap(*it) == owner); msg->tt_id.num_keys = num_keys; /* pack the metadata */ @@ -1863,7 +1867,7 @@ namespace ttg_parsec { assert(size > 0 && "TT::set_argstream_size(key,size) called with size=0"); // body - const auto owner = keymap(key); + const auto owner = this->keymap(key); if (owner != world.rank()) { ttg::trace(world.rank(), ":", get_name(), ":", key, " : forwarding stream size for terminal ", i); using msg_t = detail::msg_t; @@ -1913,7 +1917,7 @@ namespace ttg_parsec { assert(size > 0 && "TT::set_argstream_size(key,size) called with size=0"); // body - const auto owner = keymap(); + const auto owner = this->keymap(); if (owner != world.rank()) { ttg::trace(world.rank(), ":", get_name(), " : forwarding stream size for terminal ", i); using msg_t = detail::msg_t; @@ -1960,7 +1964,7 @@ namespace ttg_parsec { assert(std::get(input_reducers) && "TT::finalize_argstream called on nonstreaming input terminal"); // body - const auto owner = keymap(key); + const auto owner = this->keymap(key); if (owner != world.rank()) { ttg::trace(world.rank(), ":", get_name(), " : ", key, ": forwarding stream finalize for terminal ", i); using msg_t = detail::msg_t; @@ -2006,7 +2010,7 @@ namespace ttg_parsec { assert(std::get(input_reducers) && "TT::finalize_argstream called on nonstreaming input terminal"); // body - const auto owner = keymap(); + const auto owner = this->keymap(); if (owner != world.rank()) { ttg::trace(world.rank(), ":", get_name(), ": forwarding stream finalize for terminal ", i); using msg_t = detail::msg_t; @@ -2215,17 +2219,12 @@ namespace ttg_parsec { } public: - template , - typename priomapT = ttg::detail::default_priomap> + template TT(const std::string &name, const std::vector &innames, const std::vector &outnames, - ttg::World world, keymapT &&keymap_ = keymapT(), priomapT &&priomap_ = priomapT()) + ttg::World world, policyT_&& policy) : ttg::TTBase(name, numinedges, numouts) , world(world) - // if using default keymap, rebind to the given world - , keymap(std::is_same>::value - ? decltype(keymap)(ttg::detail::default_keymap(world)) - : decltype(keymap)(std::forward(keymap_))) - , priomap(decltype(keymap)(std::forward(priomap_))) + , PolicyWrapper(world, std::forward(policy)) , static_stream_goal() { // Cannot call these in base constructor since terminals not yet constructed if (innames.size() != numinedges) throw std::logic_error("ttg_parsec::TT: #input names != #input terminals"); @@ -2324,29 +2323,109 @@ namespace ttg_parsec { NULL); } - template , - typename priomapT = ttg::detail::default_priomap> - TT(const std::string &name, const std::vector &innames, const std::vector &outnames, - keymapT &&keymap = keymapT(ttg::default_execution_context()), priomapT &&priomap = priomapT()) - : TT(name, innames, outnames, ttg::default_execution_context(), std::forward(keymap), - std::forward(priomap)) {} - - template , - typename priomapT = ttg::detail::default_priomap> - TT(const input_edges_type &inedges, const output_edges_type &outedges, const std::string &name, - const std::vector &innames, const std::vector &outnames, ttg::World world, - keymapT &&keymap_ = keymapT(), priomapT &&priomap = priomapT()) - : TT(name, innames, outnames, world, std::forward(keymap_), std::forward(priomap)) { + template >>> + TT(const std::string &name, + const std::vector &innames, + const std::vector &outnames, + ttg::World world, + ttg::meta::detail::keymap_t keymap) + : TT(name, innames, outnames, world, policyT(std::move(keymap))) + {} + + TT(const std::string &name, + const std::vector &innames, + const std::vector &outnames, + ttg::World world) + : TT(name, innames, outnames, world, policyT()) + {} + + template , policyT>>> + TT(const std::string &name, + const std::vector &innames, + const std::vector &outnames, + policyT_&& policy) + : TT(name, innames, outnames, ttg::default_execution_context(), std::forward(policy)) + {} + + template >>> + TT(const std::string &name, + const std::vector &innames, + const std::vector &outnames, + ttg::meta::detail::keymap_t keymap) + : TT(name, innames, outnames, ttg::default_execution_context(), + policyT_(std::move(keymap))) + {} + + + template >> + TT(const std::string &name, + const std::vector &innames, + const std::vector &outnames) + : TT(name, innames, outnames, ttg::default_execution_context(), policyT_()) + {} + + template , policyT>>> + TT(const input_edges_type &inedges, + const output_edges_type &outedges, + const std::string &name, + const std::vector &innames, + const std::vector &outnames, + ttg::World world, + policyT_&& policy) + : TT(name, innames, outnames, world, std::forward(policy)) { connect_my_inputs_to_incoming_edge_outputs(std::make_index_sequence{}, inedges); connect_my_outputs_to_outgoing_edge_inputs(std::make_index_sequence{}, outedges); } - template , - typename priomapT = ttg::detail::default_priomap> - TT(const input_edges_type &inedges, const output_edges_type &outedges, const std::string &name, - const std::vector &innames, const std::vector &outnames, - keymapT &&keymap = keymapT(ttg::default_execution_context()), priomapT &&priomap = priomapT()) + + + template >>> + TT(const input_edges_type &inedges, + const output_edges_type &outedges, + const std::string &name, + const std::vector &innames, + const std::vector &outnames, + ttg::World world, + ttg::meta::detail::keymap_t keymap) + : TT(inedges, outedges, name, innames, outnames, world, + policyT_(std::move(keymap))) + { } + + template >> + TT(const input_edges_type &inedges, + const output_edges_type &outedges, + const std::string &name, + const std::vector &innames, + const std::vector &outnames, + ttg::World world) + : TT(inedges, outedges, name, innames, outnames, world, policyT_()) + { } + + template + TT(const input_edges_type &inedges, + const output_edges_type &outedges, + const std::string &name, + const std::vector &innames, + const std::vector &outnames, + policyT_&& policy) : TT(inedges, outedges, name, innames, outnames, ttg::default_execution_context(), - std::forward(keymap), std::forward(priomap)) {} + std::forward(policy)) + {} + + template >> + TT(const input_edges_type &inedges, + const output_edges_type &outedges, + const std::string &name, + const std::vector &innames, + const std::vector &outnames) + : TT(inedges, outedges, name, innames, outnames, ttg::default_execution_context(), policyT_()) + {} // Destructor checks for unexecuted tasks virtual ~TT() { release(); } @@ -2473,27 +2552,6 @@ namespace ttg_parsec { ttg::TTBase::make_executable(); } - /// keymap accessor - /// @return the keymap - const decltype(keymap) &get_keymap() const { return keymap; } - - /// keymap setter - template - void set_keymap(Keymap &&km) { - keymap = km; - } - - /// priority map accessor - /// @return the priority map - const decltype(priomap) &get_priomap() const { return priomap; } - - /// priomap setter - /// @arg pm a function that maps a key to an integral priority value. - template - void set_priomap(Priomap &&pm) { - priomap = pm; - } - // Register the static_op function to associate it to instance_id void register_static_op_function(void) { int rank; @@ -2504,8 +2562,6 @@ namespace ttg_parsec { static_map_mutex.lock(); static_id_to_op_map.insert(std::make_pair(get_instance_id(), call)); if (delayed_unpack_actions.count(get_instance_id()) > 0) { - auto tp = world_impl.taskpool(); - ttg::trace("ttg_parsec(", rank, ") There are ", delayed_unpack_actions.count(get_instance_id()), " messages delayed with op_id ", get_instance_id()); diff --git a/ttg/ttg/policies.h b/ttg/ttg/policies.h new file mode 100644 index 000000000..614ee42b9 --- /dev/null +++ b/ttg/ttg/policies.h @@ -0,0 +1,655 @@ +#ifndef TTG_POLICIES_H +#define TTG_POLICIES_H + +#include "ttg/base/keymap.h" +#include "ttg/util/meta.h" + +namespace ttg { + + namespace detail { + template + struct is_default_keymap : std::false_type + { }; + + template + struct is_default_keymap> : std::true_type + { }; + + template + constexpr const bool is_default_keymap_v = is_default_keymap::value; + + template + struct map_type { + using type = std::decay_t; + }; + + template + struct map_type> { + using type = ttg::meta::detail::keymap_t; + }; + + template + using map_type_t = typename map_type::type; + + /* query the type of the first argument and the return type of a callable */ + template + struct keymap_signature + : keymap_signature::operator())> + {}; + + template + struct keymap_signature { + using arg_t = std::decay_t; + using ret_t = std::decay_t; + }; + + template + struct keymap_signature + : keymap_signature + { }; + + template + struct keymap_signature + : keymap_signature + { }; + + template + struct keymap_signature { + using arg_t = void; + using ret_t = std::decay_t; + }; + + template + struct keymap_signature + : keymap_signature + { }; + + template + struct keymap_signature + : keymap_signature + { }; + + + template + using keymap_arg_t = typename keymap_signature::arg_t; + + template + using keymap_ret_t = typename keymap_signature::ret_t; + + } // namespace detail + + /** + * \brief Base class for task execution policies. + * + * Policies are properties of tasks. Tasks are identified through the key. + * A policy implementation maps a key to an integer value and can be set per TT. + * Supported policies include: + * * Process mapping: maps a key identifying a task to a process to run on. + * * Priority mapping: assigns a priority (positive integer) to a task identified by a key. + * Higher values increase the task's priority. + * * Inline mapping: whether a task can be executed inline, i.e., without dispatching the + * task to a scheduler first. The task will be executed in the send or + * broadcast call. The returned value denotes the maximum recirsion depth, + * i.e., how many tasks may be executed inline consecutively. Zero denotes + * no inlining. This is the default. + * + * The default mapping functions are not inlined and can be set dynamically + * in the TT. By inheriting from \c TTPolicyBase and passing callables to its + * constructor, applications can define policies at compile-time. This may + * yield improved performance since the compiler is potentially able to inline + * the calls. In that case, the dynamic setting of mapping functions in the + * TT will be disabled. + * + * \tparam Key The type of the key used to identify tasks. + * \tparam ProcMap The type of the process map callback. + * \tparam PrioMap The type of the priority map callback. + * \tparam InlineMap The type of the inline map callback. + * + * \sa ttg::make_policy + */ + template, + typename PrioMap = typename ttg::meta::detail::keymap_t, + typename InlineMap = typename ttg::meta::detail::keymap_t> + struct TTPolicyBase { + + using PolicyBaseT = TTPolicyBase; + + using procmap_t = detail::map_type_t; + using priomap_t = detail::map_type_t; + using inlinemap_t = detail::map_type_t; + + using key_type = Key; + + procmap_t procmap; + priomap_t priomap; + inlinemap_t inlinemap; + + template && + std::is_default_constructible_v && + std::is_default_constructible_v>> + TTPolicyBase() + { } + + template && + std::is_default_constructible_v && + std::is_default_constructible_v>> + TTPolicyBase(ProcMap_&& procmap) + : procmap(std::forward(procmap)) + { } + + template>> + TTPolicyBase(ProcMap_&& procmap, PrioMap_&& priomap) + : procmap(std::forward(procmap)) + , priomap(std::forward(priomap)) + { } + + template + TTPolicyBase(ProcMap_&& procmap, + PrioMap_&& priomap, + InlineMap_&& im) + : procmap(std::forward(procmap)) + , priomap(std::forward(priomap)) + , inlinemap(std::forward(im)) + { } + + TTPolicyBase(const PolicyBaseT&) = default; + TTPolicyBase(PolicyBaseT&&) = default; + }; + + + template, + typename PrioMap = typename ttg::detail::default_priomap_impl, + typename InlineMap = typename ttg::detail::default_inlinemap_impl> + struct StaticTTPolicyBase { + + using PolicyBaseT = StaticTTPolicyBase; + + using procmap_t = detail::map_type_t; + using priomap_t = detail::map_type_t; + using inlinemap_t = detail::map_type_t; + + using key_type = Key; + + procmap_t procmap; + priomap_t priomap; + inlinemap_t inlinemap; + + template && + std::is_default_constructible_v && + std::is_default_constructible_v>> + StaticTTPolicyBase() + { } + + template && + std::is_default_constructible_v && + std::is_default_constructible_v>> + StaticTTPolicyBase(ProcMap_&& procmap) + : procmap(std::forward(procmap)) + { } + + template>> + StaticTTPolicyBase(ProcMap_&& procmap, PrioMap_&& priomap) + : procmap(std::forward(procmap)) + , priomap(std::forward(priomap)) + { } + + template + StaticTTPolicyBase(ProcMap_&& procmap, + PrioMap_&& priomap, + InlineMap_&& im) + : procmap(std::forward(procmap)) + , priomap(std::forward(priomap)) + , inlinemap(std::forward(im)) + { } + + StaticTTPolicyBase(const PolicyBaseT&) = default; + StaticTTPolicyBase(PolicyBaseT&&) = default; + }; + + namespace detail { + + /** + * Wrapper around a policy implementation. + * The wrapper provides default implementations for properties that + * are not set at compile-time and not yet set at runtime. + * By using a wrapper object, we can inspect the \c procmap(), \c priomap(), + * and \c inlinemap() of the policy to see whether a compile-time implementation + * of them was provided and gracefull catch attempts at setting + * properties that were provided at compile-time. + * + * TT implementations can inherit from this class to get the necessary + * mapping functions as well as functions to query and set mapping functions. + */ + template + struct TTPolicyWrapper { + private: + TTPolicyImpl m_policy; + + ttg::detail::default_keymap_impl m_default_procmap; + ttg::detail::default_priomap_impl m_default_priomap; + ttg::detail::default_inlinemap_impl m_default_inlinemap; + + static constexpr bool procmap_is_std_function = meta::is_std_function_ptr_v; + static constexpr bool priomap_is_std_function = meta::is_std_function_ptr_v; + static constexpr bool inlinemap_is_std_function = meta::is_std_function_ptr_v; + + public: + + /** + * Construct a wrapper from a provided world (needed for some of the defaults) + * and provided policy implementation. + */ + template + TTPolicyWrapper(WorldT world, ImplT&& impl) + : m_policy(std::forward(impl)) + , m_default_procmap(world.size()) + { } + + /** + * Return a copy of the used policy, with proper defaults. + */ + auto get_policy() { + TTPolicyImpl policy = m_policy; + if constexpr (procmap_is_std_function) { + if (!m_policy.procmap) { + /* create a std::function from the default implementation */ + policy.procmap = m_default_procmap; + } + } + if constexpr (priomap_is_std_function) { + if (!m_policy.priomap) { + /* create a std::function from the default implementation */ + policy.priomap = m_default_priomap; + } + } + if constexpr (inlinemap_is_std_function) { + if (!m_policy.inlinemap) { + /* create a std::function from the default implementation */ + policy.inlinemap = m_default_inlinemap; + } + } + return policy; + } + + /** + * Return a callable for the current process map. + * Returns a std::function object (not a reference) that can be invoked. + */ + inline auto get_procmap() const { + if constexpr (procmap_is_std_function) { + if (!m_policy.procmap) { + /* create a std::function from the default implementation */ + return ttg::meta::detail::keymap_t(m_default_procmap); + } else { + /* return the current std::function */ + return m_policy.procmap; + } + } else { + if constexpr (!meta::is_member_fn_ptr_v) { + /* we can return any callable object directly */ + return m_policy.procmap; + } else { + /** + * wrap the member function in a lambda + * assuming that a policy is a small object, we'll capture it by value + * to avoid lifetime issues + */ + if constexpr (!std::is_void_v) { + return [=](const Key& key){ return m_policy.procmap(key); }; + } else { + return [=](){ return m_policy.procmap(); }; + } + } + } + } + + /** + * Return a callable for the current priority map. + * Returns a std::function object (not a reference) that can be invoked. + */ + inline auto get_priomap() const { + if constexpr (priomap_is_std_function) { + if (!m_policy.priomap) { + /* create a std::function from the default implementation */ + return ttg::meta::detail::keymap_t(m_default_priomap); + } else { + /* return the current std::function */ + return m_policy.priomap; + } + } else { + if constexpr (!meta::is_member_fn_ptr_v) { + /* we can return any callable object directly */ + return m_policy.priomap; + } else { + /** + * wrap the member function in a lambda + * assuming that a policy is a small object, we'll capture it by value + * to avoid lifetime issues + */ + if constexpr (!std::is_void_v) { + return [=](const Key& key){ return m_policy.priomap(key); }; + } else { + return [=](){ return m_policy.priomap(); }; + } + } + } + } + + inline auto get_inlinemap() const { + if constexpr (inlinemap_is_std_function) { + if (!m_policy.inlinemap) { + /* create a std::function from the default implementation */ + return ttg::meta::detail::keymap_t(m_default_inlinemap); + } else { + /* return the current std::function */ + return m_policy.inlinemap; + } + } else { + if constexpr (!meta::is_member_fn_ptr_v) { + /* we can return any callable object directly */ + return m_policy.inlinemap; + } else { + /** + * wrap the member function in a lambda + * assuming that a policy is a small object, we'll capture it by value + * to avoid lifetime issues + */ + if constexpr (!std::is_void_v) { + return [=](const Key& key){ return m_policy.inlinemap(key); }; + } else { + return [=](){ return m_policy.inlinemap(); }; + } + } + } + } + + template>> + inline int procmap(const KeyT& key) const { + if constexpr (procmap_is_std_function) { + if (m_policy.procmap) return m_policy.procmap(key); + else return m_default_procmap(key); + } else { + return m_policy.procmap(key); + } + } + + template>> + inline int procmap() const { + if constexpr (procmap_is_std_function) { + if (m_policy.procmap) return m_policy.procmap(); + else return m_default_procmap(); + } else { + return m_policy.procmap(); + } + } + + /** Deprecated, use procmap instead */ + template>> + inline int keymap(const KeyT& key) const { + return procmap(key); + } + + /** Deprecated, use procmap instead */ + template>> + inline int keymap() const { + return procmap(); + } + + template>> + inline int priomap(const KeyT& key) const { + if constexpr (priomap_is_std_function) { + if (m_policy.priomap) return m_policy.priomap(key); + else return m_default_priomap(key); + } else { + return m_policy.priomap(key); + } + } + + template>> + inline int priomap() const { + if constexpr (priomap_is_std_function) { + if (m_policy.priomap) return m_policy.priomap(); + else return m_default_priomap(); + } else { + return m_policy.priomap(); + } + } + + template>> + inline int inlinemap(const KeyT& key) const { + if constexpr (inlinemap_is_std_function) { + if (m_policy.inlinemap) return m_policy.inlinemap(key); + else return m_default_inlinemap(key); + } else { + return m_policy.inlinemap(key); + } + } + + template>> + inline int inlinemap() const { + if constexpr (inlinemap_is_std_function) { + if (m_policy.inlinemap) return m_policy.inlinemap(); + else return m_default_inlinemap(); + } else { + return m_policy.inlinemap(); + } + } + + template + void set_procmap(ProcMap&& pm) { + static_assert(std::is_assignable_v, + "Cannot set process map on compile-time policy property!"); + m_policy.procmap = std::forward(pm); + } + + template + void set_keymap(KeyMap&& pm) { + set_procmap(std::forward(pm)); + } + + template + void set_priomap(PrioMap&& pm) { + static_assert(std::is_assignable_v, + "Cannot set process map on compile-time policy property!"); + m_policy.priomap = std::forward(pm); + } + + template + void set_inlinemap(InlineMap&& pm) { + static_assert(std::is_assignable_v, + "Cannot set process map on compile-time policy property!"); + m_policy.inlinemap = std::forward(pm); + } + + }; + } // namespace detail + + /** + * Helper function to create a TT policy from arbitrary function objects. + * The order of callables is + * 1) Process map + * 2) Priority map + * 3) Inline map + * + * Any mapping function provided to make_policy cannot be changed at runtime. + * All other mapping functions are defaulted and can be set dynamically. + * + * Example use: + * + * ttg::make_policy( + * // Process map: round robin on field i of key + * [&](const Key& key){ return key.i % world.size(); }, + * // Priority map: use key field p as priority + * [&](const Key& key){ return key.p; }, + * // Inline map: never inline + * [&](const Key& key){ return 0; }); + * + * \sa TTPolicy + */ + template + auto make_policy(Fn&& fn, Fns&& ...fns) + { + using key_t = detail::keymap_arg_t>; + static_assert(std::is_assignable_v>> && + (std::is_assignable_v>>&&...), + "All return types of mapping callables must be assignable to int."); + if constexpr (!std::is_void_v) { + static_assert(std::is_invocable_v>> && + (std::is_invocable_v>> && ...), + "All mapping callables must accept keys as const Key&."); + } + return TTPolicyBase(std::forward(fn), std::forward(fns)...); + } + + /** + * Helper function to create a dynamic TT policy. All mapping functions are + * defaulted and can be set at runtime. + */ + template + auto make_policy() + { + return TTPolicyBase(); + } + + + /** + * Helper function to create a compile-time static TT policy from arbitrary + * function objects. + * The order of callables is + * 1) Process map + * 2) Priority map + * 3) Inline map + * + * All mapping functions which were not provided explicitly will be statically + * defaulted. No mapping function in this policy can be changed at runtime. + * + * Example use: + * + * ttg::make_policy( + * // Process map: round robin on field i of key + * [&](const Key& key){ return key.i % world.size(); }, + * // Priority map: use key field p as priority + * [&](const Key& key){ return key.p; }, + * // Inline map: never inline + * [&](const Key& key){ return 0; }); + * + * \sa TTPolicy + */ + template + auto make_static_policy(Fn&& fn, Fns&& ...fns) + { + using key_t = detail::keymap_arg_t>; + static_assert(std::is_assignable_v>> && + (std::is_assignable_v>>&&...), + "All return types of mapping callables must be assignable to int."); + if constexpr (!std::is_void_v) { + static_assert(std::is_invocable_v>> && + (std::is_invocable_v>> && ...), + "All mapping callables must accept keys as const Key&."); + } + return StaticTTPolicyBase(std::forward(fn), std::forward(fns)...); + } + + /** + * Helper function to create a TT policy with default implementations statically + * compiled. No policy mapping function can be changed at runtime. + */ + template + auto make_static_policy() + { + return StaticTTPolicyBase(); + } + + + namespace detail { + + /** + * Generate traits to check policy objects for procmap(), priomap(), and inlinemap() members + */ +#define TTG_POLICY_CREATE_CHECK_FOR(_Pol) \ + /* specialization that does the checking */ \ + template \ + struct has_##_Pol { \ + private: \ + template \ + static constexpr auto check(T*) \ + -> typename \ + std::is_same< \ + /* policy function take a key and return int */ \ + decltype( std::declval(). _Pol ( std::declval() ) ), \ + int \ + >::type; \ + template \ + static constexpr std::false_type check(...); \ + typedef decltype(check(0)) type; \ + public: \ + static constexpr bool value = type::value; \ + }; \ + template \ + struct has_##_Pol { \ + private: \ + template \ + static constexpr auto check(T*) \ + -> typename \ + std::is_same< \ + /* policy function for void simply return int */ \ + decltype( std::declval(). _Pol ( ) ), \ + int \ + >::type; \ + template \ + static constexpr std::false_type check(...); \ + typedef decltype(check(0)) type; \ + public: \ + static constexpr bool value = type::value; \ + }; \ + template \ + constexpr const bool has_##_Pol ## _v = has_ ## _Pol::value; + + TTG_POLICY_CREATE_CHECK_FOR(procmap); + TTG_POLICY_CREATE_CHECK_FOR(priomap); + TTG_POLICY_CREATE_CHECK_FOR(inlinemap); + + /** Whether PolicyT is a valid policy object using KeyT */ + template + struct is_policy { + static constexpr bool value = has_procmap_v && + has_priomap_v && + has_inlinemap_v; + }; + + /** Whether PolicyT is a valid policy object using KeyT */ + template + constexpr const bool is_policy_v = is_policy::value; + + /* sanity base check */ + static_assert(is_policy_v>); + static_assert(is_policy_v>>); + + static_assert(is_policy_v>); + static_assert(is_policy_v>>); + } // namespace detail + + +} // namespace ttg + +#endif // TTG_POLICIES_H diff --git a/ttg/ttg/reduce.h b/ttg/ttg/reduce.h index 39571cf45..d2e4f565d 100644 --- a/ttg/ttg/reduce.h +++ b/ttg/ttg/reduce.h @@ -91,7 +91,7 @@ namespace ttg { // iterate over keys that map to me ... if keys are equivalent to ranks this can be made simpler const auto my_rank = this->get_world().rank(); for (auto key = 0; key != tree_.size(); ++key) { - if (my_rank == this->get_keymap()(key)) { + if (my_rank == this->procmap(key)) { auto keys = tree_.child_keys(key); if (keys.first == -1) this->template set_arg<1>(key, Value()); if (keys.second == -1) this->template set_arg<2>(key, Value()); diff --git a/ttg/ttg/util/meta.h b/ttg/ttg/util/meta.h index 179bf2dbc..089fe8580 100644 --- a/ttg/ttg/util/meta.h +++ b/ttg/ttg/util/meta.h @@ -558,6 +558,42 @@ namespace ttg { template class TT, class... Args> constexpr bool is_detected_convertible_v = is_detected_convertible::value; + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // traits for pointer to callabels (pointer to std::function, pointer to member function) + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + template + struct is_std_function_ptr : std::false_type + { }; + + /** std::function member pointer */ + template + struct is_std_function_ptr T::*> : std::true_type + { }; + + template + struct is_std_function_ptr*> : std::true_type + { }; + + template + constexpr bool is_std_function_ptr_v = is_std_function_ptr::value; + + template + struct is_member_fn_ptr : std::false_type + { }; + + template + struct is_member_fn_ptr : std::true_type + { }; + + template + struct is_member_fn_ptr : std::true_type + { }; + + template + constexpr bool is_member_fn_ptr_v = is_member_fn_ptr::value; + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // type_printer useful to print types in metaprograms ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////