MPI从进程在没有更多工作时挂起 我有一个我希望并行化的串行C++程序。我知道MPI的基本知识,MPI_Send,MPI_Recv,等等。基本上,我有一个比数据处理算法运行得快得多的数据生成算法。目前它们是串联运行的,但我认为在根进程中运行数据生成,在从进程上完成数据处理,并从根进程向从进程发送一条消息,其中包含要处理的数据。这样,每个从机处理一个数据集,然后等待下一个数据集

MPI从进程在没有更多工作时挂起 我有一个我希望并行化的串行C++程序。我知道MPI的基本知识,MPI_Send,MPI_Recv,等等。基本上,我有一个比数据处理算法运行得快得多的数据生成算法。目前它们是串联运行的,但我认为在根进程中运行数据生成,在从进程上完成数据处理,并从根进程向从进程发送一条消息,其中包含要处理的数据。这样,每个从机处理一个数据集,然后等待下一个数据集,c++,parallel-processing,mpi,master-slave,C++,Parallel Processing,Mpi,Master Slave,问题是,一旦根进程生成数据,程序就会挂起,因为从属进程正在等待更多数据 这是问题的一个例子: #include "mpi.h" #include <cassert> #include <cstdio> class Generator { public: Generator(int min, int max) : value(min - 1), max(max) {} bool NextValue() { ++value; r

问题是,一旦根进程生成数据,程序就会挂起,因为从属进程正在等待更多数据

这是问题的一个例子:

#include "mpi.h"

#include <cassert>
#include <cstdio>

class Generator {
  public:
    Generator(int min, int max) : value(min - 1), max(max) {}
    bool NextValue() {
      ++value;
      return value < max;
    }
    int Value() { return value; }
  private:
    int value, max;

    Generator() {}
    Generator(const Generator &other) {}
    Generator &operator=(const Generator &other) { return *this; }
};

long fibonnaci(int n) {
  assert(n > 0);
  if (n == 1 || n == 2) return 1;
  return fibonnaci(n-1) + fibonnaci(n-2);
}

