3.13. Pack and unpack


Up: Point-to-Point Communication Next: Collective Communication Previous: Examples

Some existing communication libraries provide pack/unpack functions for sending noncontiguous data. In these, the user explicitly packs data into a contiguous buffer before sending it, and unpacks it from a contiguous buffer after receiving it. Derived datatypes, which are described in Section Derived datatypes , allow one, in most cases, to avoid explicit packing and unpacking. The user specifies the layout of the data to be sent or received, and the communication library directly accesses a noncontiguous buffer. The pack/unpack routines are provided for compatibility with previous libraries. Also, they provide some functionality that is not otherwise available in MPI. For instance, a message can be received in several parts, where the receive operation done on a later part may depend on the content of a former part. Another use is that outgoing messages may be explicitly buffered in user supplied space, thus overriding the system buffering policy. Finally, the availability of pack and unpack operations facilitates the development of additional communication libraries layered on top of MPI.

MPI_PACK(inbuf, incount, datatype, outbuf, outsize, position, comm)
[ IN inbuf] input buffer start (choice)
[ IN incount] number of input data items (integer)
[ IN datatype] datatype of each input data item (handle)
[ OUT outbuf] output buffer start (choice)
[ IN outsize] output buffer size, in bytes (integer)
[ INOUT position] current position in buffer, in bytes (integer)
[ IN comm] communicator for packed message (handle)

int MPI_Pack(void* inbuf, int incount, MPI_Datatype datatype, void *outbuf, int outsize, int *position, MPI_Comm comm)

MPI_PACK(INBUF, INCOUNT, DATATYPE, OUTBUF, OUTSIZE, POSITION, COMM, IERROR)
<type> INBUF(*), OUTBUF(*)
INTEGER INCOUNT, DATATYPE, OUTSIZE, POSITION, COMM, IERROR

Packs the message in the send buffer specified by inbuf, incount, datatype into the buffer space specified by outbuf and outcount. The input buffer can be any communication buffer allowed in MPI_SEND. The output buffer is a contiguous storage area containing outsize bytes, starting at the address outbuf (length is counted in bytes, not elements, as if it were a communication buffer for a message of type MPI_PACKED).

The input value of position is the first location in the output buffer to be used for packing. position is incremented by the size of the packed message, and the output value of position is the first location in the output buffer following the locations occupied by the packed message. The comm argument is the communicator that will be subsequently used for sending the packed message.

MPI_UNPACK(inbuf, insize, position, outbuf, outcount, datatype, comm)
[ IN inbuf] input buffer start (choice)
[ IN insize] size of input buffer, in bytes (integer)
[ INOUT position] current position in bytes (integer)
[ OUT outbuf] output buffer start (choice)
[ IN outcount] number of items to be unpacked (integer)
[ IN datatype] datatype of each output data item (handle)
[ IN comm] communicator for packed message (handle)

int MPI_Unpack(void* inbuf, int insize, int *position, void *outbuf, int outcount, MPI_Datatype datatype, MPI_Comm comm)

MPI_UNPACK(INBUF, INSIZE, POSITION, OUTBUF, OUTCOUNT, DATATYPE, COMM, IERROR)
<type> INBUF(*), OUTBUF(*)
INTEGER INSIZE, POSITION, OUTCOUNT, DATATYPE, COMM, IERROR

Unpacks a message into the receive buffer specified by outbuf, outcount, datatype from the buffer space specified by inbuf and insize. The output buffer can be any communication buffer allowed in MPI_RECV. The input buffer is a contiguous storage area containing insize bytes, starting at address inbuf. The input value of position is the first location in the input buffer occupied by the packed message. position is incremented by the size of the packed message, so that the output value of position is the first location in the input buffer after the locations occupied by the message that was unpacked. comm is the communicator used to receive the packed message.


[] Advice to users.

