next up previous index
Next: Data Type Inquiry Functions Up: Datatype Constructors Previous: Indexed


The last, and the most complex MPI data type constructor is MPI_Type_struct. This function lets MPI programmers do all that they can do with MPI_Type_indexed, and on top of all that the items picked up from various memory locations can have different types. The items, as before, are assumed to be present in contiguous blocks at every location pointed to by the displacement array and within those blocks they are all assumed to be of the same type. But the types may vary from block to block. Here is the synopsis of this function in C:

MPI_Type_struct(int count, int *array_of_blocklengths,
                MPI_Aint *array_of_displacements, MPI_Datatype *array_of_types,
                MPI_Datatype *newtype)
and in Fortran:
mpi_type_struct(count, array_of_blocklengths, array_of_displacements,
                array_of_types, newtype, ierror)
In summary the function works as follows: we are going to collect count blocks of data. Each block comprises a number of elements given by the corresponding entry in array_of_blocklengths. Each block begins at a location given by the corresponding entry in array_of_displacements. The type of data in each block is given by the corresponding entry in array_of_types. What are the displacements measured in? This is not a trivial question, because now every block can comprise elements of a different type. There is only one way to measure the displacements in this context. They have to be measured in bytes. Consequently there is no MPI_Type_hstruct function. You may say that MPI_Type_struct is the MPI_Type_hstruct: the displacements are measured in bytes and there is no no-byte version of this function.

Here is an example of how this function works. Let

type1 = {(double, 0), (char, 8)}
The call:
MPI_Type_struct(3, (2, 1, 3), (0, 16, 26), (MPI_FLOAT, type1, MPI_CHAR), 
constructs a new data type with the following map:
newtype = 
{(float, 0), (float, 4), (double, 16), (char, 24), (char, 26), (char, 27), 
 (char, 28)}
The call grabs 3 data blocks from locations given by the following displacements in bytes: 0, 16, and 26. The first block comprises 2 floating point numbers. The second block comprises one item of type type1, which is a double precision number followed by a character. The third block comprises 3 characters. Observe that the characters of the third block commence from a half-word boundary, i.e., byte number 26, and then the following 1-byte characters are written one after another without gaps in between. Strings are usually stored like that. It would be tremendously wasteful to pad every 8-bit character with 3 empty bytes (so as to reach a 32-bit boundary on a 32-bit system). Likewise, some architectures allow to pack up to two separate items into one word, so that if you have to pad you only pad up to the half-word boundary.

All these things are obviously very system dependent. If you ever decide to write programs like that you must very carefully annotate what you do. A program that makes 32-bit architecture assumptions may not work on a 64-bit or a 128-bit system. Today 128-bit systems become increasingly common. The new Macintoshes and the new games machines are 128-bit architectures. The IBM Power-3 and Power-4 chips are in a way also 128-bit chips (split into $2\times64$ for easier coding and better register utilization). As these systems become more popular people who wrote MPI programs for 32-bit or 64-bit architectures utilizing hand-defined derived MPI data types are going to get into trouble. Low level coding is always very difficult to port.

next up previous index
Next: Data Type Inquiry Functions Up: Datatype Constructors Previous: Indexed
Zdzislaw Meglicki