Основной процесс MPI не ожидает вычислений других процедур

Здравствуйте, я новичок в программировании с MPI. Я пытаюсь умножить две матрицы вместе (матрица NxN (A) и матрица Nx1 (B)), чтобы получить результирующую матрицу C (Nx1). Предполагается, что каждый процесс вычисляет строку (элемент) в матрице C, однако только процесс 0 (мой основной процесс) вычисляется правильно, так как он, похоже, не ожидает завершения других процессов. Я также не уверен, корректно ли отправляют результаты неосновные процессы (или даже нуждаются ли они в этом?). Вот мой код:

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

#define PRINT_VECS 1
#define MAX_RAND 100
#define MASTER 0
#define COLUMNS_B 1
#define N 4

void init_vec(int *vec, int len);
void print_vec(const char *label, int *vec, int len);

void init_vec(int *vec, int len)
{
    int i;
    for (i = 0; i < len; i++)
    {
        vec[i] = rand() % MAX_RAND;
    }    
}

void print_vec(const char *label, int *vec, int len)
{
#if PRINT_VECS
    printf("%s", label);     
    int i;
    for (i = 0; i < len; i++)
    {
        printf("%d ", vec[i]);
    }
    printf("\n\n");
#endif
}

void init_matrix(int** matrix, int rows, int cols)
{
    int i,j;
    for (i = 0; i < rows; i++)
    {
        for (j = 0; j < cols; j++)
        {
            matrix[i][j] = rand() % MAX_RAND;
        }
    }
}

void print_matrix(int** matrix, int rows, int cols)
{
    int i;
    for (i = 0; i < rows; i++)
    {
        printf("|");
        int j;
        for (j = 0; j < cols; j++)
        {
            printf("%d ", matrix[i][j]);
        }
        printf("|\n");
    }
}   


int main(int argc, char *argv[])
{
    int my_rank;
    int num_procs;
    MPI_Init(&argc, &argv);
    MPI_Comm_rank(MPI_COMM_WORLD, &my_rank); //grab this process's rank
    MPI_Comm_size(MPI_COMM_WORLD, &num_procs); //grab the total num of processes

    int results[num_procs]; // used to store the partial sum computed
    int rows, cols, colsB;
    rows = N;
    cols = N;
    colsB = COLUMNS_B;
    int **A; // N x N Matrix
    int B[N]; // N x 1 Matrix
    int **C; // N x 1 Matrix

    double start_time; // use these for timing
    double stop_time;

    if (my_rank == MASTER)
    {
        printf("Number of processes: %d\n", num_procs);
        printf("N: %d\n", N);
        srand(time(NULL));

        // init A
        int i;
        A = malloc(rows * sizeof *A);
        for (i = 0; i < rows; i++)
        {
            A[i] = malloc(cols * sizeof *A[i]);
        }
        init_matrix(A, rows, cols);
        printf("Matrix A:\n");
        print_matrix(A, rows, cols);

        // init B
        init_vec(B, N);
        print_vec("Matrix B:\n", B, N);

        // init C
        C = malloc(rows * sizeof *C);
        for (i = 0; i < rows; i++)
        {
            C[i] = malloc(colsB * sizeof *C[i]);
        }

        start_time = MPI_Wtime();
    }

    MPI_Bcast(B, N, MPI_INT, 0, MPI_COMM_WORLD);
    //MPI_Bcast(A, N, MPI_INT, 0, MPI_COMM_WORLD);

    int row = my_rank;

    int my_sum = 0;

    int i;
    if (my_rank < N)
    {
        for (i = 0; i < N; i++)
        {
            int num = A[row][i] * B[i];
            my_sum = my_sum + num;
        }



    C[row] = &my_sum;
    printf("HAI FROM PROCESS %d! I will calculate row %d. My calculation: %d\n", my_rank, row, my_sum);
}

//MPI_Gather(&C, 1, MPI_INT, results, 1, MPI_INT, 0, MPI_COMM_WORLD);

if (my_rank == MASTER)
{
    stop_time = MPI_Wtime();
    printf("\nMatrix C:\n");
    print_matrix(C, rows, colsB);
    printf("Total time (sec): %f\n", stop_time - start_time);
}

MPI_Finalize();

return EXIT_SUCCESS;;

}