Note the difference between MPI_RECV and MPI_UNPACK: in MPI_RECV, the count argument specifies the maximum number of items that can be received. The actual number of items received is determined by the length of the incoming message. In MPI_UNPACK, the count argument specifies the actual number of items that are unpacked; the ``size'' of the corresponding message is the increment in position. The reason for this change is that the ``incoming message size'' is not predetermined since the user decides how much to unpack; nor is it easy to determine the ``message size'' from the number of items to be unpacked. In fact, in a heterogeneous system, this number may not be determined a priori. ( End of advice to users.)
To understand the behavior of pack and unpack, it is convenient to think of the data part of a message as being the sequence obtained by concatenating the successive values sent in that message. The pack operation stores this sequence in the buffer space, as if sending the message to that buffer. The unpack operation retrieves this sequence from buffer space, as if receiving a message from that buffer. (It is helpful to think of internal Fortran files or sscanf in C, for a similar function.)

Several messages can be successively packed into one packing unit. This is effected by several successive related calls to MPI_PACK, where the first call provides position = 0, and each successive call inputs the value of position that was output by the previous call, and the same values for outbuf, outcount and comm. This packing unit now contains the equivalent information that would have been stored in a message by one send call with a send buffer that is the ``concatenation'' of the individual send buffers.

A packing unit can be sent using type MPI_PACKED. Any point to point or collective communication function can be used to move the sequence of bytes that forms the packing unit from one process to another. This packing unit can now be received using any receive operation, with any datatype: the type matching rules are relaxed for messages sent with type MPI_PACKED.

A message sent with any type (including MPI_PACKED) can be received using the type MPI_PACKED. Such a message can then be unpacked by calls to MPI_UNPACK.

A packing unit (or a message created by a regular, ``typed'' send) can be unpacked into several successive messages. This is effected by several successive related calls to MPI_UNPACK, where the first call provides position = 0, and each successive call inputs the value of position that was output by the previous call, and the same values for inbuf, insize and comm.

The concatenation of two packing units is not necessarily a packing unit; nor is a substring of a packing unit necessarily a packing unit. Thus, one cannot concatenate two packing units and then unpack the result as one packing unit; nor can one unpack a substring of a packing unit as a separate packing unit. Each packing unit, that was created by a related sequence of pack calls, or by a regular send, must be unpacked as a unit, by a sequence of related unpack calls.


[] Rationale.

The restriction on ``atomic'' packing and unpacking of packing units allows the implementation to add at the head of packing units additional information, such as a description of the sender architecture (to be used for type conversion, in a heterogeneous environment) ( End of rationale.)
The following call allows the user to find out how much space is needed to pack a message and, thus, manage space allocation for buffers.

MPI_PACK_SIZE(incount, datatype, comm, size)
[ IN incount] count argument to packing call (integer)
[ IN datatype] datatype argument to packing call (handle)
[ IN comm] communicator argument to packing call (handle)
[ OUT size] upper bound on size of packed message, in bytes (integer)

int MPI_Pack_size(int incount, MPI_Datatype datatype, MPI_Comm comm, int *size)

MPI_PACK_SIZE(INCOUNT, DATATYPE, COMM, SIZE, IERROR)
INTEGER INCOUNT, DATATYPE, COMM, SIZE, IERROR

A call to MPI_PACK_SIZE(incount, datatype, comm, size) returns in size an upper bound on the increment in position that is effected by a call to MPI_PACK(inbuf, incount, datatype, outbuf, outcount, position, comm).


[] Rationale.

The call returns an upper bound, rather than an exact bound, since the exact amount of space needed to pack the message may depend on the context (e.g., first message packed in a packing unit may take more space). ( End of rationale.)

Example An example using MPI_PACK.

int position, i, j, a[2]; 
char buff[1000]; 

....

MPI_Comm_rank(MPI_COMM_WORLD, &myrank); if (myrank == 0) { / * SENDER CODE */

position = 0; MPI_Pack(&i, 1, MPI_INT, buff, 1000, &position, MPI_COMM_WORLD); MPI_Pack(&j, 1, MPI_INT, buff, 1000, &position, MPI_COMM_WORLD); MPI_Send( buff, position, MPI_PACKED, 1, 0, MPI_COMM_WORLD); } else /* RECEIVER CODE */ MPI_Recv( a, 2, MPI_INT, 0, 0, MPI_COMM_WORLD)

}


