kokkos-execution 0.0.1
Loading...
Searching...
No Matches
operation_state.hpp
Go to the documentation of this file.
1#ifndef KOKKOS_EXECUTION_GRAPH_OPERATION_STATE_HPP
2#define KOKKOS_EXECUTION_GRAPH_OPERATION_STATE_HPP
3
5
6#if defined(KOKKOS_EXECUTION_ENABLE_DEBUG_LOGGING)
7# include "plog/Log.h"
8#endif
9
10#include "Kokkos_Core.hpp"
11#include "Kokkos_Graph.hpp"
12
14
26
28
29template <typename Clsr>
30concept Closure = requires {
31 typename Clsr::execution_space;
32 requires std::same_as<typename Clsr::device_handle_t, Kokkos::Impl::DeviceHandle<typename Clsr::execution_space>>;
33 typename Clsr::node_props_t;
34};
35
37template <stdexec::operation_state OpState, Kokkos::ExecutionSpace Exec>
38requires (
39 requires { typename OpState::inner_opstate_t; } &&
40 requires(const OpState& opstate) {
43 } &&
44 requires { typename OpState::execution_space; } &&
45 std::same_as<typename OpState::execution_space, Exec>
46)
47struct GraphOperationStateFor<OpState, Exec> : public std::true_type { };
48
50template <stdexec::operation_state OpState, Kokkos::ExecutionSpace Exec>
51requires(stdexec::__is_instance_of<OpState, Kokkos::Execution::GraphImpl::Scheduler<Exec>::template OpState>)
52struct GraphOperationStateFor<OpState, Exec> : public std::true_type { };
53
55template <stdexec::operation_state OpState, Kokkos::ExecutionSpace Exec>
56requires(
58 && stdexec::__is_instance_of<OpState, Kokkos::Execution::GraphImpl::Scheduler<Exec>::template OpState>)
59struct RemainsOnGraphFor<OpState, Exec> : public std::true_type { };
60
62template <stdexec::operation_state OpState, Kokkos::ExecutionSpace Exec>
64struct RemainsOnGraphFor<OpState, Exec> : public RemainsOnGraphFor<typename OpState::inner_opstate_t, Exec> { };
65
66template <typename GraphCompositionPolicy, Kokkos::ExecutionSpace Exec>
67struct State;
68
70template <Kokkos::ExecutionSpace Exec>
71struct State<GraphComposition::Attach, Exec> {
73
74 using graph_t = Kokkos::Experimental::Graph<Exec>;
75
76 explicit State(const Kokkos::Impl::DeviceHandle<Exec>&) {
77 }
78};
79
81template <Kokkos::ExecutionSpace Exec>
82struct State<GraphComposition::Create, Exec> {
84
85 using device_handle_t = Kokkos::Impl::DeviceHandle<Exec>;
86
87 using graph_t = Kokkos::Experimental::Graph<Exec>;
88 using root_t = typename graph_t::root_t;
89
92 mutable root_t m_root{};
93
94 explicit State(const device_handle_t& device_handle)
95 : graph(Kokkos::Execution::GraphImpl::create_graph(device_handle)) {
96 }
97
99 return graph.get_device_handle();
100 }
101
102 const root_t& get_root_node() const {
103 if (Kokkos::Impl::GraphAccess::get_node_ptr(m_root) == nullptr) {
104 m_root = graph.root_node();
105 }
106 return m_root;
107 }
108};
109
115template <Kokkos::ExecutionSpace Exec, stdexec::receiver Rcvr>
117 static consteval auto select_completion_signal_policy() noexcept {
120 } else {
122 }
123 }
124
127
129
130 constexpr explicit OpStateBase(Rcvr rcvr) noexcept(std::is_nothrow_constructible_v<completion_signal_t, Rcvr&&>)
131 : completion_signal(std::move(rcvr)) {
132 }
133
134 template <typename Error>
135 void complete(stdexec::set_error_t, Error&& error) noexcept {
136 stdexec::set_error(std::move(completion_signal.rcvr), std::forward<Error>(error));
137 }
138
139 void complete(stdexec::set_stopped_t) noexcept {
140 stdexec::set_stopped(std::move(completion_signal.rcvr));
141 }
142};
143
145template <typename Predecessor, Closure FirstClosure, Closure... RestOfClosures>
146requires NodeRef<std::remove_cvref_t<Predecessor>>
147static auto add_nodes(Predecessor&& predecessor, FirstClosure&& clsr, RestOfClosures&&... clsrs) {
148 auto node = std::forward<FirstClosure>(clsr).add_node(std::forward<Predecessor>(predecessor));
149 if constexpr (sizeof...(RestOfClosures) == 0) {
150 return node;
151 } else {
152 return add_nodes(std::move(node), std::forward<RestOfClosures>(clsrs)...);
153 }
154}
155
157template <stdexec::sender Sndr, stdexec::receiver Rcvr, Closure FirstClosure, Closure... RestOfClosures>
159 : public Impl::Immovable
160 , public OpStateBase<typename FirstClosure::execution_space, Rcvr> {
162
163 using execution_space = typename FirstClosure::execution_space;
164 using device_handle_t = typename FirstClosure::device_handle_t;
165
167 static_assert((std::same_as<typename RestOfClosures::execution_space, execution_space> && ...));
168
170 using inner_opstate_t = stdexec::connect_result_t<Sndr, rcvr_t>;
174
175 static constexpr bool is_graph_create = std::same_as<graph_composition_policy_t, GraphComposition::Create>;
176
177 using node_t = decltype(add_nodes(
178 std::declval<predecessor_t>(),
179 std::declval<FirstClosure>(),
180 std::declval<RestOfClosures>()...));
181
185
187 constexpr OpState(
188 Sndr&& sndr, // NOLINT(cppcoreguidelines-rvalue-reference-param-not-moved)
189 Rcvr rcvr,
190 FirstClosure clsr,
191 RestOfClosures... clsrs) noexcept(false)
192 : OpStateBase<execution_space, Rcvr>(std::move(rcvr))
193 , inner_opstate(stdexec::connect(std::forward<Sndr>(sndr), rcvr_t{this}))
194 , state{Kokkos::Impl::get_property<device_handle_t>(clsr.node_props)}
195 , node{add_nodes(this->get_predecessor(), std::move(clsr), std::move(clsrs)...)} {
196#if defined(KOKKOS_EXECUTION_ENABLE_DEBUG_LOGGING)
197 PLOG_INFO << "Operation state graph composition policy is "
198 << Kokkos::Impl::TypeInfo<graph_composition_policy_t>::name() << ", the receiver is of type "
199 << Kokkos::Impl::TypeInfo<Rcvr>::name() << " and the inner operation state is of type "
200 << Kokkos::Impl::TypeInfo<inner_opstate_t>::name() << '.';
201#endif
202 }
203
208 const predecessor_t& get_predecessor() const noexcept {
209 if constexpr (is_graph_create) {
210#if defined(KOKKOS_EXECUTION_ENABLE_DEBUG_LOGGING)
211 PLOG_INFO << "The predecessor is the root node of graph " << get_graph_impl_ptr(state.get_root_node())
212 << '.';
213#endif
214 return state.get_root_node();
215 } else {
216 if constexpr (stdexec::__queryable_with<inner_opstate_t, get_node_t>) {
217 return inner_opstate.query(get_node);
218 } else {
219 return this->completion_signal.rcvr.query(get_node);
220 }
221 }
222 }
223
224 void complete(stdexec::set_value_t) noexcept {
225 this->submit();
226 }
227
228 template <typename Error>
229 void complete(stdexec::set_error_t, Error&& error) noexcept {
230 stdexec::set_error(std::move(this->completion_signal.rcvr), std::forward<Error>(error));
231 }
232
233 void complete(stdexec::set_stopped_t) noexcept {
234 stdexec::set_stopped(std::move(this->completion_signal.rcvr));
235 }
236
237 void submit() noexcept {
238 if constexpr (is_graph_create) {
239#if defined(KOKKOS_EXECUTION_ENABLE_DEBUG_LOGGING)
240 PLOG_INFO << "Submitting graph " << get_graph_impl_ptr(state.get_root_node()) << " on "
241 << Kokkos::Tools::Experimental::device_id(state.get_device_handle().m_exec) << '.';
242#endif
243 try {
244 submit_graph(state.graph, state.get_device_handle().m_exec);
245 } catch (...) {
246 this->complete(stdexec::set_error, std::current_exception());
247 return;
248 }
249 }
250 this->completion_signal.propagate(Impl::get_exec(*this).get());
251 }
252
253 [[nodiscard]]
254 constexpr auto query(get_node_t) const & noexcept -> const node_t& {
255 return node;
256 }
257
258 [[nodiscard]]
259 constexpr auto query(get_graph_t) const & noexcept -> const typename state_t::graph_t& {
260 if constexpr (is_graph_create) {
261 return state.graph;
262 } else {
263 return inner_opstate.query(get_graph);
264 }
265 }
266
267 [[nodiscard]]
268 constexpr auto
270 {
271 return Impl::ExecutionSpaceRef{state.get_device_handle().m_exec};
272 }
273
274 [[nodiscard]]
275 constexpr auto query(Impl::get_exec_t) const noexcept -> decltype(auto) requires(!is_graph_create)
276 {
278 }
279
280 void start() & noexcept {
281 stdexec::start(inner_opstate);
282 }
283
285};
286
287template <typename Sndr, typename Rcvr, typename... Clsrs>
289
290template <typename Sndr, typename Rcvr, typename... Clsrs>
291using opstate_t = typename make_opstate_t<Sndr, Rcvr, Clsrs...>::type;
292
293#define KOKKOS_EXECUTION_GRAPH_OPERATION_STATE_CONNECT \
294 template <stdexec::receiver Rcvr> \
295 constexpr auto connect(Rcvr rcvr) && noexcept(noexcept(make_opstate_t<Sndr, Rcvr, closure_t>{}( \
296 std::declval<Sndr>(), std::declval<Rcvr>(), std::declval<closure_t>()))) -> opstate_t<Sndr, Rcvr, closure_t> { \
297 return make_opstate_t<Sndr, Rcvr, closure_t>{}(std::forward<Sndr>(sndr), std::move(rcvr), std::move(clsr)); \
298 }
299
300#if defined(KOKKOS_EXECUTION_ENABLE_DEBUG_LOGGING) // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
301# define KOKKOS_EXECUTION_IMPL_GRAPH_ADD_NODE_DEBUG_LOGGING(_type_, _node_, _predecessor_) \
302 PLOG_INFO << "Adding '" _type_ "' node " << get_node_ptr(_node_) << " to graph " << get_graph_impl_ptr(_node_) \
303 << " after " << get_node_ptr(_predecessor_) << " on device " \
304 << Kokkos::Tools::Experimental::device_id(get_node_ptr(_node_)->get_device_handle().m_exec) << '.';
305#else
306# define KOKKOS_EXECUTION_IMPL_GRAPH_ADD_NODE_DEBUG_LOGGING(_type_, _node_, _predecessor_)
307#endif
308} // namespace Kokkos::Execution::GraphImpl
309
310// NOLINTBEGIN(bugprone-reserved-identifier)
312template <stdexec::sender Sndr, stdexec::receiver Rcvr, typename FirstClosure, typename... RestOfClosures>
313extern __mtype<Kokkos::Execution::GraphImpl::OpState<__demangle_t<Sndr>, Rcvr, FirstClosure, RestOfClosures...>>
314 __demangle_v<Kokkos::Execution::GraphImpl::OpState<Sndr, Rcvr, FirstClosure, RestOfClosures...>>;
315} // namespace stdexec::__detail
316// NOLINTEND(bugprone-reserved-identifier)
317
318#endif // KOKKOS_EXECUTION_GRAPH_OPERATION_STATE_HPP
#define KOKKOS_EXECUTION_GET_ENV(_type_, _obj_)
Retrieve the environment of _obj_. // NOLINTNEXTLINE(cppcoreguidelines-macro-usage).
Definition env.hpp:14
typename make_opstate_t< Sndr, Rcvr, Clsrs... >::type opstate_t
constexpr get_node_t get_node
Definition get_node.hpp:15
auto create_graph(const Kokkos::Impl::DeviceHandle< Exec > &device_handle, Args &&... args)
Create a graph and record the associated event with graph_create_event.
Definition events.hpp:117
Impl::MakeOpState< Domain, OpState >::Huddle< Sndr, Rcvr, Clsrs... > make_opstate_t
constexpr get_graph_t get_graph
Definition get_graph.hpp:17
auto * get_graph_impl_ptr(const NodeType &node) noexcept
Retrieve the raw graph pointer from a node.
Definition events.hpp:93
static auto add_nodes(Predecessor &&predecessor, FirstClosure &&clsr, RestOfClosures &&... clsrs)
Add all nodes as a sequence. Hence, only the first node may be added after the root node.
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:178
constexpr get_exec_t get_exec
Definition get_exec.hpp:19
Attach to the existing graph of the predecessor.
Definition get_graph.hpp:33
Create a new graph and attach after the root node.
Definition get_graph.hpp:36
Inspired by https://github.com/NVIDIA/stdexec/blob/8c5eedd0fcf9a8ebcdb75d988f72f88efcf64a37/include/s...
Definition get_graph.hpp:31
typename node_helper_t< Exec, Queryables... >::type node_t
Definition get_graph.hpp:78
std::conditional_t<(stdexec::__queryable_with< Queryables, get_node_t >||...), Attach, Create > policy_t
Use the Attach policy if any Queryables is queryable with get_node_t.
Definition get_graph.hpp:40
decltype(select_completion_signal_policy()) completion_signal_policy_t
Impl::CompletionSignal< completion_signal_policy_t, Exec, Rcvr > completion_signal_t
void complete(stdexec::set_error_t, Error &&error) noexcept
static consteval auto select_completion_signal_policy() noexcept
void complete(stdexec::set_stopped_t) 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.
GraphComposition::node_t< execution_space, inner_opstate_t, Rcvr > predecessor_t
stdexec::connect_result_t< Sndr, rcvr_t > inner_opstate_t
void complete(stdexec::set_error_t, Error &&error) noexcept
Impl::SubmittedOperationStateTag operation_state_concept
constexpr OpState(Sndr &&sndr, Rcvr rcvr, FirstClosure clsr, RestOfClosures... clsrs) noexcept(false)
State< graph_composition_policy_t, execution_space > state_t
typename FirstClosure::device_handle_t device_handle_t
void complete(stdexec::set_stopped_t) noexcept
Impl::Receiver< OpState, stdexec::env_of_t< Rcvr > > rcvr_t
Ensure that all closures are on the same execution space type.
typename FirstClosure::execution_space execution_space
decltype(add_nodes( std::declval< predecessor_t >(), std::declval< FirstClosure >(), std::declval< RestOfClosures >()...)) node_t
void complete(stdexec::set_value_t) noexcept
const predecessor_t & get_predecessor() const noexcept
constexpr auto query(get_node_t) const &noexcept -> const node_t &
GraphComposition::policy_t< inner_opstate_t, Rcvr > graph_composition_policy_t
constexpr auto query(Impl::get_exec_t) const noexcept -> Impl::ExecutionSpaceRef< execution_space >
constexpr auto query(get_graph_t) const &noexcept -> const typename state_t::graph_t &
constexpr auto query(Impl::get_exec_t) const noexcept -> decltype(auto) requires(!is_graph_create)
Wrap a Kokkos execution space to make it cheap to copy/move in new environments.
Definition get_exec.hpp:47
Receiver for an object parent_op that implements complete.
Definition receiver.hpp:13