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 // @HEADER
2 // *****************************************************************************
3 // Teuchos: Common Tools Package
4 //
5 // Copyright 2004 NTESS and the Teuchos contributors.
6 // SPDX-License-Identifier: BSD-3-Clause
7 // *****************************************************************************
8 // @HEADER
9 
12 #include "mpi.h"
13 
14 // mfh 10 Nov 2016: This file depends on MPI >= 2 functions and
15 // predefined constants. Teuchos::Details::MpiTypeTraits should still
16 // work with MPI 1, so we simply disable the test in that case. We
17 // don't normally test with very old MPI implementations, so I would
18 // rather not claim to support MPI 1.
19 #if MPI_VERSION >= 2
20 
21 namespace { // (anonymous)
22 
23 #define REPORT_MPI_ERR( errCode, mpiFunctionName ) \
24 do { \
25  if (errCode != MPI_SUCCESS) { \
26  char errString[MPI_MAX_ERROR_STRING]; \
27  int len = 0; \
28  (void) MPI_Error_string (errCode, errString, &len); \
29  TEUCHOS_TEST_FOR_EXCEPTION \
30  (true, std::logic_error, "MPI routine " << mpiFunctionName << " returned " \
31  "err != MPI_SUCCESS. Reported error: \"" << errString << "\"."); \
32  } \
33 } while (false)
34 
35 
37 bool
38 mpiDatatypeIsCustom (MPI_Datatype dt)
39 {
40 #if MPI_VERSION < 2
42  (true, std::logic_error, "mpiDatatypeIsCustom: This function requires MPI "
43  "2.0 at least, since it relies on MPI_Type_get_envelope. MPI 2.0 came "
44  "out in 1996, so I think it's time to upgrade your MPI implementation.");
45 #else // MPI_VERSION >= 2
46  int numInts = 0;
47  int numAddrs = 0;
48  int numTypes = 0;
49  int combiner = 0;
50  // This does not exist in MPI 1.1, but does exist in MPI 2.0.
51  const int err =
52  MPI_Type_get_envelope (dt, &numInts, &numAddrs, &numTypes, &combiner);
54  (err != MPI_SUCCESS, std::logic_error, "MPI_Type_get_envelope "
55  "returned err != MPI_SUCCESS.");
56 
57  switch (combiner) {
58  case MPI_COMBINER_NAMED:
59  return false;
60  case MPI_COMBINER_DUP:
61  case MPI_COMBINER_CONTIGUOUS:
62  case MPI_COMBINER_VECTOR:
63  case MPI_COMBINER_HVECTOR:
64  case MPI_COMBINER_INDEXED:
65  case MPI_COMBINER_HINDEXED:
66  case MPI_COMBINER_INDEXED_BLOCK:
67 #if MPI_VERSION >= 3
68  case MPI_COMBINER_HINDEXED_BLOCK: // in MPI 3.0, not in MPI 2.{0,1,2}
69 #endif // 0
70  case MPI_COMBINER_STRUCT:
71  case MPI_COMBINER_SUBARRAY:
72  case MPI_COMBINER_DARRAY:
73  case MPI_COMBINER_F90_REAL:
74  case MPI_COMBINER_F90_COMPLEX:
75  case MPI_COMBINER_F90_INTEGER:
76  case MPI_COMBINER_RESIZED:
77  default:
78  return true;
79  }
80 #endif // MPI_VERSION >= 2
81 }
82 
83 // mfh 09 Nov 2016: MPI (at least as of 3.1) has no built-in function
84 // that lets you compare MPI_Datatype instances. Direct equality
85 // comparison (==) certainly does not work, because it's possible to
86 // create two distinct MPI_Datatype instances that represent the same
87 // type. The right way to do this is to decode the datatype, using
88 // MPI_Type_get_envelope (to get sizes of arrays) and
89 // MPI_Type_get_contents recursively, until one reaches primitive
90 // (built-in, not custom) MPI_Datatype instances. This recursion
91 // takes stack space and could, in theory, overflow. However, most
92 // MPI_Datatype instances are not deeply nested.
93 
94 bool
95 mpiDatatypeIsSame (MPI_Datatype t1, MPI_Datatype t2)
96 {
97 #if MPI_VERSION < 2
99  (true, std::logic_error, "mpiDatatypeIsSame: This function requires MPI "
100  "2.0 at least, since it relies on MPI_Type_get_envelope and "
101  "MPI_Type_get_contents. MPI 2.0 came out in 1996, so I think it's time "
102  "to upgrade your MPI implementation.");
103 #else // MPI_VERSION >= 2
104  int err = MPI_SUCCESS;
105 
106  int numInts1, numAddrs1, numTypes1, combiner1;
107  // This does not exist in MPI 1.1, but does exist in MPI 2.0.
108  err = MPI_Type_get_envelope (t1, &numInts1, &numAddrs1, &numTypes1, &combiner1);
109  REPORT_MPI_ERR(err, "MPI_Type_get_envelope");
110  int numInts2, numAddrs2, numTypes2, combiner2;
111  // This does not exist in MPI 1.1, but does exist in MPI 2.0.
112  err = MPI_Type_get_envelope (t2, &numInts2, &numAddrs2, &numTypes2, &combiner2);
113  REPORT_MPI_ERR(err, "MPI_Type_get_envelope");
114 
115  if (combiner1 != combiner2 ||
116  numInts1 != numInts2 ||
117  numAddrs1 != numAddrs2 ||
118  numTypes1 != numTypes2) {
119  return false;
120  }
121  else if (combiner1 == MPI_COMBINER_NAMED) {
122  // For built-in MPI_Datatype, OpenMPI 1.10.1 reports an internal
123  // error when one attempts to call MPI_Type_get_contents. On
124  // the other hand, for built-in MPI_Datatype, one may compare
125  // the "combiner" values directly.
126  return t1 == t2;
127  }
128  else if (numTypes1 == 0) {
129  return true; // no types to check
130  }
131  else {
132  // The most general recursive case takes some stack space, and
133  // thus could, in theory, overflow. However, most MPI_Datatype
134  // instances in practice are not deeply nested.
135  std::vector<MPI_Datatype> theTypes1 (numTypes1);
136  std::vector<MPI_Datatype> theTypes2 (numTypes2);
137 
138  // We don't actually need anything but the arrays of addresses
139  // for the recursion. Restricting the other std::vector
140  // instances to this inner scope that closes before the
141  // recursion ensures that they don't stick around on the stack.
142  {
143  // Minimum of one entry, to please MPI_Type_get_contents.
144  std::vector<int> theInts1 (numInts1);
145  std::vector<int> theInts2 (numInts2);
146  std::vector<MPI_Aint> theAddrs1 (numAddrs1);
147  std::vector<MPI_Aint> theAddrs2 (numAddrs2);
148 
149  // This does not exist in MPI 1.1, but does exist in MPI 2.0.
150  err = MPI_Type_get_contents (t1, numInts1, numAddrs1, numTypes1,
151  numInts1 == 0 ? NULL : &theInts1[0],
152  numAddrs1 == 0 ? NULL : &theAddrs1[0],
153  numTypes1 == 0 ? NULL : &theTypes1[0]);
154  REPORT_MPI_ERR(err, "MPI_Type_get_contents");
155  // This does not exist in MPI 1.1, but does exist in MPI 2.0.
156  err = MPI_Type_get_contents (t2, numInts2, numAddrs2, numTypes2,
157  numInts2 == 0 ? NULL : &theInts2[0],
158  numAddrs2 == 0 ? NULL : &theAddrs2[0],
159  numTypes2 == 0 ? NULL : &theTypes2[0]);
160  REPORT_MPI_ERR(err, "MPI_Type_get_contents");
162  (err != MPI_SUCCESS, std::logic_error, "MPI_Type_get_contents "
163  "returned err != MPI_SUCCESS.");
164 
165  for (int k = 0; k < numInts1; ++k) {
166  if (theInts1[k] != theInts2[k]) {
167  return false;
168  }
169  }
170  for (int k = 0; k < numAddrs1; ++k) {
171  if (theAddrs1[k] != theAddrs2[k]) {
172  return false;
173  }
174  }
175  }
176 
177  // Compare all the MPI_Datatype instances, recursively.
178  for (int k = 0; k < numTypes1; ++k) {
179  const bool same = mpiDatatypeIsSame (theTypes1[k], theTypes2[k]);
180  // For non-built-in (custom) MPI_Datatype instances, the
181  // instance returned from MPI_Type_get_contents is a "new"
182  // instance and must therefore be freed. It is illegal to
183  // call MPI_Type_free on a built-in MPI_Datatype instance.
184  if (mpiDatatypeIsCustom (theTypes1[k])) {
185  err = MPI_Type_free (&theTypes1[k]);
187  (err != MPI_SUCCESS, std::logic_error, "MPI_Type_free "
188  "returned err != MPI_SUCCESS.");
189  }
190  if (mpiDatatypeIsCustom (theTypes2[k])) {
191  err = MPI_Type_free (&theTypes2[k]);
193  (err != MPI_SUCCESS, std::logic_error, "MPI_Type_free "
194  "returned err != MPI_SUCCESS.");
195  }
196  if (! same) {
197  return false;
198  }
199  }
200  return true;
201  }
202 #endif // MPI_VERSION < 2
203 }
204 
205 TEUCHOS_UNIT_TEST( MpiTypeTraits, TestMpiDatatypeIsCustom )
206 {
207  using Teuchos::Details::MpiTypeTraits;
208  using std::endl;
209  int err = MPI_SUCCESS;
210 
211  out << "Test mpiDatatypeIsCustom" << endl;
212  Teuchos::OSTab tab1 (out);
213 
214  // In order to debug any MPI errors, tell MPI to let its functions
215  // return error codes, instead of aborting right away.
216  //
217  // This function does not exist in MPI 1.1, but does exist in MPI
218  // 2.0. MPI 1 has MPI_Errhandler_set, which is deprecated as of MPI
219  // 2.0.
220  err = MPI_Comm_set_errhandler (MPI_COMM_WORLD, MPI_ERRORS_RETURN);
222  (err != MPI_SUCCESS, std::logic_error, "MPI_Comm_set_errhandler "
223  "returned err != MPI_SUCCESS.");
224 
225  TEST_ASSERT( ! mpiDatatypeIsCustom (MPI_INT) );
226  TEST_ASSERT( ! mpiDatatypeIsCustom (MPI_DOUBLE) );
227 
228  MPI_Datatype mpi3doubles;
229  err = MPI_Type_contiguous (3, MPI_DOUBLE, &mpi3doubles);
231  (err != MPI_SUCCESS, std::logic_error, "MPI_Type_contiguous "
232  "returned err != MPI_SUCCESS.");
233 
234  TEST_ASSERT( mpiDatatypeIsCustom (mpi3doubles) );
235 
236  err = MPI_Type_free (&mpi3doubles);
238  (err != MPI_SUCCESS, std::logic_error, "MPI_Type_free "
239  "returned err != MPI_SUCCESS.");
240 }
241 
242 TEUCHOS_UNIT_TEST( MpiTypeTraits, TestMpiDatatypeIsSame )
243 {
244  using Teuchos::Details::MpiTypeTraits;
245  using std::endl;
246  int err = MPI_SUCCESS;
247 
248  out << "Test mpiDatatypeIsSame" << endl;
249  Teuchos::OSTab tab1 (out);
250 
251  TEST_ASSERT( mpiDatatypeIsSame (MPI_INT, MPI_INT) );
252  TEST_ASSERT( mpiDatatypeIsSame (MPI_DOUBLE, MPI_DOUBLE) );
253  TEST_ASSERT( ! mpiDatatypeIsSame (MPI_DOUBLE, MPI_INT) );
254  TEST_ASSERT( ! mpiDatatypeIsSame (MPI_INT, MPI_DOUBLE) );
255 
256  MPI_Datatype mpi3doubles;
257  err = MPI_Type_contiguous (3, MPI_DOUBLE, &mpi3doubles);
259  (err != MPI_SUCCESS, std::logic_error, "MPI_Type_contiguous "
260  "returned err != MPI_SUCCESS.");
261  MPI_Datatype mpi4doubles;
262  err = MPI_Type_contiguous (4, MPI_DOUBLE, &mpi4doubles);
264  (err != MPI_SUCCESS, std::logic_error, "MPI_Type_contiguous "
265  "returned err != MPI_SUCCESS.");
266 
267  TEST_ASSERT( ! mpiDatatypeIsSame (mpi3doubles, MPI_DOUBLE) );
268  TEST_ASSERT( ! mpiDatatypeIsSame (MPI_DOUBLE, mpi3doubles) );
269  TEST_ASSERT( ! mpiDatatypeIsSame (mpi3doubles, MPI_INT) );
270  TEST_ASSERT( ! mpiDatatypeIsSame (MPI_INT, mpi3doubles) );
271  TEST_ASSERT( ! mpiDatatypeIsSame (mpi4doubles, MPI_DOUBLE) );
272  TEST_ASSERT( ! mpiDatatypeIsSame (MPI_DOUBLE, mpi4doubles) );
273  TEST_ASSERT( ! mpiDatatypeIsSame (mpi4doubles, MPI_INT) );
274  TEST_ASSERT( ! mpiDatatypeIsSame (MPI_INT, mpi4doubles) );
275 
276  TEST_ASSERT( mpiDatatypeIsSame (mpi3doubles, mpi3doubles) );
277  TEST_ASSERT( mpiDatatypeIsSame (mpi4doubles, mpi4doubles) );
278  TEST_ASSERT( ! mpiDatatypeIsSame (mpi3doubles, mpi4doubles) );
279  TEST_ASSERT( ! mpiDatatypeIsSame (mpi4doubles, mpi3doubles) );
280 
281  err = MPI_Type_free (&mpi3doubles);
283  (err != MPI_SUCCESS, std::logic_error, "MPI_Type_free "
284  "returned err != MPI_SUCCESS.");
285  err = MPI_Type_free (&mpi4doubles);
287  (err != MPI_SUCCESS, std::logic_error, "MPI_Type_free "
288  "returned err != MPI_SUCCESS.");
289 }
290 
291 
292 TEUCHOS_UNIT_TEST( MpiTypeTraits, CompareWithRawMpi )
293 {
294  using Teuchos::Details::MpiTypeTraits;
295  using std::endl;
296 
297  out << "Compare MpiTypeTraits<T>::getType result with known result" << endl;
298  Teuchos::OSTab tab1 (out);
299  out << "Test one-argument version of MpiTypeTraits<T>::getType" << endl;
300 
301  MPI_Datatype dt_char = MpiTypeTraits<char>::getType (static_cast<char> ('a'));
302 
303  MPI_Datatype dt_short =
304  MpiTypeTraits<short>::getType (static_cast<short> (42));
305  MPI_Datatype dt_unsigned_short =
306  MpiTypeTraits<unsigned short>::getType (static_cast<unsigned short> (42));
307 
308  MPI_Datatype dt_int =
309  MpiTypeTraits<int>::getType (static_cast<int> (42));
310  MPI_Datatype dt_unsigned_int =
311  MpiTypeTraits<unsigned int>::getType (static_cast<unsigned int> (42));
312 
313  MPI_Datatype dt_long =
314  MpiTypeTraits<long>::getType (static_cast<long> (42));
315  MPI_Datatype dt_unsigned_long =
316  MpiTypeTraits<unsigned long>::getType (static_cast<unsigned long> (42));
317 
318  MPI_Datatype dt_float =
319  MpiTypeTraits<float>::getType (static_cast<float> (4.2));
320  MPI_Datatype dt_double =
321  MpiTypeTraits<double>::getType (static_cast<double> (4.2));
322 
323  // Sanity check.
324  TEST_ASSERT( ! mpiDatatypeIsSame (dt_double, dt_int) );
325 
326  TEST_ASSERT( mpiDatatypeIsSame (dt_char, MPI_CHAR) );
327  TEST_ASSERT( mpiDatatypeIsSame (dt_short, MPI_SHORT) );
328  TEST_ASSERT( mpiDatatypeIsSame (dt_unsigned_short, MPI_UNSIGNED_SHORT) );
329  TEST_ASSERT( mpiDatatypeIsSame (dt_int, MPI_INT) );
330  TEST_ASSERT( mpiDatatypeIsSame (dt_unsigned_int, MPI_UNSIGNED) );
331  TEST_ASSERT( mpiDatatypeIsSame (dt_long, MPI_LONG) );
332  TEST_ASSERT( mpiDatatypeIsSame (dt_unsigned_long, MPI_UNSIGNED_LONG) );
333 
334  TEST_ASSERT( mpiDatatypeIsSame (dt_float, MPI_FLOAT) );
335  TEST_ASSERT( mpiDatatypeIsSame (dt_double, MPI_DOUBLE) );
336 
337  out << "Test zero-argument version of MpiTypeTraits<T>::getType, "
338  "for types T where that version is known to exist" << endl;
339 
340  dt_char = MpiTypeTraits<char>::getType ();
341  dt_short = MpiTypeTraits<short>::getType ();
342  dt_unsigned_short = MpiTypeTraits<unsigned short>::getType ();
343  dt_int = MpiTypeTraits<int>::getType ();
344  dt_unsigned_int = MpiTypeTraits<unsigned int>::getType ();
345  dt_long = MpiTypeTraits<long>::getType ();
346  dt_unsigned_long = MpiTypeTraits<unsigned long>::getType ();
347  dt_float = MpiTypeTraits<float>::getType ();
348  dt_double = MpiTypeTraits<double>::getType ();
349 
350  // Sanity check.
351  TEST_ASSERT( ! mpiDatatypeIsSame (dt_double, dt_int) );
352 
353  TEST_ASSERT( mpiDatatypeIsSame (dt_char, MPI_CHAR) );
354  TEST_ASSERT( mpiDatatypeIsSame (dt_short, MPI_SHORT) );
355  TEST_ASSERT( mpiDatatypeIsSame (dt_unsigned_short, MPI_UNSIGNED_SHORT) );
356  TEST_ASSERT( mpiDatatypeIsSame (dt_int, MPI_INT) );
357  TEST_ASSERT( mpiDatatypeIsSame (dt_unsigned_int, MPI_UNSIGNED) );
358  TEST_ASSERT( mpiDatatypeIsSame (dt_long, MPI_LONG) );
359  TEST_ASSERT( mpiDatatypeIsSame (dt_unsigned_long, MPI_UNSIGNED_LONG) );
360 
361  TEST_ASSERT( mpiDatatypeIsSame (dt_float, MPI_FLOAT) );
362  TEST_ASSERT( mpiDatatypeIsSame (dt_double, MPI_DOUBLE) );
363 
364  TEST_ASSERT( MpiTypeTraits<char>::isSpecialized );
365  TEST_ASSERT( MpiTypeTraits<short>::isSpecialized );
366  TEST_ASSERT( MpiTypeTraits<unsigned short>::isSpecialized );
367  TEST_ASSERT( MpiTypeTraits<int>::isSpecialized );
368  TEST_ASSERT( MpiTypeTraits<unsigned int>::isSpecialized );
369  TEST_ASSERT( MpiTypeTraits<long>::isSpecialized );
370  TEST_ASSERT( MpiTypeTraits<unsigned long>::isSpecialized );
371  TEST_ASSERT( MpiTypeTraits<float>::isSpecialized );
372  TEST_ASSERT( MpiTypeTraits<double>::isSpecialized );
373 
374  TEST_ASSERT( ! MpiTypeTraits<char>::needsFree );
375  TEST_ASSERT( ! MpiTypeTraits<short>::needsFree );
376  TEST_ASSERT( ! MpiTypeTraits<unsigned short>::needsFree );
377  TEST_ASSERT( ! MpiTypeTraits<int>::needsFree );
378  TEST_ASSERT( ! MpiTypeTraits<unsigned int>::needsFree );
379  TEST_ASSERT( ! MpiTypeTraits<long>::needsFree );
380  TEST_ASSERT( ! MpiTypeTraits<unsigned long>::needsFree );
381  TEST_ASSERT( ! MpiTypeTraits<float>::needsFree );
382  TEST_ASSERT( ! MpiTypeTraits<double>::needsFree );
383 
384 #ifdef HAVE_TEUCHOS_COMPLEX
385 
386  TEST_ASSERT( MpiTypeTraits<std::complex<float> >::isSpecialized );
387  TEST_ASSERT( MpiTypeTraits<std::complex<double> >::isSpecialized );
388 
389  MPI_Datatype dt_complex_float =
390  MpiTypeTraits<std::complex<float> >::getType ();
391  MPI_Datatype dt_complex_double =
392  MpiTypeTraits<std::complex<float> >::getType ();
393 
394 // We make no promises about this for MPI_VERSION < 3.
395 #if MPI_VERSION >= 3
396  TEST_ASSERT( ! MpiTypeTraits<std::complex<float> >::needsFree );
397  TEST_ASSERT( ! MpiTypeTraits<std::complex<double> >::needsFree );
398 #endif // MPI_VERSION >= 3
399 
400  if (MpiTypeTraits<std::complex<float> >::needsFree) {
401  (void) MPI_Type_free (&dt_complex_float);
402  }
403  if (MpiTypeTraits<std::complex<double> >::needsFree) {
404  (void) MPI_Type_free (&dt_complex_double);
405  }
406 #endif // HAVE_TEUCHOS_COMPLEX
407 }
408 
409 #endif // MPI_VERSION >= 2
410 
411 } // 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)