Kokkos Core Kernels Package Version of the Day
Loading...
Searching...
No Matches
Kokkos_GraphNode.hpp
1//@HEADER
2// ************************************************************************
3//
4// Kokkos v. 4.0
5// Copyright (2022) National Technology & Engineering
6// Solutions of Sandia, LLC (NTESS).
7//
8// Under the terms of Contract DE-NA0003525 with NTESS,
9// the U.S. Government retains certain rights in this software.
10//
11// Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions.
12// See https://kokkos.org/LICENSE for license information.
13// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
14//
15//@HEADER
16
17#ifndef KOKKOS_IMPL_PUBLIC_INCLUDE
18#include <Kokkos_Macros.hpp>
19static_assert(false,
20 "Including non-public Kokkos header files is not allowed.");
21#endif
22#ifndef KOKKOS_KOKKOS_GRAPHNODE_HPP
23#define KOKKOS_KOKKOS_GRAPHNODE_HPP
24
25#include <Kokkos_Macros.hpp>
26
27#include <impl/Kokkos_Error.hpp> // contract macros
28
29#include <Kokkos_Core_fwd.hpp>
30#include <Kokkos_Graph_fwd.hpp>
31#include <impl/Kokkos_GraphImpl_fwd.hpp>
32#include <Kokkos_Parallel_Reduce.hpp>
33#include <impl/Kokkos_GraphImpl_Utilities.hpp>
34#include <impl/Kokkos_GraphImpl.hpp> // GraphAccess
35
36#include <memory> // std::shared_ptr
37
38namespace Kokkos {
39namespace Experimental {
40
41template <class ExecutionSpace, class Kernel /*= TypeErasedTag*/,
42 class Predecessor /*= TypeErasedTag*/>
43class GraphNodeRef {
44 //----------------------------------------------------------------------------
45 // <editor-fold desc="template parameter constraints"> {{{2
46
47 // Note: because of these assertions, instantiating this class template is not
48 // intended to be SFINAE-safe, so do validation before you instantiate.
49
50 static_assert(
51 std::is_same_v<Predecessor, TypeErasedTag> ||
52 Kokkos::Impl::is_specialization_of<Predecessor, GraphNodeRef>::value,
53 "Invalid predecessor template parameter given to GraphNodeRef");
54
55 static_assert(
56 Kokkos::is_execution_space<ExecutionSpace>::value,
57 "Invalid execution space template parameter given to GraphNodeRef");
58
59 static_assert(std::is_same_v<Predecessor, TypeErasedTag> ||
60 Kokkos::Impl::is_graph_kernel<Kernel>::value,
61 "Invalid kernel template parameter given to GraphNodeRef");
62
63 static_assert(!Kokkos::Impl::is_more_type_erased<Kernel, Predecessor>::value,
64 "The kernel of a graph node can't be more type-erased than the "
65 "predecessor");
66
67 // </editor-fold> end template parameter constraints }}}2
68 //----------------------------------------------------------------------------
69
70 public:
71 //----------------------------------------------------------------------------
72 // <editor-fold desc="public member types"> {{{2
73
74 using execution_space = ExecutionSpace;
75 using graph_kernel = Kernel;
76 using graph_predecessor = Predecessor;
77
78 // </editor-fold> end public member types }}}2
79 //----------------------------------------------------------------------------
80
81 private:
82 //----------------------------------------------------------------------------
83 // <editor-fold desc="Friends"> {{{2
84
85 template <class, class, class>
86 friend class GraphNodeRef;
87 friend struct Kokkos::Impl::GraphAccess;
88
89 // </editor-fold> end Friends }}}2
90 //----------------------------------------------------------------------------
91
92 //----------------------------------------------------------------------------
93 // <editor-fold desc="Private Data Members"> {{{2
94
95 using graph_impl_t = Kokkos::Impl::GraphImpl<ExecutionSpace>;
96 std::weak_ptr<graph_impl_t> m_graph_impl;
97
98 // TODO @graphs figure out if we can get away with a weak reference here?
99 // GraphNodeRef instances shouldn't be stored by users outside
100 // of the create_graph closure, and so if we restructure things
101 // slightly, we could make it so that the graph owns the
102 // node_impl_t instance and this only holds a std::weak_ptr to
103 // it.
104 using node_impl_t =
105 Kokkos::Impl::GraphNodeImpl<ExecutionSpace, Kernel, Predecessor>;
106 std::shared_ptr<node_impl_t> m_node_impl;
107
108 // </editor-fold> end Private Data Members }}}2
109 //----------------------------------------------------------------------------
110
111 //----------------------------------------------------------------------------
112 // <editor-fold desc="Implementation detail accessors"> {{{2
113
114 // Internally, use shallow constness
115 node_impl_t& get_node_impl() const { return *m_node_impl.get(); }
116 std::shared_ptr<node_impl_t> const& get_node_ptr() const& {
117 return m_node_impl;
118 }
119 std::shared_ptr<node_impl_t> get_node_ptr() && {
120 return std::move(m_node_impl);
121 }
122 std::weak_ptr<graph_impl_t> get_graph_weak_ptr() const {
123 return m_graph_impl;
124 }
125
126 // </editor-fold> end Implementation detail accessors }}}2
127 //----------------------------------------------------------------------------
128
129 // TODO kernel name propagation and exposure
130
131 template <class NextKernelDeduced>
132 auto _then_kernel(NextKernelDeduced&& arg_kernel) const {
133 // readability note:
134 // std::remove_cvref_t<NextKernelDeduced> is a specialization of
135 // Kokkos::Impl::GraphNodeKernelImpl:
136 static_assert(Kokkos::Impl::is_specialization_of<
137 Kokkos::Impl::remove_cvref_t<NextKernelDeduced>,
138 Kokkos::Impl::GraphNodeKernelImpl>::value,
139 "Kokkos internal error");
140
141 auto graph_ptr = m_graph_impl.lock();
142 KOKKOS_EXPECTS(bool(graph_ptr))
143
144 using next_kernel_t = Kokkos::Impl::remove_cvref_t<NextKernelDeduced>;
145
146 using return_t = GraphNodeRef<ExecutionSpace, next_kernel_t, GraphNodeRef>;
147
148 auto rv = Kokkos::Impl::GraphAccess::make_graph_node_ref(
149 m_graph_impl,
150 Kokkos::Impl::GraphAccess::make_node_shared_ptr<
151 typename return_t::node_impl_t>(
152 m_node_impl->execution_space_instance(),
153 Kokkos::Impl::_graph_node_kernel_ctor_tag{},
154 (NextKernelDeduced&&)arg_kernel,
155 // *this is the predecessor
156 Kokkos::Impl::_graph_node_predecessor_ctor_tag{}, *this));
157
158 // Add the node itself to the backend's graph data structure, now that
159 // everything is set up.
160 graph_ptr->add_node(rv.m_node_impl);
161 // Add the predecessaor we stored in the constructor above in the backend's
162 // data structure, now that everything is set up.
163 graph_ptr->add_predecessor(rv.m_node_impl, *this);
164 KOKKOS_ENSURES(bool(rv.m_node_impl))
165 return rv;
166 }
167
168 //----------------------------------------------------------------------------
169 // <editor-fold desc="Private constructors"> {{{2
170
171 GraphNodeRef(std::weak_ptr<graph_impl_t> arg_graph_impl,
172 std::shared_ptr<node_impl_t> arg_node_impl)
173 : m_graph_impl(std::move(arg_graph_impl)),
174 m_node_impl(std::move(arg_node_impl)) {}
175
176 // </editor-fold> end Private constructors }}}2
177 //----------------------------------------------------------------------------
178
179 public:
180 //----------------------------------------------------------------------------
181 // <editor-fold desc="Constructors, destructors, and assignment"> {{{2
182
183 //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
184 // <editor-fold desc="rule of 6 ctors"> {{{3
185
186 // Copyable and movable (basically just shared_ptr semantics
187 GraphNodeRef() noexcept = default;
188 GraphNodeRef(GraphNodeRef const&) = default;
189 GraphNodeRef(GraphNodeRef&&) noexcept = default;
190 GraphNodeRef& operator=(GraphNodeRef const&) = default;
191 GraphNodeRef& operator=(GraphNodeRef&&) noexcept = default;
192 ~GraphNodeRef() = default;
193
194 // </editor-fold> end rule of 6 ctors }}}3
195 //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
196
197 //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
198 // <editor-fold desc="Type-erasing converting ctor and assignment"> {{{3
199
200 template <class OtherKernel, class OtherPredecessor,
201 std::enable_if_t<
202 // Not a copy/move constructor
203 !std::is_same_v<GraphNodeRef,
204 GraphNodeRef<execution_space, OtherKernel,
205 OtherPredecessor>> &&
206 // must be an allowed type erasure of the kernel
207 Kokkos::Impl::is_compatible_type_erasure<
208 OtherKernel, graph_kernel>::value &&
209 // must be an allowed type erasure of the predecessor
210 Kokkos::Impl::is_compatible_type_erasure<
211 OtherPredecessor, graph_predecessor>::value,
212 int> = 0>
213 /* implicit */
214 GraphNodeRef(
215 GraphNodeRef<execution_space, OtherKernel, OtherPredecessor> const& other)
216 : m_graph_impl(other.m_graph_impl), m_node_impl(other.m_node_impl) {}
217
218 // Note: because this is an implicit conversion (as is supposed to be the
219 // case with most type-erasing wrappers like this), we don't also need
220 // a converting assignment operator.
221
222 // </editor-fold> end Type-erasing converting ctor and assignment }}}3
223 //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
224
225 // </editor-fold> end Constructors, destructors, and assignment }}}2
226 //----------------------------------------------------------------------------
227
228 //----------------------------------------------------------------------------
229 // <editor-fold desc="then_parallel_for"> {{{2
230
231 template <
232 class Policy, class Functor,
233 std::enable_if_t<
234 // equivalent to:
235 // requires Kokkos::ExecutionPolicy<remove_cvref_t<Policy>>
236 is_execution_policy<Kokkos::Impl::remove_cvref_t<Policy>>::value,
237 // --------------------
238 int> = 0>
239 auto then_parallel_for(std::string arg_name, Policy&& arg_policy,
240 Functor&& functor) const {
241 //----------------------------------------
242 KOKKOS_EXPECTS(!m_graph_impl.expired())
243 KOKKOS_EXPECTS(bool(m_node_impl))
244 // TODO @graph restore this expectation once we add comparability to space
245 // instances
246 // KOKKOS_EXPECTS(
247 // arg_policy.space() == m_graph_impl->get_execution_space());
248
249 // needs to static assert constraint: DataParallelFunctor<Functor>
250
251 using policy_t = Kokkos::Impl::remove_cvref_t<Policy>;
252 // constraint check: same execution space type (or defaulted, maybe?)
253 static_assert(
254 std::is_same<typename policy_t::execution_space,
255 execution_space>::value,
256 // TODO @graph make defaulted execution space work
257 //|| policy_t::execution_space_is_defaulted,
258 "Execution Space mismatch between execution policy and graph");
259
260 auto policy = Experimental::require((Policy&&)arg_policy,
261 Kokkos::Impl::KernelInGraphProperty{});
262
263 using next_policy_t = decltype(policy);
264 using next_kernel_t =
265 Kokkos::Impl::GraphNodeKernelImpl<ExecutionSpace, next_policy_t,
266 std::decay_t<Functor>,
267 Kokkos::ParallelForTag>;
268 return this->_then_kernel(next_kernel_t{std::move(arg_name), policy.space(),
269 (Functor&&)functor,
270 (Policy&&)policy});
271 }
272
273 template <
274 class Policy, class Functor,
275 std::enable_if_t<
276 // equivalent to:
277 // requires Kokkos::ExecutionPolicy<remove_cvref_t<Policy>>
278 is_execution_policy<Kokkos::Impl::remove_cvref_t<Policy>>::value,
279 // --------------------
280 int> = 0>
281 auto then_parallel_for(Policy&& policy, Functor&& functor) const {
282 // needs to static assert constraint: DataParallelFunctor<Functor>
283 return this->then_parallel_for("", (Policy&&)policy, (Functor&&)functor);
284 }
285
286 template <class Functor>
287 auto then_parallel_for(std::string name, std::size_t n,
288 Functor&& functor) const {
289 // needs to static assert constraint: DataParallelFunctor<Functor>
290 return this->then_parallel_for(std::move(name),
291 Kokkos::RangePolicy<execution_space>(0, n),
292 (Functor&&)functor);
293 }
294
295 template <class Functor>
296 auto then_parallel_for(std::size_t n, Functor&& functor) const {
297 // needs to static assert constraint: DataParallelFunctor<Functor>
298 return this->then_parallel_for("", n, (Functor&&)functor);
299 }
300
301 // </editor-fold> end then_parallel_for }}}2
302 //----------------------------------------------------------------------------
303
304 //----------------------------------------------------------------------------
305 // <editor-fold desc="then_parallel_reduce"> {{{2
306
307 template <
308 class Policy, class Functor, class ReturnType,
309 std::enable_if_t<
310 // equivalent to:
311 // requires Kokkos::ExecutionPolicy<remove_cvref_t<Policy>>
312 is_execution_policy<Kokkos::Impl::remove_cvref_t<Policy>>::value,
313 // --------------------
314 int> = 0>
315 auto then_parallel_reduce(std::string arg_name, Policy&& arg_policy,
316 Functor&& functor,
317 ReturnType&& return_value) const {
318 auto graph_impl_ptr = m_graph_impl.lock();
319 KOKKOS_EXPECTS(bool(graph_impl_ptr))
320 KOKKOS_EXPECTS(bool(m_node_impl))
321 // TODO @graph restore this expectation once we add comparability to space
322 // instances
323 // KOKKOS_EXPECTS(
324 // arg_policy.space() == m_graph_impl->get_execution_space());
325
326 // needs static assertion of constraint:
327 // DataParallelReductionFunctor<Functor, ReturnType>
328
329 using policy_t = std::remove_cv_t<std::remove_reference_t<Policy>>;
330 static_assert(
331 std::is_same<typename policy_t::execution_space,
332 execution_space>::value,
333 // TODO @graph make defaulted execution space work
334 // || policy_t::execution_space_is_defaulted,
335 "Execution Space mismatch between execution policy and graph");
336
337 // This is also just an expectation, but it's one that we expect the user
338 // to interact with (even in release mode), so we should throw an exception
339 // with an explanation rather than just doing a contract assertion.
340 // We can't static_assert this because of the way that Reducers store
341 // whether or not they point to a View as a runtime boolean rather than part
342 // of the type.
343 if (Kokkos::Impl::parallel_reduce_needs_fence(
344 graph_impl_ptr->get_execution_space(), return_value)) {
345 Kokkos::Impl::throw_runtime_exception(
346 "Parallel reductions in graphs can't operate on Reducers that "
347 "reference a scalar because they can't complete synchronously. Use a "
348 "Kokkos::View instead and keep in mind the result will only be "
349 "available once the graph is submitted (or in tasks that depend on "
350 "this one).");
351 }
352
353 //----------------------------------------
354 // This is a disaster, but I guess it's not a my disaster to fix right now
355 using return_type_remove_cvref =
356 std::remove_cv_t<std::remove_reference_t<ReturnType>>;
357 static_assert(Kokkos::is_view<return_type_remove_cvref>::value ||
358 Kokkos::is_reducer<return_type_remove_cvref>::value,
359 "Output argument to parallel reduce in a graph must be a "
360 "View or a Reducer");
361
362 if constexpr (Kokkos::is_reducer_v<return_type_remove_cvref>) {
363 static_assert(
364 Kokkos::SpaceAccessibility<
365 ExecutionSpace, typename return_type_remove_cvref::
366 result_view_type::memory_space>::accessible,
367 "The reduction target must be accessible by the graph execution "
368 "space.");
369 } else {
370 static_assert(
371 Kokkos::SpaceAccessibility<
372 ExecutionSpace,
373 typename return_type_remove_cvref::memory_space>::accessible,
374 "The reduction target must be accessible by the graph execution "
375 "space.");
376 }
377
378 using return_type =
379 // Yes, you do really have to do this...
380 std::conditional_t<Kokkos::is_reducer<return_type_remove_cvref>::value,
381 return_type_remove_cvref,
382 const return_type_remove_cvref>;
383 using functor_type = Kokkos::Impl::remove_cvref_t<Functor>;
384 // see Kokkos_Parallel_Reduce.hpp for how these details are used there;
385 // we're just doing the same thing here
386 using return_value_adapter =
387 Kokkos::Impl::ParallelReduceReturnValue<void, return_type,
388 functor_type>;
389 // End of Kokkos reducer disaster
390 //----------------------------------------
391
392 auto policy = Experimental::require((Policy&&)arg_policy,
393 Kokkos::Impl::KernelInGraphProperty{});
394
395 using passed_reducer_type = typename return_value_adapter::reducer_type;
396
397 using reducer_selector = Kokkos::Impl::if_c<
398 std::is_same<InvalidType, passed_reducer_type>::value, functor_type,
399 passed_reducer_type>;
400 using analysis = Kokkos::Impl::FunctorAnalysis<
401 Kokkos::Impl::FunctorPatternInterface::REDUCE, Policy,
402 typename reducer_selector::type,
403 typename return_value_adapter::value_type>;
404 typename analysis::Reducer final_reducer(
405 reducer_selector::select(functor, return_value));
406 Kokkos::Impl::CombinedFunctorReducer<functor_type,
407 typename analysis::Reducer>
408 functor_reducer(functor, final_reducer);
409
410 using next_policy_t = decltype(policy);
411 using next_kernel_t =
412 Kokkos::Impl::GraphNodeKernelImpl<ExecutionSpace, next_policy_t,
413 decltype(functor_reducer),
414 Kokkos::ParallelReduceTag>;
415
416 return this->_then_kernel(next_kernel_t{
417 std::move(arg_name), graph_impl_ptr->get_execution_space(),
418 functor_reducer, (Policy&&)policy,
419 return_value_adapter::return_value(return_value, functor)});
420 }
421
422 template <
423 class Policy, class Functor, class ReturnType,
424 std::enable_if_t<
425 // equivalent to:
426 // requires Kokkos::ExecutionPolicy<remove_cvref_t<Policy>>
427 is_execution_policy<Kokkos::Impl::remove_cvref_t<Policy>>::value,
428 // --------------------
429 int> = 0>
430 auto then_parallel_reduce(Policy&& arg_policy, Functor&& functor,
431 ReturnType&& return_value) const {
432 return this->then_parallel_reduce("", (Policy&&)arg_policy,
433 (Functor&&)functor,
434 (ReturnType&&)return_value);
435 }
436
437 template <class Functor, class ReturnType>
438 auto then_parallel_reduce(std::string label,
439 typename execution_space::size_type idx_end,
440 Functor&& functor,
441 ReturnType&& return_value) const {
442 return this->then_parallel_reduce(
443 std::move(label), Kokkos::RangePolicy<execution_space>{0, idx_end},
444 (Functor&&)functor, (ReturnType&&)return_value);
445 }
446
447 template <class Functor, class ReturnType>
448 auto then_parallel_reduce(typename execution_space::size_type idx_end,
449 Functor&& functor,
450 ReturnType&& return_value) const {
451 return this->then_parallel_reduce("", idx_end, (Functor&&)functor,
452 (ReturnType&&)return_value);
453 }
454
455 // </editor-fold> end then_parallel_reduce }}}2
456 //----------------------------------------------------------------------------
457
458 // TODO @graph parallel scan, deep copy, etc.
459};
460
461} // end namespace Experimental
462} // end namespace Kokkos
463
464#endif // KOKKOS_KOKKOS_GRAPHNODE_HPP
ReturnType