Я почти уверен, что я близко, но я что-то упускаю. Я попытался добавить некоторые из этих закомментированных утверждений, передавая матрицу A и / или вызывая MPI_GATHER, но, похоже, ничто не дает результатов от каких-либо процедур, кроме основного, поэтому ясно, что я все еще делаю что-то не так. Вот пример вывода:

Number of processes: 28
N: 4
Matrix A:
|11 30 69 24 |
|83 38 66 71 |
|68 71 27 33 |
|58 5 50 10 |
Matrix B:
1 58 81 44

HAI FROM PROCESS 0! I will calculate row 0. My calculation: 8396

Matrix C:
|8396 |
|-2107258888 |
|-2107258920 |
|-2107258888 |
Total time (sec): 0.000078

Таким образом, proc 0 вычисляется правильно, однако мое сообщение об ошибке состоит в том, что proc 1 получает ошибку сегмента, и я не могу понять, почему. Я получаю ошибку:mpirun noticed that process rank 1 with PID 0 exited on signal 11 (Segmentation fault). Любая помощь будет принята с благодарностью!

2 ответа

Решение

Это ваша программа с исправленными проблемами:

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

#define PRINT_VECS 1
#define MAX_RAND 100
#define MASTER 0
#define COLUMNS_B 1
#define N 4

void init_vec(int *vec, int len);
void print_vec(const char *label, int *vec, int len);

void init_vec(int *vec, int len)
{
    int i;
    for (i = 0; i < len; i++)
    {
        vec[i] = rand() % MAX_RAND;
    }
}

void print_vec(const char *label, int *vec, int len)
{
#if PRINT_VECS
    printf("%s", label);
    int i;
    for (i = 0; i < len; i++)
    {
        printf("%d ", vec[i]);
    }
    printf("\n\n");
#endif
}

void init_matrix(int** matrix, int rows, int cols)
{
    int i,j;
    for (i = 0; i < rows; i++)
    {
        for (j = 0; j < cols; j++)
        {
            matrix[i][j] = rand() % MAX_RAND;
        }
    }
}

void print_matrix(int** matrix, int rows, int cols)
{
    int i;
    for (i = 0; i < rows; i++)
    {
        printf("|");
        int j;
        for (j = 0; j < cols; j++)
        {
            printf("%d ", matrix[i][j]);
        }
        printf("|\n");
    }
}


int main(int argc, char *argv[])
{
    int my_rank;
    int num_procs;
    MPI_Init(&argc, &argv);
    MPI_Comm_rank(MPI_COMM_WORLD, &my_rank); //grab this process's rank
    MPI_Comm_size(MPI_COMM_WORLD, &num_procs); //grab the total num of processes

    int results[num_procs]; // used to store the partial sum computed
    int rows, cols, colsB, k;
    rows = N;
    cols = N;
    colsB = COLUMNS_B;
    int **A; // N x N Matrix
    int B[N]; // N x 1 Matrix
    int C[N]; // N x 1 Matrix

    // Allocate memory for the NxN matrix on all processes
    A = (int**) malloc(N * sizeof(int*));
    for(k=0;k<N;k++)
        A[k]= (int*) malloc(N * sizeof(int));

    double start_time; // use these for timing
    double stop_time;

    if (my_rank == MASTER)
    {
        printf("Number of processes: %d\n", num_procs);
        printf("N: %d\n", N);
        srand(time(NULL));

        // Initilize arrays on root only
        init_matrix(A, rows, cols);
        printf("Matrix A:\n");
        print_matrix(A, rows, cols);

        init_vec(B, N);
        print_vec("Matrix B:\n", B, N);

        start_time = MPI_Wtime();
    }

    // Be consistent with names vs. values to avoid bugs
    MPI_Bcast(B, N, MPI_INT, MASTER, MPI_COMM_WORLD);

    for (k=0; k<N; k++)
        MPI_Bcast(&(A[k][0]), N, MPI_INT, MASTER, MPI_COMM_WORLD);

    int row = my_rank;  
    int my_sum = 0;

    int i,num;
    if (my_rank < N)
    {
        for (i = 0; i < N; i++)
        {
            num = A[row][i] * B[i];
            my_sum = my_sum + num;
        }

        C[row] = my_sum;
        printf("HAI FROM PROCESS %d! I will calculate row %d. My calculation: %d\n", my_rank, row, my_sum);
   }

    MPI_Gather(&C[row], 1, MPI_INT, &C[row], 1, MPI_INT, MASTER, MPI_COMM_WORLD);

    if (my_rank == MASTER)
    {
        stop_time = MPI_Wtime();
        print_vec("Matrix C:\n", C, N);
        printf("Total time (sec): %f\n", stop_time - start_time);
    }

    // Free matrix A
    for(k=0;k<N;k++)
        free(A[k]);
    free(A);

    MPI_Finalize();

    return EXIT_SUCCESS;
}

