用于OpenMP中进程间通信的线程



我有一个OpenMP并行程序,看起来像这样:

[...]
#pragma omp parallel
{
//initialize threads
#pragma omp for
for(...)
  {
  //Work is done here
  }
}

现在我添加MPI支持。我需要的是一个处理通信的线程,在我的情况下,它一直调用GatherAll,并填充/清空一个链表,以便从其他进程接收/发送数据。该线程应该发送/接收,直到设置了标志为止。所以现在这个例子中没有MPI的东西,我的问题是关于OpenMP中该例程的实现。我该如何实现这样一个线程?例如,我试图在这里引入一个指令:

[...]
int kill=0
#pragma omp parallel shared(kill)
{
//initialize threads
#pragma omp single nowait
 {
  while(!kill)
   send_receive(); 
 }
#pragma omp for
for(...)
  {
  //Work is done here
  }
kill=1
} 

但在这种情况下,程序会被卡住,因为for循环之后的隐式屏障会等待上面while循环中的线程。

谢谢你,鲁格尔米尼。

您可以尝试将nowait子句添加到single构造中:

编辑:响应第一条评论

如果您为OpenMP启用嵌套并行,您可能能够通过创建两个级别的并行来实现您想要的。在顶层中,有两个并行的部分,一个用于MPI通信,另一个用于本地计算。最后一部分本身可以并行化,这为您提供了第二级的并行化。只有执行此级别的线程才会受到其中屏障的影响。

#include <iostream>
#include <omp.h>
int main()
{
  int kill = 0;
#pragma omp parallel sections
  {
#pragma omp section
    {
      while (kill == 0){
        /* manage MPI communications */
      }
    }
#pragma omp section
    {
#pragma omp parallel
#pragma omp for
      for (int i = 0; i < 10000 ; ++i) {
        /* your workload */
      }
      kill = 1;
    }
  }
}

然而,您必须意识到,如果您至少没有两个线程,您的代码就会崩溃,这意味着您正在打破代码的顺序版本和并行版本应该做同样事情的假设。

将OpenMP内核封装在更全局的MPI通信方案中(可能使用异步通信将通信与计算重叠)会更干净。

您必须小心,因为您不能让MPI调用线程"跳过"omp-for循环;线程团队中的所有线程都必须通过for循环。

有几种方法可以做到这一点:使用嵌套的并行性和任务,您可以启动一个任务来进行消息传递,另一个任务则调用一个具有omp并行性的工作例程:

#include <mpi.h>
#include <omp.h>
#include <stdio.h>
void work(int rank) {
    const int n=14;
    #pragma omp parallel for
    for (int i=0; i<n; i++) {
        int tid = omp_get_thread_num();
        printf("%d:%d working on item %dn", rank, tid, i);
    }
}
void sendrecv(int rank, int sneighbour, int rneighbour, int *data) {
    const int tag=1;
    MPI_Sendrecv(&rank, 1, MPI_INT, sneighbour, tag,
                  data, 1, MPI_INT, rneighbour, tag,
                  MPI_COMM_WORLD, MPI_STATUS_IGNORE);
}
int main(int argc, char **argv) {
    int rank, size;
    int sneighbour;
    int rneighbour;
    int data;
    int got;
    MPI_Init_thread(&argc, &argv, MPI_THREAD_FUNNELED, &got);
    MPI_Comm_size(MPI_COMM_WORLD,&size);
    MPI_Comm_rank(MPI_COMM_WORLD,&rank);
    omp_set_nested(1);
    sneighbour = rank + 1;
    if (sneighbour >= size) sneighbour = 0;
    rneighbour = rank - 1;
    if (rneighbour <0 ) rneighbour = size-1;
    #pragma omp parallel 
    {
        #pragma omp single
        {
            #pragma omp task 
            {
                sendrecv(rank, sneighbour, rneighbour, &data);
                printf("Got data from %dn", data);
            }
            #pragma omp task
            work(rank);
        }
    }

    MPI_Finalize();
    return 0;
}

或者,您可以为循环schedule(dynamic)制作omp,这样其他线程就可以在主线程发送时获得一些空闲,而主线程可以在完成后获得一些工作:

#include <mpi.h>
#include <omp.h>
#include <stdio.h>
void sendrecv(int rank, int sneighbour, int rneighbour, int *data) {
    const int tag=1;
    MPI_Sendrecv(&rank, 1, MPI_INT, sneighbour, tag,
                  data, 1, MPI_INT, rneighbour, tag,
                  MPI_COMM_WORLD, MPI_STATUS_IGNORE);
}
int main(int argc, char **argv) {
    int rank, size;
    int sneighbour;
    int rneighbour;
    int data;
    int got;
    const int n=14;
    MPI_Init_thread(&argc, &argv, MPI_THREAD_FUNNELED, &got);
    MPI_Comm_size(MPI_COMM_WORLD,&size);
    MPI_Comm_rank(MPI_COMM_WORLD,&rank);
    omp_set_nested(1);
    sneighbour = rank + 1;
    if (sneighbour >= size) sneighbour = 0;
    rneighbour = rank - 1;
    if (rneighbour <0 ) rneighbour = size-1;
    #pragma omp parallel 
    {
            #pragma omp master 
            {
                sendrecv(rank, sneighbour, rneighbour, &data);
                printf("Got data from %dn", data);
            }
            #pragma omp for schedule(dynamic)
            for (int i=0; i<n; i++) {
                int tid = omp_get_thread_num();
                printf("%d:%d working on item %dn", rank, tid, i);
            }
    }

    MPI_Finalize();
    return 0;
}

嗯。如果您确实在程序中添加MPI"支持",那么您应该使用mpi_allgather,因为mpi_gatherall不存在。请注意,mpi_allgather是一个集体操作,也就是说通信器中的所有进程都调用它。你不能让一个进程收集数据,而其他进程做他们做的任何事情。你可以做的是使用MPI单面通信来实现你的想法;如果一个进程只读取其他进程的内存,这将有点棘手,但仅限于此。

我对你在MPI中使用"线程"一词感到困惑。我担心您混淆了OpenMP和MPI,其中一个变体被称为OpenMPI。尽管有这个名字,它和OpenMP的区别就像粉笔和奶酪一样。MPI程序是根据进程而不是线程编写的。典型的OpenMP实现确实使用了线程,尽管细节通常对程序员来说是隐藏的。

我对您正在尝试或似乎正在尝试在OpenMP代码中使用MPI印象深刻。这与我所做的工作正好相反,我也看到其他人在一些非常大的电脑上所做的。这种"混合"并行化的标准模式是编写调用OpenMP代码的MPI程序。今天的许多非常大的计算机实际上是由多核盒子组成的集合。编程其中一个的典型方法是在每个盒子上运行一个MPI进程,并且对于这些进程中的每一个,为盒子中的每个核心使用一个OpenMP线程。

最新更新