MPI-/ provides datatype objects, which allow users to specify an arbitrary layout of data in memory. The layout information, once put in a datatype, could not be decoded from the datatype. There are several cases, however, where accessing the layout information in opaque datatype objects would be useful.
The two functions in this section are used together to decode datatypes to recreate the calling sequence used in their initial definition. These can be used to allow a user to determine the type map and type signature of a datatype.
MPI_TYPE_GET_ENVELOPE(datatype, num_integers,
num_addresses, num_datatypes, combiner)
int MPI_Type_get_envelope(MPI_Datatype datatype, int *num_integers, int *num_addresses, int *num_datatypes, int *combiner)
MPI_TYPE_GET_ENVELOPE(DATATYPE, NUM_INTEGERS, NUM_ADDRESSES, NUM_DATATYPES, COMBINER, IERROR)INTEGER DATATYPE, NUM_INTEGERS, NUM_ADDRESSES, NUM_DATATYPES, COMBINER, IERROR
int MPI::Datatype::Get_envelope(int& num_integers, int& num_addresses, int& num_datatypes, int& combiner) const
void
For the given datatype, MPI_TYPE_GET_ENVELOPE returns information on the number and type of input arguments used in the call that created the datatype. The number-of-arguments values returned can be used to provide sufficiently large arrays in the decoding routine MPI_TYPE_GET_CONTENTS. This call and the meaning of the returned values is described below. The combiner reflects the MPI-/ datatype constructor call that was used in creating datatype.
The decoded information keeps track of datatype duplications. This is important as one needs to distinguish between a predefined datatype and a dup of a predefined datatype. The former is a constant object that cannot be freed, while the latter is a derived datatype that can be freed. (End of rationale.)
The list below has the values that can be returned in combiner on the left and the call associated with them on the right.
If combiner is MPI_COMBINER_NAMED then datatype is a named predefined datatype.
For calls with address arguments, we sometimes need to differentiate whether the call used an integer or an address size argument. For example, there are two combiners for hvector: MPI_COMBINER_HVECTOR_INTEGER and MPI_COMBINER_HVECTOR. The former is used if it was the MPI-/ call from Fortran, and the latter is used if it was the MPI-/ call from C or C++. However, on systems where MPI_ADDRESS_KIND = MPI_INTEGER_KIND (i.e., where integer arguments and address size arguments are the same), the combiner MPI_COMBINER_HVECTOR may be returned for a datatype constructed by a call to MPI_TYPE_HVECTOR from Fortran. Similarly, MPI_COMBINER_HINDEXED may be returned for a datatype constructed by a call to MPI_TYPE_HINDEXED from Fortran, and MPI_COMBINER_STRUCT may be returned for a datatype constructed by a call to MPI_TYPE_STRUCT from Fortran. On such systems, one need not differentiate constructors that take address size arguments from constructors that take integer arguments, since these are the same. The new MPI-// calls all use address sized arguments.
The actual arguments used in the creation call for a datatype can be obtained from the call:
MPI_TYPE_GET_CONTENTS(datatype, max_integers,
max_addresses, max_datatypes, array_of_integers, array_of_addresses, array_of_datatypes)
int MPI_Type_get_contents(MPI_Datatype datatype, int max_integers, int max_addresses, int max_datatypes, int array_of_integers[], MPI_Aint array_of_addresses[], MPI_Datatype array_of_datatypes[])
MPI_TYPE_GET_CONTENTS(DATATYPE, MAX_INTEGERS, MAX_ADDRESSES, MAX_DATATYPES, ARRAY_OF_INTEGERS, ARRAY_OF_ADDRESSES, ARRAY_OF_DATATYPES, IERROR)INTEGER DATATYPE, MAX_INTEGERS, MAX_ADDRESSES, MAX_DATATYPES, ARRAY_OF_INTEGERS(*), ARRAY_OF_DATATYPES(*), IERROR
INTEGER(KIND=MPI_ADDRESS_KIND) ARRAY_OF_ADDRESSES(*)
int MPI::Datatype::Get_contents(int max_integers, int max_addresses, int max_datatypes, int array_of_integers[], MPI::Aint array_of_addresses[], MPI::Datatype array_of_datatypes[]) const
void
datatype must be a predefined unnamed or a derived datatype; the call is erroneous if datatype is a predefined named datatype.
The values given for max_integers, max_addresses, and max_datatypes must be at least as large as the value returned in num_integers, num_addresses, and num_datatypes, respectively, in the call MPI_TYPE_GET_ENVELOPE for the same datatype argument.
The datatypes returned in array_of_datatypes are handles to datatype objects that are equivalent to the datatypes used in the original construction call. If these were derived datatypes, then the returned datatypes are new datatype objects, and the user is responsible for freeing these datatypes with MPI_TYPE_FREE. If these were predefined datatypes, then the returned datatype is equal to that (constant) predefined datatype and cannot be freed.
The committed state of returned derived datatypes is undefined, i.e., the datatypes may or may not be committed. Furthermore, the content of attributes of returned datatypes is undefined.
Note that MPI_TYPE_GET_CONTENTS can be invoked with a datatype argument that was constructed using MPI_TYPE_CREATE_F90_REAL, MPI_TYPE_CREATE_F90_INTEGER, or MPI_TYPE_CREATE_F90_COMPLEX (an unnamed predefined datatype). In such a case, an empty array_of_datatypes is returned.
In the MPI-/ datatype constructor calls, the address arguments in Fortran are of type INTEGER. In the new MPI-// calls, the address arguments are of type INTEGER(KIND=MPI_ADDRESS_KIND). The call MPI_TYPE_GET_CONTENTS returns all addresses in an argument of type INTEGER(KIND=MPI_ADDRESS_KIND). This is true even if the old MPI-/ calls were used. Thus, the location of values returned can be thought of as being returned by the C bindings. It can also be determined by examining the new MPI-// calls for datatype constructors for the deprecated MPI-/ calls that involve addresses.
The following defines what values are placed in each entry of the returned arrays depending on the datatype constructor used for datatype. It also specifies the size of the arrays needed which is the values returned by MPI_TYPE_GET_ENVELOPE. In Fortran, the following calls were made:
PARAMETER (LARGE = 1000) INTEGER TYPE, NI, NA, ND, COMBINER, I(LARGE), D(LARGE), IERROR INTEGER(KIND=MPI_ADDRESS_KIND) A(LARGE) ! CONSTRUCT DATATYPE TYPE (NOT SHOWN) CALL MPI_TYPE_GET_ENVELOPE(TYPE, NI, NA, ND, COMBINER, IERROR) IF ((NI .GT. LARGE) .OR. (NA .GT. LARGE) .OR. (ND .GT. LARGE)) THEN WRITE (*, *) "NI, NA, OR ND = ", NI, NA, ND, & " RETURNED BY MPI_TYPE_GET_ENVELOPE IS LARGER THAN LARGE = ", LARGE CALL MPI_ABORT(MPI_COMM_WORLD, 99) ENDIF CALL MPI_TYPE_GET_CONTENTS(TYPE, NI, NA, ND, I, A, D, IERROR)
or in C the analogous calls of:
#define LARGE 1000 int ni, na, nd, combiner, i[LARGE]; MPI_Aint a[LARGE]; MPI_Datatype type, d[LARGE]; /* construct datatype type (not shown) */ MPI_Type_get_envelope(type, &ni, &na, &nd, &combiner); if ((ni > LARGE) || (na > LARGE) || (nd > LARGE)) { fprintf(stderr, "ni, na, or nd = %d %d %d returned by ", ni, na, nd); fprintf(stderr, "MPI_Type_get_envelope is larger than LARGE = %d\n", LARGE); MPI_Abort(MPI_COMM_WORLD, 99); }; MPI_Type_get_contents(type, ni, na, nd, i, a, d);
The C++ code is in analogy to the C code above with the same values returned.
In the descriptions that follow, the lower case name of arguments is used.
If combiner is MPI_COMBINER_NAMED then it is erroneous to call MPI_TYPE_GET_CONTENTS.
If combiner is MPI_COMBINER_DUP then
truein
Constructor argument | C & C++ location | Fortran location |
oldtype | d[0] | D(1) |
and ni = 0, na = 0, nd = 1.
If combiner is MPI_COMBINER_CONTIGUOUS then
truein
Constructor argument | C & C++ location | Fortran location |
count | i[0] | I(1) |
oldtype | d[0] | D(1) |
and ni = 1, na = 0, nd = 1.
If combiner is MPI_COMBINER_VECTOR then
truein
Constructor argument | C & C++ location | Fortran location |
count | i[0] | I(1) |
blocklength | i[1] | I(2) |
stride | i[2] | I(3) |
oldtype | d[0] | D(1) |
and ni = 3, na = 0, nd = 1.
If combiner is MPI_COMBINER_HVECTOR_INTEGER or MPI_COMBINER_HVECTOR then
truein
Constructor argument | C & C++ location | Fortran location |
count | i[0] | I(1) |
blocklength | i[1] | I(2) |
stride | a[0] | A(1) |
oldtype | d[0] | D(1) |
and ni = 2, na = 1, nd = 1.
If combiner is MPI_COMBINER_INDEXED then
truein
Constructor argument | C & C++ location | Fortran location |
count | i[0] | I(1) |
array_of_blocklengths | i[1] to i[i[0]] | I(2) to I(I(1)+1) |
array_of_displacements | i[i[0]+1] to i[2*i[0]] | I(I(1)+2) to I(2*I(1)+1) |
oldtype | d[0] | D(1) |
and ni = 2*count+1, na = 0, nd = 1.
If combiner is MPI_COMBINER_HINDEXED_INTEGER or MPI_COMBINER_HINDEXED then
truein
Constructor argument | C & C++ location | Fortran location |
count | i[0] | I(1) |
array_of_blocklengths | i[1] to i[i[0]] | I(2) to I(I(1)+1) |
array_of_displacements | a[0] to a[i[0]-1] | A(1) to A(I(1)) |
oldtype | d[0] | D(1) |
and ni = count+1, na = count, nd = 1.
If combiner is MPI_COMBINER_INDEXED_BLOCK then
truein
Constructor argument | C & C++ location | Fortran location |
count | i[0] | I(1) |
blocklength | i[1] | I(2) |
array_of_displacements | i[2] to i[i[0]+1] | I(3) to I(I(1)+2) |
oldtype | d[0] | D(1) |
and ni = count+2, na = 0, nd = 1.
If combiner is MPI_COMBINER_STRUCT_INTEGER or MPI_COMBINER_STRUCT then
truein
Constructor argument | C & C++ location | Fortran location |
count | i[0] | I(1) |
array_of_blocklengths | i[1] to i[i[0]] | I(2) to I(I(1)+1) |
array_of_displacements | a[0] to a[i[0]-1] | A(1) to A(I(1)) |
array_of_types | d[0] to d[i[0]-1] | D(1) to D(I(1)) |
and ni = count+1, na = count, nd = count.
If combiner is MPI_COMBINER_SUBARRAY then
truein
Constructor argument | C & C++ location | Fortran location |
ndims | i[0] | I(1) |
array_of_sizes | i[1] to i[i[0]] | I(2) to I(I(1)+1) |
array_of_subsizes | i[i[0]+1] to i[2*i[0]] | I(I(1)+2) to I(2*I(1)+1) |
array_of_starts | i[2*i[0]+1] to i[3*i[0]] | I(2*I(1)+2) to I(3*I(1)+1) |
order | i[3*i[0]+1] | I(3*I(1)+2] |
oldtype | d[0] | D(1) |
and ni = 3*ndims+2, na = 0, nd = 1.
If combiner is MPI_COMBINER_DARRAY then
truein
Constructor argument | C & C++ location | Fortran location |
size | i[0] | I(1) |
rank | i[1] | I(2) |
ndims | i[2] | I(3) |
array_of_gsizes | i[3] to i[i[2]+2] | I(4) to I(I(3)+3) |
array_of_distribs | i[i[2]+3] to i[2*i[2]+2] | I(I(3)+4) to I(2*I(3)+3) |
array_of_dargs | i[2*i[2]+3] to i[3*i[2]+2] | I(2*I(3)+4) to I(3*I(3)+3) |
array_of_psizes | i[3*i[2]+3] to i[4*i[2]+2] | I(3*I(3)+4) to I(4*I(3)+3) |
order | i[4*i[2]+3] | I(4*I(3)+4) |
oldtype | d[0] | D(1) |
and ni = 4*ndims+4, na = 0, nd = 1.
If combiner is MPI_COMBINER_F90_REAL then
truein
Constructor argument | C & C++ location | Fortran location |
p | i[0] | I(1) |
r | i[1] | I(2) |
and ni = 2, na = 0, nd = 0.
If combiner is MPI_COMBINER_F90_COMPLEX then
truein
Constructor argument | C & C++ location | Fortran location |
p | i[0] | I(1) |
r | i[1] | I(2) |
and ni = 2, na = 0, nd = 0.
If combiner is MPI_COMBINER_F90_INTEGER then
truein
Constructor argument | C & C++ location | Fortran location |
r | i[0] | I(1) |
and ni = 1, na = 0, nd = 0.
If combiner is MPI_COMBINER_RESIZED then
truein
Constructor argument | C & C++ location | Fortran location |
lb | a[0] | A(1) |
extent | a[1] | A(2) |
oldtype | d[0] | D(1) |
and ni = 0, na = 2, nd = 1.
/* Example of decoding a datatype. Returns 0 if the datatype is predefined, 1 otherwise */ #include <stdio.h> #include <stdlib.h> #include "mpi.h" int printdatatype( MPI_Datatype datatype ) { int *array_of_ints; MPI_Aint *array_of_adds; MPI_Datatype *array_of_dtypes; int num_ints, num_adds, num_dtypes, combiner; int i; MPI_Type_get_envelope( datatype, &num_ints, &num_adds, &num_dtypes, &combiner ); switch (combiner) { case MPI_COMBINER_NAMED: printf( "Datatype is named:" ); /* To print the specific type, we can match against the predefined forms. We can NOT use a switch statement here We could also use MPI_TYPE_GET_NAME if we prefered to use names that the user may have changed. */ if (datatype == MPI_INT) printf( "MPI_INT\n" ); else if (datatype == MPI_DOUBLE) printf( "MPI_DOUBLE\n" ); ... else test for other types ... return 0; break; case MPI_COMBINER_STRUCT: case MPI_COMBINER_STRUCT_INTEGER: printf( "Datatype is struct containing" ); array_of_ints = (int *)malloc( num_ints * sizeof(int) ); array_of_adds = (MPI_Aint *) malloc( num_adds * sizeof(MPI_Aint) ); array_of_dtypes = (MPI_Datatype *) malloc( num_dtypes * sizeof(MPI_Datatype) ); MPI_Type_get_contents( datatype, num_ints, num_adds, num_dtypes, array_of_ints, array_of_adds, array_of_dtypes ); printf( " %d datatypes:\n", array_of_ints[0] ); for (i=0; i<array_of_ints[0]; i++) { printf( "blocklength %d, displacement %ld, type:\n", array_of_ints[i+1], array_of_adds[i] ); if (printdatatype( array_of_dtypes[i] )) { /* Note that we free the type ONLY if it is not predefined */ MPI_Type_free( &array_of_dtypes[i] ); } } free( array_of_ints ); free( array_of_adds ); free( array_of_dtypes ); break; ... other combiner values ... default: printf( "Unrecognized combiner type\n" ); } return 1; }
MPI-Standard for MARMOT