The following examples illustrate the use of derived datatypes.
Example
Send and receive a section of a 3D array.
REAL a(100,100,100), e(9,9,9)
INTEGER oneslice, twoslice, threeslice, myrank, ierr
INTEGER(KIND=MPI_ADDRESS_KIND) lb, sizeofreal
INTEGER status(MPI_STATUS_SIZE)
! extract the section a(1:17:2, 3:11, 2:10)
! and store it in e(:,:,:).
CALL MPI_COMM_RANK(MPI_COMM_WORLD, myrank, ierr)
CALL MPI_TYPE_GET_EXTENT(MPI_REAL, lb, sizeofreal, ierr)
! create datatype for a 1D section
CALL MPI_TYPE_VECTOR(9, 1, 2, MPI_REAL, oneslice, ierr)
! create datatype for a 2D section
CALL MPI_TYPE_CREATE_HVECTOR(9, 1, 100*sizeofreal, oneslice, &
twoslice, ierr)
! create datatype for the entire section
CALL MPI_TYPE_CREATE_HVECTOR(9, 1, 100*100*sizeofreal, twoslice, &
threeslice, ierr)
CALL MPI_TYPE_COMMIT(threeslice, ierr)
CALL MPI_SENDRECV(a(1,3,2), 1, threeslice, myrank, 0, e, 9*9*9, &
MPI_REAL, myrank, 0, MPI_COMM_WORLD, status, ierr)
Example
Copy the (strictly) lower triangular part of a matrix.
REAL a(100,100), b(100,100)
INTEGER disp(100), blocklen(100), ltype, myrank, ierr
INTEGER status(MPI_STATUS_SIZE)
! copy lower triangular part of array a
! onto lower triangular part of array b
CALL MPI_COMM_RANK(MPI_COMM_WORLD, myrank, ierr)
! compute start and size of each column
DO i=1,100
disp(i) = 100*(i-1) + i
blocklen(i) = 100-i
END DO
! create datatype for lower triangular part
CALL MPI_TYPE_INDEXED(100, blocklen, disp, MPI_REAL, ltype, ierr)
CALL MPI_TYPE_COMMIT(ltype, ierr)
CALL MPI_SENDRECV(a, 1, ltype, myrank, 0, b, 1, &
ltype, myrank, 0, MPI_COMM_WORLD, status, ierr)
Example
Transpose a matrix.
REAL a(100,100), b(100,100)
INTEGER row, xpose, myrank, ierr
INTEGER(KIND=MPI_ADDRESS_KIND) lb, sizeofreal
INTEGER status(MPI_STATUS_SIZE)
! transpose matrix a onto b
CALL MPI_COMM_RANK(MPI_COMM_WORLD, myrank, ierr)
CALL MPI_TYPE_GET_EXTENT(MPI_REAL, lb, sizeofreal, ierr)
! create datatype for one row
CALL MPI_TYPE_VECTOR(100, 1, 100, MPI_REAL, row, ierr)
! create datatype for matrix in row-major order
CALL MPI_TYPE_CREATE_HVECTOR(100, 1, sizeofreal, row, xpose, ierr)
CALL MPI_TYPE_COMMIT(xpose, ierr)
! send matrix in row-major order and receive in column major order
CALL MPI_SENDRECV(a, 1, xpose, myrank, 0, b, 100*100, &
MPI_REAL, myrank, 0, MPI_COMM_WORLD, status, ierr)
Example
Another approach to the transpose problem:
REAL a(100,100), b(100,100)
INTEGER row, row1
INTEGER(KIND=MPI_ADDRESS_KIND) lb, sizeofreal
INTEGER myrank, ierr
INTEGER status(MPI_STATUS_SIZE)
CALL MPI_COMM_RANK(MPI_COMM_WORLD, myrank, ierr)
! transpose matrix a onto b
CALL MPI_TYPE_GET_EXTENT(MPI_REAL, lb, sizeofreal, ierr)
! create datatype for one row
CALL MPI_TYPE_VECTOR(100, 1, 100, MPI_REAL, row, ierr)
! create datatype for one row, with the extent of one real number
lb = 0
CALL MPI_TYPE_CREATE_RESIZED(row, lb, sizeofreal, row1, ierr)
CALL MPI_TYPE_COMMIT(row1, ierr)
! send 100 rows and receive in column major order
CALL MPI_SENDRECV(a, 100, row1, myrank, 0, b, 100*100, &
MPI_REAL, myrank, 0, MPI_COMM_WORLD, status, ierr)
Example
Use of MPI datatypes to manipulate an array of structures.
struct Partstruct
{
int type; /* particle type */
double d[6]; /* particle coordinates */
char b[7]; /* some additional information */
};
struct Partstruct particle[1000];
int i, dest, tag;
MPI_Comm comm;
/* build datatype describing structure */
MPI_Datatype Particlestruct, Particletype;
MPI_Datatype type[3] = {MPI_INT, MPI_DOUBLE, MPI_CHAR};
int blocklen[3] = {1, 6, 7};
MPI_Aint disp[3];
MPI_Aint base, lb, sizeofentry;
/* compute displacements of structure components */
MPI_Get_address(particle, disp);
MPI_Get_address(particle[0].d, disp+1);
MPI_Get_address(particle[0].b, disp+2);
base = disp[0];
for (i=0; i < 3; i++) disp[i] = MPI_Aint_diff(disp[i], base);
MPI_Type_create_struct(3, blocklen, disp, type, &Particlestruct);
/* Since the compiler may pad the structure, it is best to explicitly
set the extent of the MPI datatype for a structure element using
MPI_Type_create_resized */
/* compute extent of the structure */
MPI_Get_address(particle+1, &sizeofentry);
sizeofentry = MPI_Aint_diff(sizeofentry, base);
/* build datatype describing structure */
MPI_Type_create_resized(Particlestruct, 0, sizeofentry, &Particletype);
/* 4.1: send the entire array */
MPI_Type_commit(&Particletype);
MPI_Send(particle, 1000, Particletype, dest, tag, comm);
/* 4.2: send only the entries of type zero particles,
preceded by the number of such entries */
MPI_Datatype Zparticles; /* datatype describing all particles
with type zero (needs to be recomputed
if types change) */
MPI_Datatype Ztype;
int zdisp[1000];
int zblock[1000], j, k;
int zzblock[2] = {1,1};
MPI_Aint zzdisp[2];
MPI_Datatype zztype[2];
/* compute displacements of type zero particles */
j = 0;
for (i=0; i < 1000; i++)
if (particle[i].type == 0)
{
zdisp[j] = i;
zblock[j] = 1;
j++;
}
/* create datatype for type zero particles */
MPI_Type_indexed(j, zblock, zdisp, Particletype, &Zparticles);
/* prepend particle count */
MPI_Get_address(&j, zzdisp);
MPI_Get_address(particle, zzdisp+1);
zztype[0] = MPI_INT;
zztype[1] = Zparticles;
MPI_Type_create_struct(2, zzblock, zzdisp, zztype, &Ztype);
MPI_Type_commit(&Ztype);
MPI_Send(MPI_BOTTOM, 1, Ztype, dest, tag, comm);
/* A probably more efficient way of defining Zparticles */
/* consecutive particles with index zero are handled as one block */
j=0;
for (i=0; i < 1000; i++)
if (particle[i].type == 0)
{
for (k=i+1; (k < 1000)&&(particle[k].type == 0); k++);
zdisp[j] = i;
zblock[j] = k-i;
j++;
i = k;
}
MPI_Type_indexed(j, zblock, zdisp, Particletype, &Zparticles);
/* 4.3: send the first two coordinates of all entries */
MPI_Datatype Allpairs; /* datatype for all pairs of coordinates */
MPI_Type_get_extent(Particletype, &lb, &sizeofentry);
/* sizeofentry can also be computed by subtracting the address
of particle[0] from the address of particle[1] */
MPI_Type_create_hvector(1000, 2, sizeofentry, MPI_DOUBLE, &Allpairs);
MPI_Type_commit(&Allpairs);
MPI_Send(particle[0].d, 1, Allpairs, dest, tag, comm);
/* an alternative solution to 4.3 */
MPI_Datatype Twodouble;
MPI_Type_contiguous(2, MPI_DOUBLE, &Twodouble);
MPI_Datatype Onepair; /* datatype for one pair of coordinates, with
the extent of one particle entry */
MPI_Type_create_resized(Twodouble, 0, sizeofentry, &Onepair );
MPI_Type_commit(&Onepair);
MPI_Send(particle[0].d, 1000, Onepair, dest, tag, comm);
Example
The same manipulations as in the previous example, but use absolute
addresses in datatypes.
struct Partstruct
{
int type;
double d[6];
char b[7];
};
struct Partstruct particle[1000];
/* build datatype describing first array entry */
MPI_Datatype Particletype;
MPI_Datatype type[3] = {MPI_INT, MPI_DOUBLE, MPI_CHAR};
int block[3] = {1, 6, 7};
MPI_Aint disp[3];
MPI_Get_address(particle, disp);
MPI_Get_address(particle[0].d, disp+1);
MPI_Get_address(particle[0].b, disp+2);
MPI_Type_create_struct(3, block, disp, type, &Particletype);
/* Particletype describes first array entry -- using absolute
addresses */
/* 5.1: send the entire array */
MPI_Type_commit(&Particletype);
MPI_Send(MPI_BOTTOM, 1000, Particletype, dest, tag, comm);
/* 5.2: send the entries of type zero,
preceded by the number of such entries */
MPI_Datatype Zparticles, Ztype;
int zdisp[1000];
int zblock[1000], i, j, k;
int zzblock[2] = {1,1};
MPI_Datatype zztype[2];
MPI_Aint zzdisp[2];
j=0;
for (i=0; i < 1000; i++)
if (particle[i].type == 0)
{
for (k=i+1; (k < 1000)&&(particle[k].type == 0); k++);
zdisp[j] = i;
zblock[j] = k-i;
j++;
i = k;
}
MPI_Type_indexed(j, zblock, zdisp, Particletype, &Zparticles);
/* Zparticles describe particles with type zero, using
their absolute addresses*/
/* prepend particle count */
MPI_Get_address(&j, zzdisp);
zzdisp[1] = (MPI_Aint)0;
zztype[0] = MPI_INT;
zztype[1] = Zparticles;
MPI_Type_create_struct(2, zzblock, zzdisp, zztype, &Ztype);
MPI_Type_commit(&Ztype);
MPI_Send(MPI_BOTTOM, 1, Ztype, dest, tag, comm);
Example
This example shows how datatypes can be used to handle unions.
union {
int ival;
float fval;
} u[1000];
int i, utype;
/* All entries of u have identical type; variable
utype keeps track of their current type */
MPI_Datatype mpi_utype[2];
MPI_Aint ubase, extent;
/* compute an MPI datatype for each possible union type;
assume values are left-aligned in union storage. */
MPI_Get_address(u, &ubase);
MPI_Get_address(u+1, &extent);
extent = MPI_Aint_diff(extent, ubase);
MPI_Type_create_resized(MPI_INT, 0, extent, &mpi_utype[0]);
MPI_Type_create_resized(MPI_FLOAT, 0, extent, &mpi_utype[1]);
for(i=0; i<2; i++) MPI_Type_commit(&mpi_utype[i]);
/* actual communication */
MPI_Send(u, 1000, mpi_utype[utype], dest, tag, comm);
Example
This
example shows how a datatype can be decoded. The routine
printdatatype prints out the elements of the datatype. Note the use
of MPI_Type_free for datatypes that are not predefined.
/*
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:
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], (long)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;
}