Kokkos Core Kernels Package  Version of the Day
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
Kokkos_DualView.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 
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 
33 namespace 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 
71 namespace Impl {
72 
73 #ifdef KOKKOS_ENABLE_CUDA
74 
75 inline const Kokkos::Cuda& get_cuda_space(const Kokkos::Cuda& in) { return in; }
76 
77 inline const Kokkos::Cuda& get_cuda_space() {
78  return *Kokkos::Impl::cuda_get_deep_copy_space();
79 }
80 
81 template <typename NonCudaExecSpace>
82 inline 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
91 template <class DataType, class Arg1Type = void, class Arg2Type = void,
92  class Arg3Type = void>
93 class DualView;
94 #else
95 template <class DataType, class... Properties>
96 class DualView;
97 #endif
98 
99 template <class>
100 struct is_dual_view : public std::false_type {};
101 
102 template <class DT, class... DP>
103 struct is_dual_view<DualView<DT, DP...>> : public std::true_type {};
104 
105 template <class DT, class... DP>
106 struct is_dual_view<const DualView<DT, DP...>> : public std::true_type {};
107 
108 template <class T>
109 inline constexpr bool is_dual_view_v = is_dual_view<T>::value;
110 
111 #ifdef KOKKOS_ENABLE_DEPRECATED_CODE_4
112 template <class DataType, class Arg1Type, class Arg2Type, class Arg3Type>
113 class DualView : public ViewTraits<DataType, Arg1Type, Arg2Type, Arg3Type> {
114  template <class, class, class, class>
115 #else
116 template <class DataType, class... Properties>
117 class 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,
162  Kokkos::MemoryTraits<Kokkos::RandomAccess>>;
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,
193  Kokkos::MemoryTraits<Kokkos::Unmanaged | Kokkos::RandomAccess>>;
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:
213 
214 #ifdef KOKKOS_ENABLE_DEPRECATED_CODE_4
215  public:
216 #else
217  private:
218 #endif
219 
220  // Moved this specifically after modified_flags to resolve an alignment issue
221  // on MSVC/NVCC
223 
224  t_dev d_view;
225  t_host h_view;
227 
228  public:
230 
231 
237  DualView() = default;
238 
248  DualView(const std::string& label,
249  const size_t n0 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
250  const size_t n1 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
251  const size_t n2 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
252  const size_t n3 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
253  const size_t n4 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
254  const size_t n5 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
255  const size_t n6 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
256  const size_t n7 = KOKKOS_IMPL_CTOR_DEFAULT_ARG)
257  : modified_flags(
258  Kokkos::view_alloc(typename t_modified_flags::execution_space{},
259  "DualView::modified_flags")),
260  d_view(label, n0, n1, n2, n3, n4, n5, n6, n7),
261  h_view(create_mirror_view(d_view)) // without UVM, host View mirrors
262  {}
263 
274  template <class... P>
275  DualView(const Impl::ViewCtorProp<P...>& arg_prop,
276  std::enable_if_t<!Impl::ViewCtorProp<P...>::has_pointer,
277  size_t> const n0 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
278  const size_t n1 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
279  const size_t n2 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
280  const size_t n3 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
281  const size_t n4 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
282  const size_t n5 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
283  const size_t n6 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
284  const size_t n7 = KOKKOS_IMPL_CTOR_DEFAULT_ARG)
285  : modified_flags(t_modified_flags("DualView::modified_flags")) {
286  if constexpr (Impl::ViewCtorProp<P...>::sequential_host_init) {
287  h_view = t_host(arg_prop, n0, n1, n2, n3, n4, n5, n6, n7);
288  static_assert(Impl::ViewCtorProp<P...>::initialize,
289  "DualView: SequentialHostInit isn't compatible with "
290  "WithoutInitializing!");
291  static_assert(!Impl::ViewCtorProp<P...>::has_execution_space,
292  "DualView: SequentialHostInit isn't compatible with "
293  "providing an execution space instance!");
294 
295  d_view = Kokkos::create_mirror_view_and_copy(
296  typename traits::memory_space{}, h_view);
297  } else {
298  d_view = t_dev(arg_prop, n0, n1, n2, n3, n4, n5, n6, n7);
299 
300  // without UVM, host View mirrors
301  if constexpr (Kokkos::Impl::has_type<Impl::WithoutInitializing_t,
302  P...>::value)
303  h_view =
304  Kokkos::create_mirror_view(Kokkos::WithoutInitializing, d_view);
305  else
306  h_view = Kokkos::create_mirror_view(d_view);
307  }
308  }
309 
311  template <typename DT, typename... DP>
312  DualView(const DualView<DT, DP...>& src)
313  : modified_flags(src.modified_flags),
314  d_view(src.d_view),
315  h_view(src.h_view) {}
316 
318  template <class DT, class... DP, class Arg0, class... Args>
319  DualView(const DualView<DT, DP...>& src, const Arg0& arg0, Args... args)
320  : modified_flags(src.modified_flags),
321  d_view(Kokkos::subview(src.d_view, arg0, args...)),
322  h_view(Kokkos::subview(src.h_view, arg0, args...)) {}
323 
334  DualView(const t_dev& d_view_, const t_host& h_view_)
335  : modified_flags(t_modified_flags("DualView::modified_flags")),
336  d_view(d_view_),
337  h_view(h_view_) {
338  if (int(d_view.rank) != int(h_view.rank) ||
339  d_view.extent(0) != h_view.extent(0) ||
340  d_view.extent(1) != h_view.extent(1) ||
341  d_view.extent(2) != h_view.extent(2) ||
342  d_view.extent(3) != h_view.extent(3) ||
343  d_view.extent(4) != h_view.extent(4) ||
344  d_view.extent(5) != h_view.extent(5) ||
345  d_view.extent(6) != h_view.extent(6) ||
346  d_view.extent(7) != h_view.extent(7) ||
347  d_view.stride_0() != h_view.stride_0() ||
348  d_view.stride_1() != h_view.stride_1() ||
349  d_view.stride_2() != h_view.stride_2() ||
350  d_view.stride_3() != h_view.stride_3() ||
351  d_view.stride_4() != h_view.stride_4() ||
352  d_view.stride_5() != h_view.stride_5() ||
353  d_view.stride_6() != h_view.stride_6() ||
354  d_view.stride_7() != h_view.stride_7() ||
355  d_view.span() != h_view.span()) {
356  Kokkos::Impl::throw_runtime_exception(
357  "DualView constructed with incompatible views");
358  }
359  }
360  // does the DualView have only one device
361  struct impl_dualview_is_single_device {
362  enum : bool {
363  value = std::is_same_v<typename t_dev::device_type,
364  typename t_host::device_type>
365  };
366  };
367 
368  // does the given device match the device of t_dev?
369  template <typename Device>
370  struct impl_device_matches_tdev_device {
371  enum : bool { value = std::is_same_v<typename t_dev::device_type, Device> };
372  };
373  // does the given device match the device of t_host?
374  template <typename Device>
375  struct impl_device_matches_thost_device {
376  enum : bool {
377  value = std::is_same_v<typename t_host::device_type, Device>
378  };
379  };
380 
381  // does the given device match the execution space of t_host?
382  template <typename Device>
383  struct impl_device_matches_thost_exec {
384  enum : bool {
385  value = std::is_same_v<typename t_host::execution_space, Device>
386  };
387  };
388 
389  // does the given device match the execution space of t_dev?
390  template <typename Device>
391  struct impl_device_matches_tdev_exec {
392  enum : bool {
393  value = std::is_same_v<typename t_dev::execution_space, Device>
394  };
395  };
396 
397  // does the given device's memory space match the memory space of t_dev?
398  template <typename Device>
399  struct impl_device_matches_tdev_memory_space {
400  enum : bool {
401  value = std::is_same_v<typename t_dev::memory_space,
402  typename Device::memory_space>
403  };
404  };
405 
407 
409 
427  template <class Device>
428  KOKKOS_FUNCTION auto view() const {
429  if constexpr (std::is_same_v<Device, typename Device::memory_space>) {
430  if constexpr (std::is_same_v<typename Device::memory_space,
431  typename t_dev::memory_space>) {
432  return d_view;
433  } else {
434  static_assert(std::is_same_v<typename Device::memory_space,
435  typename t_host::memory_space>,
436  "The template argument is a memory space but doesn't "
437  "match either of DualView's memory spaces!");
438  return h_view;
439  }
440  } else {
441  if constexpr (std::is_same_v<Device, typename Device::execution_space>) {
442  if constexpr (std::is_same_v<typename Device::execution_space,
443  typename t_dev::execution_space>) {
444  return d_view;
445  } else {
446  static_assert(std::is_same_v<typename Device::execution_space,
447  typename t_host::execution_space>,
448  "The template argument is an execution space but "
449  "doesn't match either of DualView's execution spaces!");
450  return h_view;
451  }
452  } else {
453  static_assert(std::is_same_v<Device, typename Device::device_type>,
454  "The template argument is neither a memory space, "
455  "execution space, or device!");
456  if constexpr (std::is_same_v<Device, typename t_dev::device_type>)
457  return d_view;
458  else {
459  static_assert(std::is_same_v<Device, typename t_host::device_type>,
460  "The template argument is a device but "
461  "doesn't match either of DualView's devices!");
462  return h_view;
463  }
464  }
465  }
466  }
467 
468 #ifdef KOKKOS_ENABLE_DEPRECATED_CODE_4
469  KOKKOS_INLINE_FUNCTION
470  t_host view_host() const { return h_view; }
471 
472  KOKKOS_INLINE_FUNCTION
473  t_dev view_device() const { return d_view; }
474 #else
475  KOKKOS_INLINE_FUNCTION
476  const t_host& view_host() const { return h_view; }
477 
478  KOKKOS_INLINE_FUNCTION
479  const t_dev& view_device() const { return d_view; }
480 #endif
481 
482  KOKKOS_INLINE_FUNCTION constexpr bool is_allocated() const {
483  return (d_view.is_allocated() && h_view.is_allocated());
484  }
485 
486  template <class Device>
487  static int get_device_side() {
488  constexpr bool device_is_memspace =
489  std::is_same_v<Device, typename Device::memory_space>;
490  constexpr bool device_is_execspace =
491  std::is_same_v<Device, typename Device::execution_space>;
492  constexpr bool device_exec_is_t_dev_exec =
493  std::is_same_v<typename Device::execution_space,
494  typename t_dev::execution_space>;
495  constexpr bool device_mem_is_t_dev_mem =
496  std::is_same_v<typename Device::memory_space,
497  typename t_dev::memory_space>;
498  constexpr bool device_exec_is_t_host_exec =
499  std::is_same_v<typename Device::execution_space,
500  typename t_host::execution_space>;
501  constexpr bool device_mem_is_t_host_mem =
502  std::is_same_v<typename Device::memory_space,
503  typename t_host::memory_space>;
504  constexpr bool device_is_t_host_device =
505  std::is_same_v<typename Device::execution_space,
506  typename t_host::device_type>;
507  constexpr bool device_is_t_dev_device =
508  std::is_same_v<typename Device::memory_space,
509  typename t_host::device_type>;
510 
511  static_assert(
512  device_is_t_dev_device || device_is_t_host_device ||
513  (device_is_memspace &&
514  (device_mem_is_t_dev_mem || device_mem_is_t_host_mem)) ||
515  (device_is_execspace &&
516  (device_exec_is_t_dev_exec || device_exec_is_t_host_exec)) ||
517  ((!device_is_execspace && !device_is_memspace) &&
518  ((device_mem_is_t_dev_mem || device_mem_is_t_host_mem) ||
519  (device_exec_is_t_dev_exec || device_exec_is_t_host_exec))),
520  "Template parameter to .sync() must exactly match one of the "
521  "DualView's device types or one of the execution or memory spaces");
522 
523  int dev = -1;
524  if (device_is_t_dev_device)
525  dev = 1;
526  else if (device_is_t_host_device)
527  dev = 0;
528  else {
529  if (device_is_memspace) {
530  if (device_mem_is_t_dev_mem) dev = 1;
531  if (device_mem_is_t_host_mem) dev = 0;
532  if (device_mem_is_t_host_mem && device_mem_is_t_dev_mem) dev = -1;
533  }
534  if (device_is_execspace) {
535  if (device_exec_is_t_dev_exec) dev = 1;
536  if (device_exec_is_t_host_exec) dev = 0;
537  if (device_exec_is_t_host_exec && device_exec_is_t_dev_exec) dev = -1;
538  }
539  if (!device_is_execspace && !device_is_memspace) {
540  if (device_mem_is_t_dev_mem) dev = 1;
541  if (device_mem_is_t_host_mem) dev = 0;
542  if (device_mem_is_t_host_mem && device_mem_is_t_dev_mem) dev = -1;
543  if (device_exec_is_t_dev_exec) dev = 1;
544  if (device_exec_is_t_host_exec) dev = 0;
545  if (device_exec_is_t_host_exec && device_exec_is_t_dev_exec) dev = -1;
546  }
547  }
548  return dev;
549  }
550  static constexpr const int view_header_size = 128;
551  void impl_report_host_sync() const noexcept {
552  if (Kokkos::Tools::Experimental::get_callbacks().sync_dual_view !=
553  nullptr) {
554  Kokkos::Tools::syncDualView(
555  h_view.label(),
556  reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(h_view.data()) -
557  view_header_size),
558  false);
559  }
560  }
561  void impl_report_device_sync() const noexcept {
562  if (Kokkos::Tools::Experimental::get_callbacks().sync_dual_view !=
563  nullptr) {
564  Kokkos::Tools::syncDualView(
565  d_view.label(),
566  reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(d_view.data()) -
567  view_header_size),
568  true);
569  }
570  }
571 
589  // deliberately passing args by cref as they're used multiple times
590  template <class Device, class... Args>
591  void sync_impl(std::true_type, Args const&... args) {
592  if (modified_flags.data() == nullptr) return;
593 
594  int dev = get_device_side<Device>();
595 
596  if (dev == 1) { // if Device is the same as DualView's device type
597  if ((modified_flags(0) > 0) && (modified_flags(0) >= modified_flags(1))) {
598 #ifdef KOKKOS_ENABLE_CUDA
599  if (std::is_same<typename t_dev::memory_space,
600  Kokkos::CudaUVMSpace>::value) {
601  if (d_view.data() == h_view.data())
602  Kokkos::Impl::cuda_prefetch_pointer(
603  Impl::get_cuda_space(args...), d_view.data(),
604  sizeof(typename t_dev::value_type) * d_view.span(), true);
605  }
606 #endif
607 
608  deep_copy(args..., d_view, h_view);
609  modified_flags(0) = modified_flags(1) = 0;
610  impl_report_device_sync();
611  }
612  }
613  if (dev == 0) { // hopefully Device is the same as DualView's host type
614  if ((modified_flags(1) > 0) && (modified_flags(1) >= modified_flags(0))) {
615 #ifdef KOKKOS_ENABLE_CUDA
616  if (std::is_same<typename t_dev::memory_space,
617  Kokkos::CudaUVMSpace>::value) {
618  if (d_view.data() == h_view.data())
619  Kokkos::Impl::cuda_prefetch_pointer(
620  Impl::get_cuda_space(args...), d_view.data(),
621  sizeof(typename t_dev::value_type) * d_view.span(), false);
622  }
623 #endif
624 
625  deep_copy(args..., h_view, d_view);
626  modified_flags(0) = modified_flags(1) = 0;
627  impl_report_host_sync();
628  }
629  }
630  if constexpr (std::is_same_v<typename t_host::memory_space,
631  typename t_dev::memory_space>) {
632  typename t_dev::execution_space().fence(
633  "Kokkos::DualView<>::sync: fence after syncing DualView");
634  typename t_host::execution_space().fence(
635  "Kokkos::DualView<>::sync: fence after syncing DualView");
636  }
637  }
638 
639  template <class Device>
640  void sync(const std::enable_if_t<
641  (std::is_same_v<typename traits::data_type,
642  typename traits::non_const_data_type>) ||
643  (std::is_same_v<Device, int>),
644  int>& = 0) {
645  sync_impl<Device>(std::true_type{});
646  }
647 
648  template <class Device, class ExecutionSpace>
649  void sync(const ExecutionSpace& exec,
650  const std::enable_if_t<
651  (std::is_same_v<typename traits::data_type,
652  typename traits::non_const_data_type>) ||
653  (std::is_same_v<Device, int>),
654  int>& = 0) {
655  sync_impl<Device>(std::true_type{}, exec);
656  }
657 
658  // deliberately passing args by cref as they're used multiple times
659  template <class Device, class... Args>
660  void sync_impl(std::false_type, Args const&...) {
661  if (modified_flags.data() == nullptr) return;
662 
663  int dev = get_device_side<Device>();
664 
665  if (dev == 1) { // if Device is the same as DualView's device type
666  if ((modified_flags(0) > 0) && (modified_flags(0) >= modified_flags(1))) {
667  Impl::throw_runtime_exception(
668  "Calling sync on a DualView with a const datatype.");
669  }
670  impl_report_device_sync();
671  }
672  if (dev == 0) { // hopefully Device is the same as DualView's host type
673  if ((modified_flags(1) > 0) && (modified_flags(1) >= modified_flags(0))) {
674  Impl::throw_runtime_exception(
675  "Calling sync on a DualView with a const datatype.");
676  }
677  impl_report_host_sync();
678  }
679  }
680 
681  template <class Device>
682  void sync(const std::enable_if_t<
683  (!std::is_same_v<typename traits::data_type,
684  typename traits::non_const_data_type>) ||
685  (std::is_same_v<Device, int>),
686  int>& = 0) {
687  sync_impl<Device>(std::false_type{});
688  }
689  template <class Device, class ExecutionSpace>
690  void sync(const ExecutionSpace& exec,
691  const std::enable_if_t<
692  (!std::is_same_v<typename traits::data_type,
693  typename traits::non_const_data_type>) ||
694  (std::is_same_v<Device, int>),
695  int>& = 0) {
696  sync_impl<Device>(std::false_type{}, exec);
697  }
698 
699  // deliberately passing args by cref as they're used multiple times
700  template <typename... Args>
701  void sync_host_impl(Args const&... args) {
702  if (!std::is_same_v<typename traits::data_type,
703  typename traits::non_const_data_type>)
704  Impl::throw_runtime_exception(
705  "Calling sync_host on a DualView with a const datatype.");
706  if (modified_flags.data() == nullptr) return;
707  if (modified_flags(1) > modified_flags(0)) {
708 #ifdef KOKKOS_ENABLE_CUDA
709  if (std::is_same<typename t_dev::memory_space,
710  Kokkos::CudaUVMSpace>::value) {
711  if (d_view.data() == h_view.data())
712  Kokkos::Impl::cuda_prefetch_pointer(
713  Impl::get_cuda_space(args...), d_view.data(),
714  sizeof(typename t_dev::value_type) * d_view.span(), false);
715  }
716 #endif
717 
718  deep_copy(args..., h_view, d_view);
719  modified_flags(1) = modified_flags(0) = 0;
720  impl_report_host_sync();
721  }
722  }
723 
724  template <class ExecSpace>
725  void sync_host(const ExecSpace& exec) {
726  sync_host_impl(exec);
727  }
728  void sync_host() { sync_host_impl(); }
729 
730  // deliberately passing args by cref as they're used multiple times
731  template <typename... Args>
732  void sync_device_impl(Args const&... args) {
733  if (!std::is_same_v<typename traits::data_type,
734  typename traits::non_const_data_type>)
735  Impl::throw_runtime_exception(
736  "Calling sync_device on a DualView with a const datatype.");
737  if (modified_flags.data() == nullptr) return;
738  if (modified_flags(0) > modified_flags(1)) {
739 #ifdef KOKKOS_ENABLE_CUDA
740  if (std::is_same<typename t_dev::memory_space,
741  Kokkos::CudaUVMSpace>::value) {
742  if (d_view.data() == h_view.data())
743  Kokkos::Impl::cuda_prefetch_pointer(
744  Impl::get_cuda_space(args...), d_view.data(),
745  sizeof(typename t_dev::value_type) * d_view.span(), true);
746  }
747 #endif
748 
749  deep_copy(args..., d_view, h_view);
750  modified_flags(1) = modified_flags(0) = 0;
751  impl_report_device_sync();
752  }
753  }
754 
755  template <class ExecSpace>
756  void sync_device(const ExecSpace& exec) {
757  sync_device_impl(exec);
758  }
759  void sync_device() { sync_device_impl(); }
760 
761  template <class Device>
762  bool need_sync() const {
763  if (modified_flags.data() == nullptr) return false;
764  int dev = get_device_side<Device>();
765 
766  if (dev == 1) { // if Device is the same as DualView's device type
767  if ((modified_flags(0) > 0) && (modified_flags(0) >= modified_flags(1))) {
768  return true;
769  }
770  }
771  if (dev == 0) { // hopefully Device is the same as DualView's host type
772  if ((modified_flags(1) > 0) && (modified_flags(1) >= modified_flags(0))) {
773  return true;
774  }
775  }
776  return false;
777  }
778 
779  inline bool need_sync_host() const {
780  if (modified_flags.data() == nullptr) return false;
781  return modified_flags(0) < modified_flags(1);
782  }
783 
784  inline bool need_sync_device() const {
785  if (modified_flags.data() == nullptr) return false;
786  return modified_flags(1) < modified_flags(0);
787  }
788  void impl_report_device_modification() {
789  if (Kokkos::Tools::Experimental::get_callbacks().modify_dual_view !=
790  nullptr) {
791  Kokkos::Tools::modifyDualView(
792  d_view.label(),
793  reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(d_view.data()) -
794  view_header_size),
795  true);
796  }
797  }
798  void impl_report_host_modification() {
799  if (Kokkos::Tools::Experimental::get_callbacks().modify_dual_view !=
800  nullptr) {
801  Kokkos::Tools::modifyDualView(
802  h_view.label(),
803  reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(h_view.data()) -
804  view_header_size),
805  false);
806  }
807  }
813  template <class Device, class Dummy = DualView,
814  std::enable_if_t<!Dummy::impl_dualview_is_single_device::value>* =
815  nullptr>
816  void modify() {
817  if (modified_flags.data() == nullptr) {
818  modified_flags = t_modified_flags("DualView::modified_flags");
819  }
820 
821  int dev = get_device_side<Device>();
822 
823  if (dev == 1) { // if Device is the same as DualView's device type
824  // Increment the device's modified count.
825  modified_flags(1) =
826  (modified_flags(1) > modified_flags(0) ? modified_flags(1)
827  : modified_flags(0)) +
828  1;
829  impl_report_device_modification();
830  }
831  if (dev == 0) { // hopefully Device is the same as DualView's host type
832  // Increment the host's modified count.
833  modified_flags(0) =
834  (modified_flags(1) > modified_flags(0) ? modified_flags(1)
835  : modified_flags(0)) +
836  1;
837  impl_report_host_modification();
838  }
839 
840 #ifdef KOKKOS_ENABLE_DEBUG_DUALVIEW_MODIFY_CHECK
841  if (modified_flags(0) && modified_flags(1)) {
842  std::string msg = "Kokkos::DualView::modify ERROR: ";
843  msg += "Concurrent modification of host and device views ";
844  msg += "in DualView \"";
845  msg += d_view.label();
846  msg += "\"\n";
847  Kokkos::abort(msg.c_str());
848  }
849 #endif
850  }
851 
852  template <
853  class Device, class Dummy = DualView,
854  std::enable_if_t<Dummy::impl_dualview_is_single_device::value>* = nullptr>
855  void modify() {
856  return;
857  }
858 
859  template <class Dummy = DualView,
860  std::enable_if_t<!Dummy::impl_dualview_is_single_device::value>* =
861  nullptr>
862  inline void modify_host() {
863  if (modified_flags.data() != nullptr) {
864  modified_flags(0) =
865  (modified_flags(1) > modified_flags(0) ? modified_flags(1)
866  : modified_flags(0)) +
867  1;
868  impl_report_host_modification();
869 #ifdef KOKKOS_ENABLE_DEBUG_DUALVIEW_MODIFY_CHECK
870  if (modified_flags(0) && modified_flags(1)) {
871  std::string msg = "Kokkos::DualView::modify_host ERROR: ";
872  msg += "Concurrent modification of host and device views ";
873  msg += "in DualView \"";
874  msg += d_view.label();
875  msg += "\"\n";
876  Kokkos::abort(msg.c_str());
877  }
878 #endif
879  }
880  }
881 
882  template <
883  class Dummy = DualView,
884  std::enable_if_t<Dummy::impl_dualview_is_single_device::value>* = nullptr>
885  inline void modify_host() {
886  return;
887  }
888 
889  template <class Dummy = DualView,
890  std::enable_if_t<!Dummy::impl_dualview_is_single_device::value>* =
891  nullptr>
892  inline void modify_device() {
893  if (modified_flags.data() != nullptr) {
894  modified_flags(1) =
895  (modified_flags(1) > modified_flags(0) ? modified_flags(1)
896  : modified_flags(0)) +
897  1;
898  impl_report_device_modification();
899 #ifdef KOKKOS_ENABLE_DEBUG_DUALVIEW_MODIFY_CHECK
900  if (modified_flags(0) && modified_flags(1)) {
901  std::string msg = "Kokkos::DualView::modify_device ERROR: ";
902  msg += "Concurrent modification of host and device views ";
903  msg += "in DualView \"";
904  msg += d_view.label();
905  msg += "\"\n";
906  Kokkos::abort(msg.c_str());
907  }
908 #endif
909  }
910  }
911 
912  template <
913  class Dummy = DualView,
914  std::enable_if_t<Dummy::impl_dualview_is_single_device::value>* = nullptr>
915  inline void modify_device() {
916  return;
917  }
918 
919  inline void clear_sync_state() {
920  if (modified_flags.data() != nullptr)
921  modified_flags(1) = modified_flags(0) = 0;
922  }
923 
925 
927 
933  template <class... ViewCtorArgs>
934  void impl_realloc(const size_t n0, const size_t n1, const size_t n2,
935  const size_t n3, const size_t n4, const size_t n5,
936  const size_t n6, const size_t n7,
937  const Impl::ViewCtorProp<ViewCtorArgs...>& arg_prop) {
938  using alloc_prop_input = Impl::ViewCtorProp<ViewCtorArgs...>;
939 
940  static_assert(!alloc_prop_input::has_label,
941  "The view constructor arguments passed to Kokkos::realloc "
942  "must not include a label!");
943  static_assert(
944  !alloc_prop_input::has_pointer,
945  "The view constructor arguments passed to Kokkos::realloc must "
946  "not include a pointer!");
947  static_assert(
948  !alloc_prop_input::has_memory_space,
949  "The view constructor arguments passed to Kokkos::realloc must "
950  "not include a memory space instance!");
951 
952  const size_t new_extents[8] = {n0, n1, n2, n3, n4, n5, n6, n7};
953  const bool sizeMismatch =
954  Impl::size_mismatch(h_view, h_view.rank_dynamic, new_extents);
955 
956  if (sizeMismatch) {
957  if constexpr (alloc_prop_input::sequential_host_init) {
958  static_assert(alloc_prop_input::initialize,
959  "DualView: SequentialHostInit isn't compatible with "
960  "WithoutInitializing!");
961  ::Kokkos::realloc(arg_prop, h_view, n0, n1, n2, n3, n4, n5, n6, n7);
962  d_view =
963  create_mirror_view_and_copy(typename t_dev::memory_space(), h_view);
964  } else {
965  ::Kokkos::realloc(arg_prop, d_view, n0, n1, n2, n3, n4, n5, n6, n7);
966  if constexpr (alloc_prop_input::initialize) {
967  h_view = create_mirror_view(typename t_host::memory_space(), d_view);
968  } else {
969  h_view = create_mirror_view(Kokkos::WithoutInitializing,
970  typename t_host::memory_space(), d_view);
971  }
972  }
973  } else if constexpr (alloc_prop_input::initialize) {
974  if constexpr (alloc_prop_input::has_execution_space) {
975  const auto& exec_space =
976  Impl::get_property<Impl::ExecutionSpaceTag>(arg_prop);
977  ::Kokkos::deep_copy(exec_space, d_view, typename t_dev::value_type{});
978  } else
979  ::Kokkos::deep_copy(d_view, typename t_dev::value_type{});
980  }
981 
982  /* Reset dirty flags */
983  if (modified_flags.data() == nullptr) {
984  modified_flags = t_modified_flags("DualView::modified_flags");
985  } else
986  modified_flags(1) = modified_flags(0) = 0;
987  }
988 
989  template <class... ViewCtorArgs>
990  void realloc(const Impl::ViewCtorProp<ViewCtorArgs...>& arg_prop,
991  const size_t n0 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
992  const size_t n1 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
993  const size_t n2 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
994  const size_t n3 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
995  const size_t n4 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
996  const size_t n5 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
997  const size_t n6 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
998  const size_t n7 = KOKKOS_IMPL_CTOR_DEFAULT_ARG) {
999  impl_realloc(n0, n1, n2, n3, n4, n5, n6, n7, arg_prop);
1000  }
1001 
1002  void realloc(const size_t n0 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
1003  const size_t n1 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
1004  const size_t n2 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
1005  const size_t n3 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
1006  const size_t n4 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
1007  const size_t n5 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
1008  const size_t n6 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
1009  const size_t n7 = KOKKOS_IMPL_CTOR_DEFAULT_ARG) {
1010  impl_realloc(n0, n1, n2, n3, n4, n5, n6, n7, Impl::ViewCtorProp<>{});
1011  }
1012 
1013  template <typename I>
1014  std::enable_if_t<Impl::is_view_ctor_property<I>::value> realloc(
1015  const I& arg_prop, const size_t n0 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
1016  const size_t n1 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
1017  const size_t n2 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
1018  const size_t n3 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
1019  const size_t n4 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
1020  const size_t n5 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
1021  const size_t n6 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
1022  const size_t n7 = KOKKOS_IMPL_CTOR_DEFAULT_ARG) {
1023  impl_realloc(n0, n1, n2, n3, n4, n5, n6, n7, Kokkos::view_alloc(arg_prop));
1024  }
1025 
1030  template <class... ViewCtorArgs>
1031  void impl_resize(const Impl::ViewCtorProp<ViewCtorArgs...>& arg_prop,
1032  const size_t n0, const size_t n1, const size_t n2,
1033  const size_t n3, const size_t n4, const size_t n5,
1034  const size_t n6, const size_t n7) {
1035  using alloc_prop_input = Impl::ViewCtorProp<ViewCtorArgs...>;
1036 
1037  static_assert(!alloc_prop_input::has_label,
1038  "The view constructor arguments passed to Kokkos::resize "
1039  "must not include a label!");
1040  static_assert(
1041  !alloc_prop_input::has_pointer,
1042  "The view constructor arguments passed to Kokkos::resize must "
1043  "not include a pointer!");
1044  static_assert(
1045  !alloc_prop_input::has_memory_space,
1046  "The view constructor arguments passed to Kokkos::resize must "
1047  "not include a memory space instance!");
1048 
1049  const size_t new_extents[8] = {n0, n1, n2, n3, n4, n5, n6, n7};
1050  const bool sizeMismatch =
1051  Impl::size_mismatch(h_view, h_view.rank_dynamic, new_extents);
1052 
1053  if (modified_flags.data() == nullptr) {
1054  modified_flags = t_modified_flags("DualView::modified_flags");
1055  }
1056 
1057  [[maybe_unused]] auto resize_on_device = [&](const auto& properties) {
1058  /* Resize on Device */
1059  if (sizeMismatch) {
1060  ::Kokkos::resize(properties, d_view, n0, n1, n2, n3, n4, n5, n6, n7);
1061  // this part of the lambda was relocated in a method as it contains a
1062  // `if constexpr`. In some cases, both branches were evaluated
1063  // leading to a compile error
1064  resync_host(properties);
1065 
1066  /* Mark Device copy as modified */
1067  ++modified_flags(1);
1068  }
1069  };
1070 
1071  [[maybe_unused]] auto resize_on_host = [&](const auto& properties) {
1072  /* Resize on Host */
1073  if (sizeMismatch) {
1074  ::Kokkos::resize(properties, h_view, n0, n1, n2, n3, n4, n5, n6, n7);
1075  // this part of the lambda was relocated in a method as it contains a
1076  // `if constexpr`. In some cases, both branches were evaluated
1077  // leading to a compile error
1078  resync_device(properties);
1079 
1080  /* Mark Host copy as modified */
1081  ++modified_flags(0);
1082  }
1083  };
1084 
1085  if constexpr (alloc_prop_input::sequential_host_init) {
1086  static_assert(alloc_prop_input::initialize,
1087  "DualView: SequentialHostInit isn't compatible with "
1088  "WithoutInitializing!");
1089  static_assert(!alloc_prop_input::has_execution_space,
1090  "DualView: SequentialHostInit isn't compatible with "
1091  "providing an execution space instance!");
1092 
1093  if (sizeMismatch) {
1094  sync<typename t_host::memory_space>();
1095  ::Kokkos::resize(arg_prop, h_view, n0, n1, n2, n3, n4, n5, n6, n7);
1096  d_view =
1097  create_mirror_view_and_copy(typename t_dev::memory_space(), h_view);
1098  }
1099  return;
1100  } else if constexpr (alloc_prop_input::has_execution_space) {
1101  using ExecSpace = typename alloc_prop_input::execution_space;
1102  const auto& exec_space =
1103  Impl::get_property<Impl::ExecutionSpaceTag>(arg_prop);
1104  constexpr bool exec_space_can_access_device =
1105  SpaceAccessibility<ExecSpace,
1106  typename t_dev::memory_space>::accessible;
1107  constexpr bool exec_space_can_access_host =
1108  SpaceAccessibility<ExecSpace,
1109  typename t_host::memory_space>::accessible;
1110  static_assert(exec_space_can_access_device || exec_space_can_access_host);
1111  if constexpr (exec_space_can_access_device) {
1112  sync<typename t_dev::memory_space>(exec_space);
1113  resize_on_device(arg_prop);
1114  return;
1115  }
1116  if constexpr (exec_space_can_access_host) {
1117  sync<typename t_host::memory_space>(exec_space);
1118  resize_on_host(arg_prop);
1119  return;
1120  }
1121  } else {
1122  if (modified_flags(1) >= modified_flags(0)) {
1123  resize_on_device(arg_prop);
1124  } else {
1125  resize_on_host(arg_prop);
1126  }
1127  }
1128  }
1129 
1130  private:
1131  // resync host mirror from device
1132  // this code was relocated from a lambda as it contains a `if constexpr`.
1133  // In some cases, both branches were evaluated, leading to a compile error
1134  template <class... ViewCtorArgs>
1135  inline void resync_host(Impl::ViewCtorProp<ViewCtorArgs...> const&) {
1136  using alloc_prop_input = Impl::ViewCtorProp<ViewCtorArgs...>;
1137 
1138  if constexpr (alloc_prop_input::initialize) {
1139  h_view = create_mirror_view(typename t_host::memory_space(), d_view);
1140  } else {
1141  h_view = create_mirror_view(Kokkos::WithoutInitializing,
1142  typename t_host::memory_space(), d_view);
1143  }
1144  }
1145 
1146  // resync device mirror from host
1147  // this code was relocated from a lambda as it contains a `if constexpr`
1148  // In some cases, both branches were evaluated leading to a compile error
1149  template <class... ViewCtorArgs>
1150  inline void resync_device(Impl::ViewCtorProp<ViewCtorArgs...> const&) {
1151  using alloc_prop_input = Impl::ViewCtorProp<ViewCtorArgs...>;
1152 
1153  if constexpr (alloc_prop_input::initialize) {
1154  d_view = create_mirror_view(typename t_dev::memory_space(), h_view);
1155 
1156  } else {
1157  d_view = create_mirror_view(Kokkos::WithoutInitializing,
1158  typename t_dev::memory_space(), h_view);
1159  }
1160  }
1161 
1162  public:
1163  void resize(const size_t n0 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
1164  const size_t n1 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
1165  const size_t n2 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
1166  const size_t n3 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
1167  const size_t n4 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
1168  const size_t n5 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
1169  const size_t n6 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
1170  const size_t n7 = KOKKOS_IMPL_CTOR_DEFAULT_ARG) {
1171  impl_resize(Impl::ViewCtorProp<>{}, n0, n1, n2, n3, n4, n5, n6, n7);
1172  }
1173 
1174  template <class... ViewCtorArgs>
1175  void resize(const Impl::ViewCtorProp<ViewCtorArgs...>& arg_prop,
1176  const size_t n0 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
1177  const size_t n1 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
1178  const size_t n2 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
1179  const size_t n3 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
1180  const size_t n4 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
1181  const size_t n5 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
1182  const size_t n6 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
1183  const size_t n7 = KOKKOS_IMPL_CTOR_DEFAULT_ARG) {
1184  impl_resize(arg_prop, n0, n1, n2, n3, n4, n5, n6, n7);
1185  }
1186 
1187  template <class I>
1188  std::enable_if_t<Impl::is_view_ctor_property<I>::value> resize(
1189  const I& arg_prop, const size_t n0 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
1190  const size_t n1 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
1191  const size_t n2 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
1192  const size_t n3 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
1193  const size_t n4 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
1194  const size_t n5 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
1195  const size_t n6 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
1196  const size_t n7 = KOKKOS_IMPL_CTOR_DEFAULT_ARG) {
1197  impl_resize(Kokkos::view_alloc(arg_prop), n0, n1, n2, n3, n4, n5, n6, n7);
1198  }
1199 
1201 
1203 
1205  KOKKOS_INLINE_FUNCTION constexpr size_t span() const { return d_view.span(); }
1206 
1207  KOKKOS_INLINE_FUNCTION bool span_is_contiguous() const {
1208  return d_view.span_is_contiguous();
1209  }
1210 
1212  template <typename iType>
1213  void stride(iType* stride_) const {
1214  d_view.stride(stride_);
1215  }
1216 
1217  template <typename iType>
1218  KOKKOS_INLINE_FUNCTION constexpr std::enable_if_t<std::is_integral_v<iType>,
1219  size_t>
1220  extent(const iType& r) const {
1221  return d_view.extent(r);
1222  }
1223 
1224  template <typename iType>
1225  KOKKOS_INLINE_FUNCTION constexpr std::enable_if_t<std::is_integral_v<iType>,
1226  int>
1227  extent_int(const iType& r) const {
1228  return static_cast<int>(d_view.extent(r));
1229  }
1230 
1232 };
1233 
1234 } // namespace Kokkos
1235 
1236 //----------------------------------------------------------------------------
1237 //----------------------------------------------------------------------------
1238 //
1239 // Partial specializations of Kokkos::subview() for DualView objects.
1240 //
1241 
1242 namespace Kokkos {
1243 namespace Impl {
1244 
1245 template <class V>
1246 struct V2DV;
1247 
1248 template <class D, class... P>
1249 struct V2DV<View<D, P...>> {
1250  using type = DualView<D, P...>;
1251 };
1252 } /* namespace Impl */
1253 
1254 template <class DataType, class... Properties, class... Args>
1255 auto subview(const DualView<DataType, Properties...>& src, Args&&... args) {
1256  // leverage Kokkos::View facilities to deduce the properties of the subview
1257  using deduce_subview_type =
1258  decltype(subview(std::declval<View<DataType, Properties...>>(),
1259  std::forward<Args>(args)...));
1260  // map it back to dual view
1261  return typename Impl::V2DV<deduce_subview_type>::type(
1262  src, std::forward<Args>(args)...);
1263 }
1264 
1265 } /* namespace Kokkos */
1266 
1267 //----------------------------------------------------------------------------
1268 //----------------------------------------------------------------------------
1269 
1270 namespace Kokkos {
1271 
1272 //
1273 // Partial specialization of Kokkos::deep_copy() for DualView objects.
1274 //
1275 
1276 template <class DT, class... DP, class ST, class... SP>
1277 void deep_copy(DualView<DT, DP...>& dst, const DualView<ST, SP...>& src) {
1278  if (src.need_sync_device()) {
1279  deep_copy(dst.view_host(), src.view_host());
1280  dst.modify_host();
1281  } else {
1282  deep_copy(dst.view_device(), src.view_device());
1283  dst.modify_device();
1284  }
1285 }
1286 
1287 template <class ExecutionSpace, class DT, class... DP, class ST, class... SP>
1288 void deep_copy(const ExecutionSpace& exec, DualView<DT, DP...>& dst,
1289  const DualView<ST, SP...>& src) {
1290  if (src.need_sync_device()) {
1291  deep_copy(exec, dst.view_host(), src.view_host());
1292  dst.modify_host();
1293  } else {
1294  deep_copy(exec, dst.view_device(), src.view_device());
1295  dst.modify_device();
1296  }
1297 }
1298 
1299 } // namespace Kokkos
1300 
1301 //----------------------------------------------------------------------------
1302 //----------------------------------------------------------------------------
1303 
1304 namespace Kokkos {
1305 
1306 //
1307 // Non-member resize and realloc
1308 //
1309 
1310 template <class... Properties, class... Args>
1311 void resize(DualView<Properties...>& dv, Args&&... args) noexcept(
1312  noexcept(dv.resize(std::forward<Args>(args)...))) {
1313  dv.resize(std::forward<Args>(args)...);
1314 }
1315 
1316 template <class... ViewCtorArgs, class... Properties, class... Args>
1317 void resize(
1318  const Impl::ViewCtorProp<ViewCtorArgs...>& arg_prop,
1319  DualView<Properties...>& dv,
1320  Args&&... args) noexcept(noexcept(dv.resize(arg_prop,
1321  std::forward<Args>(args)...))) {
1322  dv.resize(arg_prop, std::forward<Args>(args)...);
1323 }
1324 
1325 template <class I, class... Properties, class... Args>
1326 std::enable_if_t<Impl::is_view_ctor_property<I>::value> resize(
1327  const I& arg_prop, DualView<Properties...>& dv,
1328  Args&&... args) noexcept(noexcept(dv.resize(arg_prop,
1329  std::forward<Args>(args)...))) {
1330  dv.resize(arg_prop, std::forward<Args>(args)...);
1331 }
1332 
1333 template <class... ViewCtorArgs, class... Properties, class... Args>
1334 void realloc(const Impl::ViewCtorProp<ViewCtorArgs...>& arg_prop,
1335  DualView<Properties...>& dv,
1336  Args&&... args) noexcept(noexcept(dv
1337  .realloc(std::forward<Args>(
1338  args)...))) {
1339  dv.realloc(arg_prop, std::forward<Args>(args)...);
1340 }
1341 
1342 template <class... Properties, class... Args>
1343 void realloc(DualView<Properties...>& dv, Args&&... args) noexcept(
1344  noexcept(dv.realloc(std::forward<Args>(args)...))) {
1345  dv.realloc(std::forward<Args>(args)...);
1346 }
1347 
1348 template <class I, class... Properties, class... Args>
1349 std::enable_if_t<Impl::is_view_ctor_property<I>::value> realloc(
1350  const I& arg_prop, DualView<Properties...>& dv,
1351  Args&&... args) noexcept(noexcept(dv.realloc(arg_prop,
1352  std::forward<Args>(
1353  args)...))) {
1354  dv.realloc(arg_prop, std::forward<Args>(args)...);
1355 }
1356 
1357 } // end namespace Kokkos
1358 
1359 #ifdef KOKKOS_IMPL_PUBLIC_INCLUDE_NOTDEFINED_DUALVIEW
1360 #undef KOKKOS_IMPL_PUBLIC_INCLUDE
1361 #undef KOKKOS_IMPL_PUBLIC_INCLUDE_NOTDEFINED_DUALVIEW
1362 #endif
1363 #endif