MPI_Bcast a dynamic 2d array
Asked Answered
M

5

10

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?

Moselle answered 24/2, 2011 at 12:45 Comment(1)
@davidb and @jackn: guys he wants an array with pointers to 10 arrays (those arrays shall then contain floats), if you tell him to malloc 100*sizeof(float) you should also tell him how to set the pointers for the row-major or column-major ordering.Apostate
A
7

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.

Ause answered 24/2, 2011 at 13:0 Comment(0)
A
29

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 .

Arbiter answered 24/2, 2011 at 16:29 Comment(1)
Alternatively, one could use a 1D buffer-array and re-construct the n-dimensional array from the bcasted bufferPapilla
A
7

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.

Ause answered 24/2, 2011 at 13:0 Comment(0)
D
2

Array should be 100, not 10.

array = (float **)malloc(100*sizeof(float)); 
Debit answered 24/2, 2011 at 12:51 Comment(2)
You mean that if i want a 2d array of 10x10 i have to allocate an array of 100x1? I can access this like a 2d array then?Moselle
Yes. And get rid of the for(i=0;i<10;i++) array[i] = (float *)malloc(10*sizeof(float)); A segmentation fault occurs when attempting to access a memory location that is not allowed.Debit
A
1

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;
Apostate answered 24/2, 2011 at 12:59 Comment(0)
M
0

if you want to allocate an array with 10*10, your code:

array = (float **)malloc(10*sizeof(float))

should be

array = (float **)malloc(10*sizeof(float*))
Marcusmarcy answered 10/5, 2013 at 22:11 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.