21 template <
typename... Args>
22 struct count_num_tasks;
24 template <
typename Fn>
25 struct count_num_tasks<Fn> {
26 static constexpr
int value = 1;
29 template <
typename Fn1,
typename Fn2,
typename... Rest>
30 struct count_num_tasks<Fn1, Fn2, Rest...> {
31 static constexpr
int value = 1 + count_num_tasks<Fn2, Rest...>::value;
34 template <
typename Fn,
typename... Args>
35 struct count_num_tasks<Fn, std::tuple<Args...>> {
36 static constexpr
int value = 1;
39 template <
typename Fn,
typename... Args,
typename... Rest>
40 struct count_num_tasks<Fn, std::tuple<Args...>, Rest...> {
41 static constexpr
int value = 1 + count_num_tasks<Rest...>::value;
44 static_assert(count_num_tasks<
void (*)()>::value == 1);
45 static_assert(count_num_tasks<
void (*)(
int), std::tuple<int>>::value == 1);
46 static_assert(count_num_tasks<
void (*)(),
int (*)()>::value == 2);
47 static_assert(count_num_tasks<
void (*)(
int,
int), std::tuple<int, int>,
void (*)()>::value == 2);
48 static_assert(count_num_tasks<
void (*)(
int), std::tuple<int>,
int (*)(),
void (*)()>::value == 3);
50 template <
typename Fn,
typename W>
51 constexpr
inline W get_total_work(
const Fn&, workhint<W> wh) {
55 template <
typename Fn,
typename... Args,
typename W>
56 constexpr
inline W get_total_work(
const Fn&,
const std::tuple<Args...>&, workhint<W> wh) {
60 template <
typename Fn,
typename W,
typename... Rest>
61 constexpr
inline W get_total_work(
const Fn&, workhint<W> wh,
const Rest&... rest) {
62 return wh.value + get_total_work(rest...);
65 template <
typename Fn,
typename... Args,
typename W,
typename... Rest>
66 constexpr
inline W get_total_work(
const Fn&,
const std::tuple<Args...>&, workhint<W> wh,
const Rest&... rest) {
67 return wh.value + get_total_work(rest...);
70 template <
typename ReleaseHandler>
71 struct parallel_invoke_state {
73 parallel_invoke_state(ReleaseHandler rh) : rh_(rh) {}
75 bool all_serialized()
const {
return all_serialized_; }
77 inline auto parallel_invoke_aux() {
78 return std::make_tuple();
81 template <
typename Fn>
82 inline auto parallel_invoke_aux(Fn&& fn) {
84 return parallel_invoke_with_args(std::forward<Fn>(fn), std::make_tuple());
87 template <
typename Fn1,
typename Fn2,
typename... Rest>
88 inline auto parallel_invoke_aux(Fn1&& fn1, Fn2&& fn2, Rest&&... rest) {
90 return parallel_invoke_with_args(std::forward<Fn1>(fn1), std::make_tuple(),
91 std::forward<Fn2>(fn2), std::forward<Rest>(rest)...);
94 template <
typename Fn,
typename... Args,
typename... Rest>
95 inline auto parallel_invoke_aux(Fn&& fn,
const std::tuple<Args...>&
args, Rest&&... rest) {
97 return parallel_invoke_with_args(std::forward<Fn>(fn),
args, std::forward<Rest>(rest)...);
100 template <
typename Fn,
typename... Args,
typename... Rest>
101 inline auto parallel_invoke_aux(Fn&& fn, std::tuple<Args...>&&
args, Rest&&... rest) {
102 return parallel_invoke_with_args(std::forward<Fn>(fn),
std::move(
args), std::forward<Rest>(rest)...);
107 template <
typename Fn,
typename W,
typename... Rest>
108 inline auto parallel_invoke_aux(Fn&& fn, workhint<W> wh, Rest&&... rest) {
109 return parallel_invoke_with_args(std::forward<Fn>(fn), std::make_tuple(), wh, std::forward<Rest>(rest)...);
112 template <
typename Fn,
typename... Args,
typename W,
typename... Rest>
113 inline auto parallel_invoke_aux(Fn&& fn,
const std::tuple<Args...>&
args, workhint<W> wh, Rest&&... rest) {
114 return parallel_invoke_with_args(std::forward<Fn>(fn),
args, wh, std::forward<Rest>(rest)...);
117 template <
typename Fn,
typename... Args,
typename W,
typename... Rest>
118 inline auto parallel_invoke_aux(Fn&& fn, std::tuple<Args...>&&
args, workhint<W> wh, Rest&&... rest) {
119 return parallel_invoke_with_args(std::forward<Fn>(fn),
std::move(
args), wh, std::forward<Rest>(rest)...);
123 template <
typename Fn,
typename ArgsTuple>
124 inline auto parallel_invoke_with_args(Fn&& fn, ArgsTuple&& args_tuple) {
125 return do_parallel_invoke(std::forward<Fn>(fn), std::forward<ArgsTuple>(args_tuple));
128 template <
typename Fn,
typename ArgsTuple,
typename... Rest>
129 inline auto parallel_invoke_with_args(Fn&& fn, ArgsTuple&& args_tuple, Rest&&... rest) {
130 constexpr
int n_rest_tasks = count_num_tasks<Rest...>::value;
131 static_assert(n_rest_tasks > 0);
132 return do_parallel_invoke(std::forward<Fn>(fn), std::forward<ArgsTuple>(args_tuple),
133 ito::workhint(1, n_rest_tasks), std::forward<Rest>(rest)...);
136 template <
typename Fn,
typename ArgsTuple,
typename W>
137 inline auto parallel_invoke_with_args(Fn&& fn, ArgsTuple&& args_tuple, workhint<W>) {
138 return do_parallel_invoke(std::forward<Fn>(fn), std::forward<ArgsTuple>(args_tuple));
141 template <
typename Fn,
typename ArgsTuple,
typename W,
typename... Rest>
142 inline auto parallel_invoke_with_args(Fn&& fn, ArgsTuple&& args_tuple, workhint<W> wh, Rest&&... rest) {
143 W wh_rest = get_total_work(rest...);
144 return do_parallel_invoke(std::forward<Fn>(fn), std::forward<ArgsTuple>(args_tuple),
145 ito::workhint(wh.value, wh_rest), std::forward<Rest>(rest)...);
148 template <
typename Fn,
typename ArgsTuple>
149 inline auto do_parallel_invoke(Fn&& fn, ArgsTuple&& args_tuple) {
150 using retval_t = std::invoke_result_t<decltype(std::apply<Fn, ArgsTuple>), Fn, ArgsTuple>;
152 if constexpr (std::is_void_v<retval_t>) {
153 std::apply(std::forward<Fn>(fn), std::forward<ArgsTuple>(args_tuple));
154 return std::make_tuple(std::monostate{});
157 auto&& ret = std::apply(std::forward<Fn>(fn), std::forward<ArgsTuple>(args_tuple));
158 return std::make_tuple(std::forward<decltype(ret)>(ret));
162 template <
typename Fn,
typename ArgsTuple,
typename W,
typename... Rest>
163 inline auto do_parallel_invoke(Fn&& fn, ArgsTuple&& args_tuple, ito::workhint<W> iwh, Rest&&... rest) {
164 using retval_t = std::invoke_result_t<decltype(std::apply<Fn, ArgsTuple>), Fn, ArgsTuple>;
173 ito::thread<retval_t> th(
175 [fn = std::forward<Fn>(fn),
176 args_tuple = std::forward<ArgsTuple>(args_tuple)]()
mutable {
177 return std::apply(std::forward<decltype(fn)>(fn),
178 std::forward<decltype(args_tuple)>(args_tuple));
180 all_serialized_ &= th.serialized();
182 auto&& ret_rest = parallel_invoke_aux(std::forward<Rest>(rest)...);
184 if constexpr (std::is_void_v<retval_t>) {
185 if (!th.serialized()) {
190 return std::tuple_cat(std::make_tuple(std::monostate{}),
194 if (!th.serialized()) {
198 auto&& ret = th.join();
199 return std::tuple_cat(std::make_tuple(std::forward<decltype(ret)>(ret)),
205 bool all_serialized_ =
true;
237 template <
typename... Args>
244 internal::parallel_invoke_state s(rh);
245 auto&& ret = s.parallel_invoke_aux(std::forward<Args>(
args)...);
252 if (!s.all_serialized()) {
258 ITYR_TEST_CASE(
"[ityr::pattern::parallel_invoke] parallel invoke") {
287 [](
int i) {
return i ; }, std::make_tuple(1),
288 [](
int i) {
return i * 2; }, std::make_tuple(2)
296 [](
int i,
int j ) {
return i + j ; }, std::make_tuple(1, 2),
297 [](
int i,
int j,
int k) {
return i + j + k; }, std::make_tuple(3, 4, 5)
307 []() {
return 1; }, workhint(1),
308 []() {
return 2; }, workhint(1)
316 [](
int i) {
return i ; }, std::make_tuple(1), workhint(1.0),
317 [](
int i) {
return i * 2; }, std::make_tuple(2), workhint(2.0)
325 []( ) {
return 1; }, workhint(1),
326 [](
int i,
int j) {
return i + j + 1; }, std::make_tuple(2, 4), workhint(2),
327 [](
int i ) {
return i * 2; }, std::make_tuple(2) , workhint(3)
344 ITYR_CHECK(ret == std::make_tuple(std::monostate{}));
349 ITYR_CHECK(ret == std::make_tuple(std::monostate{}, std::monostate{}));
354 ITYR_CHECK(ret == std::make_tuple(std::monostate{}, std::monostate{}, 1));
359 ITYR_CHECK(ret == std::make_tuple(1, std::monostate{}, std::monostate{}));
363 auto ret =
parallel_invoke([](
int){}, std::make_tuple(1), []{
return 1; }, []{});
364 ITYR_CHECK(ret == std::make_tuple(std::monostate{}, 1, std::monostate{}));
#define ITYR_SUBCASE(name)
Definition: util.hpp:41
#define ITYR_CHECK(cond)
Definition: util.hpp:48
va_list args
Definition: util.hpp:76
void fini()
Definition: ito.hpp:45
auto root_exec(Fn &&fn, Args &&... args)
Definition: ito.hpp:50
void task_group_begin(task_group_data *tgdata)
Definition: ito.hpp:105
void init(MPI_Comm comm=MPI_COMM_WORLD)
Definition: ito.hpp:41
void poll(PreSuspendCallback &&pre_suspend_cb, PostSuspendCallback &&post_suspend_cb)
Definition: ito.hpp:96
constexpr with_callback_t with_callback
Definition: thread.hpp:11
void task_group_end(PreSuspendCallback &&pre_suspend_cb, PostSuspendCallback &&post_suspend_cb)
Definition: ito.hpp:112
scheduler::task_group_data task_group_data
Definition: ito.hpp:103
void fini()
Definition: ori.hpp:49
void init(MPI_Comm comm=MPI_COMM_WORLD)
Definition: ori.hpp:45
auto release_lazy()
Definition: ori.hpp:200
core::instance::instance_type::release_handler release_handler
Definition: ori.hpp:204
void poll()
Definition: ori.hpp:224
void release()
Definition: ori.hpp:196
void acquire()
Definition: ori.hpp:206
Definition: allocator.hpp:16
auto parallel_invoke(Args &&... args)
Fork parallel tasks and join them.
Definition: parallel_invoke.hpp:238
ForwardIteratorD move(const ExecutionPolicy &policy, ForwardIterator1 first1, ForwardIterator1 last1, ForwardIteratorD first_d)
Move a range to another.
Definition: parallel_loop.hpp:934
Definition: parallel_invoke.hpp:14
W value
Definition: parallel_invoke.hpp:16
constexpr workhint(W w)
Definition: parallel_invoke.hpp:15