我正在考虑为MPI实现一个模仿OpenMP方式的包装器循环的并行化。
begin_parallel_region( chunk_size=100 , num_proc=10 );
for( int i=0 ; i<1000 ; i++ )
{
//some computation
}
end_parallel_region();
上面的代码将for循环中的计算分配给10个从属MPI处理器。在进入并行区域时,提供块大小和从处理器的数量。离开并行区域后,MPI处理器将同步并处于空闲状态。
编辑以响应高性能标记。
我无意模拟OpenMP的共享内存模型。我提出这个建议是因为我需要它。我正在开发一个从数学函数构建图所需的库。在这些数学函数中,经常存在如下循环。
for( int i=0 ; i<n ; i++ )
{
s = s + sin(x[i]);
}
因此,我希望首先能够将sin(x[I])分发到从属处理器,并在最后像在OpenMP中一样减少到单个变量。
我想知道是否有这样一个包装,这样我就不必重新发明轮子了。
谢谢。
没有这样的包装器从研究实验室逃脱并得到广泛使用。你的建议与其说是重新发明轮子,不如说是发明飞行汽车。
我可以看到你建议如何编写MPI代码来模拟OpenMP分担循环负担的方法,但不太清楚的是你建议如何让MPI模拟OpenMP的共享内存模型?
在一个简单的OpenMP程序中,正如你所建议的,每个程序可能有10个线程执行一个大循环10%的迭代,可能会更新一个大(共享)数据结构的值。为了在MPI中模拟这种情况,您必须(i)说服单面通信表现得像共享内存(这可能是可行的,但肯定会很困难),或者(ii)将数据分发给所有进程,让每个进程独立计算10%的结果,然后将结果广播给所有人,以便在执行结束时每个进程都具有其他进程所具有的所有数据。
在分布式内存硬件上模拟共享内存计算是并行计算中的一个热门话题,一直都是,永远都是。谷歌搜索分布式共享内存计算并加入其中。
编辑
好吧,如果你已经在进程之间分配了x
,那么单个进程就可以计算sin(x[i])
,并且你可以使用MPI_Reduce
将总和减少到一个进程。
我一定对您的需求有所遗漏,因为我不明白您为什么要在MPI已经提供的基础上构建任何上层结构。尽管如此,我对你最初问题的回答仍然是不,没有你所寻求的包装,我的其余回答都只是评论。
是的,对于特定的任务,您可以这样做。但你不应该。
考虑如何实现这一点;开始部分将分发数据,结束部分将返回答案:
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <mpi.h>
typedef struct state_t {
int globaln;
int localn;
int *locals;
int *offsets;
double *localin;
double *localout;
double (*map)(double);
} state;
state *begin_parallel_mapandsum(double *in, int n, double (*map)(double)) {
state *s = malloc(sizeof(state));
s->globaln = n;
s->map = map;
/* figure out decomposition */
int size, rank;
MPI_Comm_size(MPI_COMM_WORLD, &size);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
s->locals = malloc(size * sizeof(int));
s->offsets = malloc(size * sizeof(int));
s->offsets[0] = 0;
for (int i=0; i<size; i++) {
s->locals[i] = (n+i)/size;
if (i < size-1) s->offsets[i+1] = s->offsets[i] + s->locals[i];
}
/* allocate local arrays */
s->localn = s->locals[rank];
s->localin = malloc(s->localn*sizeof(double));
s->localout = malloc(s->localn*sizeof(double));
/* distribute */
MPI_Scatterv( in, s->locals, s->offsets, MPI_DOUBLE,
s->localin, s->locals[rank], MPI_DOUBLE,
0, MPI_COMM_WORLD);
return s;
}
double end_parallel_mapandsum(state **s) {
double localanswer=0., answer;
/* sum up local answers */
for (int i=0; i<((*s)->localn); i++) {
localanswer += ((*s)->localout)[i];
}
/* and get global result. Everyone gets answer */
MPI_Allreduce(&localanswer, &answer, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD);
free( (*s)->localin );
free( (*s)->localout );
free( (*s)->locals );
free( (*s)->offsets );
free( (*s) );
return answer;
}
int main(int argc, char **argv) {
int rank;
double *inputs;
double result;
int n=100;
const double pi=4.*atan(1.);
MPI_Init(&argc, &argv);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
if (rank == 0) {
inputs = malloc(n * sizeof(double));
for (int i=0; i<n; i++) {
inputs[i] = 2.*pi/n*i;
}
}
state *s=begin_parallel_mapandsum(inputs, n, sin);
for (int i=0; i<s->localn; i++) {
s->localout[i] = (s->map)(s->localin[i]);
}
result = end_parallel_mapandsum(&s);
if (rank == 0) {
printf("Calculated result: %lfn", result);
double trueresult = 0.;
for (int i=0; i<n; i++) trueresult += sin(inputs[i]);
printf("True result: %lfn", trueresult);
}
MPI_Finalize();
}
用几个数字来概括,这种持续的分布/聚集是一种可怕的通信负担,与整个分布式内存计算模型背道而驰。
首先,共享内存方法-OpenMP、pthreads、IPP,等等-是关于更快地扩展计算;关于在同一块内存中抛出更多处理器。另一方面,分布式内存计算是将计算放大;关于使用比一台计算机上更多的资源,特别是内存。使用MPI的最大好处是,当您处理无法适应任何一个节点内存的问题集时。因此,在进行分布式内存计算时,可以避免将所有数据都放在任何一个地方。
重要的是,即使您只是在节点上使用MPI来使用所有处理器,也要记住这个基本方法。上述分散/聚集方法只会降低性能。更惯用的分布式内存计算方法是程序的逻辑已经分发了数据——也就是说,上面的begin_parallel_region
和end_parallel_region
在一开始就已经构建到循环上方的代码中。然后,每个循环都只是
for( int i=0 ; i<localn ; i++ )
{
s = s + sin(x[i]);
}
当您需要在任务之间交换数据(或者减少结果,或者有什么)时,您可以调用MPI函数来执行这些特定任务。
MPI是必须的,还是您只是想在集群上运行类似OpenMP的代码?在后一种情况下,我建议您看看英特尔的Cluster OpenMP:
http://www.hpcwire.com/hpcwire/2006-05-19/openmp_on_clusters-1.html