1 /*
2 //@HEADER
3 // ************************************************************************
4 //
5 // Kokkos
6 // Manycore Performance-Portable Multidimensional Arrays
7 //
8 // Copyright (2012) Sandia Corporation
9 //
10 // Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
11 // the U.S. Government retains certain rights in this software.
12 //
13 // Redistribution and use in source and binary forms, with or without
14 // modification, are permitted provided that the following conditions are
15 // met:
16 //
17 // 1. Redistributions of source code must retain the above copyright
18 // notice, this list of conditions and the following disclaimer.
19 //
20 // 2. Redistributions in binary form must reproduce the above copyright
21 // notice, this list of conditions and the following disclaimer in the
22 // documentation and/or other materials provided with the distribution.
23 //
24 // 3. Neither the name of the Corporation nor the names of the
25 // contributors may be used to endorse or promote products derived from
26 // this software without specific prior written permission.
27 //
28 // THIS SOFTWARE IS PROVIDED BY SANDIA CORPORATION "AS IS" AND ANY
29 // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
31 // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SANDIA CORPORATION OR THE
32 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
33 // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
34 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
35 // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
36 // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
37 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
38 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39 //
40 // Questions? Contact H. Carter Edwards (hcedwar@sandia.gov)
41 //
42 // ************************************************************************
43 //@HEADER
44 */
45
46 #include <Teuchos_UnitTestHarness.hpp>
47 #include <KokkosCompat_TMM.hpp>
48 #include <Teuchos_Array.hpp>
49
50 namespace TestTeuchosKokkosCompat {
51
52 // Test functor, that fills the given 1-D vector in parallel. The
53 // functor is like APL's iota, except that it starts with an initial
54 // constant instead of with zero. That makes it more "interesting"
55 // so that tests are more likely to catch bugs.
56 template<class Device>
57 class FillFunctor {
58 public:
FillFunctor(const Kokkos::View<double *,Device> & vec)59 FillFunctor (const Kokkos::View<double*, Device>& vec) : vec_ (vec) {}
operator ()(const int i) const60 KOKKOS_INLINE_FUNCTION void operator () (const int i) const {
61 vec_[i] = 42.0 + static_cast<double> (i);
62 }
63 private:
64 Kokkos::View<double*, Device> vec_;
65 };
66
67 // Custom deallocator for Teuchos::ArrayRCP. It doesn't actually
68 // deallocate the ArrayRCP's data (which belongs to the View passed
69 // into Deallocator's constructor, not to the ArrayRCP). Instead,
70 // it just keeps a reference to the View. That way, the data won't
71 // go away until the View's reference count goes to zero.
72 template<class Scalar, class ViewType>
73 class Deallocator {
74 public:
75 // Constructor takes the View that owns the memory.
Deallocator(const ViewType & view)76 Deallocator (const ViewType& view) : view_ (view) {}
77
78 // "Deallocation function" doesn't actually deallocate its input
79 // pointer; the View is responsible for deallocation of its
80 // memory.
free(Scalar *)81 void free (Scalar*) {}
82 private:
83 ViewType view_; // View that owns the memory
84 };
85
86 } // namespace TestTeuchosKokkosCompat
87
88 // Just test whether Teuchos memory management objects and Kokkos
89 // Array Views can coexist in the same program. This test does not
90 // have the Teuchos and Kokkos objects interact with each other.
TEUCHOS_UNIT_TEST(LinkTeuchosAndKokkos,NoInteraction)91 TEUCHOS_UNIT_TEST( LinkTeuchosAndKokkos, NoInteraction ) {
92 typedef Teuchos::Array<double>::size_type size_type;
93
94 const size_type numElts = 10;
95 Teuchos::Array<double> x (numElts);
96 for (size_type k = 0; k < numElts; ++k) {
97 x[k] = 42.0 + static_cast<double> (k);
98 }
99 Teuchos::ArrayView<double> xView = x.view (3, 5); // view of [3, 4, 5, 6, 7]
100 TEST_EQUALITY_CONST( xView.size(), 5 );
101 for (size_type k = 0; k < xView.size (); ++k) {
102 TEST_EQUALITY( xView[k], x[k+3] );
103 }
104
105 typedef Kokkos::View<double*> ka_view_type;
106 ka_view_type y ("y", numElts);
107
108 using execution_space = ka_view_type::execution_space;
109 Kokkos::RangePolicy<execution_space, int> range (0, y.extent (0));
110 using functor_type = TestTeuchosKokkosCompat::FillFunctor<execution_space>;
111 Kokkos::parallel_for ("FillFunctor", range, functor_type (y));
112 }
113
114
115 // Get a Teuchos::ArrayView of a Kokkos::View, and make sure that
116 // it points to the same data.
TEUCHOS_UNIT_TEST(LinkTeuchosAndKokkos,ArrayViewOfView)117 TEUCHOS_UNIT_TEST( LinkTeuchosAndKokkos, ArrayViewOfView ) {
118 typedef Teuchos::Array<double>::size_type size_type;
119 typedef Kokkos::View<double*> ka_view_type;
120
121 const size_type numElts = 10;
122 ka_view_type y ("y", numElts);
123
124 using execution_space = ka_view_type::execution_space;
125 Kokkos::RangePolicy<execution_space, int> range (0, y.extent (0));
126 using functor_type = TestTeuchosKokkosCompat::FillFunctor<execution_space>;
127 Kokkos::parallel_for ("FillFunctor", range, functor_type (y));
128
129 auto y_host = Kokkos::create_mirror_view (y);
130 Kokkos::deep_copy (y_host, y);
131
132 Teuchos::ArrayView<double> y_view (y_host.data (), y_host.extent (0));
133 TEST_EQUALITY_CONST( size_t (y_view.size ()), size_t (y.extent (0)) );
134 if (success) {
135 for (size_type k = 0; k < size_type (y.extent (0)); ++k) {
136 TEST_EQUALITY( y_view[k], y[k] );
137 }
138 }
139 }
140
141 template<class DataType, class LayoutType = Kokkos::LayoutLeft>
142 using ManagedHostView =
143 Kokkos::View<DataType, LayoutType,
144 Kokkos::Device<Kokkos::DefaultHostExecutionSpace, Kokkos::HostSpace> >;
145
146 template<class DataType, class LayoutType = Kokkos::LayoutLeft>
147 using UnmanagedHostView =
148 Kokkos::View<DataType, LayoutType,
149 Kokkos::Device<Kokkos::DefaultHostExecutionSpace, Kokkos::HostSpace>,
150 Kokkos::MemoryTraits<Kokkos::Unmanaged> >;
151
152 // Get a Kokkos::View of a Teuchos::ArrayView, and make sure that it
153 // points to the same data. Thanks to Christian Trott for
154 // implementing the necessary functionality (View constructor for
155 // certain View specializations, that takes a raw pointer and
156 // dimensions) in Kokkos Array.
157 //
158 // This example will be useful for implementing the
159 // Tpetra::MultiVector methods get1dCopy and get2dCopy.
TEUCHOS_UNIT_TEST(LinkTeuchosAndKokkos,ViewOfArrayView)160 TEUCHOS_UNIT_TEST( LinkTeuchosAndKokkos, ViewOfArrayView ) {
161 using size_type = Teuchos::Array<double>::size_type;
162
163 const size_type numElts = 10;
164 Teuchos::Array<double> x (numElts);
165 for (size_type k = 0; k < numElts; ++k) {
166 x[k] = 42.0 + static_cast<double> (k);
167 }
168 // You can make an (unmanaged) View of a raw array with left or
169 // right (Fortran or C) layout on the HostSpace device, just by
170 // passing the array and stride(s) into the constructor. If you
171 // want the dimensions to differ from the strides, you'll have to
172 // create a subview with the desired dimensions.
173 UnmanagedHostView<double*> x_view (x.getRawPtr (), x.size ());
174
175 TEST_EQUALITY( x.size(), static_cast<size_type> (x_view.extent (0)) );
176 for (size_type k = 0; k < x.size (); ++k) {
177 TEST_EQUALITY( x_view(k), x[k] );
178 }
179
180 UnmanagedHostView<const double*> x_view_const (x.getRawPtr (), x.size ());
181
182 TEST_EQUALITY( x.size (), static_cast<size_type> (x_view_const.extent (0)) );
183 for (size_type k = 0; k < x.size (); ++k) {
184 TEST_EQUALITY( x_view_const(k), x[k] );
185 }
186 }
187
188 // Create a 2-D View (LayoutLeft, in HostSpace memory), and then create an
189 // ArrayRCP which owns that View (using a custom destructor). This
190 // will be useful for implementing Tpetra::MultiVector's getData,
191 // getDataNonConst, get1dView, and get1dViewNonConst methods.
TEUCHOS_UNIT_TEST(LinkTeuchosAndKokkos,ArrayRCP1D_of_2DView)192 TEUCHOS_UNIT_TEST( LinkTeuchosAndKokkos, ArrayRCP1D_of_2DView ) {
193 using ka_view_type = ManagedHostView<double**>;
194
195 const size_t numRows = 75;
196 const size_t numCols = 5;
197 const size_t stride = 100;
198 const size_t ZERO = static_cast<size_t> (0);
199
200 ka_view_type X ("X", stride, numCols);
201 ka_view_type X_view = Kokkos::subview (X, std::make_pair (ZERO, numRows),
202 std::make_pair (ZERO, numCols));
203 TEST_EQUALITY(X_view.extent(0), numRows);
204 TEST_EQUALITY(X_view.extent(1), numCols);
205
206 // Test that the strides of X_view are correct, for int. Kokkos
207 // Array templates the stride() method on the integer type of the
208 // input. I just want to make sure that this works for different
209 // integer types; I'll test int here and size_t.
210 {
211 int strides[3] = { 0 , 0 , 0 };
212 X_view.stride (strides);
213 TEST_EQUALITY_CONST(strides[0], 1); // stride between X_view(i,j) and X_view(i+1,j)
214 // The stride must be at least as given, but can be greater (due to possible padding for alignment).
215 TEST_ASSERT(static_cast<size_t>(strides[1]) >= stride); // stride between X_view(i,j) and X_view(i,j+1)
216 }
217
218 // Test that the strides of X_view are correct, for size_t.
219 {
220 size_t strides[3] = { 0 , 0 , 0 };
221 X_view.stride (strides);
222 TEST_EQUALITY_CONST(strides[0], static_cast<size_t>(1)); // stride between X_view(i,j) and X_view(i+1,j)
223 // The stride must be at least as given, but can be greater (due to possible padding for alignment).
224 TEST_ASSERT(strides[1] >= stride); // stride between X_view(i,j) and X_view(i,j+1)
225 }
226
227 // Create a nonowning "view" of X's data. The ArrayRCP has a custom
228 // destructor which simply holds a reference to the View and doesn't
229 // actually deallocate memory. That way, the ArrayRCP can use the
230 // View's raw pointer, but still defers to the View for memory
231 // management.
232
233 using dealloc_type = TestTeuchosKokkosCompat::Deallocator<double, ka_view_type>;
234 Teuchos::ArrayRCP<double> Y_values (X.data (), 0, stride*numCols,
235 dealloc_type (X), true);
236 TEST_EQUALITY(Y_values.getRawPtr(), X.data());
237 TEST_EQUALITY(Y_values.getRawPtr(), X_view.data());
238 TEST_EQUALITY(Y_values.size(), stride*numCols);
239 }
240
241
242 // Create a 2-D View (LayoutLeft, in HostSpace memory), and then create an
243 // ArrayRCP<ArrayRCP<double> >, each entry of which owns the
244 // corresponding column of that View (using a custom destructor).
245 // This will be useful for implementing Tpetra::MultiVector's
246 // get2dView and get2dViewNonConst methods.
TEUCHOS_UNIT_TEST(LinkTeuchosAndKokkos,ArrayRCP2D_of_2DView)247 TEUCHOS_UNIT_TEST( LinkTeuchosAndKokkos, ArrayRCP2D_of_2DView ) {
248 // View<double*, LayoutLeft, ...> and View<double*, LayoutRight,
249 // ...> always have unit stride. Furthermore, subview(X,j) returns
250 // a view of a column for LayoutLeft, and a view of a row for
251 // LayoutRight. Thus, to support both layouts, each column of the
252 // multivector still needs to be a View<double**, ...>, and we have
253 // to use the subview() overload that takes two ranges of row and
254 // column indices.
255 using ka_view_type = ManagedHostView<double**>;
256
257 const size_t numRows = 75;
258 const size_t numCols = 5;
259 const size_t stride = 100;
260 const size_t ZERO = static_cast<size_t> (0);
261
262 ka_view_type X ("X", stride, numCols);
263 ka_view_type X_view = Kokkos::subview (X, std::make_pair (ZERO, numRows),
264 std::make_pair (ZERO, numCols));
265 TEST_EQUALITY( & X(0,0) , & X_view(0,0) );
266 TEST_EQUALITY(X_view.extent(0), numRows);
267 TEST_EQUALITY(X_view.extent(1), numCols);
268
269 // Test that the strides of X_view are correct, for size_t.
270 {
271 size_t strides[3]; // stride[rank] is the maximum span
272 strides[0] = static_cast<size_t> (0);
273 strides[1] = static_cast<size_t> (0);
274 strides[2] = static_cast<size_t> (0);
275 X_view.stride (strides);
276 TEST_EQUALITY_CONST(strides[0], static_cast<size_t>(1)); // stride between X_view(i,j) and X_view(i+1,j)
277 // The stride must be at least as given, but can be greater (due to possible padding for alignment).
278 TEST_ASSERT(strides[1] >= stride); // stride between X_view(i,j) and X_view(i,j+1)
279 }
280
281 // Make a 2-D "view" (array of arrays) of X_view. This is how we
282 // will implement Tpetra::MultiVector methods like get2dView.
283 Teuchos::ArrayRCP<Teuchos::ArrayRCP<double> > Y_2D (X_view.extent (1));
284 for (size_t j = 0; j < static_cast<size_t> (X_view.extent (1)); ++j) {
285 ka_view_type X_j = Kokkos::subview (X_view, std::make_pair (ZERO, numRows),
286 std::make_pair (j, j+1));
287 TEST_EQUALITY( & X_view(0,j) , & X_j(0,0) );
288 TEST_EQUALITY(static_cast<size_t>(X_j.extent(0)), numRows);
289 TEST_EQUALITY_CONST(X_j.extent(1), 1);
290
291 // Test that the strides of X_j are correct.
292 {
293 size_t strides[3]; // stride[rank] is the maximum span
294 strides[0] = static_cast<size_t> (0);
295 strides[1] = static_cast<size_t> (0);
296 strides[2] = static_cast<size_t> (0);
297 X_j.stride (strides);
298 TEST_EQUALITY_CONST(strides[0], static_cast<size_t>(1)); // stride between X_j(i,j) and X_j(i+1,j)
299 // Stride between X_j(i,j) and X_j(i,j+1), even though X_j only
300 // has one column. The stride must be at least as given, but
301 // can be greater (due to possible padding for alignment).
302 TEST_ASSERT(strides[1] >= stride);
303 }
304
305 // Create a nonowning "view" of X_j's data. The ArrayRCP has a
306 // custom destructor which simply holds a reference to the View
307 // X_j, and doesn't actually deallocate memory. That way, the
308 // ArrayRCP can use the View's raw pointer, but still defers to
309 // the View for memory management.
310 using dealloc_type = TestTeuchosKokkosCompat::Deallocator<double, ka_view_type>;
311 Teuchos::ArrayRCP<double> Y_j (X_j.data (), 0, numRows,
312 dealloc_type (X_j), true);
313 Y_2D[j] = Y_j;
314 }
315 }
316
317
318 int
main(int argc,char * argv[])319 main (int argc, char* argv[])
320 {
321 // Initialize MPI (if enabled) before initializing Kokkos. This
322 // lets MPI control things like pinning processes to sockets.
323 Teuchos::GlobalMPISession mpiSession (&argc, &argv);
324 Kokkos::ScopeGuard kokkosScope (argc, argv);
325 const int errCode =
326 Teuchos::UnitTestRepository::runUnitTestsFromMain (argc, argv);
327 return errCode;
328 }
329