MPI_Bcast динамический 2d массив - PullRequest
9 голосов
/ 24 февраля 2011

Я пытаюсь передать динамический 2d массив с bcast во все ряды. У меня есть следующий код.

#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();
}

По какой-то причине я не могу понять, что у меня ошибка сегментации. Кто-нибудь знает, в чем проблема?

Ответы [ 5 ]

26 голосов
/ 24 февраля 2011

Здесь есть три проблемы - одна связана с распределением, одна связана с тем, где он распределен, а другая - с тем, как работает MPI, и ни один из других ответов не затрагивает все из них.

Первая и самая серьезная проблемаэто где вещи распределяются.Как правильно указал @davidb, в нынешнем виде вы выделяете память только для задачи ноль, поэтому у других задач нет памяти для приема трансляции.

Что касается 2d-распределения в C в целом,ваш код почти точноВ этом блоке кода:

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

единственная реальная проблема заключается в том, что первый malloc должен иметь 10 float указателей , а не float:

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

Thisбыло отмечено @eznme.Первый способ может фактически работать в зависимости от того, с какой моделью памяти вы компилируете / связываете и т. Д., И почти наверняка будет работать на 32-битных ОС / машинах - но только потому, что он работает, не всегда означает, что он прав:)

Теперь, последняя проблема в том, что вы объявили совершенно хороший 2d массив в C, но это не то, чего ожидает MPI.Когда вы делаете этот вызов

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

, вы говорите MPI отправить 100 непрерывных чисел с плавающей точкой, на которые указывает array.Вы замечаете, что у библиотечной подпрограммы нет способа узнать, является ли массив указателем на начало массива 2d, 3d или 12d, или каковы отдельные измерения;он не знает, должен ли он следовать указателям, и если он это сделал, он не будет знать, сколько следует следовать.

Таким образом, вы хотите отправить указатель с плавающей точкой на 100 смежных с плавающей точкой - и в обычномC способ выделения псевдо-многомерных массивов (*), вы не обязательно имеете это.Вы не обязательно знаете, как далеко 2-й ряд от 1-го ряда в этом макете - или даже в каком направлении.Итак, что вы действительно хотите сделать, это что-то вроде этого:

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;
}

Таким образом, и только так, вы гарантируете, что память непрерывна.Затем вы можете сделать

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);

Обратите внимание, что для произвольного расположения данных вы все равно можете сделать Bcast, определив тип данных MPI, который описывает, как массив 2d фактически заложенв памяти;но это проще и ближе к тому, что вы, вероятно, на самом деле хотите.

(*) реальная проблема здесь в том, что языки на основе C и C не имеют реальных массивов multi-d в качестве объектов первого класса - чтоотлично подходит для языка системного программирования, но неумолимо раздражает при научном программировании.

6 голосов
/ 24 февраля 2011

array должно быть 100, а не 10, поскольку вы назначаете 10 поплавков для каждой строки.Ответ Джекна содержит код для этого.

Однако в любом процессе, кроме ранга 0, указатель на массив будет null.Вам нужно инициализировать массив во всех процессах, а затем заполнить массив в корне.

Вы можете просто переместить код malloc из блока if (rank ==0), и он должен работать так, как вы ожидаете.

2 голосов
/ 24 февраля 2011

Массив должен быть 100, а не 10.

array = (float **)malloc(100*sizeof(float)); 
1 голос
/ 24 февраля 2011

Вы, вероятно, хотите изменить первый malloc на

malloc(10*sizeof(void*)) 

потому что в массиве хранятся указатели и плавающие числа вместо целых:

array[i][j]=1.0;
0 голосов
/ 11 мая 2013

, если вы хотите выделить массив с 10 * 10, ваш код:

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

должно быть

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