Teuchos Package Browser (Single Doxygen Collection) Version of the Day
Loading...
Searching...
No Matches
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
55namespace { // (anonymous)
56
57#define REPORT_MPI_ERR( errCode, mpiFunctionName ) \
58do { \
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
71bool
72mpiDatatypeIsCustom (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
128bool
129mpiDatatypeIsSame (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
239TEUCHOS_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
276TEUCHOS_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
326TEUCHOS_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)
Declaration of Teuchos::Details::MpiTypeTraits (only if building with MPI)
#define TEST_ASSERT(v1)
Assert the given statement is true.
Unit testing support.
#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.
#define TEUCHOS_TEST_FOR_EXCEPTION(throw_exception_test, Exception, msg)
Macro for throwing an exception with breakpointing to ease debugging.