Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MPI_Bcast a dynamic 2d array

Tags:

c

mpi

I am trying to pass a dynamic 2d array with bcast to all ranks. I have the following code.

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

int main(int argc, char **argv)
{   
    float **array;
    int rank,size,i,j;

    MPI_Init(&argc,&argv);
    MPI_Comm_rank(MPI_COMM_WORLD,&rank);
    MPI_Comm_size(MPI_COMM_WORLD,&size);

    if(rank==0)
    {
        array = (float **)malloc(10*sizeof(float));
        for(i=0;i<10;i++)
            array[i] = (float *)malloc(10*sizeof(float));

        for(i=0;i<10;i++)
        for(j=0;j<10;j++)
            array[i][j]=i+j;
    }
    MPI_Bcast(array,10*10,MPI_FLOAT,0,MPI_COMM_WORLD);
    MPI_Finalize();
}

For some reason i cant understand i get segmentation fault. Anyone that knows what is the problem?

like image 455
faulpin Avatar asked Feb 24 '11 12:02

faulpin


4 Answers

There's three issues here - one involving allocations, one involving where it's allocated, and one involving how MPI works, and none of the other answers quite touch on all of them.

The first and most serious issue is where things are allocated. As correctly pointed out by @davidb, as it stands you're allocating memory only on task zero, so the other tasks have no memory in which to recieve the broadcast.

As for 2d allocations in C in general, your code is almost exactly right. In this block of code:

     array = (float **)malloc(10*sizeof(float));
     for(i=0;i<10;i++)
         array[i] = (float *)malloc(10*sizeof(float));

the only real issue is that the first malloc should be of 10 float pointers, not floats:

     array = (float **)malloc(10*sizeof(float *));
     for(i=0;i<10;i++)
         array[i] = (float *)malloc(10*sizeof(float));

This was pointed out by @eznme. The first way may actually work depending on what memory model you're compiling/linking with, etc, and will almost certainly work on 32-bit OSs/machines - but just because it works doesn't always mean it's right :)

Now, the final issue is that you've declared a perfectly good 2d array in C, but that's not what MPI is expecting. When you make this call

MPI_Bcast(array,10*10,MPI_FLOAT,0,MPI_COMM_WORLD);

you are telling MPI to send 100 contiguous floats pointed to by array. You notice that the library routine has no way of knowing if array is the pointer to the start of a 2d or 3d or 12d array , or what the individual dimensions are; it doesn't know if it has to follow pointers, and if it did, it wouldn't know how many to follow.

So you want to send a float pointer to 100 contiguous floats - and in the normal C way of allocating pseudo-multidimensional arrays(*), you don't necessarily have that. You don't necessarily know how far away the 2nd row is from the 1st row in this layout - or even in which direction. So what you really want to do is something like this:

int malloc2dfloat(float ***array, int n, int m) {

    /* allocate the n*m contiguous items */
    float *p = (float *)malloc(n*m*sizeof(float));
    if (!p) return -1;

    /* allocate the row pointers into the memory */
    (*array) = (float **)malloc(n*sizeof(float*));
    if (!(*array)) {
       free(p);
       return -1;
    }

    /* set up the pointers into the contiguous memory */
    for (int i=0; i<n; i++) 
       (*array)[i] = &(p[i*m]);

    return 0;
}

int free2dfloat(float ***array) {
    /* free the memory - the first element of the array is at the start */
    free(&((*array)[0][0]));

    /* free the pointers into the memory */
    free(*array);

    return 0;
}

This way, and only this way, you're guaranteed that the memory is contiguous. Then you can do

float **array;
/* ... */
malloc2dfloat(&array, 10, 10);
if (rank == 0) {
    for(i=0;i<10;i++)
         for(j=0;j<10;j++)
              array[i][j]=i+j;
}
MPI_Bcast(&(array[0][0]), 10*10, MPI_FLOAT, 0, MPI_COMM_WORLD);

Note that for an arbitrary arrangement of data, you could still do the Bcast by defining an MPI datatype which described how the 2d array is actually laid out in memory; but this is simpler and closer to what you likely actually want.

(*) the real issue here is that C and C-derived languages don't have real multi-d arrays as first class objects - which is fine for a systems programming language, but is irredemably irritating when doing scientific programming .

like image 174
Jonathan Dursi Avatar answered Nov 16 '22 22:11

Jonathan Dursi


The array should be 100 rather than 10, since you assign 10 floats per each row. JackN's answer has the code to do this.

However, on any process other than rank 0, the pointer to array will be null. You need to initialise array on all processes, then fill the array on the root.

You can just move the malloc code out of the if (rank ==0) block and it should work how you expect.

like image 36
davidb Avatar answered Nov 16 '22 22:11

davidb


Array should be 100, not 10.

array = (float **)malloc(100*sizeof(float)); 
like image 2
jacknad Avatar answered Nov 16 '22 21:11

jacknad


You probably want to change the first malloc to

malloc(10*sizeof(void*)) 

because the array stores pointers and store floats instead of ints:

array[i][j]=1.0;
like image 1
Bernd Elkemann Avatar answered Nov 16 '22 21:11

Bernd Elkemann