【问题标题】:Getting undesired behavior when sending-receiving messages using MPI使用 MPI 发送-接收消息时出现不良行为
【发布时间】:2019-04-14 17:21:14
【问题描述】:

我正在探索 C++ 中的 MPI,我想并行创建 Mandelbrot 集的图片。我正在使用ppm 格式。每个处理器构建自己的部分并将其发送回作为 MPI_CHAR 接收它的主进程。这是代码:

#include "mpi.h"
#include <iostream>
#include <string>
#include <fstream>
#include <complex>

using namespace std;

int mandelbrot(int x, int y, int width, int height, int max)  {
    complex<float> point((float) (y - height/2.0) * 4.0/width, (float) (x - width/2.0) * 4.0/width);
    complex<float> z(0, 0);
    unsigned int iteration = 0;

    while (abs(z) < 4 && iteration < max) {
           z = z * z + point;
           iteration++;
    }
    return iteration;
}

int main(int argc, char **argv) {
  int numprocs;
  int myid;
  int buff_size = 404270; // 200x200 
  char buff[buff_size];
  int i;

  MPI_Status stat;

  MPI_Init(&argc,&argv);
  MPI_Comm_size(MPI_COMM_WORLD,&numprocs);
  MPI_Comm_rank(MPI_COMM_WORLD,&myid);

  int width = 200, height = 200, max_iter = 1000;

  if (myid == 0) {

    ofstream image("mandel.ppm");
    image << "P3\n" << width << " " << height << " 255\n";

    for(i=1; i < numprocs; i++) {
      MPI_Probe(i, 0, MPI_COMM_WORLD, &stat);
      int length;
      MPI_Get_count(&stat, MPI_CHAR, &length);
      MPI_Recv(buff, length, MPI_CHAR, i, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
      image << buff;
    }

  } else {

    stringstream ss;
    // proc rank: 1, 2, ..., n
    int part = height/(numprocs-1), start = (myid - 1) * part, end = part * myid;
    printf("%d -> %d\n", start, end);

    for (int row = start; row < end; row++) {
        for (int col = 0; col < width; col++) {

            int iteration = mandelbrot(row, col, width, height, max_iter);

            if (row == start) ss << 255 << ' ' << 255 << ' ' << 255 << "\n";
            else if (iteration < max_iter) ss << iteration * 255 << ' ' << iteration * 20 << ' ' << iteration * 5 << "\n";
            else ss << 0 << ' ' << 0 << ' ' << 0 << "\n";
        }
    }

    printf("\n sizeof = %d\n", ss.str().length());
    MPI_Send(ss.str().c_str(), ss.str().length(), MPI_CHAR, 0, 0, MPI_COMM_WORLD);
  }

  MPI_Finalize();

  return 0;  
}

代码编译:

$ mpic++ -std=c++0x mpi.mandel.cpp -o mpi.mandel

运行 3 个进程(进程 main + 进程等级 1 和 2)

$ mpirun -np 3 ./mpi.mandel

使用 3、4 和 5 进程运行时产生的ppm 图片:

当超过3个进程尝试将MPI_CHAR元素发送到主进程时,似乎发送-接收的点对点通信正在混合结果。如何避免这种行为?

【问题讨论】:

    标签: mpi mpic++


    【解决方案1】:

    在创建与接收消息长度相同的缓冲区buff时有效:

    .
    .
       for (int i=1; i < numprocs; i++) {
          MPI_Probe(i, 0, MPI_COMM_WORLD, &stat);
          int length;
          MPI_Get_count(&stat, MPI_CHAR, &length);
          printf("\nfrom %d <<-- %d (stat.source=%d) Receiving %d chars\n", myid, i, stat.MPI_SOURCE, length);
          char buff[length + 1];
          MPI_Recv(buff, length, MPI_CHAR, i, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
          buff[length] = '\0';
          image << buff;
       }
    .
    .
    

    因此,我们不再需要开头的声明 int buff_size = 404270;char buff[buff_size];

    【讨论】:

    • 您检查buff_size 是否足够大?例如,在MPI_Get_count() 之后添加assert(length &lt;= buff_size)。注意如果buff_size 足够大,您可以简单地MPI_Recv(..., &amp;stat) 然后MPI_Get_count() 来检索消息长度。一种更类似于 MPI 的方法是让所有等级计算图像的一部分,然后在根等级上 MPI_Gatherv() 它。 FWIW,我相信根级别有办法知道在发布MPI_Recv()MPI_Gatherv()之前要接收的消息大小
    • 是的,我检查了缓冲区大小是否足够大,但我没有编写assert 指令。我认为问题在于'\0'image &lt;&lt; buff 时丢失了。我已经在响应中添加了代码。
    • 现在我正在尝试使用MPI_IsendMPI_Irecv 做同样的事情,但使用非阻塞消息。我还不确定如何知道非阻塞消息的消息大小。 MPI_Gatherv() 是非阻塞的吗?
    • MPI_Gatherv() 被阻止。如果我理解正确,消息大小为5 * width * (end - start)endstart 都依赖于heigthmy_idnum_procs。所有这些值在等级0 上都是已知的。如果没有,您可以MPI_Gather() 大小,然后MPI_Gatherv() 图像。作为替代方案,您可以考虑使用 MPI-IO 让所有等级并行写入其图像子集。无论如何,就性能而言,您的下一步应该是让排名0 像其他排名一样计算图像的子集。
    • 几乎5 * width * (end - start)ppm 文件的每一行(一个像素)并不总是相同的长度。有时是0 0 0,或255 255 255,或其他数字。整个消息根据计算像素的 Mandelbrot 地图的区域而有所不同。
    猜你喜欢
    • 2015-10-01
    • 1970-01-01
    • 2018-05-25
    • 2013-01-28
    • 2018-03-12
    • 2014-10-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多