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

时间:2012-03-13 13:54:46

标签: multithreading parallel-processing 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-loop之后的隐式屏障等待上面的while循环中的线程。

谢谢你,rugermini。

3 个答案:

答案 0 :(得分:0)

您可以尝试在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通信方案中可能会更加清晰(可能使用异步通信来重叠与计算的通信)。

答案 1 :(得分:0)

嗯。如果您确实在程序中添加了MPI“支持”,那么您应该使用mpi_allgather,因为mpi_gatherall不存在。请注意,mpi_allgather是一个集合操作,即通信器中的所有进程都会调用它。在其他进程执行任何操作时,您不能拥有收集数据的进程。您可以做的是使用MPI 单面通信来实现您的想法;如果一个进程只读取其他进程的内存,这将是一个有点棘手的问题,但不会更多。

我对你使用术语'线'和MPI这个术语感到困惑。我担心你会混淆OpenMP和MPI,其中一个变种称为OpenMPI。尽管如此,它与OpenMP不同,因为它与奶酪中的粉笔不同。 MPI程序是根据进程而不是线程编写的。典型的OpenMP实现确实使用了线程,但细节通常是程序员隐藏的。

我对您尝试或似乎尝试在您的OpenMP代码中使用MPI感到非常印象深刻。这与我的工作完全相反,并且看到其他人在一些非常大的计算机上做。这种“混合”并行化的标准模式是编写调用OpenMP代码的MPI程序。当今许多非常大的计算机都包含多核盒子的集合。编程其中一个的典型方法是在每个盒子上运行一个MPI进程,并为每个进程在框中为每个核心使用一个OpenMP线程。

答案 2 :(得分:0)

你必须要小心,因为你不能让你的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 %d\n", 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 %d\n", data);
            }

            #pragma omp task
            work(rank);
        }
    }


    MPI_Finalize();
    return 0;
}

或者,你可以使你的omp for循环schedule(dynamic),以便其他线程可以在主线程发送时从中获取一些松弛,并且主线程可以在完成时获取一些工作:

#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 %d\n", 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 %d\n", rank, tid, i);
            }
    }


    MPI_Finalize();
    return 0;
}