Teuchos Package Browser (Single Doxygen Collection)  Version of the Day
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)
TEST_ASSERT
#define TEST_ASSERT(v1)
Assert the given statement is true.
Definition: Teuchos_LocalTestingHelpers.hpp:71
Teuchos_UnitTestHarness.hpp
Unit testing support.
TEUCHOS_UNIT_TEST
#define TEUCHOS_UNIT_TEST(TEST_GROUP, TEST_NAME)
Macro for defining a (non-templated) unit test.
Definition: Teuchos_UnitTestHelpers.hpp:83
Teuchos_Details_MpiTypeTraits.hpp
Declaration of Teuchos::Details::MpiTypeTraits (only if building with MPI)
Teuchos::basic_OSTab
Tabbing class for helping to create formated, indented output for a basic_FancyOStream object.
Definition: Teuchos_FancyOStream.hpp:653
TEUCHOS_TEST_FOR_EXCEPTION
#define TEUCHOS_TEST_FOR_EXCEPTION(throw_exception_test, Exception, msg)
Macro for throwing an exception with breakpointing to ease debugging.
Definition: Teuchos_TestForException.hpp:170