All point-to-point message passing routines in MPI take as an argument the datatype of the data communicated. In the simplest case this will be a primitive datatype, such as an integer or floating point number. However, MPI also supports more general datatypes, and thereby supports the communication of array sections and structures involving combinations of primitive datatypes.
A general datatype is a sequence of pairs of primitive datatypes and integer byte displacements. Thus,
Together with a base address, a datatype specifies a communication buffer. General datatypes are built up hierarchically from simpler components. There are four basic constructors for datatypes, namely the contiguous, vector, indexed, and struct constructors. We will now discuss each of these in turn.
The contiguous constructor creates a new datatype from repetitions of a specified old datatype. This requires us to specify the old datatype and the number of repetitions, n. For example, if the old datatype is and n=3, then the new datatype would be,
It should be noted how each repeated unit in the new datatype is aligned with a double word boundary. This alignment is dictated by the appearance of a double in the old datatype, so that the extent of the old datatype is taken as 16 bytes, rather than 9 bytes.
The vector constructor builds a new datatype by replicating an old datatype in blocks at fixed offsets. The new datatype consists of count blocks, each of which is a repetition of blocklen items of some specified old datatype. The starts of successive blocks are offset by stride items of the old datatype. Thus, if , , and then the new datatype would be,
Here the offset between the two blocks is 64 bytes, which is the stride multiplied by the extent of the old datatype.
The indexed constructor is a generalization of the vector constructor in which each block has a different size and offset. The sizes and offsets are given by the entries in two integer arrays, B and I. The new datatype consists of count blocks, and the ith block is of length B[i] items of the specified old datatype. The offset of the start of the ith block is I[i] items of the old datatype. Thus, if , , and , then the new datatype would be,
The struct constructor is the most general of the datatype constructors. This constructor generalizes the indexed constructor by allowing each block to be of a different datatype. Thus, in addition to specifying the number of blocks, count, and the block length and offset arrays, B and I, we must also give the datatype of the replicated unit in each block. Let us assume this is specified in an array T. The length of the ith block is B[i] items of type T[i], and the offset of the start of the ith block is I[i] bytes. Thus, if count=3, , , and , then the new datatype would be,
In addition to the constructors described above, there is a variant of the vector constructor in which the stride is given in bytes instead of the number of items. There is also a variant of the indexed constructor in which the block offsets are given in bytes.
To better understand the use of general data structures consider the example of an application in which particles move on a one-dimensional domain. We assume that each process is responsible for a different section of this domain. In each time step particles may move from the subdomain of one process to that of another, and so the data for such particles must be communicated between processes. We shall just consider here the task of migrating particles across the righthand boundary of each process, as shown in Figure 4. The particle data are stored in an array of structures, with each entry in this structure consisting of the particle position, x, velocity, v, and type, k:
The C code for migrating particles across the righthand boundary is shown in Figure 5.
Figure 4: Particle migration in a one-dimensional code. The left and right
edges of a process domain are shown. We shall consider just the migration
of particles across the righthand boundary.
In Figure 5 the code in the first box creates a datatype, Ptype, that represents the Pstruct structure for a single particle. This datatype is,
Figure 5: Fragment of C code for migrating particles
across the righthand process
boundary. Outgoing particles are sent to process dest using the
derived datatype, Ztype. Incoming particles from process source
are received into the particle array recvbuf which has enough space
for 100 particles.
In the second code box the particles that have crossed the righthand boundary are identified, and their index in the particle array is stored in Pindex. It is assumed that no more than 100 particles cross the boundary. The call to MPI_Type_indexed uses an indexed constructor to create a new datatype, Ztype, that references all the migrating particles. Before communicating the data, the Ztype datatype must be committed. This is done to allow the system to use a different internal representation for Ztype, and to optimize the communication operation. Committing a datatype is most likely to be advantageous when reusing a datatype many times, which is not the case in this example. Finally, the migrating particles are communicated by a call to MPI_Sendrecv. The offsets in the Ztype datatype are interpreted relative to the address of the start of the particle array.