我有一个看起来像这样的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。
答案 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;
}