Example A elaborate example.

int position, i; 
float a[1000]; 
char buff[1000] 

....

MPI_Comm_rank(MPI_Comm_world, &myrank); if (myrank == 0) { / * SENDER CODE */

int len[2]; MPI_Aint disp[2]; MPI_Datatype type[2], newtype;

/* build datatype for i followed by a[0]...a[i-1] */

len[0] = 1; len[1] = i; MPI_Address( &i, disp); MPI_Address( a, disp+1); type[0] = MPI_INT; type[1] = MPI_FLOAT; MPI_Type_struct( 2, len, disp, type, &newtype); MPI_Type_commit( &newtype);

/* Pack i followed by a[0]...a[i-1]*/

position = 0; MPI_Pack( MPI_BOTTOM, 1, newtype, buff, 1000, &position, MPI_COMM_WORLD);

/* Send */

MPI_Send( buff, position, MPI_PACKED, 1, 0, MPI_COMM_WORLD)

/* ***** One can replace the last three lines with MPI_Send( MPI_BOTTOM, 1, newtype, 1, 0, MPI_COMM_WORLD); ***** */ } else /* myrank == 1 */ { /* RECEIVER CODE */

MPI_Status status;

/* Receive */

MPI_Recv( buff, 1000, MPI_PACKED, 0, 0, &status);

/* Unpack i */

position = 0; MPI_Unpack(buff, 1000, &position, &i, 1, MPI_INT, MPI_COMM_WORLD);

/* Unpack a[0]...a[i-1] */ MPI_Unpack(buff, 1000, &position, a, i, MPI_FLOAT, MPI_COMM_WORLD); }


Example Each process sends a count, followed by count characters to the root; the root concatenate all characters into one string.

int count, gsize, counts[64], totalcount, k1, k2, k, 
    displs[64], position, concat_pos; 
char chr[100], *lbuf, *rbuf, *cbuf; 
... 
MPI_Comm_size(comm, &gsize); 
MPI_Comm_rank(comm, &myrank); 

/* allocate local pack buffer */ MPI_Pack_size(1, MPI_INT, comm, &k1); MPI_Pack_size(count, MPI_CHAR, &k2); k = k1+k2; lbuf = (char *)malloc(k);

/* pack count, followed by count characters */ position = 0; MPI_Pack(&count, 1, MPI_INT, lbuf, k, &position, comm); MPI_Pack(chr, count, MPI_CHAR, &lbuf, k, &position, comm);

if (myrank != root) /* gather at root sizes of all packed messages */ MPI_Gather( &position, 1, MPI_INT, NULL, NULL, NULL, root, comm);

/* gather at root packed messages */ MPI_Gatherv( &buf, position, MPI_PACKED, NULL, NULL, NULL, NULL, root, comm);

else { /* root code */ /* gather sizes of all packed messages */ MPI_Gather( &position, 1, MPI_INT, counts, 1, MPI_INT, root, comm);

/* gather all packed messages */ displs[0] = 0; for (i=1; i < gsize; i++) displs[i] = displs[i-1] + counts[i-1]; totalcount = dipls[gsize-1] + counts[gsize-1]; rbuf = (char *)malloc(totalcount); cbuf = (char *)malloc(totalcount); MPI_Gatherv( lbuf, position, MPI_PACKED, rbuf, counts, displs, MPI_PACKED, root, comm);

/* unpack all messages and concatenate strings */ concat_pos = 0; for (i=0; i < gsize; i++) { position = 0; MPI_Unpack( rbuf+displs[i], totalcount-displs[i], &position, &count, 1, MPI_INT, comm); MPI_Unpack( rbuf+displs[i], totalcount-displs[i], &position, cbuf+concat_pos, count, MPI_CHAR, comm); concat_pos += count; } cbuf[concat_pos] = `\0'; }



Up: Point-to-Point Communication Next: Collective Communication Previous: Examples


Return to MPI Standard Index
Return to MPI home page