Multithreading OpenMP中进程间通信的线程

Multithreading OpenMP中进程间通信的线程,multithreading,parallel-processing,openmp,Multithreading,Parallel Processing,Openmp,我有一个OpenMP并行程序,看起来像: [...] #pragma omp parallel { //initialize threads #pragma omp for for(...) { //Work is done here } } 现在我正在添加MPI支持。我需要的是一个线程来处理通信,在我的例子中,它始终调用Gathere并填充/清空一个链表,以便从其他进程接收/发送数据。在设置标志之前,该线程应该发送/接收。所以现在这个示例中没有MPI内容,我的问题是关于Op

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

[...]
#pragma omp parallel
{
//initialize threads

#pragma omp for
for(...)
  {
  //Work is done here

  }

}
现在我正在添加MPI支持。我需要的是一个线程来处理通信,在我的例子中,它始终调用Gathere并填充/清空一个链表,以便从其他进程接收/发送数据。在设置标志之前,该线程应该发送/接收。所以现在这个示例中没有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
子句:

编辑:响应第一条评论

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

#包括
#包括
int main()
{
int kill=0;
#pragma-omp平行截面
{
#pragma-omp段
{
while(kill==0){
/*管理MPI通信*/
}
}
#pragma-omp段
{
#pragma-omp并行
#pragma omp for
对于(int i=0;i<10000;++i){
/*你的工作量*/
}
kill=1;
}
}
}
但是,您必须意识到,如果您没有至少两个线程,那么您的代码将被破坏,这意味着您正在打破这样的假设,即代码的顺序版本和并行版本应该做同样的事情


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

Hmmm。如果您确实在程序中添加MPI“支持”,那么您应该使用
mpi\u allgather
,因为
mpi\u gatherall
不存在。请注意,
mpi\u allgather
是一个集合操作,即通信器中的所有进程都调用它。你不能让一个进程收集数据,而让其他进程做任何事情。您可以使用MPI单边通信来实现您的想法;这将有点棘手,但如果一个进程只读取其他进程的内存,则不会比这更棘手

我对你使用的术语“线程”wrt MPI感到困惑。我担心您混淆了OpenMP和MPI,其中一种变体叫做OpenMPI。尽管有这个名字,它和OpenMP的区别就像粉笔和奶酪的区别一样。MPI程序是根据进程而不是线程编写的。典型的OpenMP实现确实使用线程,尽管程序员通常很好地隐藏了细节


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

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

有两种方法可以做到这一点:使用嵌套的parallism和tasks,可以启动一个task进行消息传递,另一个调用包含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;
}

实际上,您希望single和for同时执行,并且当for完成时停止single?这正是我想要做的:每个节点一个MPI进程,每个进程多个OpenMP线程。你说得对,这是HPC代码。很抱歉将gatherall改为allgather。我想我看到了问题所在;好吧,这让人困惑。在我上面的代码中没有mpi相关的东西。我只是想提一下我提出这个问题的动机。
#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;
}