Как сказано в комментариях, в этом случае вам нужно выделить память для всех ваших матриц на всех ваших процессах. Этот процесс отличается от инициализации A и B, которую программа выполняет только для корневого процесса. Здесь А выделяется с помощью mallocв то время как C выделяется статически и далее используется в качестве вектора таким же образом, как и B. Это не является необходимым, но кажется лучшим выбором, поскольку C является одномерным массивом и по своей сути эквивалентен B.

B передается на весь процесс, как и раньше, но вместо 0 программа использует MASTER поэтому, когда вы случайно измените значение MASTER все случаи этого также изменяются. Как правило, это хорошая практика программирования, и она применяется везде в новом коде.

А транслируется простым, но, безусловно, менее эффективным способом, чем предлагал @Gilles Gouailardet - программа просто транслирует каждую строку А отдельно,

for (k=0; k<N; k++)
    MPI_Bcast(&(A[k][0]), N, MPI_INT, MASTER, MPI_COMM_WORLD);

Это связано с упорядочением основных рядов и тем фактом, что к этим N элементам k-й строки в A обращаются непрерывно. Это потерпит неудачу, если A будет отправлен столбцами.

Оставшиеся изменения присваивают значение my_sum а не указатель на это C[row], C[row] = my_sum; и операция сбора:

MPI_Gather(&C[row], 1, MPI_INT, &C[row], 
       1, MPI_INT, MASTER, MPI_COMM_WORLD);

Здесь каждый процесс отправляет свое значение C[row] к C[row] на корневой процесс. C напечатан на корне с помощью print_vec функция.

В C2D-матрица - это массив указателей, и это основная строка, что означает, что 2D-матрица - это массив указателей, а данный указатель указывает на строку (ане на столбец).

Что, как говорится, MPI ожидает, что строки находятся в последовательной памяти, и, следовательно, массив указателей не подходит для MPI.

Статический массив хорошо подходит, но если вам нужен динамический массив, вам нужно выделить матрицу "все сразу", а затем вручную построить массив указателей.

Таким образом, вы можете заменить

    A = malloc(rows * sizeof *A);
    for (i = 0; i < rows; i++)
    {
        A[i] = malloc(cols * sizeof *A[i]);
    }

с

    A = (int **)malloc(cols * sizeof(int *);
    A[0] = (int *)malloc(cols * rows * sizeof(int));
    for (i = 1; i < cols; i++)
    {
        A[i] = A[i-1] + rows;
    }

и тогда вы сможете транслировать свою матрицу с

MPI_Bcast(A[0], cols*rows, MPI_INT, 0, MPI_COMM_WORLD);

Пожалуйста, обратитесь к MPI_Bcast динамический 2d массив для длинного объяснения

я не понимаю твоей логики насчет C данные. насколько я понимаю, это простой вектор, поэтому объявляйте его вот так и не играйте с указателями (поскольку MPI не будет обрабатывать их правильно)

Другие вопросы по тегам