Tpetra parallel linear algebra  Version of the Day
Tpetra_Details_WrappedDualView.hpp
1 // @HEADER
2 // ***********************************************************************
3 //
4 // Tpetra: Templated Linear Algebra Services Package
5 // Copyright (2008) Sandia Corporation
6 //
7 // Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
8 // the U.S. Government retains certain rights in this software.
9 //
10 // Redistribution and use in source and binary forms, with or without
11 // modification, are permitted provided that the following conditions are
12 // met:
13 //
14 // 1. Redistributions of source code must retain the above copyright
15 // notice, this list of conditions and the following disclaimer.
16 //
17 // 2. Redistributions in binary form must reproduce the above copyright
18 // notice, this list of conditions and the following disclaimer in the
19 // documentation and/or other materials provided with the distribution.
20 //
21 // 3. Neither the name of the Corporation nor the names of the
22 // contributors may be used to endorse or promote products derived from
23 // this software without specific prior written permission.
24 //
25 // THIS SOFTWARE IS PROVIDED BY SANDIA CORPORATION "AS IS" AND ANY
26 // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28 // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SANDIA CORPORATION OR THE
29 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
30 // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
31 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
32 // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
33 // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
34 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
35 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 //
37 // ************************************************************************
38 // @HEADER
39 
40 #ifndef TPETRA_DETAILS_WRAPPEDDUALVIEW_HPP
41 #define TPETRA_DETAILS_WRAPPEDDUALVIEW_HPP
42 
43 #include <Tpetra_Access.hpp>
44 #include <Kokkos_DualView.hpp>
45 #include "Teuchos_TestForException.hpp"
46 #include <sstream>
47 
48 // #define DEBUG_UVM_REMOVAL // Works only with gcc > 4.8
49 
50 #ifdef DEBUG_UVM_REMOVAL
51 
52 #define DEBUG_UVM_REMOVAL_ARGUMENT ,const char* callerstr = __builtin_FUNCTION()
53 
54 #define DEBUG_UVM_REMOVAL_PRINT_CALLER(fn) \
55  { \
56  auto envVarSet = std::getenv("TPETRA_UVM_REMOVAL"); \
57  if (envVarSet && (std::strcmp(envVarSet,"1") == 0)) \
58  std::cout << (fn) << " called from " << callerstr \
59  << " host cnt " << dualView.h_view.use_count() \
60  << " device cnt " << dualView.d_view.use_count() \
61  << std::endl; \
62  }
63 
64 #else
65 
66 #define DEBUG_UVM_REMOVAL_ARGUMENT
67 #define DEBUG_UVM_REMOVAL_PRINT_CALLER(fn)
68 
69 #endif
70 
72 namespace Tpetra {
73 
76 namespace Details {
77 
78 namespace impl {
79 
80 template <typename DualViewType>
81 struct hasConstData {
82  using valueType = typename DualViewType::value_type;
83  using constValueType = typename DualViewType::const_value_type;
84  static constexpr bool value = std::is_same<valueType, constValueType>::value;
85 };
86 
87 template <typename DualViewType>
88 using enableIfConstData = std::enable_if_t<hasConstData<DualViewType>::value>;
89 
90 template <typename DualViewType>
91 using enableIfNonConstData = std::enable_if_t<!hasConstData<DualViewType>::value>;
92 
93 template <typename DualViewType>
94 enableIfNonConstData<DualViewType>
95 sync_host(DualViewType dualView) {
96  dualView.sync_host();
97 }
98 
99 template <typename DualViewType>
100 enableIfConstData<DualViewType>
101 sync_host(DualViewType dualView) { }
102 
103 template <typename DualViewType>
104 enableIfNonConstData<DualViewType>
105 sync_device(DualViewType dualView) {
106  dualView.sync_device();
107 }
108 
109 template <typename DualViewType>
110 enableIfConstData<DualViewType>
111 sync_device(DualViewType dualView) { }
112 
113 }
114 
115 template <typename DualViewType>
116 class WrappedDualView {
117 public:
118  using HostViewType = typename DualViewType::t_host;
119  using DeviceViewType = typename DualViewType::t_dev;
120 
121 private:
122  static constexpr bool dualViewHasNonConstData = !impl::hasConstData<DualViewType>::value;
123  static constexpr bool deviceMemoryIsHostAccessible =
124  Kokkos::SpaceAccessibility<Kokkos::DefaultHostExecutionSpace, typename DeviceViewType::memory_space>::accessible;
125 
126 public:
127  WrappedDualView() {}
128 
129  WrappedDualView(DualViewType dualV)
130  : originalDualView(dualV),
131  dualView(originalDualView)
132  { }
133 
134  WrappedDualView(const DeviceViewType deviceView) {
135  TEUCHOS_TEST_FOR_EXCEPTION(
136  deviceView.data() != nullptr && deviceView.use_count() == 0,
137  std::invalid_argument,
138  "Tpetra::Details::WrappedDualView: cannot construct with a device view that\n"
139  "does not own its memory (i.e. constructed with a raw pointer and dimensions)\n"
140  "because the WrappedDualView needs to assume ownership of the memory.");
141  //If the provided view is default-constructed (null, 0 extent, 0 use count),
142  //leave the host mirror default-constructed as well in order to have a matching use count of 0.
143  HostViewType hostView;
144  if(deviceView.use_count() != 0)
145  {
146  hostView = Kokkos::create_mirror_view_and_copy(
147  typename HostViewType::memory_space(),
148  deviceView);
149  }
150  originalDualView = DualViewType(deviceView, hostView);
151  dualView = originalDualView;
152  }
153 
154  WrappedDualView(const WrappedDualView parent, int offset, int numEntries) {
155  originalDualView = parent.originalDualView;
156  dualView = getSubview(parent.dualView, offset, numEntries);
157  }
158 
159  size_t extent(const int i) const {
160  return dualView.extent(i);
161  }
162 
163  typename HostViewType::const_type
164  getHostView(Access::ReadOnlyStruct
165  DEBUG_UVM_REMOVAL_ARGUMENT
166  ) const
167  {
168  DEBUG_UVM_REMOVAL_PRINT_CALLER("getHostViewReadOnly");
169  throwIfDeviceViewAlive();
170  impl::sync_host(originalDualView);
171  return dualView.view_host();
172  }
173 
174  HostViewType
175  getHostView(Access::ReadWriteStruct
176  DEBUG_UVM_REMOVAL_ARGUMENT
177  )
178  {
179  DEBUG_UVM_REMOVAL_PRINT_CALLER("getHostViewReadWrite");
180  static_assert(dualViewHasNonConstData,
181  "ReadWrite views are not available for DualView with const data");
182  throwIfDeviceViewAlive();
183  impl::sync_host(originalDualView);
184  originalDualView.modify_host();
185  return dualView.view_host();
186  }
187 
188  HostViewType
189  getHostView(Access::OverwriteAllStruct
190  DEBUG_UVM_REMOVAL_ARGUMENT
191  )
192  {
193  DEBUG_UVM_REMOVAL_PRINT_CALLER("getHostViewOverwriteAll");
194  static_assert(dualViewHasNonConstData,
195  "OverwriteAll views are not available for DualView with const data");
196  if (iAmASubview()) {
197  return getHostView(Access::ReadWrite);
198  }
199  throwIfDeviceViewAlive();
200  if (deviceMemoryIsHostAccessible) Kokkos::fence();
201  dualView.clear_sync_state();
202  dualView.modify_host();
203  return dualView.view_host();
204  }
205 
206  typename DeviceViewType::const_type
207  getDeviceView(Access::ReadOnlyStruct
208  DEBUG_UVM_REMOVAL_ARGUMENT
209  ) const
210  {
211  DEBUG_UVM_REMOVAL_PRINT_CALLER("getDeviceViewReadOnly");
212  throwIfHostViewAlive();
213  impl::sync_device(originalDualView);
214  return dualView.view_device();
215  }
216 
217  DeviceViewType
218  getDeviceView(Access::ReadWriteStruct
219  DEBUG_UVM_REMOVAL_ARGUMENT
220  )
221  {
222  DEBUG_UVM_REMOVAL_PRINT_CALLER("getDeviceViewReadWrite");
223  static_assert(dualViewHasNonConstData,
224  "ReadWrite views are not available for DualView with const data");
225  throwIfHostViewAlive();
226  impl::sync_device(originalDualView);
227  originalDualView.modify_device();
228  return dualView.view_device();
229  }
230 
231  DeviceViewType
232  getDeviceView(Access::OverwriteAllStruct
233  DEBUG_UVM_REMOVAL_ARGUMENT
234  )
235  {
236  DEBUG_UVM_REMOVAL_PRINT_CALLER("getDeviceViewOverwriteAll");
237  static_assert(dualViewHasNonConstData,
238  "OverwriteAll views are not available for DualView with const data");
239  if (iAmASubview()) {
240  return getDeviceView(Access::ReadWrite);
241  }
242  throwIfHostViewAlive();
243  dualView.clear_sync_state();
244  dualView.modify_device();
245  return dualView.view_device();
246  }
247 
248  typename HostViewType::const_type
249  getHostSubview(int offset, int numEntries, Access::ReadOnlyStruct
250  DEBUG_UVM_REMOVAL_ARGUMENT
251  ) const
252  {
253  DEBUG_UVM_REMOVAL_PRINT_CALLER("getHostSubviewReadOnly");
254  throwIfDeviceViewAlive();
255  impl::sync_host(originalDualView);
256  return getSubview(dualView.view_host(), offset, numEntries);
257  }
258 
259  HostViewType
260  getHostSubview(int offset, int numEntries, Access::ReadWriteStruct
261  DEBUG_UVM_REMOVAL_ARGUMENT
262  )
263  {
264  DEBUG_UVM_REMOVAL_PRINT_CALLER("getHostSubviewReadWrite");
265  static_assert(dualViewHasNonConstData,
266  "ReadWrite views are not available for DualView with const data");
267  throwIfDeviceViewAlive();
268  impl::sync_host(originalDualView);
269  originalDualView.modify_host();
270  return getSubview(dualView.view_host(), offset, numEntries);
271  }
272 
273  HostViewType
274  getHostSubview(int offset, int numEntries, Access::OverwriteAllStruct
275  DEBUG_UVM_REMOVAL_ARGUMENT
276  )
277  {
278  DEBUG_UVM_REMOVAL_PRINT_CALLER("getHostSubviewOverwriteAll");
279  static_assert(dualViewHasNonConstData,
280  "OverwriteAll views are not available for DualView with const data");
281  return getHostSubview(offset, numEntries, Access::ReadWrite);
282  }
283 
284  typename DeviceViewType::const_type
285  getDeviceSubview(int offset, int numEntries, Access::ReadOnlyStruct
286  DEBUG_UVM_REMOVAL_ARGUMENT
287  ) const
288  {
289  DEBUG_UVM_REMOVAL_PRINT_CALLER("getDeviceSubviewReadOnly");
290  throwIfHostViewAlive();
291  impl::sync_device(originalDualView);
292  return getSubview(dualView.view_device(), offset, numEntries);
293  }
294 
295  DeviceViewType
296  getDeviceSubview(int offset, int numEntries, Access::ReadWriteStruct
297  DEBUG_UVM_REMOVAL_ARGUMENT
298  )
299  {
300  DEBUG_UVM_REMOVAL_PRINT_CALLER("getDeviceSubviewReadWrite");
301  static_assert(dualViewHasNonConstData,
302  "ReadWrite views are not available for DualView with const data");
303  throwIfHostViewAlive();
304  impl::sync_device(originalDualView);
305  originalDualView.modify_device();
306  return getSubview(dualView.view_device(), offset, numEntries);
307  }
308 
309  DeviceViewType
310  getDeviceSubview(int offset, int numEntries, Access::OverwriteAllStruct
311  DEBUG_UVM_REMOVAL_ARGUMENT
312  )
313  {
314  DEBUG_UVM_REMOVAL_PRINT_CALLER("getDeviceSubviewOverwriteAll");
315  static_assert(dualViewHasNonConstData,
316  "OverwriteAll views are not available for DualView with const data");
317  return getDeviceSubview(offset, numEntries, Access::ReadWrite);
318  }
319 
320 private:
321  template <typename ViewType>
322  ViewType getSubview(ViewType view, int offset, int numEntries) const {
323  return Kokkos::subview(view, Kokkos::pair<int, int>(offset, offset+numEntries));
324  }
325 
326  void throwIfHostViewAlive() const {
327  if( deviceMemoryIsHostAccessible && dualView.h_view.data() == dualView.d_view.data()) return;
328 
329  if (dualView.h_view.use_count() > dualView.d_view.use_count()) {
330  std::ostringstream msg;
331  msg << "Tpetra::Details::WrappedDualView (name = " << dualView.d_view.label()
332  << "; host use_count = " << dualView.h_view.use_count()
333  << "; device use_count = " << dualView.d_view.use_count() << "): "
334  << "Cannot access data on device while a host view is alive";
335  throw std::runtime_error(msg.str());
336  }
337  }
338 
339  void throwIfDeviceViewAlive() const {
340  if(deviceMemoryIsHostAccessible && dualView.h_view.data() == dualView.d_view.data()) return;
341 
342  if (dualView.d_view.use_count() > dualView.h_view.use_count()) {
343  std::ostringstream msg;
344  msg << "Tpetra::Details::WrappedDualView (name = " << dualView.d_view.label()
345  << "; host use_count = " << dualView.h_view.use_count()
346  << "; device use_count = " << dualView.d_view.use_count() << "): "
347  << "Cannot access data on host while a device view is alive";
348  throw std::runtime_error(msg.str());
349  }
350  }
351 
352  bool iAmASubview() {
353  return originalDualView.h_view != dualView.h_view;
354  }
355 
356  mutable DualViewType originalDualView;
357  mutable DualViewType dualView;
358 };
359 
360 } // namespace Details
361 
362 } // namespace Tpetra
363 
364 #endif
Implementation details of Tpetra.
Namespace Tpetra contains the class and methods constituting the Tpetra library.