what I understand studying MPI specification is that
an MPI send primitive refer to a memory location (or a send buffer) pointed by the data to be sent
and take the data in that location which then passed as a message to the another Process.
Though it is true that virtual address of a give process will be meaningless in another process memory address;
It is ok to send data pointed by pointer such as void pointer as MPI will any way pass the data itself as a message
For example the following works correctly:
// Sender Side.
int x = 100;
void* snd;
MPI_Send(snd,4,MPI_BYTE,1,0,MPI_COMM_WORLD);
// Receiver Side.
void* rcv;
MPI_Recv(rcv, 4,MPI_BYTE,0,0,MPI_COMM_WORLD);
but when I add void* snd in a struct and try to send the struct this will no succeed.
I don’t understand why the previous example work correctly but not the following.
Here, I have defined a typedef struct and then create an MPI_DataType from it.
With the same explanation of the above the following should also have succeed,
unfortunately it is not working.
here is the code:
#include "mpi.h"
#include<stdio.h>
int main(int args, char *argv[])
{
int rank, source =0, tag=1, dest=1;
int bloackCount[2];
MPI_Init(&args, &argv);
typedef struct {
void* data;
int tag;
} data;
data myData;
MPI_Datatype structType, oldType[2];
MPI_Status stat;
/* MPI_Aint type used to idetify byte displacement of each block (array)*/
MPI_Aint offsets[2], extent;
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
offsets[0] = 0;
oldType[0] = MPI_BYTE;
bloackCount[0] = 1;
MPI_Type_extent(MPI_INT, &extent);
offsets[1] = 4 * extent; /*let say the MPI_BYTE will contain ineteger : size of int * extent */
oldType[1] = MPI_INT;
bloackCount[1] = 1;
MPI_Type_create_struct(2, bloackCount,offsets,oldType, &structType);
MPI_Type_commit(&structType);
if(rank == 0){
int x = 100;
myData.data = &x;
myData.tag = 99;
MPI_Send(&myData,1,structType, dest, tag, MPI_COMM_WORLD);
}
if(rank == 1 ){
MPI_Recv(&myData, 1, structType, source, tag, MPI_COMM_WORLD, &stat);
// with out this the following printf() will properly print the value 99 for
// myData.tag
int x = *(int *) myData.data;
printf(" \n Process %d, Received : %d , %d \n\n", rank , myData.tag, x);
}
MPI_Type_free(&structType);
MPI_Finalize();
}
Error message running the code:
[Looks like I am trying to access an invalid memory address space in the second process]
[ubuntu:04123] *** Process received signal ***
[ubuntu:04123] Signal: Segmentation fault (11)
[ubuntu:04123] Signal code: Address not mapped (1)
[ubuntu:04123] Failing at address: 0xbfe008bc
[ubuntu:04123] [ 0] [0xb778240c]
[ubuntu:04123] [ 1] GenericstructType(main+0x161) [0x8048935]
[ubuntu:04123] [ 2] /lib/i386-linux-gnu/libc.so.6(__libc_start_main+0xf3) [0xb750f4d3]
[ubuntu:04123] [ 3] GenericstructType() [0x8048741]
[ubuntu:04123] *** End of error message ***
Can some please explain to me why it is not working.
any advice will also be appreciated
thanks,
It works (of course,
sndandrcvhave to be assigned meaningful memory locations as values), becauseMPI_SendandMPI_Recvtake the address of the data location and bothsndandrcvare pointers, i.e. their values are such addresses. For example, theMPI_Sendline is not sending the value of the pointer itself but rather 4 bytes starting from the location that snd is pointing to. The same is true about the call toMPI_Recvand the usage ofrcv. In order to send the value of the pointer rather than the value it is pointing to, you would have to use:This would send
sizeof(void *)bytes, starting from the address where the value of the pointer is stored. This would make very little sense unless for some super special cases.Why your second example doesn’t work? MPI is not a magician and it cannot recognise that part of the memory contains a pointer to another memory block and follow that pointer. That is, when you construct a structured datatype, there is no way to tell MPI that the first element of the structure is actually a pointer and make it read the data that this pointer points to. In other words, you must perform explicit data marshalling – construct and intermediate buffer that contains a copy of the memory region, pointed by
data.data. Besides, your data structure contains no information on the length of the memory region thatdatapoints to.Please note something very important. All MPI datatypes have something called a type map. A type map is a list of tuples, where each tuple, also called type signature, has the form
(basic_type, offset)wherebasic_typeis a primitive language type, e.g.char,int,double, etc. andoffsetis an offset, relative to the beginning of the buffer. One peculiar feature of MPI is that offsets could also be negative and this means that the argument toMPI_Send(or toMPI_Recv, or to any other communication function) might actually point to the middle of the memory area, that would serve as data source. When sending data, MPI traverses the type map and takes one element of typebasic_typefrom the correspondingoffset, relative to the supplied data buffer address. The built-in MPI datatypes have typemaps of only one entry with an offset of0, e.g.:NO datatype exists in MPI, that can make it dererence a pointer and take the value that it points to instead of the pointer value itself.
This code creates an MPI datatype that has the following type map (assuming
intis 4 bytes):When supplied as the type argument to
MPI_Send, it would instruct the MPI library to take one byte from the beginning of the data buffer and then to take the integer value, located at 16 bytes past the beginning of the data buffer. In total the message would be 5 bytes long, although the span of the buffer area would be 20 bytes.This code, taken from the answer of Greg Inozemtsev, creates a datatype with the following type map (assuming 32-bit machine with 32-bit wide pointes and zero padding):
The number of
(char, x)typesigs is equal tosizeof(void *)(4 by assumption). If used as a datatype, this would take 4 bytes from the beginning of the buffer (i.e. the value of the pointer, the address, not the actual int it is pointing to!) and then it would take an integer from 4 bytes after the beginning, i.e. the value of thetagfield in the structure. Once again, you would be sending the address of the pointer and not the data that this pointer points to.The difference betwen
MPI_CHARandMPI_BYTEis that no type conversion is applied to data of typeMPI_BYTE. This is only relevant when running MPI codes in heterogenous environments. WithMPI_CHARthe library might perform data conversion, e.g. convert each character from ASCII to EBCDIC character sets and vice versa. UsingMPI_CHARin this case is erroneous, but sending pointers in a heterogeneous environment is even more erroneous, so no worries 😉In the light of all this, if I were you, I would consider the solution that suszterpatt has proposed.
For the explicit data marshalling, there are two possible scenarios:
Scenario 1. Each data item, pointed to by
data.datais of constant size. In this case you can construct a structure datatype in the following way:Then use it like this:
You might also not free the temporary storage but rather reuse it for other instances of the
datastructure as well (since by presumption they are all pointing to data of the same size). The receiver would be:Scenario 2. Each data item might be of different size. This one is much more involved and hard to do in a portable way. If speed is not your greatest concern, then you might send the data in two distinct messages for maximum portability. MPI guarantees that order is preserved for messages sent with the same envelope
(source, destination, tag, communicator).You could also implement what suszterpatt proposed in the following way (given that your tags fit into the allowed range):