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(
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 Kokkos::Impl::is_graph_capture_v<Kernel> ||
62 Kokkos::Impl::is_graph_then_host_v<Kernel>,
63 "Invalid kernel template parameter given to GraphNodeRef");
64
65 static_assert(!Kokkos::Impl::is_more_type_erased<Kernel, Predecessor>::value,
66 "The kernel of a graph node can't be more type-erased than the "
67 "predecessor");
68
69 // </editor-fold> end template parameter constraints }}}2
70 //----------------------------------------------------------------------------
71
72 public:
73 //----------------------------------------------------------------------------
74 // <editor-fold desc="public member types"> {{{2
75
76 using execution_space = ExecutionSpace;
77 using graph_kernel = Kernel;
78 using graph_predecessor = Predecessor;
79
80 // </editor-fold> end public member types }}}2
81 //----------------------------------------------------------------------------
82
83 private:
84 //----------------------------------------------------------------------------
85 // <editor-fold desc="Friends"> {{{2
86
87 template <class, class, class>
88 friend class GraphNodeRef;
89 friend struct Kokkos::Impl::GraphAccess;
90 friend struct Graph<execution_space>;
91
92 // </editor-fold> end Friends }}}2
93 //----------------------------------------------------------------------------
94
95 //----------------------------------------------------------------------------
96 // <editor-fold desc="Private Data Members"> {{{2
97
98 using graph_impl_t = Kokkos::Impl::GraphImpl<ExecutionSpace>;
99 std::weak_ptr<graph_impl_t> m_graph_impl;
100
101 // TODO @graphs figure out if we can get away with a weak reference here?
102 // GraphNodeRef instances shouldn't be stored by users outside
103 // of the create_graph closure, and so if we restructure things
104 // slightly, we could make it so that the graph owns the
105 // node_impl_t instance and this only holds a std::weak_ptr to
106 // it.
107 using node_impl_t =
108 Kokkos::Impl::GraphNodeImpl<ExecutionSpace, Kernel, Predecessor>;
109 std::shared_ptr<node_impl_t> m_node_impl;
110
111 // </editor-fold> end Private Data Members }}}2
112 //----------------------------------------------------------------------------
113
114 //----------------------------------------------------------------------------
115 // <editor-fold desc="Implementation detail accessors"> {{{2
116
117 // Internally, use shallow constness
118 node_impl_t& get_node_impl() const { return *m_node_impl.get(); }
119 std::shared_ptr<node_impl_t> const& get_node_ptr() const& {
120 return m_node_impl;
121 }
122 std::shared_ptr<node_impl_t> get_node_ptr() && {
123 return std::move(m_node_impl);
124 }
125 std::weak_ptr<graph_impl_t> get_graph_weak_ptr() const {
126 return m_graph_impl;
127 }
128
129 // </editor-fold> end Implementation detail accessors }}}2
130 //----------------------------------------------------------------------------
131
132 // TODO kernel name propagation and exposure
133
134 template <class NextKernelDeduced>
135 auto _then_kernel(NextKernelDeduced&& arg_kernel) const {
136 static_assert(Kokkos::Impl::is_graph_kernel_v<
137 Kokkos::Impl::remove_cvref_t<NextKernelDeduced>>,
138 "Kokkos internal error");
139
140 auto graph_ptr = m_graph_impl.lock();
141 KOKKOS_EXPECTS(bool(graph_ptr))
142
143 using next_kernel_t = Kokkos::Impl::remove_cvref_t<NextKernelDeduced>;
144
145 using return_t = GraphNodeRef<ExecutionSpace, next_kernel_t, GraphNodeRef>;
146
147 auto rv = Kokkos::Impl::GraphAccess::make_graph_node_ref(
148 m_graph_impl,
149 Kokkos::Impl::GraphAccess::make_node_shared_ptr<
150 typename return_t::node_impl_t>(
151 m_node_impl->execution_space_instance(),
152 Kokkos::Impl::_graph_node_kernel_ctor_tag{},
153 (NextKernelDeduced&&)arg_kernel,
154 // *this is the predecessor
155 Kokkos::Impl::_graph_node_predecessor_ctor_tag{}, *this));
156
157 // Add the node itself to the backend's graph data structure, now that
158 // everything is set up.
159 graph_ptr->add_node(rv.m_node_impl);
160 // Add the predecessor we stored in the constructor above in the backend's
161 // data structure, now that everything is set up.
162 graph_ptr->add_predecessor(rv.m_node_impl, *this);
163 KOKKOS_ENSURES(bool(rv.m_node_impl))
164 return rv;
165 }
166
167 //----------------------------------------------------------------------------
168 // <editor-fold desc="Private constructors"> {{{2
169
170 GraphNodeRef(std::weak_ptr<graph_impl_t> arg_graph_impl,
171 std::shared_ptr<node_impl_t> arg_node_impl)
172 : m_graph_impl(std::move(arg_graph_impl)),
173 m_node_impl(std::move(arg_node_impl)) {}
174
175 // </editor-fold> end Private constructors }}}2
176 //----------------------------------------------------------------------------
177
178 public:
179 //----------------------------------------------------------------------------
180 // <editor-fold desc="Constructors, destructors, and assignment"> {{{2
181
182 //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
183 // <editor-fold desc="rule of 6 ctors"> {{{3
184
185 // Copyable and movable (basically just shared_ptr semantics
186 GraphNodeRef() noexcept = default;
187 GraphNodeRef(GraphNodeRef const&) = default;
188 GraphNodeRef(GraphNodeRef&&) noexcept = default;
189 GraphNodeRef& operator=(GraphNodeRef const&) = default;
190 GraphNodeRef& operator=(GraphNodeRef&&) noexcept = default;
191 ~GraphNodeRef() = default;
192
193 // </editor-fold> end rule of 6 ctors }}}3
194 //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
195
196 //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
197 // <editor-fold desc="Type-erasing converting ctor and assignment"> {{{3
198
199 template <class OtherKernel, class OtherPredecessor,
200 std::enable_if_t<
201 // Not a copy/move constructor
202 !std::is_same_v<GraphNodeRef,
203 GraphNodeRef<execution_space, OtherKernel,
204 OtherPredecessor>> &&
205 // must be an allowed type erasure of the kernel
206 Kokkos::Impl::is_compatible_type_erasure<
207 OtherKernel, graph_kernel>::value &&
208 // must be an allowed type erasure of the predecessor
209 Kokkos::Impl::is_compatible_type_erasure<
210 OtherPredecessor, graph_predecessor>::value,
211 int> = 0>
212 /* implicit */
213 GraphNodeRef(
214 GraphNodeRef<execution_space, OtherKernel, OtherPredecessor> const& other)
215 : m_graph_impl(other.m_graph_impl), m_node_impl(other.m_node_impl) {}
216
217 // Note: because this is an implicit conversion (as is supposed to be the
218 // case with most type-erasing wrappers like this), we don't also need
219 // a converting assignment operator.
220
221 // </editor-fold> end Type-erasing converting ctor and assignment }}}3
222 //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
223
224 // </editor-fold> end Constructors, destructors, and assignment }}}2
225 //----------------------------------------------------------------------------
226
227 //----------------------------------------------------------------------------
228 // <editor-fold desc="then_parallel_for"> {{{2
229
230 // TODO We should do better than a p-for (that uses registers, heavier).
231 // This should "just" launch the function on device with our driver.
232 template <typename Label, typename Functor,
233 typename = std::enable_if_t<std::is_invocable_r_v<
234 void, const Kokkos::Impl::remove_cvref_t<Functor>>>>
235 auto then(Label&& label, const ExecutionSpace& exec,
236 Functor&& functor) const {
237 using next_kernel_t =
238 Kokkos::Impl::GraphNodeThenImpl<ExecutionSpace,
239 Kokkos::Impl::remove_cvref_t<Functor>>;
240 return this->_then_kernel(next_kernel_t{std::forward<Label>(label), exec,
241 std::forward<Functor>(functor)});
242 }
243
244 template <typename Label, typename Functor,
245 typename = std::enable_if_t<std::is_invocable_r_v<
246 void, const Kokkos::Impl::remove_cvref_t<Functor>>>>
247 auto then(Label&& label, Functor&& functor) const {
248 return this->then(std::forward<Label>(label), ExecutionSpace{},
249 std::forward<Functor>(functor));
250 }
251
252 template <typename Label, typename Functor>
253 auto then_host(Label&&, Functor&& functor) const {
254 using host_t = Kokkos::Impl::GraphNodeThenHostImpl<
255 ExecutionSpace, Kokkos::Impl::remove_cvref_t<Functor>>;
256 using return_t = GraphNodeRef<ExecutionSpace, host_t, GraphNodeRef>;
257
258 auto graph_ptr = m_graph_impl.lock();
259 KOKKOS_EXPECTS(bool(graph_ptr))
260
261 auto rv = Kokkos::Impl::GraphAccess::make_graph_node_ref(
262 m_graph_impl,
263 Kokkos::Impl::GraphAccess::make_node_shared_ptr<
264 typename return_t::node_impl_t>(
265 m_node_impl->execution_space_instance(),
266 Kokkos::Impl::_graph_node_host_ctor_tag{},
267 std::forward<Functor>(functor),
268 Kokkos::Impl::_graph_node_predecessor_ctor_tag{}, *this));
269
270 // Add the node itself to the backend's graph data structure, now that
271 // everything is set up.
272 graph_ptr->add_node(rv.m_node_impl);
273 // Add the predecessor we stored in the constructor above in the
274 // backend's data structure, now that everything is set up.
275 graph_ptr->add_predecessor(rv.m_node_impl, *this);
276 KOKKOS_ENSURES(bool(rv.m_node_impl))
277 return rv;
278 }
279
280#if defined(KOKKOS_ENABLE_CUDA) || \
281 (defined(KOKKOS_ENABLE_HIP) && defined(KOKKOS_IMPL_HIP_NATIVE_GRAPH)) || \
282 (defined(KOKKOS_ENABLE_SYCL) && defined(KOKKOS_IMPL_SYCL_GRAPH_SUPPORT))
283 template <class Functor,
284 typename = std::enable_if_t<std::is_invocable_r_v<
285 void, const Kokkos::Impl::remove_cvref_t<Functor>,
286 const ExecutionSpace&>>>
287#if defined(KOKKOS_ENABLE_CUDA)
288 auto cuda_capture(const ExecutionSpace& exec, Functor&& functor) const {
289 if constexpr (std::is_same_v<ExecutionSpace, Kokkos::Cuda>) {
290#elif defined(KOKKOS_ENABLE_HIP) && defined(KOKKOS_IMPL_HIP_NATIVE_GRAPH)
291 auto hip_capture(const ExecutionSpace& exec, Functor&& functor) const {
292 if constexpr (std::is_same_v<ExecutionSpace, Kokkos::HIP>) {
293#elif defined(KOKKOS_ENABLE_SYCL) && defined(KOKKOS_IMPL_SYCL_GRAPH_SUPPORT)
294 auto sycl_capture(const ExecutionSpace& exec, Functor&& functor) const {
295 if constexpr (std::is_same_v<ExecutionSpace, Kokkos::SYCL>) {
296#endif
297 using capture_t = Kokkos::Impl::GraphNodeCaptureImpl<
298 ExecutionSpace, Kokkos::Impl::remove_cvref_t<Functor>>;
299 using return_t = GraphNodeRef<ExecutionSpace, capture_t, GraphNodeRef>;
300
301 auto graph_ptr = m_graph_impl.lock();
302 KOKKOS_EXPECTS(bool(graph_ptr))
303
304 auto rv = Kokkos::Impl::GraphAccess::make_graph_node_ref(
305 m_graph_impl,
306 Kokkos::Impl::GraphAccess::make_node_shared_ptr<
307 typename return_t::node_impl_t>(
308 m_node_impl->execution_space_instance(),
309 Kokkos::Impl::_graph_node_capture_ctor_tag{},
310 std::forward<Functor>(functor),
311 Kokkos::Impl::_graph_node_predecessor_ctor_tag{}, *this));
312
313 // Add the node itself to the backend's graph data structure, now that
314 // everything is set up.
315 graph_ptr->add_node(exec, rv.m_node_impl);
316 // Add the predecessor we stored in the constructor above in the
317 // backend's data structure, now that everything is set up.
318 graph_ptr->add_predecessor(rv.m_node_impl, *this);
319 KOKKOS_ENSURES(bool(rv.m_node_impl))
320 return rv;
321 }
322 }
323#endif
324
325 template <
326 class Policy, class Functor,
327 std::enable_if_t<
328 // equivalent to:
329 // requires Kokkos::ExecutionPolicy<remove_cvref_t<Policy>>
330 is_execution_policy<Kokkos::Impl::remove_cvref_t<Policy>>::value,
331 // --------------------
332 int> = 0>
333 auto then_parallel_for(std::string arg_name, Policy&& arg_policy,
334 Functor&& functor) const {
335 //----------------------------------------
336 KOKKOS_EXPECTS(!m_graph_impl.expired())
337 KOKKOS_EXPECTS(bool(m_node_impl))
338 // TODO @graph restore this expectation once we add comparability to space
339 // instances
340 // KOKKOS_EXPECTS(
341 // arg_policy.space() == m_graph_impl->get_execution_space());
342
343 // needs to static assert constraint: DataParallelFunctor<Functor>
344
345 using policy_t = Kokkos::Impl::remove_cvref_t<Policy>;
346 // constraint check: same execution space type (or defaulted, maybe?)
347 static_assert(
348 std::is_same_v<typename policy_t::execution_space, execution_space>,
349 // TODO @graph make defaulted execution space work
350 //|| policy_t::execution_space_is_defaulted,
351 "Execution Space mismatch between execution policy and graph");
352
353 auto policy = Experimental::require((Policy&&)arg_policy,
354 Kokkos::Impl::KernelInGraphProperty{});
355
356 using next_policy_t = decltype(policy);
357 using next_kernel_t =
358 Kokkos::Impl::GraphNodeKernelImpl<ExecutionSpace, next_policy_t,
359 std::decay_t<Functor>,
360 Kokkos::ParallelForTag>;
361 return this->_then_kernel(next_kernel_t{std::move(arg_name), policy.space(),
362 (Functor&&)functor,
363 (Policy&&)policy});
364 }
365
366 template <
367 class Policy, class Functor,
368 std::enable_if_t<
369 // equivalent to:
370 // requires Kokkos::ExecutionPolicy<remove_cvref_t<Policy>>
371 is_execution_policy<Kokkos::Impl::remove_cvref_t<Policy>>::value,
372 // --------------------
373 int> = 0>
374 auto then_parallel_for(Policy&& policy, Functor&& functor) const {
375 // needs to static assert constraint: DataParallelFunctor<Functor>
376 return this->then_parallel_for("", (Policy&&)policy, (Functor&&)functor);
377 }
378
379 template <class Functor>
380 auto then_parallel_for(std::string name, std::size_t n,
381 Functor&& functor) const {
382 // needs to static assert constraint: DataParallelFunctor<Functor>
383 return this->then_parallel_for(std::move(name),
385 (Functor&&)functor);
386 }
387
388 template <class Functor>
389 auto then_parallel_for(std::size_t n, Functor&& functor) const {
390 // needs to static assert constraint: DataParallelFunctor<Functor>
391 return this->then_parallel_for("", n, (Functor&&)functor);
392 }
393
394 // </editor-fold> end then_parallel_for }}}2
395 //----------------------------------------------------------------------------
396
397 //----------------------------------------------------------------------------
398 // <editor-fold desc="then_parallel_reduce"> {{{2
399
400 // Equivalent to std::get<I>(std::tuple) but callable on the device.
401 template <bool B, class T1, class T2>
402 static KOKKOS_FUNCTION std::conditional_t<B, T1&&, T2&&>
403 impl_forwarding_switch(T1&& v1, T2&& v2) {
404 if constexpr (B)
405 return static_cast<T1&&>(v1);
406 else
407 return static_cast<T2&&>(v2);
408 }
409
410 template <
411 class Policy, class Functor, class ReturnType,
412 std::enable_if_t<
413 // equivalent to:
414 // requires Kokkos::ExecutionPolicy<remove_cvref_t<Policy>>
415 is_execution_policy<Kokkos::Impl::remove_cvref_t<Policy>>::value,
416 // --------------------
417 int> = 0>
418 auto then_parallel_reduce(std::string arg_name, Policy&& arg_policy,
419 Functor&& functor,
420 ReturnType&& return_value) const {
421 auto graph_impl_ptr = m_graph_impl.lock();
422 KOKKOS_EXPECTS(bool(graph_impl_ptr))
423 KOKKOS_EXPECTS(bool(m_node_impl))
424 // TODO @graph restore this expectation once we add comparability to space
425 // instances
426 // KOKKOS_EXPECTS(
427 // arg_policy.space() == m_graph_impl->get_execution_space());
428
429 // needs static assertion of constraint:
430 // DataParallelReductionFunctor<Functor, ReturnType>
431
432 using policy_t = std::remove_cv_t<std::remove_reference_t<Policy>>;
433 static_assert(
434 std::is_same_v<typename policy_t::execution_space, execution_space>,
435 // TODO @graph make defaulted execution space work
436 // || policy_t::execution_space_is_defaulted,
437 "Execution Space mismatch between execution policy and graph");
438
439 // This is also just an expectation, but it's one that we expect the user
440 // to interact with (even in release mode), so we should throw an exception
441 // with an explanation rather than just doing a contract assertion.
442 // We can't static_assert this because of the way that Reducers store
443 // whether or not they point to a View as a runtime boolean rather than part
444 // of the type.
445 if (Kokkos::Impl::parallel_reduce_needs_fence(
446 graph_impl_ptr->get_execution_space(), return_value)) {
447 Kokkos::Impl::throw_runtime_exception(
448 "Parallel reductions in graphs can't operate on Reducers that "
449 "reference a scalar because they can't complete synchronously. Use a "
450 "Kokkos::View instead and keep in mind the result will only be "
451 "available once the graph is submitted (or in tasks that depend on "
452 "this one).");
453 }
454
455 //----------------------------------------
456 // This is a disaster, but I guess it's not a my disaster to fix right now
457 using return_type_remove_cvref =
458 std::remove_cv_t<std::remove_reference_t<ReturnType>>;
459 static_assert(Kokkos::is_view<return_type_remove_cvref>::value ||
461 "Output argument to parallel reduce in a graph must be a "
462 "View or a Reducer");
463
465 static_assert(
467 ExecutionSpace, typename return_type_remove_cvref::
468 result_view_type::memory_space>::accessible,
469 "The reduction target must be accessible by the graph execution "
470 "space.");
471 } else {
472 static_assert(
474 ExecutionSpace,
475 typename return_type_remove_cvref::memory_space>::accessible,
476 "The reduction target must be accessible by the graph execution "
477 "space.");
478 }
479
480 using return_type =
481 // Yes, you do really have to do this...
482 std::conditional_t<Kokkos::is_reducer<return_type_remove_cvref>::value,
483 return_type_remove_cvref,
484 const return_type_remove_cvref>;
485 using functor_type = Kokkos::Impl::remove_cvref_t<Functor>;
486 // see Kokkos_Parallel_Reduce.hpp for how these details are used there;
487 // we're just doing the same thing here
488 using return_value_adapter =
489 Kokkos::Impl::ParallelReduceReturnValue<void, return_type,
490 functor_type>;
491 // End of Kokkos reducer disaster
492 //----------------------------------------
493
494 auto policy = Experimental::require((Policy&&)arg_policy,
495 Kokkos::Impl::KernelInGraphProperty{});
496
497 using passed_reducer_type = typename return_value_adapter::reducer_type;
498
499 constexpr bool passed_reducer_type_is_invalid =
500 std::is_same_v<InvalidType, passed_reducer_type>;
501 using TheReducerType =
502 std::conditional_t<passed_reducer_type_is_invalid, functor_type,
503 passed_reducer_type>;
504
505 using analysis = Kokkos::Impl::FunctorAnalysis<
506 Kokkos::Impl::FunctorPatternInterface::REDUCE, Policy, TheReducerType,
507 typename return_value_adapter::value_type>;
508 typename analysis::Reducer final_reducer(
509 impl_forwarding_switch<passed_reducer_type_is_invalid>(functor,
510 return_value));
511 Kokkos::Impl::CombinedFunctorReducer<functor_type,
512 typename analysis::Reducer>
513 functor_reducer(functor, final_reducer);
514
515 using next_policy_t = decltype(policy);
516 using next_kernel_t =
517 Kokkos::Impl::GraphNodeKernelImpl<ExecutionSpace, next_policy_t,
518 decltype(functor_reducer),
519 Kokkos::ParallelReduceTag>;
520
521 return this->_then_kernel(next_kernel_t{
522 std::move(arg_name), graph_impl_ptr->get_execution_space(),
523 functor_reducer, (Policy&&)policy,
524 return_value_adapter::return_value(return_value, functor)});
525 }
526
527 template <
528 class Policy, class Functor, class ReturnType,
529 std::enable_if_t<
530 // equivalent to:
531 // requires Kokkos::ExecutionPolicy<remove_cvref_t<Policy>>
532 is_execution_policy<Kokkos::Impl::remove_cvref_t<Policy>>::value,
533 // --------------------
534 int> = 0>
535 auto then_parallel_reduce(Policy&& arg_policy, Functor&& functor,
536 ReturnType&& return_value) const {
537 return this->then_parallel_reduce("", (Policy&&)arg_policy,
538 (Functor&&)functor,
539 (ReturnType&&)return_value);
540 }
541
542 template <class Functor, class ReturnType>
543 auto then_parallel_reduce(std::string label,
544 typename execution_space::size_type idx_end,
545 Functor&& functor,
546 ReturnType&& return_value) const {
547 return this->then_parallel_reduce(
548 std::move(label), Kokkos::RangePolicy<execution_space>{0, idx_end},
549 (Functor&&)functor, (ReturnType&&)return_value);
550 }
551
552 template <class Functor, class ReturnType>
553 auto then_parallel_reduce(typename execution_space::size_type idx_end,
554 Functor&& functor,
555 ReturnType&& return_value) const {
556 return this->then_parallel_reduce("", idx_end, (Functor&&)functor,
557 (ReturnType&&)return_value);
558 }
559
560 // </editor-fold> end then_parallel_reduce }}}2
561 //----------------------------------------------------------------------------
562
563 // TODO @graph parallel scan, deep copy, etc.
564};
565
566} // end namespace Experimental
567} // end namespace Kokkos
568
569#endif // KOKKOS_KOKKOS_GRAPHNODE_HPP
A thread safe view to a bitset.
ReturnType
Can AccessSpace access MemorySpace ?