Tpetra parallel linear algebra  Version of the Day
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
Tpetra_Details_WrappedDualView.hpp
1 // @HEADER
2 // *****************************************************************************
3 // Tpetra: Templated Linear Algebra Services Package
4 //
5 // Copyright 2008 NTESS and the Tpetra contributors.
6 // SPDX-License-Identifier: BSD-3-Clause
7 // *****************************************************************************
8 // @HEADER
9 
10 #ifndef TPETRA_DETAILS_WRAPPEDDUALVIEW_HPP
11 #define TPETRA_DETAILS_WRAPPEDDUALVIEW_HPP
12 
13 #include <Tpetra_Access.hpp>
14 #include <Tpetra_Details_temporaryViewUtils.hpp>
15 #include <Kokkos_DualView.hpp>
16 #include "Teuchos_TestForException.hpp"
18 #include <sstream>
19 
20 //#define DEBUG_UVM_REMOVAL // Works only with gcc > 4.8
21 
22 #ifdef DEBUG_UVM_REMOVAL
23 
24 #define DEBUG_UVM_REMOVAL_ARGUMENT ,const char* callerstr = __builtin_FUNCTION(),const char * filestr=__builtin_FILE(),const int linnum = __builtin_LINE()
25 
26 #define DEBUG_UVM_REMOVAL_PRINT_CALLER(fn) \
27  { \
28  auto envVarSet = std::getenv("TPETRA_UVM_REMOVAL"); \
29  if (envVarSet && (std::strcmp(envVarSet,"1") == 0)) \
30  std::cout << (fn) << " called from " << callerstr \
31  << " at " << filestr << ":"<<linnum \
32  << " host cnt " << dualView.h_view.use_count() \
33  << " device cnt " << dualView.d_view.use_count() \
34  << std::endl; \
35  }
36 
37 #else
38 
39 #define DEBUG_UVM_REMOVAL_ARGUMENT
40 #define DEBUG_UVM_REMOVAL_PRINT_CALLER(fn)
41 
42 #endif
43 
45 namespace Tpetra {
46 
47  // We really need this forward declaration here for friend to work
48  template<typename SC, typename LO, typename GO, typename NO>
49  class MultiVector;
50 
51 
54 namespace Details {
55 
56 namespace impl {
57 
58 template <typename DualViewType>
59 struct hasConstData {
60  using valueType = typename DualViewType::value_type;
61  using constValueType = typename DualViewType::const_value_type;
62  static constexpr bool value = std::is_same<valueType, constValueType>::value;
63 };
64 
65 template <typename DualViewType>
66 using enableIfConstData = std::enable_if_t<hasConstData<DualViewType>::value>;
67 
68 template <typename DualViewType>
69 using enableIfNonConstData = std::enable_if_t<!hasConstData<DualViewType>::value>;
70 
71 template <typename DualViewType>
72 enableIfNonConstData<DualViewType>
73 sync_host(DualViewType dualView) {
74  // This will sync, but only if needed
75  dualView.sync_host();
76 }
77 
78 template <typename DualViewType>
79 enableIfConstData<DualViewType>
80 sync_host(DualViewType dualView) { }
81 
82 template <typename DualViewType>
83 enableIfNonConstData<DualViewType>
84 sync_device(DualViewType dualView) {
85  // This will sync, but only if needed
86  dualView.sync_device();
87 }
88 
89 template <typename DualViewType>
90 enableIfConstData<DualViewType>
91 sync_device(DualViewType dualView) { }
92 
93 }// end namespace Impl
94 
98 
99 extern bool wdvTrackingEnabled;
100 
105 void disableWDVTracking();
106 
108 void enableWDVTracking();
109 
112 template <typename DualViewType>
114 public:
115 
116  using DVT = DualViewType;
117  using t_host = typename DualViewType::t_host;
118  using t_dev = typename DualViewType::t_dev;
119 
120  using HostType = typename t_host::device_type;
121  using DeviceType = typename t_dev::device_type;
122 
123 private:
124  static constexpr bool dualViewHasNonConstData = !impl::hasConstData<DualViewType>::value;
125  static constexpr bool deviceMemoryIsHostAccessible =
126  Kokkos::SpaceAccessibility<Kokkos::DefaultHostExecutionSpace, typename t_dev::memory_space>::accessible;
127 
128 private:
129  template <typename>
130  friend class WrappedDualView;
131 
132 public:
133  WrappedDualView() {}
134 
135  WrappedDualView(DualViewType dualV)
136  : originalDualView(dualV),
137  dualView(originalDualView)
138  { }
139 
141  template <class SrcDualViewType>
143  : originalDualView(src.originalDualView),
144  dualView(src.dualView)
145  { }
146 
148  template <class SrcDualViewType>
150  originalDualView = src.originalDualView;
151  dualView = src.dualView;
152  return *this;
153  }
154 
155  // This is an expert-only constructor
156  // For WrappedDualView to manage synchronizations correctly,
157  // it must have an DualView which is not a subview to due the
158  // sync's on. This is what origDualV is for. In this case,
159  // dualV is a subview of origDualV.
160  WrappedDualView(DualViewType dualV,DualViewType origDualV)
161  : originalDualView(origDualV),
162  dualView(dualV)
163  { }
164 
165 
166  WrappedDualView(const t_dev deviceView) {
167  TEUCHOS_TEST_FOR_EXCEPTION(
168  deviceView.data() != nullptr && deviceView.use_count() == 0,
169  std::invalid_argument,
170  "Tpetra::Details::WrappedDualView: cannot construct with a device view that\n"
171  "does not own its memory (i.e. constructed with a raw pointer and dimensions)\n"
172  "because the WrappedDualView needs to assume ownership of the memory.");
173  //If the provided view is default-constructed (null, 0 extent, 0 use count),
174  //leave the host mirror default-constructed as well in order to have a matching use count of 0.
175  t_host hostView;
176  if(deviceView.use_count() != 0)
177  {
178  hostView = Kokkos::create_mirror_view(
179  Kokkos::WithoutInitializing,
180  typename t_host::memory_space(),
181  deviceView);
182  }
183  originalDualView = DualViewType(deviceView, hostView);
184  originalDualView.clear_sync_state();
185  originalDualView.modify_device();
186  dualView = originalDualView;
187  }
188 
189  // 1D View constructors
190  WrappedDualView(const WrappedDualView parent, int offset, int numEntries) {
191  originalDualView = parent.originalDualView;
192  dualView = getSubview(parent.dualView, offset, numEntries);
193  }
194 
195 
196  // 2D View Constructors
197  WrappedDualView(const WrappedDualView parent,const Kokkos::pair<size_t,size_t>& rowRng, const Kokkos::ALL_t& colRng) {
198  originalDualView = parent.originalDualView;
199  dualView = getSubview2D(parent.dualView,rowRng,colRng);
200  }
201 
202  WrappedDualView(const WrappedDualView parent,const Kokkos::ALL_t &rowRng, const Kokkos::pair<size_t,size_t>& colRng) {
203  originalDualView = parent.originalDualView;
204  dualView = getSubview2D(parent.dualView,rowRng,colRng);
205  }
206 
207  WrappedDualView(const WrappedDualView parent,const Kokkos::pair<size_t,size_t>& rowRng, const Kokkos::pair<size_t,size_t>& colRng) {
208  originalDualView = parent.originalDualView;
209  dualView = getSubview2D(parent.dualView,rowRng,colRng);
210  }
211 
212  size_t extent(const int i) const {
213  return dualView.h_view.extent(i);
214  }
215 
216  void stride(size_t * stride_) const {
217  dualView.stride(stride_);
218  }
219 
220 
221  size_t origExtent(const int i) const {
222  return originalDualView.h_view.extent(i);
223  }
224 
225  const char * label() const {
226  return dualView.d_view.label();
227  }
228 
229 
230  typename t_host::const_type
231  getHostView(Access::ReadOnlyStruct
232  DEBUG_UVM_REMOVAL_ARGUMENT
233  ) const
234  {
235  DEBUG_UVM_REMOVAL_PRINT_CALLER("getHostViewReadOnly");
236 
237  if(needsSyncPath()) {
238  throwIfDeviceViewAlive();
239  impl::sync_host(originalDualView);
240  }
241  return dualView.view_host();
242  }
243 
244  t_host
245  getHostView(Access::ReadWriteStruct
246  DEBUG_UVM_REMOVAL_ARGUMENT
247  )
248  {
249  DEBUG_UVM_REMOVAL_PRINT_CALLER("getHostViewReadWrite");
250  static_assert(dualViewHasNonConstData,
251  "ReadWrite views are not available for DualView with const data");
252  if(needsSyncPath()) {
253  throwIfDeviceViewAlive();
254  impl::sync_host(originalDualView);
255  originalDualView.modify_host();
256  }
257 
258  return dualView.view_host();
259  }
260 
261  t_host
262  getHostView(Access::OverwriteAllStruct
263  DEBUG_UVM_REMOVAL_ARGUMENT
264  )
265  {
266  DEBUG_UVM_REMOVAL_PRINT_CALLER("getHostViewOverwriteAll");
267  static_assert(dualViewHasNonConstData,
268  "OverwriteAll views are not available for DualView with const data");
269  if (iAmASubview()) {
270  return getHostView(Access::ReadWrite);
271  }
272  if(needsSyncPath()) {
273  throwIfDeviceViewAlive();
274  if (deviceMemoryIsHostAccessible) Kokkos::fence("WrappedDualView::getHostView");
275  dualView.clear_sync_state();
276  dualView.modify_host();
277  }
278  return dualView.view_host();
279  }
280 
281  typename t_dev::const_type
282  getDeviceView(Access::ReadOnlyStruct
283  DEBUG_UVM_REMOVAL_ARGUMENT
284  ) const
285  {
286  DEBUG_UVM_REMOVAL_PRINT_CALLER("getDeviceViewReadOnly");
287  if(needsSyncPath()) {
288  throwIfHostViewAlive();
289  impl::sync_device(originalDualView);
290  }
291  return dualView.view_device();
292  }
293 
294  t_dev
295  getDeviceView(Access::ReadWriteStruct
296  DEBUG_UVM_REMOVAL_ARGUMENT
297  )
298  {
299  DEBUG_UVM_REMOVAL_PRINT_CALLER("getDeviceViewReadWrite");
300  static_assert(dualViewHasNonConstData,
301  "ReadWrite views are not available for DualView with const data");
302  if(needsSyncPath()) {
303  throwIfHostViewAlive();
304  impl::sync_device(originalDualView);
305  originalDualView.modify_device();
306  }
307  return dualView.view_device();
308  }
309 
310  t_dev
311  getDeviceView(Access::OverwriteAllStruct
312  DEBUG_UVM_REMOVAL_ARGUMENT
313  )
314  {
315  DEBUG_UVM_REMOVAL_PRINT_CALLER("getDeviceViewOverwriteAll");
316  static_assert(dualViewHasNonConstData,
317  "OverwriteAll views are not available for DualView with const data");
318  if (iAmASubview()) {
319  return getDeviceView(Access::ReadWrite);
320  }
321  if(needsSyncPath()) {
322  throwIfHostViewAlive();
323  if (deviceMemoryIsHostAccessible) Kokkos::fence("WrappedDualView::getDeviceView");
324  dualView.clear_sync_state();
325  dualView.modify_device();
326  }
327  return dualView.view_device();
328  }
329 
330  template<class TargetDeviceType>
331  typename std::remove_reference<decltype(std::declval<DualViewType>().template view<TargetDeviceType>())>::type::const_type
332  getView (Access::ReadOnlyStruct s DEBUG_UVM_REMOVAL_ARGUMENT) const {
333  using ReturnViewType = typename std::remove_reference<decltype(std::declval<DualViewType>().template view<TargetDeviceType>())>::type::const_type;
334  using ReturnDeviceType = typename ReturnViewType::device_type;
335  constexpr bool returnDevice = std::is_same<ReturnDeviceType, DeviceType>::value;
336  if(returnDevice) {
337  DEBUG_UVM_REMOVAL_PRINT_CALLER("getView<Device>ReadOnly");
338  if(needsSyncPath()) {
339  throwIfHostViewAlive();
340  impl::sync_device(originalDualView);
341  }
342  }
343  else {
344  DEBUG_UVM_REMOVAL_PRINT_CALLER("getView<Host>ReadOnly");
345  if(needsSyncPath()) {
346  throwIfDeviceViewAlive();
347  impl::sync_host(originalDualView);
348  }
349  }
350 
351  return dualView.template view<TargetDeviceType>();
352  }
353 
354 
355  template<class TargetDeviceType>
356  typename std::remove_reference<decltype(std::declval<DualViewType>().template view<TargetDeviceType>())>::type
357  getView (Access::ReadWriteStruct s DEBUG_UVM_REMOVAL_ARGUMENT) const {
358  using ReturnViewType = typename std::remove_reference<decltype(std::declval<DualViewType>().template view<TargetDeviceType>())>::type;
359  using ReturnDeviceType = typename ReturnViewType::device_type;
360  constexpr bool returnDevice = std::is_same<ReturnDeviceType, DeviceType>::value;
361 
362  if(returnDevice) {
363  DEBUG_UVM_REMOVAL_PRINT_CALLER("getView<Device>ReadWrite");
364  static_assert(dualViewHasNonConstData,
365  "ReadWrite views are not available for DualView with const data");
366  if(needsSyncPath()) {
367  throwIfHostViewAlive();
368  impl::sync_device(originalDualView);
369  originalDualView.modify_device();
370  }
371  }
372  else {
373  DEBUG_UVM_REMOVAL_PRINT_CALLER("getView<Host>ReadWrite");
374  static_assert(dualViewHasNonConstData,
375  "ReadWrite views are not available for DualView with const data");
376  if(needsSyncPath()) {
377  throwIfDeviceViewAlive();
378  impl::sync_host(originalDualView);
379  originalDualView.modify_host();
380  }
381  }
382 
383  return dualView.template view<TargetDeviceType>();
384  }
385 
386 
387  template<class TargetDeviceType>
388  typename std::remove_reference<decltype(std::declval<DualViewType>().template view<TargetDeviceType>())>::type
389  getView (Access::OverwriteAllStruct s DEBUG_UVM_REMOVAL_ARGUMENT) const {
390  using ReturnViewType = typename std::remove_reference<decltype(std::declval<DualViewType>().template view<TargetDeviceType>())>::type;
391  using ReturnDeviceType = typename ReturnViewType::device_type;
392 
393  if (iAmASubview())
394  return getView<TargetDeviceType>(Access::ReadWrite);
395 
396  constexpr bool returnDevice = std::is_same<ReturnDeviceType, DeviceType>::value;
397 
398  if(returnDevice) {
399  DEBUG_UVM_REMOVAL_PRINT_CALLER("getView<Device>OverwriteAll");
400  static_assert(dualViewHasNonConstData,
401  "OverwriteAll views are not available for DualView with const data");
402  if(needsSyncPath()) {
403  throwIfHostViewAlive();
404  dualView.clear_sync_state();
405  dualView.modify_host();
406  }
407  }
408  else {
409  DEBUG_UVM_REMOVAL_PRINT_CALLER("getView<Host>OverwriteAll");
410  static_assert(dualViewHasNonConstData,
411  "OverwriteAll views are not available for DualView with const data");
412  if(needsSyncPath()) {
413  throwIfDeviceViewAlive();
414  dualView.clear_sync_state();
415  dualView.modify_device();
416  }
417  }
418 
419  return dualView.template view<TargetDeviceType>();
420  }
421 
422 
423  typename t_host::const_type
424  getHostSubview(int offset, int numEntries, Access::ReadOnlyStruct
425  DEBUG_UVM_REMOVAL_ARGUMENT
426  ) const
427  {
428  DEBUG_UVM_REMOVAL_PRINT_CALLER("getHostSubviewReadOnly");
429  if(needsSyncPath()) {
430  throwIfDeviceViewAlive();
431  impl::sync_host(originalDualView);
432  }
433  return getSubview(dualView.view_host(), offset, numEntries);
434  }
435 
436  t_host
437  getHostSubview(int offset, int numEntries, Access::ReadWriteStruct
438  DEBUG_UVM_REMOVAL_ARGUMENT
439  )
440  {
441  DEBUG_UVM_REMOVAL_PRINT_CALLER("getHostSubviewReadWrite");
442  static_assert(dualViewHasNonConstData,
443  "ReadWrite views are not available for DualView with const data");
444  if(needsSyncPath()) {
445  throwIfDeviceViewAlive();
446  impl::sync_host(originalDualView);
447  originalDualView.modify_host();
448  }
449  return getSubview(dualView.view_host(), offset, numEntries);
450  }
451 
452  t_host
453  getHostSubview(int offset, int numEntries, Access::OverwriteAllStruct
454  DEBUG_UVM_REMOVAL_ARGUMENT
455  )
456  {
457  DEBUG_UVM_REMOVAL_PRINT_CALLER("getHostSubviewOverwriteAll");
458  static_assert(dualViewHasNonConstData,
459  "OverwriteAll views are not available for DualView with const data");
460  return getHostSubview(offset, numEntries, Access::ReadWrite);
461  }
462 
463  typename t_dev::const_type
464  getDeviceSubview(int offset, int numEntries, Access::ReadOnlyStruct
465  DEBUG_UVM_REMOVAL_ARGUMENT
466  ) const
467  {
468  DEBUG_UVM_REMOVAL_PRINT_CALLER("getDeviceSubviewReadOnly");
469  if(needsSyncPath()) {
470  throwIfHostViewAlive();
471  impl::sync_device(originalDualView);
472  }
473  return getSubview(dualView.view_device(), offset, numEntries);
474  }
475 
476  t_dev
477  getDeviceSubview(int offset, int numEntries, Access::ReadWriteStruct
478  DEBUG_UVM_REMOVAL_ARGUMENT
479  )
480  {
481  DEBUG_UVM_REMOVAL_PRINT_CALLER("getDeviceSubviewReadWrite");
482  static_assert(dualViewHasNonConstData,
483  "ReadWrite views are not available for DualView with const data");
484  if(needsSyncPath()) {
485  throwIfHostViewAlive();
486  impl::sync_device(originalDualView);
487  originalDualView.modify_device();
488  }
489  return getSubview(dualView.view_device(), offset, numEntries);
490  }
491 
492  t_dev
493  getDeviceSubview(int offset, int numEntries, Access::OverwriteAllStruct
494  DEBUG_UVM_REMOVAL_ARGUMENT
495  )
496  {
497  DEBUG_UVM_REMOVAL_PRINT_CALLER("getDeviceSubviewOverwriteAll");
498  static_assert(dualViewHasNonConstData,
499  "OverwriteAll views are not available for DualView with const data");
500  return getDeviceSubview(offset, numEntries, Access::ReadWrite);
501  }
502 
503 
504  // Debugging functions to get copies of the view state
505  typename t_host::HostMirror getHostCopy() const {
506  auto X_dev = dualView.view_host();
507  if(X_dev.span_is_contiguous()) {
508  auto mirror = Kokkos::create_mirror_view(X_dev);
509  Kokkos::deep_copy(mirror,X_dev);
510  return mirror;
511  }
512  else {
513  auto X_contig = Tpetra::Details::TempView::toLayout<decltype(X_dev), Kokkos::LayoutLeft>(X_dev);
514  auto mirror = Kokkos::create_mirror_view(X_contig);
515  Kokkos::deep_copy(mirror,X_contig);
516  return mirror;
517  }
518  }
519 
520  typename t_dev::HostMirror getDeviceCopy() const {
521  auto X_dev = dualView.view_device();
522  if(X_dev.span_is_contiguous()) {
523  auto mirror = Kokkos::create_mirror_view(X_dev);
524  Kokkos::deep_copy(mirror,X_dev);
525  return mirror;
526  }
527  else {
528  auto X_contig = Tpetra::Details::TempView::toLayout<decltype(X_dev), Kokkos::LayoutLeft>(X_dev);
529  auto mirror = Kokkos::create_mirror_view(X_contig);
530  Kokkos::deep_copy(mirror,X_contig);
531  return mirror;
532  }
533  }
534 
535  // Debugging functions for validity checks
536  bool is_valid_host() const {
537  return dualView.view_host().size() == 0 || dualView.view_host().data();
538  }
539 
540  bool is_valid_device() const {
541  return dualView.view_device().size() == 0 || dualView.view_device().data();
542  }
543 
544 
545  bool need_sync_host() const {
546  return originalDualView.need_sync_host();
547  }
548 
549  bool need_sync_device() const {
550  return originalDualView.need_sync_device();
551  }
552 
553  int host_view_use_count() const {
554  return originalDualView.h_view.use_count();
555  }
556 
557  int device_view_use_count() const {
558  return originalDualView.d_view.use_count();
559  }
560 
561 
562  // MultiVector really needs to get at the raw DualViews,
563  // but we'd very much prefer that users not.
564  template<typename SC, typename LO, typename GO, typename NO>
565  friend class ::Tpetra::MultiVector;
566 
567 private:
568  // A Kokkos implementation of WrappedDualView will have to make these
569  // functions publically accessable, but in the Tpetra version, we'd
570  // really rather not.
571  DualViewType getOriginalDualView() const {
572  return originalDualView;
573  }
574 
575  DualViewType getDualView() const {
576  return dualView;
577  }
578 
579  template <typename ViewType>
580  ViewType getSubview(ViewType view, int offset, int numEntries) const {
581  return Kokkos::subview(view, Kokkos::pair<int, int>(offset, offset+numEntries));
582  }
583 
584  template <typename ViewType,typename int_type>
585  ViewType getSubview2D(ViewType view, Kokkos::pair<int_type,int_type> offset0, const Kokkos::ALL_t&) const {
586  return Kokkos::subview(view,offset0,Kokkos::ALL());
587  }
588 
589  template <typename ViewType,typename int_type>
590  ViewType getSubview2D(ViewType view, const Kokkos::ALL_t&, Kokkos::pair<int_type,int_type> offset1) const {
591  return Kokkos::subview(view,Kokkos::ALL(),offset1);
592  }
593 
594  template <typename ViewType,typename int_type>
595  ViewType getSubview2D(ViewType view, Kokkos::pair<int_type,int_type> offset0, Kokkos::pair<int_type,int_type> offset1) const {
596  return Kokkos::subview(view,offset0,offset1);
597  }
598 
599  bool memoryIsAliased() const {
600  return deviceMemoryIsHostAccessible && dualView.h_view.data() == dualView.d_view.data();
601  }
602 
603 
621  bool needsSyncPath() const {
622  if(!wdvTrackingEnabled)
623  return false;
624 
625  // We check to see if the memory is not aliased *or* if it is a supported
626  // (heterogeneous memory) accelerator (for shared host/device memory).
627  return !memoryIsAliased() || Spaces::is_gpu_exec_space<typename DualViewType::execution_space>();
628  }
629 
630 
631  void throwIfViewsAreDifferentSizes() const {
632  // Here we check *size* (the product of extents) rather than each extent individually.
633  // This is mostly designed to catch people resizing one view, but not the other.
634  if(dualView.d_view.size() != dualView.h_view.size()) {
635  std::ostringstream msg;
636  msg << "Tpetra::Details::WrappedDualView (name = " << dualView.d_view.label()
637  << "; host and device views are different sizes: "
638  << dualView.h_view.size() << " vs " <<dualView.h_view.size();
639  throw std::runtime_error(msg.str());
640  }
641  }
642 
643  void throwIfHostViewAlive() const {
644  throwIfViewsAreDifferentSizes();
645  if (dualView.h_view.use_count() > dualView.d_view.use_count()) {
646  std::ostringstream msg;
647  msg << "Tpetra::Details::WrappedDualView (name = " << dualView.d_view.label()
648  << "; host use_count = " << dualView.h_view.use_count()
649  << "; device use_count = " << dualView.d_view.use_count() << "): "
650  << "Cannot access data on device while a host view is alive";
651  throw std::runtime_error(msg.str());
652  }
653  }
654 
655  void throwIfDeviceViewAlive() const {
656  throwIfViewsAreDifferentSizes();
657  if (dualView.d_view.use_count() > dualView.h_view.use_count()) {
658  std::ostringstream msg;
659  msg << "Tpetra::Details::WrappedDualView (name = " << dualView.d_view.label()
660  << "; host use_count = " << dualView.h_view.use_count()
661  << "; device use_count = " << dualView.d_view.use_count() << "): "
662  << "Cannot access data on host while a device view is alive";
663  throw std::runtime_error(msg.str());
664  }
665  }
666 
667  bool iAmASubview() {
668  return originalDualView.h_view != dualView.h_view;
669  }
670 
671  mutable DualViewType originalDualView;
672  mutable DualViewType dualView;
673 };
674 
675 } // namespace Details
676 
677 } // namespace Tpetra
678 
679 #endif
One or more distributed dense vectors.
bool wdvTrackingEnabled
Whether WrappedDualView reference count checking is enabled. Initially true. Since the DualView sync ...
A wrapper around Kokkos::DualView to safely manage data that might be replicated between host and dev...
WrappedDualView(const WrappedDualView< SrcDualViewType > &src)
Conversion copy constructor.
void deep_copy(MultiVector< DS, DL, DG, DN > &dst, const MultiVector< SS, SL, SG, SN > &src)
Copy the contents of the MultiVector src into dst.
WrappedDualView & operator=(const WrappedDualView< SrcDualViewType > &src)
Conversion assignment operator.
void enableWDVTracking()
Enable WrappedDualView reference-count tracking and syncing. Call this after exiting a host-parallel ...
void disableWDVTracking()
Disable WrappedDualView reference-count tracking and syncing. Call this before entering a host-parall...