int main(int argc, char **argv) {
  MPI_Init(&argc, &argv);

  int rank, num_procs;
  MPI_Comm_rank(MPI_COMM_WORLD, &rank);
  MPI_Comm_size(MPI_COMM_WORLD, &num_procs);

  if (rank == 0) {
    Generator generator(1, 2 * num_procs);
    int proc = 1;
    while (generator.NextValue()) {
      int value = generator.Value();
      MPI_Send(&value, 1, MPI_INT, proc, 73, MPI_COMM_WORLD);
      printf("** Sent %d to process %d.\n", value, proc);
      proc = proc % (num_procs - 1) + 1;
    }
  } else {
    while (true) {
      int value;
      MPI_Status status;
      MPI_Recv(&value, 1, MPI_INT, 0, 73, MPI_COMM_WORLD, &status);
      printf("** Received %d from process %d.\n", value, status.MPI_SOURCE);
      printf("Process %d computed %d.\n", rank, fibonnaci(2 * (value + 10)));
    }
  }

  MPI_Finalize();
  return 0;
}
#包括“mpi.h”
#包括
#包括
类生成器{
公众:
生成器(int-min,int-max):值(min-1),最大值(max){
bool NextValue(){
++价值观;
返回值<最大值;
}
int Value(){return Value;}
私人:
int值,最大值;
生成器(){}
生成器(常量生成器和其他){}
生成器和运算符=(常量生成器和其他){return*this;}
};
长纤维纳奇(国际北){
断言(n>0);
如果(n==1 | | n==2)返回1;
返回fibonnaci(n-1)+fibonnaci(n-2);
}
int main(int argc,字符**argv){
MPI_Init(&argc,&argv);
int秩,num_进程;
MPI通信等级(MPI通信世界和等级);
MPI_通信大小(MPI_通信世界和数量进程);
如果(秩==0){
发电机(1,2*num_进程);
int proc=1;
while(generator.NextValue()){
int value=generator.value();
MPI_发送(&value,1,MPI_INT,proc,73,MPI_COMM_WORLD);
printf(“**已将%d发送到进程%d.\n”,值,进程);
proc=proc%(num_procs-1)+1;
}
}否则{
while(true){
int值;
MPI_状态;
MPI_Recv(&value,1,MPI_INT,0,73,MPI_COMM_WORLD,&status);
printf(“**从进程%d收到%d.\n”,值,状态.MPI\u源);
printf(“进程%d计算%d.\n”,秩,fibonnaci(2*(值+10));
}
}
MPI_Finalize();
返回0;
}
显然,上述并非所有内容都是“良好实践”,但这足以让人明白这一点

如果我从从属进程中删除
while(true)
,则当每个从属进程退出时,程序将退出。我希望程序只有在根进程完成其工作并且所有从属进程都处理了所有已发送的内容后才能退出

如果我知道将生成多少个数据集,我就可以让那么多进程运行,所有的东西都会很好地退出,但这里不是这样

有什么建议吗?API中有什么东西可以做到这一点吗?用更好的拓扑结构能更好地解决这个问题吗?
MPI\u Isend
MPI\u IRecv
会做得更好吗?我是MPI的新手,所以请容忍我


感谢

通常的做法是向所有工作进程发送一条带有特殊标记的空消息,该标记指示它们退出无限处理循环。假设这个标签是42。您可以在worker循环中执行类似的操作:

while (true) {
  int value;
  MPI_Status status;
  MPI_Recv(&value, 1, MPI_INT, 0, MPI_ANY_TAG, MPI_COMM_WORLD, &status);
  if (status.MPI_TAG == 42) {
    printf("Process %d exiting work loop.\n", rank);
    break;
  }
  printf("** Received %d from process %d.\n", value, status.MPI_SOURCE);
  printf("Process %d computed %d.\n", rank, fibonnaci(2 * (value + 10)));
}
在生成器循环之后,manager进程将执行以下操作:

for (int i = 1; i < num_procs; i++)
  MPI_Send(&i, 0, MPI_INT, i, 42, MPI_COMM_WORLD);
for(int i=1;i
关于你的下一个问题。在主进程中使用
MPI\u Isend()
将反序列化执行并提高性能。然而,事实是,您发送的消息非常小,并且这些消息通常是内部缓冲的(警告-取决于实现!),因此您的
MPI_Send()
实际上是非阻塞的,并且您已经有了非串行执行
MPI_Isend()
返回一个
MPI_请求
句柄,稍后需要处理该句柄。您可以使用
MPI\u wait()
MPI\u Waitall()
等待它完成,但也可以对其调用
MPI\u Request\u free()
,操作结束后它将自动释放。这通常是在您希望异步发送许多消息并且不关心何时完成发送的情况下完成的,但是这是一种不好的做法,因为有大量未完成的请求可能会消耗大量宝贵的内存。对于辅助进程,它们需要数据才能继续计算,因此不需要使用
MPI\u Irecv()


欢迎来到精彩的MPI编程世界

您的斐波那契实现是O(2^n)。你应该优化你的顺序算法,我知道。这不是我正在解决的实际问题,这只是我能想到的模拟问题的最简单的例子。也许我遗漏了一些东西,但在每个过程结束时,一个简单的障碍不能解决你的问题吗?@suszterpatt我认为不会。如果我理解你的建议,从进程将永远不会接受第二个(或更高)数据集。这正是我想要的。我甚至没有想过用这种方式使用标签。很酷。就小消息而言,这只是一个简化的示例,我将发送的实际消息要大得多,因此我必须使用
MPI_Send
MPI_Isend
来查看哪一个性能最好。谢谢,我非常感谢您的帮助。只需附带说明-
MPI\u Isend
发送消息的速度并不比
MPI\u send
快,但允许您覆盖通信和计算,从而隐藏前者的延迟。感谢您指出这一点。我知道这一点,但我没有足够的MPI经验来知道哪个更好。是时候做些实验了。谢谢你的建议。