Search code examples
craspberry-pimpiopenmpi

Correct method of MPI send/recv on an array of structs


I've been setting up a four-node mpi cluster with raspberry pis. As far as I can tell, I am down to one final major issue, and that is how to send an array of structs from each worker to the manager. I have cropped down the code to the below, but this could take a few tries, as I might have cropped too much. Albeit, I still get the same error (a seg fault, saying an address is not mapped), but sorry if there's a bit of back and fourth.


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

struct ticknrank
{
    char * ticker;
    int errors;
    int rank;
};

int main() //Designed for one master, three slaves
{

// i am under the impression the problem lies somewhere in this beginning section, before the commit.
    int my_id;
    MPI_Init(NULL,NULL);
    MPI_Comm_rank(MPI_COMM_WORLD, &my_id);
    MPI_Status status;

    MPI_Datatype types[3] = {MPI_CHAR,MPI_INT,MPI_INT};
    MPI_Datatype MPI_ticknrank, MPI_tmp;
    int blocklengths[3] ={8,1,1};
    MPI_Aint offsets[3];
    offsets[0] = offsetof(struct ticknrank,ticker);
    offsets[1] = offsetof(struct ticknrank,errors);
    offsets[2]= offsetof(struct ticknrank,rank);
    MPI_Aint lb, extent;
    MPI_Type_create_struct(3,blocklengths, offsets, types, &MPI_tmp);
    MPI_Type_get_extent(MPI_tmp, &lb, &extent);
    MPI_Type_create_resized(MPI_tmp, lb, extent, &MPI_ticknrank);
    MPI_Type_commit(&MPI_ticknrank);

//  NOTE: sizeof(ticknrank) = 12, while MPI_Type_size(ticknrank) = 16. Not sure what to do about that.

    if(my_id == 0) // meaning this process is a host job
    {
        //NOTE: NodethrRes and fou can be ommitted, I was just lazy and didn't wanna delete them
        //on my cluster.
        int length = 2;
        struct ticknrank * NodeTwoRes = (struct ticknrank *)malloc(length * sizeof(struct ticknrank));
        struct ticknrank * NodeThrRes = (struct ticknrank *)malloc(length * sizeof(struct ticknrank));
        struct ticknrank * NodeFouRes = (struct ticknrank *)malloc(length * sizeof(struct ticknrank));


        MPI_Recv(NodeTwoRes, length, MPI_ticknrank,1,MPI_ANY_TAG, MPI_COMM_WORLD, &status);
        MPI_Recv(NodeThrRes, length, MPI_ticknrank,2,MPI_ANY_TAG, MPI_COMM_WORLD, &status);
        MPI_Recv(NodeFouRes, length, MPI_ticknrank,3,MPI_ANY_TAG, MPI_COMM_WORLD, &status);

        printf("%s\n", NodeTwoRes[0].ticker);
        
    }
    else
    {
        int myLen = 2;
        struct ticknrank * results = malloc(myLen * sizeof(struct ticknrank));
        results[0].ticker = strdup("FIRST");
        results[0].rank = 4;
        results[0].errors = 7;
        results[1].ticker = strdup("SECON");
        results[1].rank = 3;
        results[1].errors = 15;
        MPI_Send(results,myLen,MPI_ticknrank,0,1,MPI_COMM_WORLD);
    }

    MPI_Type_free(&MPI_ticknrank);
    MPI_Finalize();

    return 0;
}



Solution

  • The C struct is a char * ticker (which is 4 bytes if you are running 32 bits), but the derived datatype is for a char ticker[8] which is indeed 8 bytes.

    If you want to send multiple struct ticknrank in one shot, then the data should be in contiguous memory, which means moving from char * ticker to char ticker[8], and replacing strdup() with strcpy() (and up to you to make sure there is no buffer overflow).