From efef286cd5d9efa070c1cd59ec02a39b0867841c Mon Sep 17 00:00:00 2001 From: Joseph Schuchart Date: Wed, 23 Aug 2023 15:04:59 -0400 Subject: [PATCH 1/3] Initial implementation of partial TT creation Idea: create a TT from a functor and add inputs, outputs, and names step by step to improve code readability. Signed-off-by: Joseph Schuchart --- tests/unit/tt.cc | 19 +++++++++ ttg/ttg/base/terminal.h | 5 +++ ttg/ttg/base/tt.h | 12 +++++- ttg/ttg/make_tt.h | 82 +++++++++++++++++++++++++++++++++++- ttg/ttg/parsec/ttg.h | 45 ++++++++++++++++++-- ttg/ttg/util/meta/callable.h | 8 ++-- 6 files changed, 161 insertions(+), 10 deletions(-) diff --git a/tests/unit/tt.cc b/tests/unit/tt.cc index f77a483ef9..04b6719cd6 100644 --- a/tests/unit/tt.cc +++ b/tests/unit/tt.cc @@ -437,4 +437,23 @@ TEST_CASE("TemplateTask", "[core]") { static_assert(!ttg::meta::is_generic_callable_v)>); } } + +//#if 0 + SECTION("split_construction") { + ttg::Edge a2b, b2a; + /* construct without edges */ + auto tta = ttg::make_tt([](int key, float value){}); + tta->set_input<0>(b2a) + .set_output<0>(a2b) + .set_name("A"); + + auto ttb = ttg::make_tt([](int key, float value){}); + ttb->set_input<0>(a2b) + .set_output<0>(b2a) + .set_name("B"); + + bool is_executable = ttg::make_graph_executable(tta); + CHECK(is_executable); + } +//#endif // 0 } diff --git a/ttg/ttg/base/terminal.h b/ttg/ttg/base/terminal.h index eacd441ac6..5191908380 100644 --- a/ttg/ttg/base/terminal.h +++ b/ttg/ttg/base/terminal.h @@ -88,6 +88,11 @@ namespace ttg { return name; } + TerminalBase& set_name(const std::string& name) { + this->name = name; + return *this; + } + /// Returns string representation of key type const std::string &get_key_type_str() const { if (!tt) throw "ttg::TerminalBase:get_key_type_str() but tt is null"; diff --git a/ttg/ttg/base/tt.h b/ttg/ttg/base/tt.h index e59db15e67..95b410bd35 100644 --- a/ttg/ttg/base/tt.h +++ b/ttg/ttg/base/tt.h @@ -60,6 +60,13 @@ namespace ttg { outputs[i] = t; } + void set_output_dynamic(size_t i, TerminalBase *t) { + if (i >= outputs.size()) { + outputs.resize(i+1); + } + outputs[i] = t; + } + template void register_terminal(terminalT &term, const std::string &name, const setfuncT setfunc) { term.set(this, i, name, detail::demangled_type_name(), @@ -211,7 +218,10 @@ namespace ttg { } /// Sets the name of this operation - void set_name(const std::string &name) { this->name = name; } + TTBase& set_name(const std::string &name) { + this->name = name; + return *this; + } /// Gets the name of this operation const std::string &get_name() const { return name; } diff --git a/ttg/ttg/make_tt.h b/ttg/ttg/make_tt.h index eabd3c34e0..b0ee4a8bcb 100644 --- a/ttg/ttg/make_tt.h +++ b/ttg/ttg/make_tt.h @@ -374,6 +374,21 @@ auto make_tt_tpl(funcT &&func, const std::tuple(std::forward(func), inedges, outedges, name, innames, outnames); } +namespace detail { + template + struct key_type_from_func + { + static_assert(!ttg::meta::is_generic_callable_v); + using func_args_t = decltype(ttg::meta::compute_arg_binding_types_r(std::declval(), ttg::typelist<>{})); + /* select the first argument as key type or Void if there are none */ + using type = std::conditional_t<0 < std::tuple_size_v, std::tuple_element_t<0, func_args_t>, ttg::Void>; + }; + + template + using key_type_from_func_t = typename key_type_from_func::type; + +} // namespace detail + // clang-format off /// @brief Factory function to assist in wrapping a callable with signature /// @@ -421,9 +436,70 @@ auto make_tt_tpl(funcT &&func, const std::tuple +auto make_tt(funcT &&func) { + + using key_type = std::conditional_t, detail::key_type_from_func_t, keyT>; + static_assert(!ttg::meta::is_Void_v, + "Failed to determine key type (no arguments given and no key type provided?)"); + + constexpr auto void_key = ttg::meta::is_void_v; + + // gross argument typelist for invoking func, can include void for optional args + constexpr static auto func_is_generic = ttg::meta::is_generic_callable_v; + + static_assert(!func_is_generic, "Generic functions not supported without edges specified!"); + + using gross_func_args_t = decltype(ttg::meta::compute_arg_binding_types_r(func, ttg::typelist<>{})); + + // ensure input types do not contain Void + static_assert(ttg::meta::is_none_Void_v, "ttg::Void is for internal use only, do not use it"); + + // net argument typelist + using func_args_t = gross_func_args_t; + constexpr auto num_args = std::tuple_size_v; + + // if given task id, make sure it's passed via const lvalue ref + constexpr bool TASK_ID_PASSED_AS_CONST_LVALUE_REF = + !void_key ? ttg::meta::probe_first_v : true; + constexpr bool TASK_ID_PASSED_AS_NONREF = + !void_key ? !ttg::meta::probe_first_v : true; + static_assert( + TASK_ID_PASSED_AS_CONST_LVALUE_REF || TASK_ID_PASSED_AS_NONREF, + "ttg::make_tt(func, ...): if given to func, the task id must be passed by const lvalue ref or by value"); + + // if given out-terminal tuple, make sure it's passed via nonconst lvalue ref + constexpr bool have_outterm_tuple = + func_is_generic ? !ttg::meta::is_last_void_v + : ttg::meta::probe_last_v; + constexpr bool OUTTERM_TUPLE_PASSED_AS_NONCONST_LVALUE_REF = + have_outterm_tuple ? ttg::meta::probe_last_v : true; + static_assert( + OUTTERM_TUPLE_PASSED_AS_NONCONST_LVALUE_REF, + "ttg::make_tt(func, ...): if given to func, the output terminal tuple must be passed by nonconst lvalue ref"); + + // input_args_t = {input_valuesT&&...} + using input_args_t = typename ttg::meta::take_first_n< + typename ttg::meta::drop_first_n::type, + std::tuple_size_v - (void_key ? 0 : 1) - (have_outterm_tuple ? 1 : 0)>::type; + constexpr auto NO_ARGUMENTS_PASSED_AS_NONCONST_LVALUE_REF = + !ttg::meta::is_any_nonconst_lvalue_reference_v; + static_assert( + NO_ARGUMENTS_PASSED_AS_NONCONST_LVALUE_REF, + "ttg::make_tt(func, inedges, outedges): one or more arguments to func can only be passed by nonconst lvalue " + "ref; this is illegal, should only pass arguments as const lvalue ref or (nonconst) rvalue ref"); + /* using empty output terminals */ + using output_terminals_type = std::tuple<>; + using wrapT = typename CallableWrapTTArgsAsTypelist::type; + std::size_t num_value_args = have_outterm_tuple ? num_args - 1 : num_args; + const std::vector &innames = std::vector(num_value_args, "input"); + return std::make_unique(std::forward(func), "DynamicTT", innames, std::vector{}); +} + template -auto make_tt(funcT &&func, const std::tuple...> &inedges = std::tuple<>{}, - const std::tuple &outedges = std::tuple<>{}, const std::string &name = "wrapper", +auto make_tt(funcT &&func, const std::tuple...> &inedges, + const std::tuple &outedges, 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 @@ -447,6 +523,7 @@ auto make_tt(funcT &&func, const std::tuple. // gross argument typelist for invoking func, can include void for optional args constexpr static auto func_is_generic = ttg::meta::is_generic_callable_v; + using gross_func_args_t = decltype(ttg::meta::compute_arg_binding_types_r(func, candidate_func_args_t{})); constexpr auto DETECTED_HOW_TO_INVOKE_GENERIC_FUNC = func_is_generic ? !std::is_same_v> : true; @@ -499,6 +576,7 @@ auto make_tt(funcT &&func, const std::tuple. return std::make_unique(std::forward(func), inedges, outedges, name, innames, outnames); } + template [[deprecated("use make_tt_tpl instead")]] inline auto wrapt( funcT &&func, const std::tuple...> &inedges, diff --git a/ttg/ttg/parsec/ttg.h b/ttg/ttg/parsec/ttg.h index b88874b0e7..42b60477a5 100644 --- a/ttg/ttg/parsec/ttg.h +++ b/ttg/ttg/parsec/ttg.h @@ -1156,6 +1156,8 @@ namespace ttg_parsec { input_terminals_type input_terminals; output_terminalsT output_terminals; + std::vector> dynamic_outterms; // used for split construction TTs + protected: const auto &get_output_terminals() const { return output_terminals; } @@ -3016,7 +3018,8 @@ namespace ttg_parsec { } // Returns reference to output terminal for purpose of connection --- terminal - // cannot be copied, moved or assigned + // cannot be copied, moved or assigned. + // Only valid if terminals are provided during construction. template std::tuple_element_t *out() { return &std::get(output_terminals); @@ -3105,8 +3108,9 @@ namespace ttg_parsec { /// keymap setter template - void set_keymap(Keymap &&km) { + auto& set_keymap(Keymap &&km) { keymap = km; + return *this; } /// priority map accessor @@ -3116,8 +3120,43 @@ namespace ttg_parsec { /// priomap setter /// @arg pm a function that maps a key to an integral priority value. template - void set_priomap(Priomap &&pm) { + auto& set_priomap(Priomap &&pm) { priomap = std::forward(pm); + return *this; + } + + template + auto& set_input(ttg::Edge>& edge, + const std::string name = {}) { + + edge.set_out(&std::get(input_terminals)); + if (!name.empty()) { + std::get(input_terminals).set_name(name); + } + return *this; + } + + template + auto& set_output(ttg::Edge& edge, + const std::string name = {}) { + { + /* check whether the terminal is already set */ + auto& outputs = this->get_outputs(); + if (outputs.size() > i && nullptr != outputs[i]) { + throw(this->get_name() + ": output terminal " + std::to_string(i) + " already set"); + } + } + std::string auto_name; + if (name.empty()) { + auto_name = "DynamicOut" + std::to_string(i); + } + //std::get(input_terminals).set_name(name); + using term_t = typename ttg::Edge::output_terminal_type; + term_t *term = new term_t(); + dynamic_outterms.push_back(std::unique_ptr(term)); + register_terminal(*term, name.empty() ? auto_name : name, &ttT::set_output_dynamic); + edge.set_in(term); + return *this; } // Register the static_op function to associate it to instance_id diff --git a/ttg/ttg/util/meta/callable.h b/ttg/ttg/util/meta/callable.h index 7cdafd445e..8c0aeed3f4 100644 --- a/ttg/ttg/util/meta/callable.h +++ b/ttg/ttg/util/meta/callable.h @@ -58,7 +58,7 @@ namespace ttg::meta { } template - auto compute_arg_binding_types_impl(Func& func, typelist argument_type_lists, + auto compute_arg_binding_types_impl(Func&& func, typelist argument_type_lists, std::index_sequence arg_idx = {}) { using arg_typelists_t = typelist; constexpr auto Order = sizeof...(Typelists); @@ -80,7 +80,7 @@ namespace ttg::meta { } template - auto compute_arg_binding_types_r_impl(Func& func, typelist argument_type_lists, + auto compute_arg_binding_types_r_impl(Func&& func, typelist argument_type_lists, std::index_sequence arg_idx = {}) { using arg_typelists_t = typelist; constexpr auto Order = sizeof...(Typelists); @@ -110,7 +110,7 @@ namespace ttg::meta { /// - the first invocable combination of argument types discovered by row-major iteration, if @p func is a /// generic callable template - auto compute_arg_binding_types(Func& func, typelist argument_type_lists) { + auto compute_arg_binding_types(Func&& func, typelist argument_type_lists) { constexpr auto is_generic__args = callable_args; constexpr bool is_generic = is_generic__args.first; if constexpr (is_generic) { @@ -131,7 +131,7 @@ namespace ttg::meta { /// - the first invocable combination of argument types discovered by row-major iteration, if @p func is a /// generic callable template - auto compute_arg_binding_types_r(Func& func, typelist argument_type_lists) { + auto compute_arg_binding_types_r(Func&& func, typelist argument_type_lists) { constexpr auto is_generic__args = callable_args; constexpr bool is_generic = is_generic__args.first; if constexpr (is_generic) { From 11468108574f25bff1d824252f53e93af03f455a Mon Sep 17 00:00:00 2001 From: Joseph Schuchart Date: Wed, 15 Nov 2023 16:26:37 -0700 Subject: [PATCH 2/3] Bump PaRSEC version for refactored GPU backend Signed-off-by: Joseph Schuchart --- cmake/modules/ExternalDependenciesVersions.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/modules/ExternalDependenciesVersions.cmake b/cmake/modules/ExternalDependenciesVersions.cmake index b0c10abf04..8ccd88e377 100644 --- a/cmake/modules/ExternalDependenciesVersions.cmake +++ b/cmake/modules/ExternalDependenciesVersions.cmake @@ -6,5 +6,5 @@ set(TTG_TRACKED_BOOST_VERSION 1.66) set(TTG_TRACKED_CATCH2_VERSION 2.13.1) set(TTG_TRACKED_CEREAL_VERSION 1.3.0) set(TTG_TRACKED_MADNESS_TAG 31b2470ca722a6a2d84d4de08d32fb72ae8fdeda) -set(TTG_TRACKED_PARSEC_TAG 9fc74b6f165605a133125d8a5b62cf55642c1907) +set(TTG_TRACKED_PARSEC_TAG 6a5a986d6ec1f9cace09f3ad475c553632077a24) set(TTG_TRACKED_BTAS_TAG d73153ad9bc41a177e441ef04eceff7fab0c766d) From c2f88367183e28dba62944ebf390acc1c0c5007d Mon Sep 17 00:00:00 2001 From: Joseph Schuchart Date: Mon, 27 Nov 2023 18:36:39 -0500 Subject: [PATCH 3/3] Enable the use of connect for dynamic producer/consumer Signed-off-by: Joseph Schuchart --- .../ExternalDependenciesVersions.cmake | 2 +- cmake/modules/FindOrFetchPARSEC.cmake | 2 +- tests/unit/tt.cc | 16 ++++++- ttg/ttg/func.h | 20 +++++++-- ttg/ttg/parsec/ttg.h | 42 ++++++++++++++++++- 5 files changed, 73 insertions(+), 9 deletions(-) diff --git a/cmake/modules/ExternalDependenciesVersions.cmake b/cmake/modules/ExternalDependenciesVersions.cmake index 8ccd88e377..e36fd7aead 100644 --- a/cmake/modules/ExternalDependenciesVersions.cmake +++ b/cmake/modules/ExternalDependenciesVersions.cmake @@ -6,5 +6,5 @@ set(TTG_TRACKED_BOOST_VERSION 1.66) set(TTG_TRACKED_CATCH2_VERSION 2.13.1) set(TTG_TRACKED_CEREAL_VERSION 1.3.0) set(TTG_TRACKED_MADNESS_TAG 31b2470ca722a6a2d84d4de08d32fb72ae8fdeda) -set(TTG_TRACKED_PARSEC_TAG 6a5a986d6ec1f9cace09f3ad475c553632077a24) +set(TTG_TRACKED_PARSEC_TAG 8690b1eb6080500af1ad32ffcd4b95aa38f65c22) set(TTG_TRACKED_BTAS_TAG d73153ad9bc41a177e441ef04eceff7fab0c766d) diff --git a/cmake/modules/FindOrFetchPARSEC.cmake b/cmake/modules/FindOrFetchPARSEC.cmake index 82d6ac0d9d..8df410039a 100644 --- a/cmake/modules/FindOrFetchPARSEC.cmake +++ b/cmake/modules/FindOrFetchPARSEC.cmake @@ -16,7 +16,7 @@ if (NOT TARGET PaRSEC::parsec) FetchContent_Declare( PARSEC - GIT_REPOSITORY https://github.com/ICLDisco/parsec.git + GIT_REPOSITORY https://github.com/devreal/parsec-1.git GIT_TAG ${TTG_TRACKED_PARSEC_TAG} ) FetchContent_MakeAvailable(PARSEC) diff --git a/tests/unit/tt.cc b/tests/unit/tt.cc index 04b6719cd6..2ee7d76125 100644 --- a/tests/unit/tt.cc +++ b/tests/unit/tt.cc @@ -438,7 +438,7 @@ TEST_CASE("TemplateTask", "[core]") { } } -//#if 0 +#if 0 SECTION("split_construction") { ttg::Edge a2b, b2a; /* construct without edges */ @@ -455,5 +455,17 @@ TEST_CASE("TemplateTask", "[core]") { bool is_executable = ttg::make_graph_executable(tta); CHECK(is_executable); } -//#endif // 0 +#endif // 0 + + + SECTION("make_connect") { + ttg::Edge a2b, b2a; + /* construct without edges */ + auto tta = ttg::make_tt([](int key, float value){}); + auto ttb = ttg::make_tt([](int key, float value){}); + ttg::connect<0, 0>(ttb, tta); // B -> A + ttg::connect<0, 0>(tta, ttb); // A -> B + bool is_executable = ttg::make_graph_executable(tta); + CHECK(is_executable); + } } diff --git a/ttg/ttg/func.h b/ttg/ttg/func.h index e383ebbc8f..cbcfe386fa 100644 --- a/ttg/ttg/func.h +++ b/ttg/ttg/func.h @@ -98,11 +98,25 @@ namespace ttg { inline void connect(ttg::TerminalBase *out, ttg::TerminalBase *in) { out->connect(in); } /// \brief Connect producer output terminal outindex to consumer input terminal inindex (via unique or otherwise - /// wrapped pointers to TTs) \tparam outindex The index of the output terminal on the producer. \tparam inindex The - /// index of the input terminal on the consumer. \param p The producer TT \param c The consumer TT + /// wrapped pointers to TTs) + /// \tparam outindex The index of the output terminal on the producer. + /// \tparam inindex The index of the input terminal on the consumer. + /// \param p The producer TT + /// \param c The consumer TT template inline void connect(producer_tt_ptr &p, successor_tt_ptr &s) { - connect(p->template out(), s->template in()); + // check whether the output terminal is available at compile time + using producer_type = typename std::pointer_traits::element_type; + if constexpr (std::tuple_size_v == 0) { + // need to make sure the output terminal is available + using successor_type = typename std::pointer_traits::element_type; + using input_terminal_type = std::tuple_element_t; + using key_type = typename input_terminal_type::key_type; + using value_type = typename input_terminal_type::value_type; + connect(p->template out(), s->template in()); + } else { + connect(p->template out(), s->template in()); + } } /// \brief Connect producer output terminal outindex to consumer input terminal inindex (via bare pointers to TTs) diff --git a/ttg/ttg/parsec/ttg.h b/ttg/ttg/parsec/ttg.h index 42b60477a5..c7ae54dba3 100644 --- a/ttg/ttg/parsec/ttg.h +++ b/ttg/ttg/parsec/ttg.h @@ -2791,7 +2791,7 @@ namespace ttg_parsec { , priomap(decltype(keymap)(std::forward(priomap_))) , 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"); + //if (innames.size() != numinedges) throw std::logic_error("ttg_parsec::TT: #input names != #input terminals"); if (outnames.size() != numouts) throw std::logic_error("ttg_parsec::TT: #output names != #output terminals"); auto &world_impl = world.impl(); @@ -3021,10 +3021,48 @@ namespace ttg_parsec { // cannot be copied, moved or assigned. // Only valid if terminals are provided during construction. template - std::tuple_element_t *out() { + auto *out() { + static_assert(std::tuple_size_v > 0, + "TT::out() only available for statically known output terminals. " + "Use TT::out() instead!"); return &std::get(output_terminals); } + template + auto *out(const std::string name = {}) { + using terminal_type = typename ttg::Out; + if constexpr (std::tuple_size_v == 0) { + auto& outputs = this->get_outputs(); + terminal_type *terminal; + if (outputs.size() <= i || nullptr == outputs[i]) { + // create a new terminal + std::string auto_name; + if (name.empty()) { + auto_name = "DynamicOut" + std::to_string(i); + } + terminal = new terminal_type(); + dynamic_outterms.push_back(std::unique_ptr(terminal)); + register_terminal(*terminal, name.empty() ? auto_name : name, &ttT::set_output_dynamic); + } else { +#ifndef NDEBUG + // use dyanmic cast in debug builds + terminal = dynamic_cast(outputs[i]); + if (nullptr == terminal) { + throw std::runtime_error(std::string("TT::out(): failed to cast existing output terminal ") + std::to_string(i)); + } +#else + terminal = static_cast(outputs[i]); +#endif // NDEBUG + } + return terminal; + } else { + static_assert(std::is_same_v>, + "TT::out() key/value types do not match compile-time output terminals."); + return &std::get(output_terminals); + } + } + + // Manual injection of a task with all input arguments specified as a tuple template std::enable_if_t && !ttg::meta::is_empty_tuple_v, void> invoke(