2

I'm trying to send a struct which has one of the member as a dynamic array, but this array doesn't seem to be sent properly. Any suggestion on how to do this?

This is what I have:

struct bar
{
    int a;
    int b;
    int* c;
};

void defineMPIType(MPI_Datatype* newType, int cLen, struct bar* msg)
{
    int blockLengths[3] = {1, 1, cLen};
    MPI_Datatype types[3] = {MPI_INT, MPI_INT, MPI_INT};
    MPI_Aint offsets[3];

    MPI_Aint addrB, addrC;
    MPI_Address(&(msg->b), &addrB);
    MPI_Address(msg->c, &addrC);

    offsets[0] = offsetof(struct bar, a);
    offsets[1] = offsetof(struct bar, b);
    offsets[2] = addrC - addrB; 

    MPI_Type_create_struct(3, blockLengths, offsets, types, newType);
    MPI_Type_commit(newType);
}

void main(int argc, char* argv[])
{   
    MPI_Init(&argc, &argv);
    int rank, p;
    MPI_Comm_rank(MPI_COMM_WORLD, &rank);
    MPI_Comm_size(MPI_COMM_WORLD, &p);

    int cLen = argv[0];    
    MPI_Datatype MPI_BAR_TYPE;

    struct bar* msg = malloc(sizeof(*msg)); 
    msg->c =  malloc(sizeof(int) * cLen);
    defineMPIType(&MPI_BAR_TYPE, cLen, msg);

    if (rank == 0)
    {
        msg->a = 1;
        msg->b = 2;
        for (int i = 0; i < cLen; ++i)
            msg->c[i] = i;
        MPI_Send(msg, 1, MPI_BAR_TYPE, 1, 111, MPI_COMM_WORLD); 
    }
    else
    {
        MPI_Status stat;        
        MPI_Recv(msg, 1, MPI_BAR_TYPE, 0, 111, MPI_COMM_WORLD, &stat);      
    }

    printf("Rank %d has c = [", rank);
    for (int i = 0; i < cLen; ++i)
        printf("%d, ", msg->c[i]);
    printf("]\n");

    free(msg);
    MPI_Type_free(&MPI_BAR_TYPE);
    MPI_Finalize();
}

Members a and b got sent properly, but c didn't.

ada lee
  • 41
  • 4

1 Answers1

3

There are a few issues in your code, even ignoring the issue of the type itself:

  • The first one is that you allocated memory for your c array only on process #0, then you (tried to) send this data to process #1. But process #1 didn't allocate any memory for storing the message. So even if the way the sending is done was correct, the code would have failed.
  • Names starting with MPI_ are reserved for the MPI library so you cannot use them as you wish. You have to find another name for your MPI_BAR_TYPE.
  • This line puzzles me somewhat: int cLen = argv[0]; I imagine you want to read from the command line the size of the array to allocate, in which case maybe that should read something like int clen = atoi(argv[1]); (forgetting about test for validity of this which would need to be properly handled...)
  • You only test if the process is of rank #0 or not, meaning that if for some reason you launched 3 processes, the process of rank #2 will wait forever for a message from process of rank #0 that will never arrive.
  • And finally the array itself: in your code there is a big confusion between the pointer c and the data pointed to by c. Your structure embeds the pointer, but not the memory pointed to. So you cannot map into an MPI structure the corresponding data... The most obvious reason is that from one call to the next (or from one process to the next), there is no guaranty that the offset from the address of the structure and the address of the data pointed to by c will be identical (and indeed, it is almost guaranteed it will be different). So you cannot reliably map them.

What you need to do for solving your problem is therefore to only transfer your 2 integers a and b in one go (possibly creating a MPI structure for transferring arrays of them if needed). Then you will transfer the memory pointed by c, which you would have allocated beforehand.

Your code could become for example:

#include <mpi.h>
#include <stdio.h>
#include <stdlib.h>

struct bar
{
    int a;
    int b;
    int* c;
};

void defineMPIType( MPI_Datatype* newType ) {
    struct bar tmp[2];
    MPI_Aint extent = &tmp[1] - &tmp[0];

    MPI_Type_create_resized( MPI_2INT, 0, extent, newType );
    MPI_Type_commit( newType );
}

int main( int argc, char* argv[] ) {   
    MPI_Init(&argc, &argv);
    int rank, p;
    MPI_Comm_rank( MPI_COMM_WORLD, &rank );
    MPI_Comm_size( MPI_COMM_WORLD, &p );

    int cLen = atoi( argv[1] );    
    MPI_Datatype Bar_type;
    defineMPIType( &Bar_type );

    struct bar msg; 
    msg.c = ( int* ) malloc( sizeof( int ) * cLen );
    if ( rank == 0 ) {
        msg.a = 1;
        msg.b = 2;
        for ( int i = 0; i < cLen; ++i ) {
            msg.c[i] = i;
        }
        MPI_Send( &msg, 1, Bar_type, 1, 111, MPI_COMM_WORLD );
        MPI_Send( msg.c, cLen, MPI_INT, 1, 222, MPI_COMM_WORLD );
    }
    else if ( rank == 1 ) {
        MPI_Recv( &msg, 1, Bar_type, 0, 111, MPI_COMM_WORLD, MPI_STATUS_IGNORE );
        MPI_Recv( msg.c, cLen, MPI_INT, 0, 222, MPI_COMM_WORLD, MPI_STATUS_IGNORE );
    }

    printf("Rank %d has a = %d, b = %d, c = [", rank, msg.a, msg.b );
    for ( int i = 0; i < cLen - 1; ++i ) {
         printf( "%d, ", msg.c[i] );
    }
    printf( "%d]\n", msg.c[cLen - 1] );

    free( msg.c );
    MPI_Type_free( &Bar_type );
    MPI_Finalize();

    return 0;
}

Which gives:

$ mpirun -n 2 ./a.out 3
Rank 0 has a = 1, b = 2, c = [0, 1, 2]
Rank 1 has a = 1, b = 2, c = [0, 1, 2]

Happy MPI coding.

Gilles
  • 9,269
  • 4
  • 34
  • 53
  • Thanks for the answer! Yes, my example was somewhat messed up because I tried to leave out some irrelevant stuff from the real code (e.g., the cLen is passed in from somewhere else, and not command line). Anyways, about your approach, yes, I've been to to do two MPI_Sends before, but I'm trying not to do so. I'm working on a project where we can't not assume order of delivery of messages. Also, I would love NOT to have match the a and b with the correct c if I send multiple messages. – ada lee Oct 04 '15 at 17:11
  • I"ve updated the code with a way to send in one msg, but it seems that the last element of the `c` array is the only one that doesn't get sent properly. – ada lee Oct 04 '15 at 17:35
  • This cannot work since, as I told you, you cannot assume a specific memory placement for the memory pointed by `c` relative to the pointer `c` itself... So it just won't work! If you cannot have a static memory allocation into your structure, you will have to transfer the structure and the data in two separated messages (unless you pack-up your data into a buffer manually, possibly with `MPI_Pack`, but I wouldn't recommend that). – Gilles Oct 04 '15 at 18:32