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_Macros.hpp"
11#ifdef KOKKOS_ENABLE_EXPERIMENTAL_CXX20_MODULES
12import kokkos.core;
13import kokkos.graph;
14import kokkos.graph_impl;
15#else
16# include "Kokkos_Core.hpp"
17# include "Kokkos_Graph.hpp"
18#endif
19
21
35
37
38template <typename Clsr>
39concept Closure = requires {
40 typename Clsr::execution_space;
41 requires std::same_as<typename Clsr::device_handle_t, Kokkos::Impl::DeviceHandle<typename Clsr::execution_space>>;
42 typename Clsr::node_props_t;
43};
44
46template <stdexec::operation_state OpState, Kokkos::ExecutionSpace Exec>
47requires (
48 requires { typename OpState::inner_op_state_t; } &&
49 requires(const OpState& op_state) {
52 } &&
53 requires { typename OpState::execution_space; } &&
54 std::same_as<typename OpState::execution_space, Exec>
55)
56struct GraphOperationStateFor<OpState, Exec> : public std::true_type { };
57
59template <stdexec::operation_state OpState, Kokkos::ExecutionSpace Exec>
60requires(stdexec::__is_instance_of<OpState, Kokkos::Execution::GraphImpl::Scheduler<Exec>::template OpState>)
61struct GraphOperationStateFor<OpState, Exec> : public std::true_type { };
62
64template <stdexec::operation_state OpState, Kokkos::ExecutionSpace Exec>
65requires(
67 && stdexec::__is_instance_of<OpState, Kokkos::Execution::GraphImpl::Scheduler<Exec>::template OpState>)
68struct RemainsOnGraphFor<OpState, Exec> : public std::true_type { };
69
71template <stdexec::operation_state OpState, Kokkos::ExecutionSpace Exec>
73struct RemainsOnGraphFor<OpState, Exec> : public RemainsOnGraphFor<typename OpState::inner_op_state_t, Exec> {
74
76
77 static constexpr auto diagnose() noexcept {
78 if constexpr (base_t::value) {
79 return stdexec::__msuccess{};
80 } else {
81 return stdexec::__mexception<
82 stdexec::_WHAT_(OPERATION_STATE_DOES_NOT_REMAIN_ON_GRAPH),
83 stdexec::_WHY_(BECAUSE_THE_INNER_OPERATION_STATE_DOES_NOT_REMAIN_ON_GRAPH),
87 >{};
88 }
89 }
90};
91
92template <typename GraphCompositionPolicy, Kokkos::ExecutionSpace Exec>
93struct State;
94
96template <Kokkos::ExecutionSpace Exec>
97struct State<GraphComposition::Attach, Exec> {
99
100 using graph_t = Kokkos::Experimental::Graph<Exec>;
101
102 explicit State(const Kokkos::Impl::DeviceHandle<Exec>&) {
103 }
104};
105
107template <Kokkos::ExecutionSpace Exec>
108struct State<GraphComposition::Create, Exec> {
110
111 using device_handle_t = Kokkos::Impl::DeviceHandle<Exec>;
112
113 using graph_t = Kokkos::Experimental::Graph<Exec>;
114 using root_t = typename graph_t::root_t;
115
118 mutable root_t m_root{};
119
120 explicit State(const device_handle_t& device_handle)
121 : graph(Kokkos::Execution::GraphImpl::create_graph(device_handle)) {
122 }
123
125 return graph.get_device_handle();
126 }
127
128 const root_t& get_root_node() const {
129 if (Kokkos::Impl::GraphAccess::get_node_ptr(m_root) == nullptr) {
130 m_root = graph.root_node();
131 }
132 return m_root;
133 }
134};
135
141template <Kokkos::ExecutionSpace Exec, stdexec::receiver Rcvr>
143 static consteval auto select_completion_signal_policy() noexcept {
146 } else {
148 }
149 }
150
153
155
156 constexpr explicit OpStateBase(Rcvr rcvr) noexcept(std::is_nothrow_constructible_v<completion_signal_t, Rcvr&&>)
157 : completion_signal(std::move(rcvr)) {
158 }
159
160 template <typename Error>
161 void complete(stdexec::set_error_t, Error&& error) noexcept {
162 stdexec::set_error(std::move(completion_signal.rcvr), std::forward<Error>(error));
163 }
164
165 void complete(stdexec::set_stopped_t) noexcept {
166 stdexec::set_stopped(std::move(completion_signal.rcvr));
167 }
168};
169
171template <typename Predecessor, Closure FirstClosure>
172requires NodeRef<std::remove_cvref_t<Predecessor>>
173static auto add_nodes(Predecessor&& predecessor, FirstClosure&& clsr) {
174 return std::forward<FirstClosure>(clsr).add_node(std::forward<Predecessor>(predecessor));
175}
176
177template <typename Predecessor, Closure FirstClosure, Closure... RestOfClosures>
178requires NodeRef<std::remove_cvref_t<Predecessor>> && (sizeof...(RestOfClosures) > 0)
179static auto add_nodes(Predecessor&& predecessor, FirstClosure&& clsr, RestOfClosures&&... clsrs) {
180 return add_nodes(
181 std::forward<FirstClosure>(clsr).add_node(std::forward<Predecessor>(predecessor)),
182 std::forward<RestOfClosures>(clsrs)...);
183}
184
186template <stdexec::sender Sndr, stdexec::receiver Rcvr, Closure FirstClosure, Closure... RestOfClosures>
188 : public Impl::Immovable
189 , public OpStateBase<typename FirstClosure::execution_space, Rcvr> {
191
192 using execution_space = typename FirstClosure::execution_space;
193 using device_handle_t = typename FirstClosure::device_handle_t;
194
196 static_assert((std::same_as<typename RestOfClosures::execution_space, execution_space> && ...));
197
199 using inner_op_state_t = stdexec::connect_result_t<Sndr, rcvr_t>;
203
204 static constexpr bool is_graph_create = std::same_as<graph_composition_policy_t, GraphComposition::Create>;
205
206 using node_t = decltype(add_nodes(
207 std::declval<predecessor_t>(),
208 std::declval<FirstClosure>(),
209 std::declval<RestOfClosures>()...));
210
212 [[no_unique_address]]
215
217 constexpr OpState(
218 Sndr&& sndr, // NOLINT(cppcoreguidelines-rvalue-reference-param-not-moved)
219 Rcvr rcvr,
220 FirstClosure clsr,
221 RestOfClosures... clsrs) noexcept(false)
222 : OpStateBase<execution_space, Rcvr>(std::move(rcvr))
223 , inner_op_state(stdexec::connect(std::forward<Sndr>(sndr), rcvr_t{this}))
224 , state{Kokkos::Impl::get_property<device_handle_t>(clsr.node_props)}
225 , node{add_nodes(this->get_predecessor(), std::move(clsr), std::move(clsrs)...)} {
226#if defined(KOKKOS_EXECUTION_ENABLE_DEBUG_LOGGING)
227 PLOG_INFO << "Operation state graph composition policy is "
228 << Impl::display_string_of<graph_composition_policy_t>() << ", the receiver is of type "
229 << Impl::display_string_of<Rcvr>() << " and the inner operation state is of type "
231#endif
232 }
233
238 [[nodiscard]]
239 auto get_predecessor() const noexcept -> const predecessor_t& {
240 if constexpr (is_graph_create) {
241#if defined(KOKKOS_EXECUTION_ENABLE_DEBUG_LOGGING)
242 PLOG_INFO << "The predecessor is the root node of graph " << get_graph_impl_ptr(state.get_root_node())
243 << '.';
244#endif
245 return state.get_root_node();
246 } else if constexpr (stdexec::__queryable_with<inner_op_state_t, get_node_t>) {
247 return inner_op_state.query(get_node);
248 } else {
249 return this->completion_signal.rcvr.query(get_node);
250 }
251 }
252
253 void complete(stdexec::set_value_t) noexcept {
254 this->submit();
255 }
256
257 template <typename Error>
258 void complete(stdexec::set_error_t, Error&& error) noexcept {
259 stdexec::set_error(std::move(this->completion_signal.rcvr), std::forward<Error>(error));
260 }
261
262 void complete(stdexec::set_stopped_t) noexcept {
263 stdexec::set_stopped(std::move(this->completion_signal.rcvr));
264 }
265
266 void submit() noexcept {
267 if constexpr (is_graph_create) {
268#if defined(KOKKOS_EXECUTION_ENABLE_DEBUG_LOGGING)
269 PLOG_INFO << "Submitting graph " << get_graph_impl_ptr(state.get_root_node()) << " on "
270 << Kokkos::Tools::Experimental::device_id(state.get_device_handle().m_exec) << '.';
271#endif
272 try {
273 submit_graph(state.graph, state.get_device_handle().m_exec);
274 } catch (...) {
275 this->complete(stdexec::set_error, std::current_exception());
276 return;
277 }
278 }
279 this->completion_signal.propagate(Impl::get_exec(*this).get());
280 }
281
282 [[nodiscard]]
283 auto query(get_node_t) const & noexcept -> const node_t& {
284 return node;
285 }
286
287 [[nodiscard]]
288 auto query(get_graph_t) const & noexcept -> const typename state_t::graph_t& {
289 if constexpr (is_graph_create) {
290 return state.graph;
291 } else {
292 return inner_op_state.query(get_graph);
293 }
294 }
295
296 [[nodiscard]]
297 constexpr auto
299 {
300 return Impl::ExecutionSpaceRef{state.get_device_handle().m_exec};
301 }
302
303 [[nodiscard]]
304 constexpr auto query(Impl::get_exec_t) const noexcept -> decltype(auto) requires(!is_graph_create)
305 {
307 }
308
309 void start() & noexcept {
310 stdexec::start(inner_op_state);
311 }
312
314};
315
316template <typename Sndr, typename Rcvr, typename... Clsrs>
318
319template <typename Sndr, typename Rcvr, typename... Clsrs>
320using op_state_t = typename make_op_state_t<Sndr, Rcvr, Clsrs...>::type;
321
322#define KOKKOS_EXECUTION_GRAPH_OPERATION_STATE_CONNECT \
323 template <stdexec::receiver Rcvr> \
324 constexpr auto connect(Rcvr rcvr) && noexcept(noexcept(make_op_state_t<Sndr, Rcvr, closure_t>{}( \
325 std::declval<Sndr>(), std::declval<Rcvr>(), std::declval<closure_t>()))) \
326 -> op_state_t<Sndr, Rcvr, closure_t> { \
327 return make_op_state_t<Sndr, Rcvr, closure_t>{}(std::forward<Sndr>(sndr), std::move(rcvr), std::move(clsr)); \
328 }
329
330#if defined(KOKKOS_EXECUTION_ENABLE_DEBUG_LOGGING) // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
331# define KOKKOS_EXECUTION_IMPL_GRAPH_ADD_NODE_DEBUG_LOGGING(_type_, _node_, _predecessor_) \
332 PLOG_INFO << "Adding '" << _type_ << "' node " << get_node_ptr(_node_) << " to graph " \
333 << get_graph_impl_ptr(_node_) << " after " << get_node_ptr(_predecessor_) << " on device " \
334 << Kokkos::Tools::Experimental::device_id(get_node_ptr(_node_)->get_device_handle().m_exec) << '.';
335#else
336# define KOKKOS_EXECUTION_IMPL_GRAPH_ADD_NODE_DEBUG_LOGGING(_type_, _node_, _predecessor_)
337#endif
338
339} // namespace Kokkos::Execution::GraphImpl
340
341#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
static auto add_nodes(Predecessor &&predecessor, FirstClosure &&clsr)
Add all nodes as a sequence. Hence, only the first node may be added after the root node.
Impl::MakeOpState< Domain, OpState >::Huddle< Sndr, Rcvr, Clsrs... > make_op_state_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:143
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: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
typename make_op_state_t< Sndr, Rcvr, Clsrs... >::type op_state_t
constexpr get_exec_t get_exec
Definition get_exec.hpp:24
typename Diagnose< Ts... >::type diagnose_t
constexpr auto display_string_of() noexcept -> std::string_view
Definition type_info.hpp:20
Attach to the existing graph of the predecessor.
Definition get_graph.hpp:34
Create a new graph and attach after the root node.
Definition get_graph.hpp:37
Inspired by https://github.com/NVIDIA/stdexec/blob/8c5eedd0fcf9a8ebcdb75d988f72f88efcf64a37/include/s...
Definition get_graph.hpp:32
typename node_helper_t< Exec, Queryables... >::type node_t
Definition get_graph.hpp:80
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:41
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.
stdexec::connect_result_t< Sndr, rcvr_t > inner_op_state_t
void complete(stdexec::set_error_t, Error &&error) noexcept
Impl::SubmittedOperationStateTag operation_state_concept
GraphComposition::node_t< execution_space, inner_op_state_t, Rcvr > predecessor_t
constexpr OpState(Sndr &&sndr, Rcvr rcvr, FirstClosure clsr, RestOfClosures... clsrs) noexcept(false)
auto get_predecessor() const noexcept -> const predecessor_t &
State< graph_composition_policy_t, execution_space > state_t
decltype(add_nodes( std::declval< predecessor_t >(), std::declval< FirstClosure >(), std::declval< RestOfClosures >()...)) node_t
typename FirstClosure::device_handle_t device_handle_t
auto query(get_graph_t) const &noexcept -> const typename state_t::graph_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
void complete(stdexec::set_value_t) noexcept
auto query(get_node_t) const &noexcept -> const node_t &
GraphComposition::policy_t< inner_op_state_t, Rcvr > graph_composition_policy_t
constexpr auto query(Impl::get_exec_t) const noexcept -> Impl::ExecutionSpaceRef< execution_space >
constexpr auto query(Impl::get_exec_t) const noexcept -> decltype(auto) requires(!is_graph_create)
RemainsOnGraphFor< typename OpState::inner_op_state_t, Exec > base_t
Wrap a Kokkos execution space to make it cheap to copy/move in new environments.
Definition get_exec.hpp:52
Receiver for an object parent_op that implements complete.
Definition receiver.hpp:13
Tag to be used to signal an execution space.