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