Teuchos Package Browser (Single Doxygen Collection)  Version of the Day
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
mpiTypeTraits.cpp
Go to the documentation of this file.
1 /*
2 // @HEADER
3 // ***********************************************************************
4 //
5 // Teuchos: Common Tools Package
6 // Copyright (2004) Sandia Corporation
7 //
8 // Under terms of Contract DE-AC04-94AL85000, there is a non-exclusive
9 // license for use of this work by or on behalf of the U.S. Government.
10 //
11 // Redistribution and use in source and binary forms, with or without
12 // modification, are permitted provided that the following conditions are
13 // met:
14 //
15 // 1. Redistributions of source code must retain the above copyright
16 // notice, this list of conditions and the following disclaimer.
17 //
18 // 2. Redistributions in binary form must reproduce the above copyright
19 // notice, this list of conditions and the following disclaimer in the
20 // documentation and/or other materials provided with the distribution.
21 //
22 // 3. Neither the name of the Corporation nor the names of the
23 // contributors may be used to endorse or promote products derived from
24 // this software without specific prior written permission.
25 //
26 // THIS SOFTWARE IS PROVIDED BY SANDIA CORPORATION "AS IS" AND ANY
27 // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SANDIA CORPORATION OR THE
30 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
31 // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
32 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
33 // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
34 // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
35 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
36 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37 //
38 // Questions? Contact Michael A. Heroux (maherou@sandia.gov)
39 //
40 // ***********************************************************************
41 // @HEADER
42 */
43 
46 #include "mpi.h"
47 
48 // mfh 10 Nov 2016: This file depends on MPI >= 2 functions and
49 // predefined constants. Teuchos::Details::MpiTypeTraits should still
50 // work with MPI 1, so we simply disable the test in that case. We
51 // don't normally test with very old MPI implementations, so I would
52 // rather not claim to support MPI 1.
53 #if MPI_VERSION >= 2
54 
55 namespace { // (anonymous)
56 
57 #define REPORT_MPI_ERR( errCode, mpiFunctionName ) \
58 do { \
59  if (errCode != MPI_SUCCESS) { \
60  char errString[MPI_MAX_ERROR_STRING]; \
61  int len = 0; \
62  (void) MPI_Error_string (errCode, errString, &len); \
63  TEUCHOS_TEST_FOR_EXCEPTION \
64  (true, std::logic_error, "MPI routine " << mpiFunctionName << " returned " \
65  "err != MPI_SUCCESS. Reported error: \"" << errString << "\"."); \
66  } \
67 } while (false)
68 
69 
71 bool
72 mpiDatatypeIsCustom (MPI_Datatype dt)
73 {
74 #if MPI_VERSION < 2
76  (true, std::logic_error, "mpiDatatypeIsCustom: This function requires MPI "
77  "2.0 at least, since it relies on MPI_Type_get_envelope. MPI 2.0 came "
78  "out in 1996, so I think it's time to upgrade your MPI implementation.");
79 #else // MPI_VERSION >= 2
80  int numInts = 0;
81  int numAddrs = 0;
82  int numTypes = 0;
83  int combiner = 0;
84  // This does not exist in MPI 1.1, but does exist in MPI 2.0.
85  const int err =
86  MPI_Type_get_envelope (dt, &numInts, &numAddrs, &numTypes, &combiner);
88  (err != MPI_SUCCESS, std::logic_error, "MPI_Type_get_envelope "
89  "returned err != MPI_SUCCESS.");
90 
91  switch (combiner) {
92  case MPI_COMBINER_NAMED:
93  return false;
94  case MPI_COMBINER_DUP:
95  case MPI_COMBINER_CONTIGUOUS:
96  case MPI_COMBINER_VECTOR:
97  case MPI_COMBINER_HVECTOR:
98  case MPI_COMBINER_INDEXED:
99  case MPI_COMBINER_HINDEXED:
100  case MPI_COMBINER_INDEXED_BLOCK:
101 #if MPI_VERSION >= 3
102  case MPI_COMBINER_HINDEXED_BLOCK: // in MPI 3.0, not in MPI 2.{0,1,2}
103 #endif // 0
104  case MPI_COMBINER_STRUCT:
105  case MPI_COMBINER_SUBARRAY:
106  case MPI_COMBINER_DARRAY:
107  case MPI_COMBINER_F90_REAL:
108  case MPI_COMBINER_F90_COMPLEX:
109  case MPI_COMBINER_F90_INTEGER:
110  case MPI_COMBINER_RESIZED:
111  default:
112  return true;
113  }
114 #endif // MPI_VERSION >= 2
115 }
116 
117 // mfh 09 Nov 2016: MPI (at least as of 3.1) has no built-in function
118 // that lets you compare MPI_Datatype instances. Direct equality
119 // comparison (==) certainly does not work, because it's possible to
120 // create two distinct MPI_Datatype instances that represent the same
121 // type. The right way to do this is to decode the datatype, using
122 // MPI_Type_get_envelope (to get sizes of arrays) and
123 // MPI_Type_get_contents recursively, until one reaches primitive
124 // (built-in, not custom) MPI_Datatype instances. This recursion
125 // takes stack space and could, in theory, overflow. However, most
126 // MPI_Datatype instances are not deeply nested.
127 
128 bool
129 mpiDatatypeIsSame (MPI_Datatype t1, MPI_Datatype t2)
130 {
131 #if MPI_VERSION < 2
133  (true, std::logic_error, "mpiDatatypeIsSame: This function requires MPI "
134  "2.0 at least, since it relies on MPI_Type_get_envelope and "
135  "MPI_Type_get_contents. MPI 2.0 came out in 1996, so I think it's time "
136  "to upgrade your MPI implementation.");
137 #else // MPI_VERSION >= 2
138  int err = MPI_SUCCESS;
139 
140  int numInts1, numAddrs1, numTypes1, combiner1;
141  // This does not exist in MPI 1.1, but does exist in MPI 2.0.
142  err = MPI_Type_get_envelope (t1, &numInts1, &numAddrs1, &numTypes1, &combiner1);
143  REPORT_MPI_ERR(err, "MPI_Type_get_envelope");
144  int numInts2, numAddrs2, numTypes2, combiner2;
145  // This does not exist in MPI 1.1, but does exist in MPI 2.0.
146  err = MPI_Type_get_envelope (t2, &numInts2, &numAddrs2, &numTypes2, &combiner2);
147  REPORT_MPI_ERR(err, "MPI_Type_get_envelope");
148 
149  if (combiner1 != combiner2 ||
150  numInts1 != numInts2 ||
151  numAddrs1 != numAddrs2 ||
152  numTypes1 != numTypes2) {
153  return false;
154  }
155  else if (combiner1 == MPI_COMBINER_NAMED) {
156  // For built-in MPI_Datatype, OpenMPI 1.10.1 reports an internal
157  // error when one attempts to call MPI_Type_get_contents. On
158  // the other hand, for built-in MPI_Datatype, one may compare
159  // the "combiner" values directly.
160  return t1 == t2;
161  }
162  else if (numTypes1 == 0) {
163  return true; // no types to check
164  }
165  else {
166  // The most general recursive case takes some stack space, and
167  // thus could, in theory, overflow. However, most MPI_Datatype
168  // instances in practice are not deeply nested.
169  std::vector<MPI_Datatype> theTypes1 (numTypes1);
170  std::vector<MPI_Datatype> theTypes2 (numTypes2);
171 
172  // We don't actually need anything but the arrays of addresses
173  // for the recursion. Restricting the other std::vector
174  // instances to this inner scope that closes before the
175  // recursion ensures that they don't stick around on the stack.
176  {
177  // Minimum of one entry, to please MPI_Type_get_contents.
178  std::vector<int> theInts1 (numInts1);
179  std::vector<int> theInts2 (numInts2);
180  std::vector<MPI_Aint> theAddrs1 (numAddrs1);
181  std::vector<MPI_Aint> theAddrs2 (numAddrs2);
182 
183  // This does not exist in MPI 1.1, but does exist in MPI 2.0.
184  err = MPI_Type_get_contents (t1, numInts1, numAddrs1, numTypes1,
185  numInts1 == 0 ? NULL : &theInts1[0],
186  numAddrs1 == 0 ? NULL : &theAddrs1[0],
187  numTypes1 == 0 ? NULL : &theTypes1[0]);
188  REPORT_MPI_ERR(err, "MPI_Type_get_contents");
189  // This does not exist in MPI 1.1, but does exist in MPI 2.0.
190  err = MPI_Type_get_contents (t2, numInts2, numAddrs2, numTypes2,
191  numInts2 == 0 ? NULL : &theInts2[0],
192  numAddrs2 == 0 ? NULL : &theAddrs2[0],
193  numTypes2 == 0 ? NULL : &theTypes2[0]);
194  REPORT_MPI_ERR(err, "MPI_Type_get_contents");
196  (err != MPI_SUCCESS, std::logic_error, "MPI_Type_get_contents "
197  "returned err != MPI_SUCCESS.");
198 
199  for (int k = 0; k < numInts1; ++k) {
200  if (theInts1[k] != theInts2[k]) {
201  return false;
202  }
203  }
204  for (int k = 0; k < numAddrs1; ++k) {
205  if (theAddrs1[k] != theAddrs2[k]) {
206  return false;
207  }
208  }
209  }
210 
211  // Compare all the MPI_Datatype instances, recursively.
212  for (int k = 0; k < numTypes1; ++k) {
213  const bool same = mpiDatatypeIsSame (theTypes1[k], theTypes2[k]);
214  // For non-built-in (custom) MPI_Datatype instances, the
215  // instance returned from MPI_Type_get_contents is a "new"
216  // instance and must therefore be freed. It is illegal to
217  // call MPI_Type_free on a built-in MPI_Datatype instance.
218  if (mpiDatatypeIsCustom (theTypes1[k])) {
219  err = MPI_Type_free (&theTypes1[k]);
221  (err != MPI_SUCCESS, std::logic_error, "MPI_Type_free "
222  "returned err != MPI_SUCCESS.");
223  }
224  if (mpiDatatypeIsCustom (theTypes2[k])) {
225  err = MPI_Type_free (&theTypes2[k]);
227  (err != MPI_SUCCESS, std::logic_error, "MPI_Type_free "
228  "returned err != MPI_SUCCESS.");
229  }
230  if (! same) {
231  return false;
232  }
233  }
234  return true;
235  }
236 #endif // MPI_VERSION < 2
237 }
238 
239 TEUCHOS_UNIT_TEST( MpiTypeTraits, TestMpiDatatypeIsCustom )
240 {
241  using Teuchos::Details::MpiTypeTraits;
242  using std::endl;
243  int err = MPI_SUCCESS;
244 
245  out << "Test mpiDatatypeIsCustom" << endl;
246  Teuchos::OSTab tab1 (out);
247 
248  // In order to debug any MPI errors, tell MPI to let its functions
249  // return error codes, instead of aborting right away.
250  //
251  // This function does not exist in MPI 1.1, but does exist in MPI
252  // 2.0. MPI 1 has MPI_Errhandler_set, which is deprecated as of MPI
253  // 2.0.
254  err = MPI_Comm_set_errhandler (MPI_COMM_WORLD, MPI_ERRORS_RETURN);
256  (err != MPI_SUCCESS, std::logic_error, "MPI_Comm_set_errhandler "
257  "returned err != MPI_SUCCESS.");
258 
259  TEST_ASSERT( ! mpiDatatypeIsCustom (MPI_INT) );
260  TEST_ASSERT( ! mpiDatatypeIsCustom (MPI_DOUBLE) );
261 
262  MPI_Datatype mpi3doubles;
263  err = MPI_Type_contiguous (3, MPI_DOUBLE, &mpi3doubles);
265  (err != MPI_SUCCESS, std::logic_error, "MPI_Type_contiguous "
266  "returned err != MPI_SUCCESS.");
267 
268  TEST_ASSERT( mpiDatatypeIsCustom (mpi3doubles) );
269 
270  err = MPI_Type_free (&mpi3doubles);
272  (err != MPI_SUCCESS, std::logic_error, "MPI_Type_free "
273  "returned err != MPI_SUCCESS.");
274 }
275 
276 TEUCHOS_UNIT_TEST( MpiTypeTraits, TestMpiDatatypeIsSame )
277 {
278  using Teuchos::Details::MpiTypeTraits;
279  using std::endl;
280  int err = MPI_SUCCESS;
281 
282  out << "Test mpiDatatypeIsSame" << endl;
283  Teuchos::OSTab tab1 (out);
284 
285  TEST_ASSERT( mpiDatatypeIsSame (MPI_INT, MPI_INT) );
286  TEST_ASSERT( mpiDatatypeIsSame (MPI_DOUBLE, MPI_DOUBLE) );
287  TEST_ASSERT( ! mpiDatatypeIsSame (MPI_DOUBLE, MPI_INT) );
288  TEST_ASSERT( ! mpiDatatypeIsSame (MPI_INT, MPI_DOUBLE) );
289 
290  MPI_Datatype mpi3doubles;
291  err = MPI_Type_contiguous (3, MPI_DOUBLE, &mpi3doubles);
293  (err != MPI_SUCCESS, std::logic_error, "MPI_Type_contiguous "
294  "returned err != MPI_SUCCESS.");
295  MPI_Datatype mpi4doubles;
296  err = MPI_Type_contiguous (4, MPI_DOUBLE, &mpi4doubles);
298  (err != MPI_SUCCESS, std::logic_error, "MPI_Type_contiguous "
299  "returned err != MPI_SUCCESS.");
300 
301  TEST_ASSERT( ! mpiDatatypeIsSame (mpi3doubles, MPI_DOUBLE) );
302  TEST_ASSERT( ! mpiDatatypeIsSame (MPI_DOUBLE, mpi3doubles) );
303  TEST_ASSERT( ! mpiDatatypeIsSame (mpi3doubles, MPI_INT) );
304  TEST_ASSERT( ! mpiDatatypeIsSame (MPI_INT, mpi3doubles) );
305  TEST_ASSERT( ! mpiDatatypeIsSame (mpi4doubles, MPI_DOUBLE) );
306  TEST_ASSERT( ! mpiDatatypeIsSame (MPI_DOUBLE, mpi4doubles) );
307  TEST_ASSERT( ! mpiDatatypeIsSame (mpi4doubles, MPI_INT) );
308  TEST_ASSERT( ! mpiDatatypeIsSame (MPI_INT, mpi4doubles) );
309 
310  TEST_ASSERT( mpiDatatypeIsSame (mpi3doubles, mpi3doubles) );
311  TEST_ASSERT( mpiDatatypeIsSame (mpi4doubles, mpi4doubles) );
312  TEST_ASSERT( ! mpiDatatypeIsSame (mpi3doubles, mpi4doubles) );
313  TEST_ASSERT( ! mpiDatatypeIsSame (mpi4doubles, mpi3doubles) );
314 
315  err = MPI_Type_free (&mpi3doubles);
317  (err != MPI_SUCCESS, std::logic_error, "MPI_Type_free "
318  "returned err != MPI_SUCCESS.");
319  err = MPI_Type_free (&mpi4doubles);
321  (err != MPI_SUCCESS, std::logic_error, "MPI_Type_free "
322  "returned err != MPI_SUCCESS.");
323 }
324 
325 
326 TEUCHOS_UNIT_TEST( MpiTypeTraits, CompareWithRawMpi )
327 {
328  using Teuchos::Details::MpiTypeTraits;
329  using std::endl;
330 
331  out << "Compare MpiTypeTraits<T>::getType result with known result" << endl;
332  Teuchos::OSTab tab1 (out);
333  out << "Test one-argument version of MpiTypeTraits<T>::getType" << endl;
334 
335  MPI_Datatype dt_char = MpiTypeTraits<char>::getType (static_cast<char> ('a'));
336 
337  MPI_Datatype dt_short =
338  MpiTypeTraits<short>::getType (static_cast<short> (42));
339  MPI_Datatype dt_unsigned_short =
340  MpiTypeTraits<unsigned short>::getType (static_cast<unsigned short> (42));
341 
342  MPI_Datatype dt_int =
343  MpiTypeTraits<int>::getType (static_cast<int> (42));
344  MPI_Datatype dt_unsigned_int =
345  MpiTypeTraits<unsigned int>::getType (static_cast<unsigned int> (42));
346 
347  MPI_Datatype dt_long =
348  MpiTypeTraits<long>::getType (static_cast<long> (42));
349  MPI_Datatype dt_unsigned_long =
350  MpiTypeTraits<unsigned long>::getType (static_cast<unsigned long> (42));
351 
352  MPI_Datatype dt_float =
353  MpiTypeTraits<float>::getType (static_cast<float> (4.2));
354  MPI_Datatype dt_double =
355  MpiTypeTraits<double>::getType (static_cast<double> (4.2));
356 
357  // Sanity check.
358  TEST_ASSERT( ! mpiDatatypeIsSame (dt_double, dt_int) );
359 
360  TEST_ASSERT( mpiDatatypeIsSame (dt_char, MPI_CHAR) );
361  TEST_ASSERT( mpiDatatypeIsSame (dt_short, MPI_SHORT) );
362  TEST_ASSERT( mpiDatatypeIsSame (dt_unsigned_short, MPI_UNSIGNED_SHORT) );
363  TEST_ASSERT( mpiDatatypeIsSame (dt_int, MPI_INT) );
364  TEST_ASSERT( mpiDatatypeIsSame (dt_unsigned_int, MPI_UNSIGNED) );
365  TEST_ASSERT( mpiDatatypeIsSame (dt_long, MPI_LONG) );
366  TEST_ASSERT( mpiDatatypeIsSame (dt_unsigned_long, MPI_UNSIGNED_LONG) );
367 
368  TEST_ASSERT( mpiDatatypeIsSame (dt_float, MPI_FLOAT) );
369  TEST_ASSERT( mpiDatatypeIsSame (dt_double, MPI_DOUBLE) );
370 
371  out << "Test zero-argument version of MpiTypeTraits<T>::getType, "
372  "for types T where that version is known to exist" << endl;
373 
374  dt_char = MpiTypeTraits<char>::getType ();
375  dt_short = MpiTypeTraits<short>::getType ();
376  dt_unsigned_short = MpiTypeTraits<unsigned short>::getType ();
377  dt_int = MpiTypeTraits<int>::getType ();
378  dt_unsigned_int = MpiTypeTraits<unsigned int>::getType ();
379  dt_long = MpiTypeTraits<long>::getType ();
380  dt_unsigned_long = MpiTypeTraits<unsigned long>::getType ();
381  dt_float = MpiTypeTraits<float>::getType ();
382  dt_double = MpiTypeTraits<double>::getType ();
383 
384  // Sanity check.
385  TEST_ASSERT( ! mpiDatatypeIsSame (dt_double, dt_int) );
386 
387  TEST_ASSERT( mpiDatatypeIsSame (dt_char, MPI_CHAR) );
388  TEST_ASSERT( mpiDatatypeIsSame (dt_short, MPI_SHORT) );
389  TEST_ASSERT( mpiDatatypeIsSame (dt_unsigned_short, MPI_UNSIGNED_SHORT) );
390  TEST_ASSERT( mpiDatatypeIsSame (dt_int, MPI_INT) );
391  TEST_ASSERT( mpiDatatypeIsSame (dt_unsigned_int, MPI_UNSIGNED) );
392  TEST_ASSERT( mpiDatatypeIsSame (dt_long, MPI_LONG) );
393  TEST_ASSERT( mpiDatatypeIsSame (dt_unsigned_long, MPI_UNSIGNED_LONG) );
394 
395  TEST_ASSERT( mpiDatatypeIsSame (dt_float, MPI_FLOAT) );
396  TEST_ASSERT( mpiDatatypeIsSame (dt_double, MPI_DOUBLE) );
397 
398  TEST_ASSERT( MpiTypeTraits<char>::isSpecialized );
399  TEST_ASSERT( MpiTypeTraits<short>::isSpecialized );
400  TEST_ASSERT( MpiTypeTraits<unsigned short>::isSpecialized );
401  TEST_ASSERT( MpiTypeTraits<int>::isSpecialized );
402  TEST_ASSERT( MpiTypeTraits<unsigned int>::isSpecialized );
403  TEST_ASSERT( MpiTypeTraits<long>::isSpecialized );
404  TEST_ASSERT( MpiTypeTraits<unsigned long>::isSpecialized );
405  TEST_ASSERT( MpiTypeTraits<float>::isSpecialized );
406  TEST_ASSERT( MpiTypeTraits<double>::isSpecialized );
407 
408  TEST_ASSERT( ! MpiTypeTraits<char>::needsFree );
409  TEST_ASSERT( ! MpiTypeTraits<short>::needsFree );
410  TEST_ASSERT( ! MpiTypeTraits<unsigned short>::needsFree );
411  TEST_ASSERT( ! MpiTypeTraits<int>::needsFree );
412  TEST_ASSERT( ! MpiTypeTraits<unsigned int>::needsFree );
413  TEST_ASSERT( ! MpiTypeTraits<long>::needsFree );
414  TEST_ASSERT( ! MpiTypeTraits<unsigned long>::needsFree );
415  TEST_ASSERT( ! MpiTypeTraits<float>::needsFree );
416  TEST_ASSERT( ! MpiTypeTraits<double>::needsFree );
417 
418 #ifdef HAVE_TEUCHOS_COMPLEX
419 
420  TEST_ASSERT( MpiTypeTraits<std::complex<float> >::isSpecialized );
421  TEST_ASSERT( MpiTypeTraits<std::complex<double> >::isSpecialized );
422 
423  MPI_Datatype dt_complex_float =
424  MpiTypeTraits<std::complex<float> >::getType ();
425  MPI_Datatype dt_complex_double =
426  MpiTypeTraits<std::complex<float> >::getType ();
427 
428 // We make no promises about this for MPI_VERSION < 3.
429 #if MPI_VERSION >= 3
430  TEST_ASSERT( ! MpiTypeTraits<std::complex<float> >::needsFree );
431  TEST_ASSERT( ! MpiTypeTraits<std::complex<double> >::needsFree );
432 #endif // MPI_VERSION >= 3
433 
434  if (MpiTypeTraits<std::complex<float> >::needsFree) {
435  (void) MPI_Type_free (&dt_complex_float);
436  }
437  if (MpiTypeTraits<std::complex<double> >::needsFree) {
438  (void) MPI_Type_free (&dt_complex_double);
439  }
440 #endif // HAVE_TEUCHOS_COMPLEX
441 }
442 
443 #endif // MPI_VERSION >= 2
444 
445 } // namespace (anonymous)
#define TEST_ASSERT(v1)
Assert the given statement is true.
#define TEUCHOS_TEST_FOR_EXCEPTION(throw_exception_test, Exception, msg)
Macro for throwing an exception with breakpointing to ease debugging.
#define TEUCHOS_UNIT_TEST(TEST_GROUP, TEST_NAME)
Macro for defining a (non-templated) unit test.
Tabbing class for helping to create formated, indented output for a basic_FancyOStream object...
Unit testing support.
Declaration of Teuchos::Details::MpiTypeTraits (only if building with MPI)