kokkos-execution 0.0.1
Loading...
Searching...
No Matches
when_all.hpp
Go to the documentation of this file.
1#ifndef KOKKOS_EXECUTION_GRAPH_WHEN_ALL_HPP
2#define KOKKOS_EXECUTION_GRAPH_WHEN_ALL_HPP
3
5
6#if defined(KOKKOS_EXECUTION_ENABLE_DEBUG_LOGGING)
7# include "plog/Log.h"
8#endif
9
10#include "Kokkos_Macros.hpp"
11#ifdef KOKKOS_ENABLE_EXPERIMENTAL_CXX20_MODULES
12import kokkos.core;
13import kokkos.graph;
14#else
15# include "Kokkos_Core.hpp"
16# include "Kokkos_Graph.hpp"
17#endif
18
20
34
36
43template <Kokkos::ExecutionSpace Exec, stdexec::receiver Rcvr, stdexec::sender... Sndrs>
45 : public Impl::Immovable
46 , public OpStateBase<Exec, Rcvr> {
47 using operation_state_concept = stdexec::operation_state_tag;
48
50 using execution_space = Exec;
51
53
55 struct WhenAllChildReceiver : public Impl::Receiver<WhenAllOpState, stdexec::env_of_t<Rcvr>> {
61 [[nodiscard]]
62 auto query(get_node_t) const & noexcept -> const predecessor_t& {
63 return this->parent_op->get_predecessor();
64 }
65 };
66
67 using children_op_states_t = stdexec::__tuple<stdexec::connect_result_t<Sndrs, WhenAllChildReceiver>...>;
68
70 static constexpr bool as_one =
72
74 static constexpr bool at_least_one_child_adds_a_node = stdexec::__mapply<
75 stdexec::__many_of<stdexec::__q<Impl::queryable_for<get_node_t>::type>>,
77 >::value;
78
80 static constexpr std::size_t num_branches = sizeof...(Sndrs);
81
83 static constexpr std::size_t num_branches_remaining_on_graph = stdexec::__mapply<
84 stdexec::__mcount_if<stdexec::__mbind_back_q<RemainsOnGraphFor, Exec>>,
86 >::value;
88
89 static_assert(
91
99 template <typename OpState>
100 static constexpr auto node_of(const OpState& opstate) noexcept {
101 if constexpr (stdexec::__queryable_with<OpState, get_node_t>) {
102 using query_t = stdexec::__query_result_t<OpState, get_node_t>;
103 static_assert(std::is_lvalue_reference_v<query_t>);
104 return std::tuple<query_t>{opstate.query(get_node)};
105 } else {
106 return std::tuple<>{};
107 }
108 }
109
110 static constexpr auto join() noexcept {
111 return Impl::Empty{};
112 }
113
114 template <typename Node>
115 static constexpr const Node& join(const Node& node) noexcept {
116 return node;
117 }
118
119 template <typename... Nodes>
120 requires(sizeof...(Nodes) >= 2)
121 static constexpr auto join(const Nodes&... nodes) {
122 auto agg = Kokkos::Experimental::when_all(nodes...);
123 graph_add_aggregate_node_event(agg, nodes...);
124 return agg;
125 }
126
127 template <typename ChildrenOpStates>
128 constexpr decltype(auto) operator()(const ChildrenOpStates& children) const {
129 return stdexec::__apply(
130 [](const auto&... opstates) -> decltype(auto) {
131 return std::apply(
132 [](const auto&... nodes) -> decltype(auto) { return join(nodes...); },
133 std::tuple_cat(node_of(opstates)...));
134 },
135 children);
136 }
137 };
138
139 using node_t = std::invoke_result_t<JoinOpStateNodes, const children_op_states_t&>;
140
144 std::conditional_t<at_least_one_child_adds_a_node, GraphComposition::policy_t<Rcvr>, GraphComposition::Attach>;
146
147 [[no_unique_address]]
150 PRAGMA_DIAGNOSTIC_PUSH
151 KOKKOS_EXECUTION_PRAGMA_DIAGNOSTIC_IGNORED_DANGLING_REFERENCE
152 [[no_unique_address]]
154 PRAGMA_DIAGNOSTIC_POP
155 [[no_unique_address]]
157
159 WhenAllOpState(stdexec::__tuple<Sndrs...>&& sndrs_, Rcvr&& rcvr_)
160 : base_t(std::move(rcvr_))
167 , state{Kokkos::Experimental::get_device_handle(execution_space{})}
169 stdexec::__apply(
170 [this](auto&&... children) -> children_op_states_t {
171 static_assert((std::same_as<decltype(children), Sndrs&&> && ...));
172 PRAGMA_DIAGNOSTIC_PUSH
173 PRAGMA_DIAGNOSTIC_IGNORED("-Wmissing-braces")
175 stdexec::connect(std::forward<Sndrs>(children), WhenAllChildReceiver{this})...};
176 PRAGMA_DIAGNOSTIC_POP
177 },
178 std::move(sndrs_)))
179 , node(JoinOpStateNodes{}(children_op_states)) {
180 }
181
182 const predecessor_t& get_predecessor() const noexcept {
183 if constexpr (std::same_as<graph_composition_policy_t, GraphComposition::Create>) {
184 return state.get_root_node();
185 } else {
186 return this->completion_signal.rcvr.query(get_node);
187 }
188 }
189
190 [[nodiscard]]
191 auto query(get_node_t) const & noexcept -> const node_t& requires(!Impl::empty<node_t>)
192 {
193 return node;
194 }
195
196 [[nodiscard]]
197 auto query(get_graph_t) const & noexcept -> const
198 typename state_t::graph_t& requires std::same_as<graph_composition_policy_t, GraphComposition::Create>
199 {
200 return state.graph;
201 }
202
224 void submit() & noexcept requires(!as_one)
225 {
226 if (count.value.fetch_sub(1) == 1) {
227 this->submit_graph();
228 }
229 }
230
231 void submit() & noexcept requires(as_one)
232 {
233 this->submit_graph();
234 }
235
236 void complete(stdexec::set_value_t) & noexcept {
237 this->submit();
238 }
239
240 template <typename Tag, typename... Args>
241 requires(!std::same_as<Tag, stdexec::set_value_t>)
242 void complete(Tag, Args&&... args) & noexcept {
243 base_t::complete(Tag{}, std::forward<Args>(args)...);
244 }
245
246 void start() & noexcept requires(!as_one)
247 {
248#if defined(KOKKOS_EXECUTION_ENABLE_DEBUG_LOGGING)
249 PLOG_INFO << "Starting all branches before submission.";
250#endif
251#if defined(__cpp_expansion_statements) && __cpp_expansion_statements >= 202506L
252 template for (auto& op: children_op_states) {
253 if constexpr (!remains_on_graph_for<std::remove_cvref_t<decltype(op)>, Exec>)
254 stdexec::start(op);
255 }
256#else
257 stdexec::__apply(
258 []<typename... ChildOpStates>([[maybe_unused]] ChildOpStates&... ops) noexcept {
259 (
260 [&] {
262 stdexec::start(ops);
263 }(),
264 ...);
265 },
267#endif
268 }
269
271 void start() & noexcept requires(as_one)
272 {
273#if defined(KOKKOS_EXECUTION_ENABLE_DEBUG_LOGGING)
274 PLOG_INFO << "Submit the graph directly without starting the branches.";
275#endif
276 this->submit();
277 }
278
279 void submit_graph() & noexcept {
280 stdexec::set_value(std::move(this->completion_signal.rcvr));
281 }
282
283 void submit_graph() & noexcept
284 requires(std::is_same_v<graph_composition_policy_t, GraphComposition::Create> && at_least_one_child_adds_a_node)
285 {
286#if defined(KOKKOS_EXECUTION_ENABLE_DEBUG_LOGGING)
287 PLOG_INFO << "Submitting graph " << get_graph_impl_ptr(state.graph.root_node()) << " on "
288 << Kokkos::Tools::Experimental::device_id(state.get_device_handle().m_exec) << '.';
289#endif
290 try {
291 Kokkos::Execution::GraphImpl::submit_graph(state.graph, state.get_device_handle().m_exec);
292 } catch (...) {
293 stdexec::set_error(std::move(this->completion_signal.rcvr), std::current_exception());
294 return;
295 }
296 this->completion_signal.propagate(state.get_device_handle().m_exec);
297 }
298
300};
301
303template <stdexec::operation_state OpState, Kokkos::ExecutionSpace Exec>
304requires(
305 stdexec::__is_instance_of<OpState, Kokkos::Execution::GraphImpl::WhenAllOpState>
306 && std::same_as<typename OpState::execution_space, Exec>)
307struct GraphOperationStateFor<OpState, Exec> : public std::true_type { };
308
310template <stdexec::operation_state OpState, Kokkos::ExecutionSpace Exec>
311requires(
313 && stdexec::__is_instance_of<OpState, Kokkos::Execution::GraphImpl::WhenAllOpState>)
315 template <stdexec::operation_state ChildOpState>
317
318 static constexpr bool value = stdexec::__mapply<
319 stdexec::__mall_of<stdexec::__q<RemainsOnGraphForChild>>,
320 typename OpState::children_op_states_t
321 >::value;
322
323 static constexpr auto diagnose() noexcept {
324 using children_t = stdexec::__mapply<
325 stdexec::__mtransform<stdexec::__q<RemainsOnGraphForChild>, stdexec::__q<Impl::diagnose_t>>,
326 typename OpState::children_op_states_t
327 >;
328
329 if constexpr (stdexec::__ok<Impl::first_error_t<children_t>>) {
330 return stdexec::__msuccess{};
331 } else {
332 return stdexec::__mexception<
333 stdexec::_WHAT_(OPERATION_STATE_DOES_NOT_REMAIN_ON_GRAPH),
334 stdexec::_WHY_(BECAUSE_AT_LEAST_ONE_CHILD_DOES_NOT_REMAIN_ON_GRAPH),
337 >{};
338 }
339 }
340};
341
343template <Kokkos::ExecutionSpace Exec, stdexec::sender... Sndrs>
345 using sender_concept = stdexec::sender_tag;
346
347 using sndrs_t = stdexec::__tuple<Sndrs...>;
348
349 struct attrs {
350 template <typename... Env>
351 [[nodiscard]]
352 constexpr auto
353 query(stdexec::get_completion_domain_t<stdexec::set_value_t>, const Env&...) const noexcept -> Domain {
354 return {};
355 }
356 };
357
359 template <typename Self, typename... Env>
360 static consteval auto get_completion_signatures() {
361 return stdexec::completion_signatures<stdexec::set_value_t(), stdexec::set_error_t(std::exception_ptr)>{};
362 }
363
364 template <stdexec::receiver Rcvr>
365 auto connect(Rcvr rcvr)
368 constexpr auto get_env() const noexcept -> attrs {
369 return {};
370 }
373};
374
375struct BECAUSE_THE_EXECUTION_SPACE_TYPE_IS_NOT_HOMOGENEOUS;
376
377template <size_t Index, typename Sndr>
379
380template <>
381struct TransformSenderFor<stdexec::when_all_t> {
382 template <typename Env, typename... Sndrs>
383 using trnsfrmd_sndr_t = WhenAllSender<Impl::exec_of_t<stdexec::__m_at_c<0, Sndrs...>, Env>, Sndrs...>;
384
385 template <typename Env, typename... Sndrs>
386 auto operator()(const Env&, stdexec::when_all_t, stdexec::__ignore, Sndrs&&... sndrs) const
387 noexcept(std::is_nothrow_constructible_v<typename trnsfrmd_sndr_t<Env, Sndrs...>::sndrs_t, Sndrs&&...>) {
388 if constexpr ((graph_completing_sender<Sndrs, Env> && ...)) {
389 using execution_space = Impl::exec_of_t<stdexec::__m_at_c<0, Sndrs...>, Env>;
390
392 if constexpr ((std::same_as<Impl::exec_of_t<Sndrs, Env>, execution_space> && ...)) {
393 PRAGMA_DIAGNOSTIC_PUSH
394 PRAGMA_DIAGNOSTIC_IGNORED("-Wmissing-braces")
395 return trnsfrmd_sndr_t<Env, Sndrs...>{.sndrs = {std::forward<Sndrs>(sndrs)...}};
396 PRAGMA_DIAGNOSTIC_POP
397 } else {
399 STDEXEC_CONSTEXPR_LOCAL bool map[] = {
400 !std::same_as<Impl::exec_of_t<stdexec::__m_at_c<0, Sndrs>, Env>, execution_space>...};
401 STDEXEC_CONSTEXPR_LOCAL std::size_t index = stdexec::__pos_of(map, map + sizeof...(Sndrs));
402 using invalid_sndr_t = stdexec::__m_at_c<index, Sndrs...>;
403 return stdexec::__not_a_sender<
404 stdexec::_WHAT_(CANNOT_DISPATCH_THIS_ALGORITHM_TO_THE_GRAPH_SCHEDULER),
405 stdexec::_WHY_(BECAUSE_THE_EXECUTION_SPACE_TYPE_IS_NOT_HOMOGENEOUS),
406 stdexec::_WHERE_(stdexec::_IN_ALGORITHM_, stdexec::when_all_t),
408 stdexec::_WITH_SENDERS_<Sndrs...>,
409 stdexec::_WITH_ENVIRONMENT_(Env)
410 >{};
411 }
412 } else {
414 STDEXEC_CONSTEXPR_LOCAL bool map[] = {!graph_completing_sender<Sndrs, Env>...};
415 STDEXEC_CONSTEXPR_LOCAL std::size_t index = stdexec::__pos_of(map, map + sizeof...(Sndrs));
416 using invalid_sndr_t = stdexec::__m_at_c<index, Sndrs...>;
418 }
419 }
420};
421
422} // namespace Kokkos::Execution::GraphImpl
423
424#endif // KOKKOS_EXECUTION_GRAPH_WHEN_ALL_HPP
Concept for a sender whose completion scheduler is Kokkos::Execution::GraphImpl::Scheduler.
#define KOKKOS_EXECUTION_GET_ENV(_type_, _obj_)
Retrieve the environment of _obj_. // NOLINTNEXTLINE(cppcoreguidelines-macro-usage).
Definition env.hpp:14
#define KOKKOS_EXECUTION_RETURNS_BRACED(_type_,...)
Define a function by returning a list-initialized object of an explicit type.
Definition macros.hpp:23
void graph_add_aggregate_node_event(const NodeType &aggregate, const Predecessors &... predecessors)
Record an event for an aggregate node added after predecessors.
Definition events.hpp:166
constexpr get_node_t get_node
Definition get_node.hpp:15
auto * get_graph_impl_ptr(const NodeType &node) noexcept
Retrieve the raw graph pointer from a node.
Definition events.hpp:117
void submit_graph(const Kokkos::Experimental::Graph< Exec > &graph, const Exec &exec)
Submit a graph and record the associated event with graph_submit_event.
Definition events.hpp:196
auto no_graph_scheduler_in_env() noexcept
Show a better compile diagnostic when there is no Kokkos::Execution::GraphImpl::Scheduler found.
decltype(rebind< To >(static_cast< From * >(nullptr))) rebind_t
typename ExecOf< Args... >::type exec_of_t
Definition get_exec.hpp:42
Attach to the existing graph of the predecessor.
Definition get_graph.hpp:34
typename node_helper_t< Exec, Queryables... >::type node_t
Definition get_graph.hpp:80
void complete(stdexec::set_error_t, Error &&error) noexcept
constexpr OpStateBase(Rcvr rcvr) noexcept(std::is_nothrow_constructible_v< completion_signal_t, Rcvr && >)
Operation state that adds all closures as a sequence of nodes.
RemainsOnGraphFor< ChildOpState, Exec > RemainsOnGraphForChild
Definition when_all.hpp:316
WhenAllSender< Impl::exec_of_t< stdexec::__m_at_c< 0, Sndrs... >, Env >, Sndrs... > trnsfrmd_sndr_t
Definition when_all.hpp:383
auto operator()(const Env &, stdexec::when_all_t, stdexec::__ignore, Sndrs &&... sndrs) const noexcept(std::is_nothrow_constructible_v< typename trnsfrmd_sndr_t< Env, Sndrs... >::sndrs_t, Sndrs &&... >)
Definition when_all.hpp:386
Join the nodes of all operation states into one aggregate node.
Definition when_all.hpp:98
static constexpr auto join(const Nodes &... nodes)
Definition when_all.hpp:121
static constexpr const Node & join(const Node &node) noexcept
Definition when_all.hpp:115
static constexpr auto node_of(const OpState &opstate) noexcept
Definition when_all.hpp:100
auto query(get_node_t) const &noexcept -> const predecessor_t &
Definition when_all.hpp:62
Operation state for stdexec::when_all.
Definition when_all.hpp:46
PRAGMA_DIAGNOSTIC_PUSH KOKKOS_EXECUTION_PRAGMA_DIAGNOSTIC_IGNORED_DANGLING_REFERENCE node_t node
Definition when_all.hpp:153
State< graph_composition_policy_t, execution_space > state_t
Definition when_all.hpp:145
static constexpr std::size_t num_branches_not_remaining_on_graph
Definition when_all.hpp:87
WhenAllOpState(stdexec::__tuple< Sndrs... > &&sndrs_, Rcvr &&rcvr_)
Definition when_all.hpp:159
static constexpr std::size_t num_branches_remaining_on_graph
Determine how many branches remain on graph (and how many don't).
Definition when_all.hpp:83
stdexec::operation_state_tag operation_state_concept
Definition when_all.hpp:47
static constexpr bool as_one
Determine if all branches remain fully on the graph, if connected to WhenAllChildReceiver.
Definition when_all.hpp:70
void start() &noexcept
If as_one is true, there is no need to start the branches.
Definition when_all.hpp:271
auto query(get_graph_t) const &noexcept -> const typename state_t::graph_t &
Definition when_all.hpp:197
std::conditional_t< at_least_one_child_adds_a_node, GraphComposition::policy_t< Rcvr >, GraphComposition::Attach > graph_composition_policy_t
Definition when_all.hpp:143
stdexec::__tuple< stdexec::connect_result_t< Sndrs, WhenAllChildReceiver >... > children_op_states_t
Definition when_all.hpp:67
static constexpr bool at_least_one_child_adds_a_node
Determine if at least one child operation state will add a node.
Definition when_all.hpp:74
auto query(get_node_t) const &noexcept -> const node_t &requires(!Impl::empty< node_t >)
Definition when_all.hpp:191
GraphComposition::node_t< Exec, Rcvr > predecessor_t
Definition when_all.hpp:52
void complete(Tag, Args &&... args) &noexcept
Definition when_all.hpp:242
void complete(stdexec::set_value_t) &noexcept
Definition when_all.hpp:236
PRAGMA_DIAGNOSTIC_POP Impl::CompileTimeOptional<!as_one, std::atomic< size_t > > count
Definition when_all.hpp:156
std::invoke_result_t< JoinOpStateNodes, const children_op_states_t & > node_t
Definition when_all.hpp:139
static constexpr std::size_t num_branches
Number of branches.
Definition when_all.hpp:80
const predecessor_t & get_predecessor() const noexcept
Definition when_all.hpp:182
constexpr auto query(stdexec::get_completion_domain_t< stdexec::set_value_t >, const Env &...) const noexcept -> Domain
Definition when_all.hpp:353
Sender for stdexec::when_all.
Definition when_all.hpp:344
auto connect(Rcvr rcvr) &&noexcept(noexcept(WhenAllOpState< Exec, Rcvr, Sndrs... >{std::move(sndrs), std::move(rcvr)})) -> WhenAllOpState< Exec, Rcvr, Sndrs... >
Definition when_all.hpp:365
static consteval auto get_completion_signatures()
Definition when_all.hpp:360
stdexec::__tuple< Sndrs... > sndrs_t
Definition when_all.hpp:347
Compile-time optional value that holds a T when Enabled, is an empty type otherwise.
Receiver for an object parent_op that implements complete.
Definition receiver.hpp:13
Kokkos::DefaultExecutionSpace execution_space