5.5 针对性例子 |
例#1a:
main(int argc,char **argv)
{
int me,size;
...
MPI_Init(&argc,&argv);
MPI_Comm_rank(MPI_COMM_WORLD,&me);
MPI_Comm_size(MPI_COMM_WORLD,&size);
(void)printf("Process %d size %d",me,size);
...
MPI_Finalize();
}
例#1a是一个对自身进行合法初始化但不做任何事的程序,程序中引用了"all" 通信 子并打印了一个消息。其自身也能合法终止。该例不意味着MPI本身支持类printf通信。
例#1b(假设size是偶数)
main(int argc,char **argv)
{
int me,size;
int SOME_TAG=0;
...
MPI_Init(&argc,&argv);
MPI_Comm_rank(MPI_COMM_WORLD,&me);/*本地*/
MPI_Comm_size(MPI_COMM_WORLD,&size);/*本地*/
if ((me%2)==0)
{
/*如果不是最高编号的进程则发送*/
if((me+1)<size)
MPI_Send(...,me+1,SOME_TAG,MPI_COMM_WORLD);
}
else
MPI_Recv(...,me-1,SOME_TAG,MPI_COMM_WORLD);
...
MPI_Finalize();
}
例#1b有计划地阐述了"all"通信子中奇偶进程间的消息交换.
main(int argc,char **argv)
{
int me,count;
void *data;
...
MPI_Init(&argc,&argv);
MPI_Comm_rank(MPI_COMM_WORLD,&me);
if(me==0)
{
/*得到输入,建立缓冲区"data"*/
...
}
MPI_Bcast(data,count,MPI_BYTE,0,MPI_COMM_WORLD);
...
MPI_Finalize();
}
本例阐述了集合通信的使用.
main(int argc,char **argv)
{
int me,count,count2;
void *send_buf,*recv_buf,*send_buf2,*recv_buf2;
MPI_Group MPI_GROUP_WORLD,grprem;
MPI_Comm commslave;
static int rank[]={0};
...
MPI_Init(&argc,&argv);
MPI_Comm_group(MPI_COMM_WORLD,&MPI_GROUP_WORLD);
MPI_Comm_rank(MPI_COMM_WORLD,&me);/*本地/
MPI_Group_excl(MPI_GROUP_WORLD,1,ranks,&grprem);/*本地*/ MPI_Comm_create(MPI_COMM_WORLD,grprem,&commslave);
if((me!=0)
{
/*在从机上计算*/
...
MPI_Reduce(send_buf,recv_buff,count,MPI_INT,MPI_SUM,1,commslave);
...
}
/*0则立即作此归约操作,其它则稍后再做*/
MPI_Reduce(send_buf2,recv_buff2,count2, MPI_INT,MPI_SUM,0,MPI_COMM_WORLD);
MPI_Comm_free(&commslave);
MPI_Group_free(&MPI_GROUP_WORLD);
MPI_Group_free(&grprem);
MPI_Finalize();
}
本例阐述了如何创建一个组,该组包含了除"all"组中第0个进程之外的所有进程,同时阐述了如何为该新组(commslave)形成一个通信子。此新通信子在一个集合调用中被使用,而且所有进程在上下文MPI_COMM_WORLD中都执行一个集合调用。本例还阐述了两个通信子(本质上具有不同的上下文)如何保护通信。亦即,MPI_COMM_WORLD中的通信与commslave中的通信隔离开,反之亦然。
概要地讲,“组安全性”是通过通信子获得的,这是因为在任何进程上通信子内的不同上下文被强制为唯一的。
下面的例子目的是阐述点对点与集合通信之间的安全性。MPI保证了单独一个通信子能够安全地进行点对点与集合通信。
#define TAG_ARBITRARY 12345
#define SOME_COUNT 50
main(int argc,char **argv)
{
int me;
MPI_Request request[2];
MPI_Status status[2];
MPI_Group MPI_GROUP_WORLD,subgroup;
int ranks[]={2,4,6,8};
MPI_Comm the_comm;
...
MPI_Init(&argc,&argv);
MPI_Comm_group(MPI_COMM_WORLD,&MPI_GROUP_WORLD);
MPI_Group_incl(MPI_GROUP_WORLD,4,ranks,&subgroup);/*本地*/
MPI_Group_rank(subgroup,&me);/*本地*/
MPI_Comm_create(MPI_COMM_WORLD,subgroup,&the_comm);
if (me!=UNDEFINED)
{
MPI_Irecv(buff1,count,MPI_DOUBLE,MPI_ANY_SOURCE,TAG_ARBITRARY,the_comm,
request); MPI_Isend(buff2,count,MPI_DOUBLE,(me+1)%4,TAG_ARBITRARY,
the_comm,request+1);
}
for(i=0;i<SOME_COUNT;i++)
MPI_Reduce(...,the_comm);
MPI_Waitall(2,request,status);
MPI_Comm_free(&the_comm);
MPI_Group_free(&MPI_GROUP_WORLD);
MPI_Group_free(&subgroup);
MPI_Finalize();
}
主程序:
main(int argc,char **argv)
{
int done=0;
user_lib_t *libh_a,*libh_b;
void *dataset1,*dataset2;
...
MPI_Init(&argc,&argv);
...
init_user_lib(MPI_COMM_WORLD,&libh_a);
init_user_lib(MPI_COMM_WORLD,&libh_b);
...
user_start_op(libh_a,dataset1);
user_start_op(libh_b,dataset2);
...
while(!done)
{
/*起作用*/
...
MPI_Reduce(...,MPI_COMM_WORLD);
...
/*如果done,则见下面*/
...
}
user_end_op(libh_a);
user_end_op(libh_b);
uninit_user_lib(libh_a);
uninit_user_lib(libh_b);
MPI_Finalize();
}
用户库初始化代码:
void init_user_lib(MPI_Comm *comm,user_lib_t **handle)
{
user_lib_t *save;
user_lib_initsave(&save);/*本地*/
MPI_Comm_dup(comm,&(save->comm));
/*其它初始化*/
...
*handle=save;
}
用户启动代码:
void user_start_op(user_lib_t *handle,void *data)
{
MPI_Irecv(...,handle->comm,&(handle->irecv_handle));
MPI_Isend(...,handle->comm,&(handle->irecv_handle));
}
用户通信清除代码:
void user_end_op(usr_lib_t *handle)
{
MPI_Status *status;
MPI_Wait(handle->isend_handle,status);
MPI_Wait(handle->irecv_handle,status);
}
用户对象清除代码:
void uninit_user_lib(user_lib_t *handle)
{
MPI_Comm_free(&(handle->comm));
free(handle);
}
主程序:
main(int argc **argv)
{
int ma, mb;
MPI_Group MPI_GROUP_WORLD,group_a, group_b;
MPI_Comm comm_a,commb;
static int list_a[] = {0,1};
#if defined(EXAMPLE_2B) |defined(EXAMPLE_2C)
static int list_b[] = {0, 2, 3};
#else/* EXAMPLE_2A */
static int list_b[] = {0, 2};
#endif
int size_list_a = sizeof(list_a)/sizeof(int);
int size_list_b = sizeof(list_b)/sizeof(int);
...
MPI_Init(&argc, &argv);
MPI_Comm_group(MPI_COMM_WORLD, &MPI_GROUP_WORLD);
MPI_Group_incl(MPI_GROUP_WORLD,size_list_a,&group_a);
MPI_group_incl(MPI_GROUP_WORLD,size-list_b,&group_b);
MPI_Comm_create(MPI_COMM_WORLD,group_a, &comm_a);
MPI_Comm_create(MPI_COMM_WORLD,group_b, &comm_b);
MPI_Comm_rank(comm_a, &ma);
MPI_Comm_rant(comm_b, &mb);
if(ma != MPI_UNDEFINED)
lib_call(comm_a);
if(mb != MPI_UNDEFINED)
{
lib_call(comm_b);
lib_call(comm_b);
}
MPI_Comm_free(&comm_a);
MPI_Comm_free(&comm_b);
MPI_Group_free(&group_a);
MPI_Group_free(&group_b);
MPI_Group_free(&MPI_GROUP_WORLD);
MPI_Finalize();
}
库:
void lib_call(MPI_Comm comm)
{
int me, done = 0;
MPI_Comm_rank(comm,&me);
if(me == 0)
while(!done)
{
MPI_Recv(..., MPI_ANY_SOURCE, MPI_ANY_TAG, comm);
...
}
else
{
/*起作用*/
MPI_Send(...,0, ARBITRARY_TAG, comm);
...
}
#ifdef EXAMPLE_2C
/* 包含(resp, exclude)用于safety (resp, no safety): */
MPI_Barrier(comm);
#endif
}
根据程序是否在lib_b中包含序列号等级3,以及是否在lib_call中进行同步,可将上面的例子实际 上当做三个例子.本例所阐述的是,不管上下文如何,使用同样上下文对lib_call进行的后续调用相互间无须是安全的(口语上,成为“后标志”). 如果加上MPI_Barrier, 就要考虑安全性.已经证明,即使使用上下文, 库也要仔细书写.当不包括序列号3时,就无须通过同步来从后标志中获取安全性.
假如MPI提供基本的保证,则类似于"reduce"和"allreduce"这样的算法就会有足够强大的选 择特性来保证其固有的正确性(无后标志).对使用相同的根和不同的根的典型树广播算法的多重调用也是这样. 这里我们依赖于MPI的两个保证: 同一上下文内的进程间的成对顺序,及资源的可选择性--删除任一个特征就等于去掉了不要求后标志这一保证.
试图作非确定性广播或包括通配符操作的其它调用一般不具备诸如 "reduce","allreduce",及"broadcast"这种确定性实现的好的特性.这种算法必须利用单调上升的标志(在一个通信子范围内)来保持正确性.
前面的所有调用都假定是用点对点操作来实现集合调用.MPI实现既可用也可不用点对点操作来实现集合调用.这些算法用于阐述正确性和安全性问题,而不管MPI如何实现其集合调用. 见5.8节.
Copyright: NPACT |