Kokkos Core Kernels Package Version of the Day
Loading...
Searching...
No Matches
Kokkos_DualView.hpp
Go to the documentation of this file.
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
22
23#ifndef KOKKOS_DUALVIEW_HPP
24#define KOKKOS_DUALVIEW_HPP
25#ifndef KOKKOS_IMPL_PUBLIC_INCLUDE
26#define KOKKOS_IMPL_PUBLIC_INCLUDE
27#define KOKKOS_IMPL_PUBLIC_INCLUDE_NOTDEFINED_DUALVIEW
28#endif
29
30#include <Kokkos_Core.hpp>
31#include <impl/Kokkos_Error.hpp>
32
33namespace Kokkos {
34
35/* \class DualView
36 * \brief Container to manage mirroring a Kokkos::View that lives
37 * in device memory with a Kokkos::View that lives in host memory.
38 *
39 * This class provides capabilities to manage data which exists in two
40 * memory spaces at the same time. It keeps views of the same layout
41 * on two memory spaces as well as modified flags for both
42 * allocations. Users are responsible for setting the modified flags
43 * manually if they change the data in either memory space, by calling
44 * the sync() method templated on the device where they modified the
45 * data. Users may synchronize data by calling the modify() function,
46 * templated on the device towards which they want to synchronize
47 * (i.e., the target of the one-way copy operation).
48 *
49 * The DualView class also provides convenience methods such as
50 * realloc, resize and capacity which call the appropriate methods of
51 * the underlying Kokkos::View objects.
52 *
53 * The four template arguments are the same as those of Kokkos::View.
54 * (Please refer to that class' documentation for a detailed
55 * description.)
56 *
57 * \tparam DataType The type of the entries stored in the container.
58 *
59 * \tparam Layout The array's layout in memory.
60 *
61 * \tparam Device The Kokkos Device type. If its memory space is
62 * not the same as the host's memory space, then DualView will
63 * contain two separate Views: one in device memory, and one in
64 * host memory. Otherwise, DualView will only store one View.
65 *
66 * \tparam MemoryTraits (optional) The user's intended memory access
67 * behavior. Please see the documentation of Kokkos::View for
68 * examples. The default suffices for most users.
69 */
70
71namespace Impl {
72
73#ifdef KOKKOS_ENABLE_CUDA
74
75inline const Kokkos::Cuda& get_cuda_space(const Kokkos::Cuda& in) { return in; }
76
77inline const Kokkos::Cuda& get_cuda_space() {
78 return *Kokkos::Impl::cuda_get_deep_copy_space();
79}
80
81template <typename NonCudaExecSpace>
82inline const Kokkos::Cuda& get_cuda_space(const NonCudaExecSpace&) {
83 return get_cuda_space();
84}
85
86#endif // KOKKOS_ENABLE_CUDA
87
88} // namespace Impl
89
90#ifdef KOKKOS_ENABLE_DEPRECATED_CODE_4
91template <class DataType, class Arg1Type = void, class Arg2Type = void,
92 class Arg3Type = void>
93class DualView;
94#else
95template <class DataType, class... Properties>
96class DualView;
97#endif
98
99template <class>
100struct is_dual_view : public std::false_type {};
101
102template <class DT, class... DP>
103struct is_dual_view<DualView<DT, DP...>> : public std::true_type {};
104
105template <class DT, class... DP>
106struct is_dual_view<const DualView<DT, DP...>> : public std::true_type {};
107
108template <class T>
109inline constexpr bool is_dual_view_v = is_dual_view<T>::value;
110
111#ifdef KOKKOS_ENABLE_DEPRECATED_CODE_4
112template <class DataType, class Arg1Type, class Arg2Type, class Arg3Type>
113class DualView : public ViewTraits<DataType, Arg1Type, Arg2Type, Arg3Type> {
114 template <class, class, class, class>
115#else
116template <class DataType, class... Properties>
117class DualView : public ViewTraits<DataType, Properties...> {
118 template <class, class...>
119#endif
120 friend class DualView;
121
122 public:
124
125#ifdef KOKKOS_ENABLE_DEPRECATED_CODE_4
126 using traits = ViewTraits<DataType, Arg1Type, Arg2Type, Arg3Type>;
127#else
128 using traits = ViewTraits<DataType, Properties...>;
129#endif
130
132 using host_mirror_space = typename traits::host_mirror_space;
133
135#ifdef KOKKOS_ENABLE_DEPRECATED_CODE_4
136 using t_dev = View<typename traits::data_type, Arg1Type, Arg2Type, Arg3Type>;
137#else
138 using t_dev = View<typename traits::data_type, Properties...>;
139#endif
140
143 using t_host = typename t_dev::HostMirror;
144
147#ifdef KOKKOS_ENABLE_DEPRECATED_CODE_4
148 using t_dev_const =
149 View<typename traits::const_data_type, Arg1Type, Arg2Type, Arg3Type>;
150#else
151 using t_dev_const = View<typename traits::const_data_type, Properties...>;
152#endif
153
156 using t_host_const = typename t_dev_const::HostMirror;
157
159 using t_dev_const_randomread =
160 View<typename traits::const_data_type, typename traits::array_layout,
161 typename traits::device_type,
163
167 using t_host_const_randomread = typename t_dev_const_randomread::HostMirror;
168
170 using t_dev_um =
171 View<typename traits::data_type, typename traits::array_layout,
172 typename traits::device_type, MemoryUnmanaged>;
173
175 using t_host_um =
176 View<typename t_host::data_type, typename t_host::array_layout,
177 typename t_host::device_type, MemoryUnmanaged>;
178
180 using t_dev_const_um =
181 View<typename traits::const_data_type, typename traits::array_layout,
182 typename traits::device_type, MemoryUnmanaged>;
183
185 using t_host_const_um =
186 View<typename t_host::const_data_type, typename t_host::array_layout,
187 typename t_host::device_type, MemoryUnmanaged>;
188
190 using t_dev_const_randomread_um =
191 View<typename t_host::const_data_type, typename t_host::array_layout,
192 typename t_host::device_type,
194
198 using t_host_const_randomread_um =
199 typename t_dev_const_randomread_um::HostMirror;
200
202
204
205 protected:
206 // modified_flags[0] -> host
207 // modified_flags[1] -> device
208 using t_modified_flags = View<unsigned int[2], LayoutLeft, Kokkos::HostSpace>;
209 t_modified_flags modified_flags;
210
211 public:
212 // does the DualView have only one device
213 static constexpr bool impl_dualview_is_single_device =
214 std::is_same_v<typename t_dev::device_type, typename t_host::device_type>;
216
217#ifdef KOKKOS_ENABLE_DEPRECATED_CODE_4
218 public:
219#else
220 private:
221#endif
222
223 // Moved this specifically after modified_flags to resolve an alignment issue
224 // on MSVC/NVCC
226
227 t_dev d_view;
228 t_host h_view;
230
231 public:
233
234
239 DualView() = default;
240
250 DualView(const std::string& label,
251 const size_t n0 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
252 const size_t n1 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
253 const size_t n2 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
254 const size_t n3 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
255 const size_t n4 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
256 const size_t n5 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
257 const size_t n6 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
258 const size_t n7 = KOKKOS_IMPL_CTOR_DEFAULT_ARG)
259 : modified_flags(
260 Kokkos::view_alloc(typename t_modified_flags::execution_space{},
261 "DualView::modified_flags")),
262 d_view(label, n0, n1, n2, n3, n4, n5, n6, n7),
263 h_view(create_mirror_view(d_view)) // without UVM, host View mirrors
264 {}
265
276 template <class... P>
277 DualView(const Impl::ViewCtorProp<P...>& arg_prop,
278 std::enable_if_t<!Impl::ViewCtorProp<P...>::has_pointer,
279 size_t> const n0 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
280 const size_t n1 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
281 const size_t n2 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
282 const size_t n3 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
283 const size_t n4 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
284 const size_t n5 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
285 const size_t n6 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
286 const size_t n7 = KOKKOS_IMPL_CTOR_DEFAULT_ARG)
287 : modified_flags(t_modified_flags("DualView::modified_flags")) {
288 if constexpr (Impl::ViewCtorProp<P...>::sequential_host_init) {
289 h_view = t_host(arg_prop, n0, n1, n2, n3, n4, n5, n6, n7);
290 static_assert(Impl::ViewCtorProp<P...>::initialize,
291 "DualView: SequentialHostInit isn't compatible with "
292 "WithoutInitializing!");
293 static_assert(!Impl::ViewCtorProp<P...>::has_execution_space,
294 "DualView: SequentialHostInit isn't compatible with "
295 "providing an execution space instance!");
296
297 d_view = Kokkos::create_mirror_view_and_copy(
298 typename traits::memory_space{}, h_view);
299 } else {
300 d_view = t_dev(arg_prop, n0, n1, n2, n3, n4, n5, n6, n7);
301
302 // without UVM, host View mirrors
303 if constexpr (Kokkos::Impl::has_type<Impl::WithoutInitializing_t,
304 P...>::value)
305 h_view =
306 Kokkos::create_mirror_view(Kokkos::WithoutInitializing, d_view);
307 else
308 h_view = Kokkos::create_mirror_view(d_view);
309 }
310 }
311
313 template <typename DT, typename... DP>
314 DualView(const DualView<DT, DP...>& src)
315 : modified_flags(src.modified_flags),
316 d_view(src.d_view),
317 h_view(src.h_view) {}
318
320 template <class DT, class... DP, class Arg0, class... Args>
321 DualView(const DualView<DT, DP...>& src, const Arg0& arg0, Args... args)
322 : modified_flags(src.modified_flags),
323 d_view(Kokkos::subview(src.d_view, arg0, args...)),
324 h_view(Kokkos::subview(src.h_view, arg0, args...)) {}
325
336 DualView(const t_dev& d_view_, const t_host& h_view_)
337 : modified_flags(t_modified_flags("DualView::modified_flags")),
338 d_view(d_view_),
339 h_view(h_view_) {
340 if (int(d_view.rank) != int(h_view.rank) ||
341 [&]() {
342 // This has a false positive in clang-tidy
343 // NOLINTNEXTLINE(bugprone-inc-dec-in-conditions)
344 for (size_t r = 0; r < d_view.rank(); ++r) {
345 if (d_view.extent(r) != h_view.extent(r) ||
346 d_view.stride(r) != h_view.stride(r))
347 return true;
348 }
349 return false;
350 }() ||
351 d_view.span() != h_view.span()) {
352 Kokkos::Impl::throw_runtime_exception(
353 "DualView constructed with incompatible views");
354 }
356 typename t_dev::memory_space>::accessible &&
357 (d_view.data() != h_view.data()))
358 Kokkos::abort(
359 "DualView storing one View constructed from two different Views");
360 }
361
363
365
383 template <class Device>
384 KOKKOS_FUNCTION auto view() const {
385 if constexpr (std::is_same_v<Device, typename Device::memory_space>) {
386 if constexpr (std::is_same_v<typename Device::memory_space,
387 typename t_dev::memory_space>) {
388 return d_view;
389 } else {
390 static_assert(std::is_same_v<typename Device::memory_space,
391 typename t_host::memory_space>,
392 "The template argument is a memory space but doesn't "
393 "match either of DualView's memory spaces!");
394 return h_view;
395 }
396 } else {
397 if constexpr (std::is_same_v<Device, typename Device::execution_space>) {
398 if constexpr (std::is_same_v<typename Device::execution_space,
399 typename t_dev::execution_space>) {
400 return d_view;
401 } else {
402 static_assert(std::is_same_v<typename Device::execution_space,
403 typename t_host::execution_space>,
404 "The template argument is an execution space but "
405 "doesn't match either of DualView's execution spaces!");
406 return h_view;
407 }
408 } else {
409 static_assert(std::is_same_v<Device, typename Device::device_type>,
410 "The template argument is neither a memory space, "
411 "execution space, or device!");
412 if constexpr (std::is_same_v<Device, typename t_dev::device_type>)
413 return d_view;
414 else {
415 static_assert(std::is_same_v<Device, typename t_host::device_type>,
416 "The template argument is a device but "
417 "doesn't match either of DualView's devices!");
418 return h_view;
419 }
420 }
421 }
422 }
423
424#ifdef KOKKOS_ENABLE_DEPRECATED_CODE_4
425 KOKKOS_INLINE_FUNCTION
426 t_host view_host() const { return h_view; }
427
428 KOKKOS_INLINE_FUNCTION
429 t_dev view_device() const { return d_view; }
430#else
431 KOKKOS_INLINE_FUNCTION
432 const t_host& view_host() const { return h_view; }
433
434 KOKKOS_INLINE_FUNCTION
435 const t_dev& view_device() const { return d_view; }
436#endif
437
438 KOKKOS_INLINE_FUNCTION constexpr bool is_allocated() const {
439 return (d_view.is_allocated() && h_view.is_allocated());
440 }
441
442 template <class Device>
443 static int get_device_side() {
444 constexpr bool device_is_memspace =
445 std::is_same_v<Device, typename Device::memory_space>;
446 constexpr bool device_is_execspace =
447 std::is_same_v<Device, typename Device::execution_space>;
448 constexpr bool device_exec_is_t_dev_exec =
449 std::is_same_v<typename Device::execution_space,
450 typename t_dev::execution_space>;
451 constexpr bool device_mem_is_t_dev_mem =
452 std::is_same_v<typename Device::memory_space,
453 typename t_dev::memory_space>;
454 constexpr bool device_exec_is_t_host_exec =
455 std::is_same_v<typename Device::execution_space,
456 typename t_host::execution_space>;
457 constexpr bool device_mem_is_t_host_mem =
458 std::is_same_v<typename Device::memory_space,
459 typename t_host::memory_space>;
460 constexpr bool device_is_t_host_device =
461 std::is_same_v<typename Device::execution_space,
462 typename t_host::device_type>;
463 constexpr bool device_is_t_dev_device =
464 std::is_same_v<typename Device::memory_space,
465 typename t_host::device_type>;
466
467 static_assert(
468 device_is_t_dev_device || device_is_t_host_device ||
469 (device_is_memspace &&
470 (device_mem_is_t_dev_mem || device_mem_is_t_host_mem)) ||
471 (device_is_execspace &&
472 (device_exec_is_t_dev_exec || device_exec_is_t_host_exec)) ||
473 ((!device_is_execspace && !device_is_memspace) &&
474 ((device_mem_is_t_dev_mem || device_mem_is_t_host_mem) ||
475 (device_exec_is_t_dev_exec || device_exec_is_t_host_exec))),
476 "Template parameter to .sync() must exactly match one of the "
477 "DualView's device types or one of the execution or memory spaces");
478
479 int dev = -1;
480 if (device_is_t_dev_device)
481 dev = 1;
482 else if (device_is_t_host_device)
483 dev = 0;
484 else {
485 if (device_is_memspace) {
486 if (device_mem_is_t_dev_mem) dev = 1;
487 if (device_mem_is_t_host_mem) dev = 0;
488 if (device_mem_is_t_host_mem && device_mem_is_t_dev_mem) dev = -1;
489 }
490 if (device_is_execspace) {
491 if (device_exec_is_t_dev_exec) dev = 1;
492 if (device_exec_is_t_host_exec) dev = 0;
493 if (device_exec_is_t_host_exec && device_exec_is_t_dev_exec) dev = -1;
494 }
495 if (!device_is_execspace && !device_is_memspace) {
496 if (device_mem_is_t_dev_mem) dev = 1;
497 if (device_mem_is_t_host_mem) dev = 0;
498 if (device_mem_is_t_host_mem && device_mem_is_t_dev_mem) dev = -1;
499 if (device_exec_is_t_dev_exec) dev = 1;
500 if (device_exec_is_t_host_exec) dev = 0;
501 if (device_exec_is_t_host_exec && device_exec_is_t_dev_exec) dev = -1;
502 }
503 }
504 return dev;
505 }
506 static constexpr const int view_header_size = 128;
507 void impl_report_host_sync() const noexcept {
508 if (Kokkos::Tools::Experimental::get_callbacks().sync_dual_view !=
509 nullptr) {
510 Kokkos::Tools::syncDualView(
511 h_view.label(),
512 reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(h_view.data()) -
513 view_header_size),
514 false);
515 }
516 }
517 void impl_report_device_sync() const noexcept {
518 if (Kokkos::Tools::Experimental::get_callbacks().sync_dual_view !=
519 nullptr) {
520 Kokkos::Tools::syncDualView(
521 d_view.label(),
522 reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(d_view.data()) -
523 view_header_size),
524 true);
525 }
526 }
527
545 // deliberately passing args by cref as they're used multiple times
546 template <class Device, class... Args>
547 void sync_impl(std::true_type, Args const&... args) {
548 if (modified_flags.data() == nullptr) return;
549
550 int dev = get_device_side<Device>();
551
552 if (dev == 1) { // if Device is the same as DualView's device type
553 if ((modified_flags(0) > 0) && (modified_flags(0) >= modified_flags(1))) {
554#ifdef KOKKOS_ENABLE_CUDA
555 if (std::is_same<typename t_dev::memory_space,
556 Kokkos::CudaUVMSpace>::value) {
557 if (d_view.data() == h_view.data())
558 Kokkos::Impl::cuda_prefetch_pointer(
559 Impl::get_cuda_space(args...), d_view.data(),
560 sizeof(typename t_dev::value_type) * d_view.span(), true);
561 }
562#endif
563
564 deep_copy(args..., d_view, h_view);
565 modified_flags(0) = modified_flags(1) = 0;
566 impl_report_device_sync();
567 }
568 }
569 if (dev == 0) { // hopefully Device is the same as DualView's host type
570 if ((modified_flags(1) > 0) && (modified_flags(1) >= modified_flags(0))) {
571#ifdef KOKKOS_ENABLE_CUDA
572 if (std::is_same<typename t_dev::memory_space,
573 Kokkos::CudaUVMSpace>::value) {
574 if (d_view.data() == h_view.data())
575 Kokkos::Impl::cuda_prefetch_pointer(
576 Impl::get_cuda_space(args...), d_view.data(),
577 sizeof(typename t_dev::value_type) * d_view.span(), false);
578 }
579#endif
580
581 deep_copy(args..., h_view, d_view);
582 modified_flags(0) = modified_flags(1) = 0;
583 impl_report_host_sync();
584 }
585 }
586 if constexpr (std::is_same_v<typename t_host::memory_space,
587 typename t_dev::memory_space>) {
588 typename t_dev::execution_space().fence(
589 "Kokkos::DualView<>::sync: fence after syncing DualView");
590 typename t_host::execution_space().fence(
591 "Kokkos::DualView<>::sync: fence after syncing DualView");
592 }
593 }
594
595 template <class Device>
596 void sync() {
597 if constexpr (impl_dualview_is_single_device) {
598 // FIXME_DUALVIEW_ASYNCHRONOUS_BACKENDS
599 // Kokkos::fence(
600 // "Kokkos::DualView: fence for sync with host-accessible memory
601 // space");
602 return;
603 } else {
604 sync_impl<Device>(
605 typename std::is_same<typename traits::data_type,
606 typename traits::non_const_data_type>::type{});
607 }
608 }
609
610 template <class Device, class ExecutionSpace>
611 void sync([[maybe_unused]] const ExecutionSpace& exec) {
612 if constexpr (impl_dualview_is_single_device)
613 return;
614 else {
615 sync_impl<Device>(
616 typename std::is_same<typename traits::data_type,
617 typename traits::non_const_data_type>::type{},
618 exec);
619 }
620 }
621
622 // deliberately passing args by cref as they're used multiple times
623 template <class Device, class... Args>
624 void sync_impl(std::false_type, Args const&...) {
625 if (modified_flags.data() == nullptr) return;
626
627 int dev = get_device_side<Device>();
628
629 if (dev == 1) { // if Device is the same as DualView's device type
630 if ((modified_flags(0) > 0) && (modified_flags(0) >= modified_flags(1))) {
631 Impl::throw_runtime_exception(
632 "Calling sync on a DualView with a const datatype.");
633 }
634 impl_report_device_sync();
635 }
636 if (dev == 0) { // hopefully Device is the same as DualView's host type
637 if ((modified_flags(1) > 0) && (modified_flags(1) >= modified_flags(0))) {
638 Impl::throw_runtime_exception(
639 "Calling sync on a DualView with a const datatype.");
640 }
641 impl_report_host_sync();
642 }
643 }
644
645 // deliberately passing args by cref as they're used multiple times
646 template <typename... Args>
647 void sync_host_impl(Args const&... args) {
648 if (!std::is_same_v<typename traits::data_type,
649 typename traits::non_const_data_type>)
650 Impl::throw_runtime_exception(
651 "Calling sync_host on a DualView with a const datatype.");
652 if (modified_flags.data() == nullptr) return;
653 if (modified_flags(1) > modified_flags(0)) {
654#ifdef KOKKOS_ENABLE_CUDA
655 if (std::is_same<typename t_dev::memory_space,
656 Kokkos::CudaUVMSpace>::value) {
657 if (d_view.data() == h_view.data())
658 Kokkos::Impl::cuda_prefetch_pointer(
659 Impl::get_cuda_space(args...), d_view.data(),
660 sizeof(typename t_dev::value_type) * d_view.span(), false);
661 }
662#endif
663
664 deep_copy(args..., h_view, d_view);
665 modified_flags(1) = modified_flags(0) = 0;
666 impl_report_host_sync();
667 }
668 }
669
670 template <class ExecSpace>
671 void sync_host([[maybe_unused]] const ExecSpace& exec) {
672 if constexpr (impl_dualview_is_single_device)
673 return;
674 else
675 sync_host_impl(exec);
676 }
677 void sync_host() {
678 if constexpr (impl_dualview_is_single_device) {
679 // FIXME_DUALVIEW_ASYNCHRONOUS_BACKENDS
680 // Kokkos::fence(
681 // "Kokkos::DualView: fence for sync_host with host-accessible
682 // memory " "space");
683 return;
684 } else
685 sync_host_impl();
686 }
687
688 // deliberately passing args by cref as they're used multiple times
689 template <typename... Args>
690 void sync_device_impl(Args const&... args) {
691 if (!std::is_same_v<typename traits::data_type,
692 typename traits::non_const_data_type>)
693 Impl::throw_runtime_exception(
694 "Calling sync_device on a DualView with a const datatype.");
695 if (modified_flags.data() == nullptr) return;
696 if (modified_flags(0) > modified_flags(1)) {
697#ifdef KOKKOS_ENABLE_CUDA
698 if (std::is_same<typename t_dev::memory_space,
699 Kokkos::CudaUVMSpace>::value) {
700 if (d_view.data() == h_view.data())
701 Kokkos::Impl::cuda_prefetch_pointer(
702 Impl::get_cuda_space(args...), d_view.data(),
703 sizeof(typename t_dev::value_type) * d_view.span(), true);
704 }
705#endif
706
707 deep_copy(args..., d_view, h_view);
708 modified_flags(1) = modified_flags(0) = 0;
709 impl_report_device_sync();
710 }
711 }
712
713 template <class ExecSpace>
714 void sync_device([[maybe_unused]] const ExecSpace& exec) {
715 if constexpr (impl_dualview_is_single_device)
716 return;
717 else
718 sync_device_impl(exec);
719 }
720 void sync_device() {
721 if constexpr (impl_dualview_is_single_device) {
722 // FIXME_DUALVIEW_ASYNCHRONOUS_BACKENDS
723 // Kokkos::fence(
724 // "Kokkos::DualView: fence for sync_device with host-accessible
725 // memory " "space");
726 return;
727 } else
728 sync_device_impl();
729 }
730
731 template <class Device>
732 bool need_sync() const {
733 if (modified_flags.data() == nullptr) return false;
734 int dev = get_device_side<Device>();
735
736 if (dev == 1) { // if Device is the same as DualView's device type
737 if ((modified_flags(0) > 0) && (modified_flags(0) >= modified_flags(1))) {
738 return true;
739 }
740 }
741 if (dev == 0) { // hopefully Device is the same as DualView's host type
742 if ((modified_flags(1) > 0) && (modified_flags(1) >= modified_flags(0))) {
743 return true;
744 }
745 }
746 return false;
747 }
748
749 inline bool need_sync_host() const {
750 if (modified_flags.data() == nullptr) return false;
751 return modified_flags(0) < modified_flags(1);
752 }
753
754 inline bool need_sync_device() const {
755 if (modified_flags.data() == nullptr) return false;
756 return modified_flags(1) < modified_flags(0);
757 }
758 void impl_report_device_modification() {
759 if (Kokkos::Tools::Experimental::get_callbacks().modify_dual_view !=
760 nullptr) {
761 Kokkos::Tools::modifyDualView(
762 d_view.label(),
763 reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(d_view.data()) -
764 view_header_size),
765 true);
766 }
767 }
768 void impl_report_host_modification() {
769 if (Kokkos::Tools::Experimental::get_callbacks().modify_dual_view !=
770 nullptr) {
771 Kokkos::Tools::modifyDualView(
772 h_view.label(),
773 reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(h_view.data()) -
774 view_header_size),
775 false);
776 }
777 }
783 template <class Device>
784 void modify() {
785 if constexpr (impl_dualview_is_single_device) {
786 return;
787 } else {
788 if (modified_flags.data() == nullptr) return;
789
790 int dev = get_device_side<Device>();
791
792 if (dev == 1) { // if Device is the same as DualView's device type
793 // Increment the device's modified count.
794 modified_flags(1) =
795 (modified_flags(1) > modified_flags(0) ? modified_flags(1)
796 : modified_flags(0)) +
797 1;
798 impl_report_device_modification();
799 }
800 if (dev == 0) { // hopefully Device is the same as DualView's host type
801 // Increment the host's modified count.
802 modified_flags(0) =
803 (modified_flags(1) > modified_flags(0) ? modified_flags(1)
804 : modified_flags(0)) +
805 1;
806 impl_report_host_modification();
807 }
808
809 if (modified_flags(0) && modified_flags(1)) {
810 std::string msg = "Kokkos::DualView::modify ERROR: ";
811 msg += "Concurrent modification of host and device views ";
812 msg += "in DualView \"";
813 msg += d_view.label();
814 msg += "\"\n";
815 Kokkos::abort(msg.c_str());
816 }
817 }
818 }
819
820 inline void modify_host() {
821 if constexpr (impl_dualview_is_single_device) {
822 return;
823 } else {
824 if (modified_flags.data() != nullptr) {
825 modified_flags(0) =
826 (modified_flags(1) > modified_flags(0) ? modified_flags(1)
827 : modified_flags(0)) +
828 1;
829 impl_report_host_modification();
830 if (modified_flags(0) && modified_flags(1)) {
831 std::string msg = "Kokkos::DualView::modify_host ERROR: ";
832 msg += "Concurrent modification of host and device views ";
833 msg += "in DualView \"";
834 msg += d_view.label();
835 msg += "\"\n";
836 Kokkos::abort(msg.c_str());
837 }
838 }
839 }
840 }
841
842 inline void modify_device() {
843 if constexpr (impl_dualview_is_single_device) {
844 return;
845 } else {
846 if (modified_flags.data() != nullptr) {
847 modified_flags(1) =
848 (modified_flags(1) > modified_flags(0) ? modified_flags(1)
849 : modified_flags(0)) +
850 1;
851 impl_report_device_modification();
852 if (modified_flags(0) && modified_flags(1)) {
853 std::string msg = "Kokkos::DualView::modify_device ERROR: ";
854 msg += "Concurrent modification of host and device views ";
855 msg += "in DualView \"";
856 msg += d_view.label();
857 msg += "\"\n";
858 Kokkos::abort(msg.c_str());
859 }
860 }
861 }
862 }
863
864 inline void clear_sync_state() {
865 if (modified_flags.data() != nullptr)
866 modified_flags(1) = modified_flags(0) = 0;
867 }
868
870
872
878 template <class... ViewCtorArgs>
879 void impl_realloc(const size_t n0, const size_t n1, const size_t n2,
880 const size_t n3, const size_t n4, const size_t n5,
881 const size_t n6, const size_t n7,
882 const Impl::ViewCtorProp<ViewCtorArgs...>& arg_prop) {
883 using alloc_prop_input = Impl::ViewCtorProp<ViewCtorArgs...>;
884
885 static_assert(!alloc_prop_input::has_label,
886 "The view constructor arguments passed to Kokkos::realloc "
887 "must not include a label!");
888 static_assert(
889 !alloc_prop_input::has_pointer,
890 "The view constructor arguments passed to Kokkos::realloc must "
891 "not include a pointer!");
892 static_assert(
893 !alloc_prop_input::has_memory_space,
894 "The view constructor arguments passed to Kokkos::realloc must "
895 "not include a memory space instance!");
896
897 const size_t new_extents[8] = {n0, n1, n2, n3, n4, n5, n6, n7};
898 const bool sizeMismatch =
899 Impl::size_mismatch(h_view, h_view.rank_dynamic, new_extents);
900
901 if (sizeMismatch) {
902 if constexpr (alloc_prop_input::sequential_host_init) {
903 static_assert(alloc_prop_input::initialize,
904 "DualView: SequentialHostInit isn't compatible with "
905 "WithoutInitializing!");
906 ::Kokkos::realloc(arg_prop, h_view, n0, n1, n2, n3, n4, n5, n6, n7);
907 d_view =
908 create_mirror_view_and_copy(typename t_dev::memory_space(), h_view);
909 } else {
910 ::Kokkos::realloc(arg_prop, d_view, n0, n1, n2, n3, n4, n5, n6, n7);
911 if constexpr (alloc_prop_input::initialize) {
912 h_view = create_mirror_view(typename t_host::memory_space(), d_view);
913 } else {
914 h_view = create_mirror_view(Kokkos::WithoutInitializing,
915 typename t_host::memory_space(), d_view);
916 }
917 }
918 } else if constexpr (alloc_prop_input::initialize) {
919 if constexpr (alloc_prop_input::has_execution_space) {
920 const auto& exec_space =
921 Impl::get_property<Impl::ExecutionSpaceTag>(arg_prop);
922 ::Kokkos::deep_copy(exec_space, d_view, typename t_dev::value_type{});
923 } else
924 ::Kokkos::deep_copy(d_view, typename t_dev::value_type{});
925 }
926
927 /* Reset dirty flags */
928 if (modified_flags.data() == nullptr) {
929 modified_flags = t_modified_flags("DualView::modified_flags");
930 } else
931 modified_flags(1) = modified_flags(0) = 0;
932 }
933
934 template <class... ViewCtorArgs>
935 void realloc(const Impl::ViewCtorProp<ViewCtorArgs...>& arg_prop,
936 const size_t n0 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
937 const size_t n1 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
938 const size_t n2 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
939 const size_t n3 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
940 const size_t n4 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
941 const size_t n5 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
942 const size_t n6 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
943 const size_t n7 = KOKKOS_IMPL_CTOR_DEFAULT_ARG) {
944 impl_realloc(n0, n1, n2, n3, n4, n5, n6, n7, arg_prop);
945 }
946
947 void realloc(const size_t n0 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
948 const size_t n1 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
949 const size_t n2 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
950 const size_t n3 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
951 const size_t n4 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
952 const size_t n5 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
953 const size_t n6 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
954 const size_t n7 = KOKKOS_IMPL_CTOR_DEFAULT_ARG) {
955 impl_realloc(n0, n1, n2, n3, n4, n5, n6, n7, Impl::ViewCtorProp<>{});
956 }
957
958 template <typename I>
959 std::enable_if_t<Impl::is_view_ctor_property<I>::value> realloc(
960 const I& arg_prop, const size_t n0 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
961 const size_t n1 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
962 const size_t n2 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
963 const size_t n3 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
964 const size_t n4 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
965 const size_t n5 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
966 const size_t n6 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
967 const size_t n7 = KOKKOS_IMPL_CTOR_DEFAULT_ARG) {
968 impl_realloc(n0, n1, n2, n3, n4, n5, n6, n7, Kokkos::view_alloc(arg_prop));
969 }
970
975 template <class... ViewCtorArgs>
976 void impl_resize(const Impl::ViewCtorProp<ViewCtorArgs...>& arg_prop,
977 const size_t n0, const size_t n1, const size_t n2,
978 const size_t n3, const size_t n4, const size_t n5,
979 const size_t n6, const size_t n7) {
980 using alloc_prop_input = Impl::ViewCtorProp<ViewCtorArgs...>;
981
982 static_assert(!alloc_prop_input::has_label,
983 "The view constructor arguments passed to Kokkos::resize "
984 "must not include a label!");
985 static_assert(
986 !alloc_prop_input::has_pointer,
987 "The view constructor arguments passed to Kokkos::resize must "
988 "not include a pointer!");
989 static_assert(
990 !alloc_prop_input::has_memory_space,
991 "The view constructor arguments passed to Kokkos::resize must "
992 "not include a memory space instance!");
993
994 const size_t new_extents[8] = {n0, n1, n2, n3, n4, n5, n6, n7};
995 const bool sizeMismatch =
996 Impl::size_mismatch(h_view, h_view.rank_dynamic, new_extents);
997
998 if (modified_flags.data() == nullptr) {
999 modified_flags = t_modified_flags("DualView::modified_flags");
1000 }
1001
1002 [[maybe_unused]] auto resize_on_device = [&](const auto& properties) {
1003 /* Resize on Device */
1004 if (sizeMismatch) {
1005 ::Kokkos::resize(properties, d_view, n0, n1, n2, n3, n4, n5, n6, n7);
1006 // this part of the lambda was relocated in a method as it contains a
1007 // `if constexpr`. In some cases, both branches were evaluated
1008 // leading to a compile error
1009 resync_host(properties);
1010
1011 /* Mark Device copy as modified */
1012 ++modified_flags(1);
1013 }
1014 };
1015
1016 [[maybe_unused]] auto resize_on_host = [&](const auto& properties) {
1017 /* Resize on Host */
1018 if (sizeMismatch) {
1019 ::Kokkos::resize(properties, h_view, n0, n1, n2, n3, n4, n5, n6, n7);
1020 // this part of the lambda was relocated in a method as it contains a
1021 // `if constexpr`. In some cases, both branches were evaluated
1022 // leading to a compile error
1023 resync_device(properties);
1024
1025 /* Mark Host copy as modified */
1026 ++modified_flags(0);
1027 }
1028 };
1029
1030 if constexpr (alloc_prop_input::sequential_host_init) {
1031 static_assert(alloc_prop_input::initialize,
1032 "DualView: SequentialHostInit isn't compatible with "
1033 "WithoutInitializing!");
1034 static_assert(!alloc_prop_input::has_execution_space,
1035 "DualView: SequentialHostInit isn't compatible with "
1036 "providing an execution space instance!");
1037
1038 if (sizeMismatch) {
1039 sync<typename t_host::memory_space>();
1040 ::Kokkos::resize(arg_prop, h_view, n0, n1, n2, n3, n4, n5, n6, n7);
1041 d_view =
1042 create_mirror_view_and_copy(typename t_dev::memory_space(), h_view);
1043 }
1044 return;
1045 } else if constexpr (alloc_prop_input::has_execution_space) {
1046 using ExecSpace = typename alloc_prop_input::execution_space;
1047 const auto& exec_space =
1048 Impl::get_property<Impl::ExecutionSpaceTag>(arg_prop);
1049 constexpr bool exec_space_can_access_device =
1050 SpaceAccessibility<ExecSpace,
1051 typename t_dev::memory_space>::accessible;
1052 constexpr bool exec_space_can_access_host =
1053 SpaceAccessibility<ExecSpace,
1054 typename t_host::memory_space>::accessible;
1055 static_assert(exec_space_can_access_device || exec_space_can_access_host);
1056 if constexpr (exec_space_can_access_device) {
1057 sync<typename t_dev::memory_space>(exec_space);
1058 resize_on_device(arg_prop);
1059 return;
1060 }
1061 if constexpr (exec_space_can_access_host) {
1062 sync<typename t_host::memory_space>(exec_space);
1063 resize_on_host(arg_prop);
1064 return;
1065 }
1066 } else {
1067 if (modified_flags(1) >= modified_flags(0)) {
1068 resize_on_device(arg_prop);
1069 } else {
1070 resize_on_host(arg_prop);
1071 }
1072 }
1073 }
1074
1075 private:
1076 // resync host mirror from device
1077 // this code was relocated from a lambda as it contains a `if constexpr`.
1078 // In some cases, both branches were evaluated, leading to a compile error
1079 template <class... ViewCtorArgs>
1080 inline void resync_host(Impl::ViewCtorProp<ViewCtorArgs...> const&) {
1081 using alloc_prop_input = Impl::ViewCtorProp<ViewCtorArgs...>;
1082
1083 if constexpr (alloc_prop_input::initialize) {
1084 h_view = create_mirror_view(typename t_host::memory_space(), d_view);
1085 } else {
1086 h_view = create_mirror_view(Kokkos::WithoutInitializing,
1087 typename t_host::memory_space(), d_view);
1088 }
1089 }
1090
1091 // resync device mirror from host
1092 // this code was relocated from a lambda as it contains a `if constexpr`
1093 // In some cases, both branches were evaluated leading to a compile error
1094 template <class... ViewCtorArgs>
1095 inline void resync_device(Impl::ViewCtorProp<ViewCtorArgs...> const&) {
1096 using alloc_prop_input = Impl::ViewCtorProp<ViewCtorArgs...>;
1097
1098 if constexpr (alloc_prop_input::initialize) {
1099 d_view = create_mirror_view(typename t_dev::memory_space(), h_view);
1100
1101 } else {
1102 d_view = create_mirror_view(Kokkos::WithoutInitializing,
1103 typename t_dev::memory_space(), h_view);
1104 }
1105 }
1106
1107 public:
1108 void resize(const size_t n0 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
1109 const size_t n1 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
1110 const size_t n2 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
1111 const size_t n3 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
1112 const size_t n4 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
1113 const size_t n5 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
1114 const size_t n6 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
1115 const size_t n7 = KOKKOS_IMPL_CTOR_DEFAULT_ARG) {
1116 impl_resize(Impl::ViewCtorProp<>{}, n0, n1, n2, n3, n4, n5, n6, n7);
1117 }
1118
1119 template <class... ViewCtorArgs>
1120 void resize(const Impl::ViewCtorProp<ViewCtorArgs...>& arg_prop,
1121 const size_t n0 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
1122 const size_t n1 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
1123 const size_t n2 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
1124 const size_t n3 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
1125 const size_t n4 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
1126 const size_t n5 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
1127 const size_t n6 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
1128 const size_t n7 = KOKKOS_IMPL_CTOR_DEFAULT_ARG) {
1129 impl_resize(arg_prop, n0, n1, n2, n3, n4, n5, n6, n7);
1130 }
1131
1132 template <class I>
1133 std::enable_if_t<Impl::is_view_ctor_property<I>::value> resize(
1134 const I& arg_prop, const size_t n0 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
1135 const size_t n1 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
1136 const size_t n2 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
1137 const size_t n3 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
1138 const size_t n4 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
1139 const size_t n5 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
1140 const size_t n6 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
1141 const size_t n7 = KOKKOS_IMPL_CTOR_DEFAULT_ARG) {
1142 impl_resize(Kokkos::view_alloc(arg_prop), n0, n1, n2, n3, n4, n5, n6, n7);
1143 }
1144
1146
1148
1150 KOKKOS_INLINE_FUNCTION constexpr size_t span() const { return d_view.span(); }
1151
1152 KOKKOS_INLINE_FUNCTION bool span_is_contiguous() const {
1153 return d_view.span_is_contiguous();
1154 }
1155
1157 template <typename iType>
1158 void stride(iType* stride_) const {
1159 d_view.stride(stride_);
1160 }
1161
1162 template <typename iType>
1163 KOKKOS_INLINE_FUNCTION constexpr std::enable_if_t<std::is_integral_v<iType>,
1164 size_t>
1165 extent(const iType& r) const {
1166 return d_view.extent(r);
1167 }
1168
1169 template <typename iType>
1170 KOKKOS_INLINE_FUNCTION constexpr std::enable_if_t<std::is_integral_v<iType>,
1171 int>
1172 extent_int(const iType& r) const {
1173 return static_cast<int>(d_view.extent(r));
1174 }
1175
1177};
1178
1179} // namespace Kokkos
1180
1181//----------------------------------------------------------------------------
1182//----------------------------------------------------------------------------
1183//
1184// Partial specializations of Kokkos::subview() for DualView objects.
1185//
1186
1187namespace Kokkos {
1188namespace Impl {
1189
1190template <class V>
1191struct V2DV;
1192
1193template <class D, class... P>
1194struct V2DV<View<D, P...>> {
1195 using type = DualView<D, P...>;
1196};
1197} /* namespace Impl */
1198
1199template <class DataType, class... Properties, class... Args>
1200auto subview(const DualView<DataType, Properties...>& src, Args&&... args) {
1201 // leverage Kokkos::View facilities to deduce the properties of the subview
1202 using deduce_subview_type =
1203 decltype(subview(std::declval<View<DataType, Properties...>>(),
1204 std::forward<Args>(args)...));
1205 // map it back to dual view
1206 return typename Impl::V2DV<deduce_subview_type>::type(
1207 src, std::forward<Args>(args)...);
1208}
1209
1210} /* namespace Kokkos */
1211
1212//----------------------------------------------------------------------------
1213//----------------------------------------------------------------------------
1214
1215namespace Kokkos {
1216
1217//
1218// Partial specialization of Kokkos::deep_copy() for DualView objects.
1219//
1220
1221template <class DT, class... DP, class ST, class... SP>
1222void deep_copy(DualView<DT, DP...>& dst, const DualView<ST, SP...>& src) {
1223 if (src.need_sync_device()) {
1224 deep_copy(dst.view_host(), src.view_host());
1225 dst.modify_host();
1226 } else {
1227 deep_copy(dst.view_device(), src.view_device());
1228 dst.modify_device();
1229 }
1230}
1231
1232template <class ExecutionSpace, class DT, class... DP, class ST, class... SP>
1233void deep_copy(const ExecutionSpace& exec, DualView<DT, DP...>& dst,
1234 const DualView<ST, SP...>& src) {
1235 if (src.need_sync_device()) {
1236 deep_copy(exec, dst.view_host(), src.view_host());
1237 dst.modify_host();
1238 } else {
1239 deep_copy(exec, dst.view_device(), src.view_device());
1240 dst.modify_device();
1241 }
1242}
1243
1244} // namespace Kokkos
1245
1246//----------------------------------------------------------------------------
1247//----------------------------------------------------------------------------
1248
1249namespace Kokkos {
1250
1251//
1252// Non-member resize and realloc
1253//
1254
1255template <class... Properties, class... Args>
1256void resize(DualView<Properties...>& dv, Args&&... args) noexcept(
1257 noexcept(dv.resize(std::forward<Args>(args)...))) {
1258 dv.resize(std::forward<Args>(args)...);
1259}
1260
1261template <class... ViewCtorArgs, class... Properties, class... Args>
1262void resize(
1263 const Impl::ViewCtorProp<ViewCtorArgs...>& arg_prop,
1264 DualView<Properties...>& dv,
1265 Args&&... args) noexcept(noexcept(dv.resize(arg_prop,
1266 std::forward<Args>(args)...))) {
1267 dv.resize(arg_prop, std::forward<Args>(args)...);
1268}
1269
1270template <class I, class... Properties, class... Args>
1271std::enable_if_t<Impl::is_view_ctor_property<I>::value> resize(
1272 const I& arg_prop, DualView<Properties...>& dv,
1273 Args&&... args) noexcept(noexcept(dv.resize(arg_prop,
1274 std::forward<Args>(args)...))) {
1275 dv.resize(arg_prop, std::forward<Args>(args)...);
1276}
1277
1278template <class... ViewCtorArgs, class... Properties, class... Args>
1279void realloc(const Impl::ViewCtorProp<ViewCtorArgs...>& arg_prop,
1280 DualView<Properties...>& dv,
1281 Args&&... args) noexcept(noexcept(dv
1282 .realloc(std::forward<Args>(
1283 args)...))) {
1284 dv.realloc(arg_prop, std::forward<Args>(args)...);
1285}
1286
1287template <class... Properties, class... Args>
1288void realloc(DualView<Properties...>& dv, Args&&... args) noexcept(
1289 noexcept(dv.realloc(std::forward<Args>(args)...))) {
1290 dv.realloc(std::forward<Args>(args)...);
1291}
1292
1293template <class I, class... Properties, class... Args>
1294std::enable_if_t<Impl::is_view_ctor_property<I>::value> realloc(
1295 const I& arg_prop, DualView<Properties...>& dv,
1296 Args&&... args) noexcept(noexcept(dv.realloc(arg_prop,
1297 std::forward<Args>(
1298 args)...))) {
1299 dv.realloc(arg_prop, std::forward<Args>(args)...);
1300}
1301
1302} // end namespace Kokkos
1303
1304#ifdef KOKKOS_IMPL_PUBLIC_INCLUDE_NOTDEFINED_DUALVIEW
1305#undef KOKKOS_IMPL_PUBLIC_INCLUDE
1306#undef KOKKOS_IMPL_PUBLIC_INCLUDE_NOTDEFINED_DUALVIEW
1307#endif
1308#endif
A thread safe view to a bitset.
Memory management for host memory.
Can AccessSpace access MemorySpace ?