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