Question

i have an this input file .txt where there are sequences:

NAMEOFSEQUENCE1/SEQUENCE1
NAMEOFSEQUENCE2/SEQUENCE2
NAMEOFSEQUENCE3/SEQUENCE3

I done a struct:

typedef struct lane{
  char *name;
  char *sequence;
}lane;

and wrote this code:

int i=0;
lane* toSend    = malloc(sizeof(*toSend)*3);
while (fgets(line,strlen(line),fileinput) != NULL){
            //GETTING NAME AND SEQUENCE, LINE PER LINE
            char *tempName = malloc(strlen(line)-strlen(strstr(line,"\\"))+1);
            strncpy(tempName,line,strlen(line)-strlen(strstr(line,"\\")));
            tempName[strlen(line)-strlen(strstr(line,"\\"))] = '\0';
            char *tempSequence = malloc(strlen(strstr(line,"\\")));
            strncpy(tempSequence,strstr(line,"\\")+1,strlen(strstr(line,"\\")));
            tempSequence[strlen(strstr(line,"\\"))-1] = '\0';

            //FILLING TOSEND
            toSend[i].name      = malloc(strlen(line)-strlen(strstr(line,"\\"))+1);
            toSend[i].sequence  = malloc(strlen(strstr(line,"\\")));
            howmuchbyte += strlen(line)+1;
            strcpy(toSend[i].name,tempName);
            strcpy(toSend[i].sequence,tempSequence);
            i++;
}

I put in "line" variable one line of the file at a time and put in tempName the NAMEOFSEQUENCEX of each sequence and put in tempSequence the SEQUENCEX.

At this point all is working. If i Print the "toSend" vector i get the right value! So i wrote this:

MPI_Send(toSend, 3, MPI_BYTE, 1, tag, MPI_COMM_WORLD);

so i send from process with rank 0 to process with rank 1 (i have 2 process). I put 3 as count parameter because i have 3 elements in the array.

The process with rank 1 do this:

lane* received  = malloc(sizeof(*received)*3);
MPI_Recv(received, 3, MPI_BYTE, 0, tag, MPI_COMM_WORLD, &status);

If i do this on process with rank 1:

printf("%s",received[0].name);

i get a segmentation fault. What do i wrong?

Was it helpful?

Solution

This could work if your strings are of fixed maximum length, e.g.

typedef struct lane{
   char name[NAME_MAX];
   char sequence[SEQ_MAX];
 }lane;

In that case you can simply define a new MPI structured datatype and use it in both send and receive operations:

int blens[2] = { NAME_MAX, SEQ_MAX };
int disps[2] = { offsetof(lane, name), offsetof(lane, sequence) };
int oldtypes[2] = { MPI_CHAR, MPI_CHAR };
MPI_Datatype type_lane;

MPI_Type_create_struct(2, blens, disps, oldtypes, &type_lane);
MPI_Type_commit(&type_lane);

lane aLane[2];

if (rank == 0)
{
   strncpy(aLane[0].name, NAME_MAX, "foo1");
   strncpy(aLane[0].sequence, SEQ_MAX, "bar");

   strncpy(aLane[1].name, NAME_MAX, "foo2");
   strncpy(aLane[1].sequence, SEQ_MAX, "baz");

   MPI_Send(aLane, 2, type_lane, 1, tag, MPI_COMM_WORLD);
}
else if (rank == 1)
{
   MPI_Recv(aLane, 2, type_lane, 0, tag, MPI_COMM_WORLD, &status);
}

If your strings are of strongly varying lengths, then you should serialize each structure before sending it. The most simple thing that comes to my mind is to just concatenate all name/sequence pairs, separated by a NUL:

int total_length = 0;

for (i = 0; i < num_to_send; i++)
   total_length += strlen(toSend[i].name) + strlen(toSend[i].sequence) + 2;

char *bigstr = malloc(total_length);
char *cur = bigstr;

for (i = 0; i < num_to_send; i++)
{
   strcpy(cur, toSend[i].name);
   cur += strlen(toSend[i].name) + 1;
   strcpy(cur, toSend[i].sequence);
   cur += strlen(toSend[i].sequence) + 1;
}

Now the content of bigstr is as follows:

toSend[0].name \0 toSend[0].sequence \0 toSend[1].name \0 toSend[1].sequence \0 ....

The sender can now send the string and dispose it:

MPI_Send(bigstr, total_length, MPI_CHAR, 1, tag, MPI_COMM_WORLD);

The receiver has to be prepared to receive a message of unknown size. That can be achieved by first calling MPI_Probe and then MPI_Recv:

MPI_Status;

MPI_Probe(1, tag, MPI_COMM_WORLD, &status);
MPI_Get_count(&status, MPI_CHAR, &total_length);

char *bigstr = malloc(total_length);
MPI_Recv(bigstr, total_length, MPI_CHAR, 1, tag, MPI_COMM_WORLD, MPI_STATUS_IGNORE);

Now comes the part where you have to deserialise the big string into a collection of tuples. One way to do it is to first walk it and count the number of NULs and divide them by two. Then walk it again and copy each item to the corresponding location:

int num_structs = 0;

for (i = 0; i < total_length; i++)
   if (bigstr[i] == '\0') num_structs++;
num_structs /= 2;

lane *lanes = malloc(num_structs * sizeof(lane));
char *cur = bigstr;

for (i = 0; i < num_structs; i++)
{
   lanes[i].name = strdup(cur);
   cur += strlen(cur);
   lanes[i].sequence = strdup(cur);
   cur += strlen(cur);
}

Another possible solution would be to utilise MPI_Pack and MPI_Unpack instead.

OTHER TIPS

You can't just send raw pointers across an MPI channel. Well, you can, but the process that receives a pointer from some other process won't have the same data at the memory location (in their own memory space) referred to by the pointer.

If you want to send a variably-sized array (such as a string) through MPI, you'll need to test for the size of the array first, and allocate an appropriately sized array on the receiving end.

For more info: How to send and receive string using MPI

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top