【问题标题】:MPI Matrix Multiplication with scatter gather具有分散聚集的 MPI 矩阵乘法
【发布时间】:2012-09-09 07:23:11
【问题描述】:

我正在尝试在 C 中使用 MPI 进行矩阵乘法,我们必须做一个顺序版本和一个并行版本。我的并行版本没有给出正确的答案,我不知道为什么。我认为我没有向流程发送正确的通信,但我不能确定。教授只是查看了不同的发送/接收/收集等消息,但并没有真正深入细节......我看过很多不同的例子,但没有一个完整的,也没有使用分散/收集的。如果有人可以查看我的代码并告诉我是否有任何内容出现在他们身上,我将不胜感激。我很确定我的问题在于分散/收集消息或 c 矩阵的实际计算。

#define N 512

#include <stdio.h>
#include <math.h>
#include <sys/time.h>
#include <stdlib.h>
#include <stddef.h>
#include "mpi.h"


print_results(char *prompt, float a[N][N]);

int main(int argc, char *argv[])
{
    int i, j, k, rank, size, tag = 99, blksz, sum = 0;
    float a[N][N], b[N][N], c[N][N];
    char *usage = "Usage: %s file\n";
    FILE *fd;
    double elapsed_time, start_time, end_time;
    struct timeval tv1, tv2;

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

    if (argc < 2) {
            fprintf (stderr, usage, argv[0]);
            return -1;
    }

    if ((fd = fopen (argv[1], "r")) == NULL) {
            fprintf (stderr, "%s: Cannot open file %s for reading.\n",
                                    argv[0], argv[1]);
            fprintf (stderr, usage, argv[0]);
            return -1;
    }

    for (i = 0; i < N; i++)
            for (j = 0; j < N; j++)
                    fscanf (fd, "%f", &a[i][j]);

    for (i = 0; i < N; i++)
            for (j = 0; j < N; j++)
                    fscanf (fd, "%f", &b[i][j]);

    MPI_Barrier(MPI_COMM_WORLD);

    gettimeofday(&tv1, NULL);

    MPI_Scatter(a, N*N/size, MPI_INT, a, N*N/size, MPI_INT, 0,
                                                    MPI_COMM_WORLD);
    MPI_Bcast(b, N*N, MPI_INT, 0, MPI_COMM_WORLD);


    if (rank != 0) {
            for (i = 0; i < N; i++)
            {
                    for (j = 0; j < N; j++)
                    {
                            for (k = 0; k < N; k++)
                            {
                                    sum = sum + a[i][k] * b[k][j];
                            }
                            c[i][j] = sum;
                            sum = 0;
                    }
            }
    }

    MPI_Gather(c, N*N/size, MPI_INT, c, N*N/size, MPI_INT, 0,
                                                     MPI_COMM_WORLD);
    MPI_Finalize();

    gettimeofday(&tv2, NULL);

    elapsed_time = (tv2.tv_sec - tv1.tv_sec) + ((tv2.tv_usec - tv1.tv_usec)/1000000.0);

    printf ("elapsed_time=\t%lf (seconds)\n", elapsed_time);

    print_results("C = ", c);
}

print_results(char *prompt, float a[N][N])
{
    int i, j;

    printf ("\n\n%s\n", prompt);
    for (i = 0; i < N; i++) {
            for (j = 0; j < N; j++) {
                    printf(" %.2f", a[i][j]);
            }
            printf ("\n");
    }
    printf ("\n\n");
}

更新部分代码:

for (i=0;i<size; i++)
            {
                    if (rank == i)
                    {
                            for (i = rank*(N/size); i < (rank*(N/size)+(N/size)); i++)
                            {
                                    for (j = rank*(N/size); j < (rank*(N/size)+(N/size)); j++)
                                    {
                                            for (k = rank*N; k < rank*N+N; k++)
                                            {
                                                    sum = sum + a[i][k] * b[k][j];
                                            }
                                            c[i][j] = sum;
                                            sum = 0;
                                    }
                            }
                    }
            }

【问题讨论】:

  • 跳出来的一件事:根应该在分散和聚集中为recvbuf 提供魔法值MPI_IN_PLACE。仅仅为sendbufrecvbuf 提供相同的指针在技术上违反了规范。此外,MPI_Finalize 可能需要很长时间。你可能不应该计时。
  • 抱歉,MPI_IN_PLACE 应在MPI_Gather 中为sendbuf 提供。此外,根参与分散和聚集,但随后您将其排除在计算之外。它应该参与,就像其他队伍一样。
  • 除了 Greg Inozemtsev 和 Francesco 已经发现的内容之外,您的计算内核循环遍历整个矩阵 a,而不仅仅是驻留在当前等级内存中的部分。 i 循环的范围应相应限制。 MPI 还提供了MPI_Wtime() 计时器功能,它与gettimeofday 具有相同的精度,甚至更高(因为 MPI 标准建议实现者使用可用的最高分辨率计时器)。

标签: c mpi matrix-multiplication scatter


【解决方案1】:

您的代码中的第一个问题是size 可能不会除以N。这意味着散射size 长度为N*N/size 的数据包不一定发送整个矩阵。这可能是最难做到的一点。

正如 Greg Inozemtsev 指出的那样,第二个问题是您将进程 0 从计算中排除,尽管它负责矩阵的一部分。

还有一个问题是所有的 I/O 操作(开始读取系数,最后输出结果)只能由进程 0 完成。

另一方面,您应该在前向声明和定义中指定 print_result 函数的返回类型(在这种情况下为 void)。

【讨论】:

  • 我认为现在唯一让我感到困惑的是如何让每个处理器只在它们负责的矩阵部分上工作......
  • 想想你正在并行化原始版本中的哪个循环。您已经在所有进程中分配了矩阵行,这意味着您正在通过i 并行化循环。现在每个进程只需要在其N/size 行上工作。 (同样,这仅在 size 除以 N 时才有效。)
  • 嗯,N 被定义为 512,我们只使用 1、4、8、16 和 32 个处理器,所以它总是偶数。我在接受您的建议后添加了一些代码(我将其放在上面的原始帖子中),我以为我已经弄清楚了,但是每当我使用多个处理器运行它时,我都会遇到段错误“mpirun 注意到该进程在 PID 中排名 2节点 compute-1-2.local 上的 30215 在信号 11 上退出(分段错误)"
  • 您必须准确了解 MPI_Scatter 的作用:它在进程 0 上获取原始的大向量,将其拆分,然后将每个块发送到另一个进程。每个接收进程只保存它的块,索引从 0 开始。因此所有进程运行完全相同的 for 循环,i 范围从 0 到 N/size
猜你喜欢
  • 1970-01-01
  • 2011-06-09
  • 2013-12-23
  • 2018-09-08
  • 2017-10-30
  • 2011-10-15
  • 2012-11-04
  • 2014-04-21
  • 2015-04-03
相关资源
最近更新 更多