【问题标题】:MPI master process not waiting for other procs to calculateMPI 主进程不等待其他进程计算
【发布时间】:2018-04-02 04:02:54
【问题描述】:

您好,我是使用 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 添加一些已注释掉的语句,但似乎除了主 proc 之外的任何 proc 都没有给出结果,所以很明显我仍然做错了什么。这是一些示例输出:

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). 任何帮助将不胜感激!

【问题讨论】:

  • 您需要提供您未提供的功能,例如init_matrix,才能解决此问题。除此之外,您正在使用这个常量MASTER,但是在您的BCAST 中,您使用 0 作为根进程。坚持一个,以防它们不一样。更重要的是,您不会在所有进程上分配内存,而只是在根上。这可能是您的程序失败的原因之一。
  • 另外,这是一个矩阵向量乘法。我用静态数组和 typedef 而不是 malloc 解决了这个问题。发布您的整个代码,我们可能会确定问题出在哪里。在我使用 malloc 的问题中,我还在每个进程上分配数组。 Malloc 返回一个指向内存位置的指针。 MPI 不是共享内存协议。您的代码原则上可以在许多不同的节点上运行,这些节点是不同且独立的机器。指向其中一台机器上的内存位置的指针在其他机器上没有任何意义。
  • @atru 感谢您的回复!我已经添加了我的其余代码。作为响应,矩阵是随机生成的,因此只有主进程生成它们。而MASTER == 0 所以我可以互换使用它们并且在某些地方不一致。而且我也知道 B 是向量而不是矩阵,但我将它视为矩阵。我觉得没有必要包含其他方法,因为我知道它们工作正常,但我现在已经发布了。

标签: c mpi matrix-multiplication multiple-processes


【解决方案1】:

这是您的程序已解决问题:

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

就像在 cmets 中所说的那样,在这种情况下,您需要为所有进程上的所有矩阵分配内存。该过程与程序仅在根进程上执行的 A 和 B 的初始化不同。这里 A 是使用 malloc 分配的,而 C 是静态分配的,并以与 B 相同的方式进一步用作向量。这不是必需的,但似乎是一个更好的选择,因为 C 是一维数组并且本质上是相当于B。

B 像以前一样被广播到所有进程,但是程序使用 MASTER 而不是 0,所以当您更改 MASTER 的值时,所有出现的它也会发生变化。这通常是一种很好的编程习惯,它在新代码中的任何地方都得到了应用。

A 以一种简单但肯定比@Gilles Gouailardet 建议的效率低的方式广播 - 该程序只是单独广播 A 的每一行,

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

这与行优先排序以及 A 中第 k 行的这些 N 个元素是连续访问的事实有关。如果 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 函数打印在根目录上。

【讨论】:

  • 哦,我现在明白了很多...我需要在所有进程上分配 A 但只在主线程上初始化...我明白了!非常感谢您的清晰解释。
  • 很高兴解释 :) 您需要在每个进程上为 A 留出空间,这样当从根发送 A 时,您就有了存储 A 的空间——这将是一个更简单的想法。
【解决方案2】:

C 中,二维矩阵是指针数组并且这是行主要的,这意味着二维矩阵是指针数组,给定的指针指向一行(不是列)。

话虽如此,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 a dynamic 2d array

fwiw,我不明白你关于 C 数据的逻辑。 据我了解,这是一个简单的向量,所以像这样声明它并且不要使用指针(因为 MPI 无法正确处理它们)

【讨论】:

    猜你喜欢
    • 2014-11-08
    • 2020-09-21
    • 1970-01-01
    • 2017-04-07
    • 1970-01-01
    • 2015-12-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多