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