从第一篇笔记开始,我们就使用了comm这个对象.
而且,几乎每次新学习的函数,都是这个comm的一个方法.
今天,我们就来重新认识comm,它到底是什么?
1.通信子
comm是一个通信子.什么是通信子?
通信子其实就是一个对讲机,每个进程都有一个对讲机.
通过对讲机,你可以和某个人对话,也可以对全部人发出广播.
2.通信组
警察有警察之间的对讲机,盗贼有盗贼之间的对讲机.
显然,警察的对讲机是不应该能和盗贼的对讲机通信的(不包括偷听).
这样的话,我们就有了通信组的概念.
警察之间形成一个通信组,盗贼之间形成一个通信组.
3.MPI里的通信组
MPI里有一个类Group,它就是充当了通信组的角色.
当我们有一个对讲机comm的时候,我们通过Get_group()的方法可以得到对应的Group对象.
Group对象有什么用?它可以用来创建新的通信组!
4.MPI里的默认通信子
我们一开始就用到了一个系统帮我们创建的全局通信子,就是COMM_WORLD,它包含了所有的进程.
其实还有两个系统默认创建的通信子,一个是COMM_SELF,另一个是COMM_NULL.
COMM_SELF仅仅包含了当前进程,而COMM_NULL则什么进程都没有包含.
在下面的代码中,我们将使用到COMM_NULL.
5.创建新通信组
在实际开发中,我们往往不止需要一个全局的通信组,还需要很多小型的通信组,我们需要创建新通信组.
其实准确来说是创建新通信子,因为通信组只是一份名单,而通信需要的是对讲机!
某个系列的对讲机一定有对应的某份名单,而某份名单不一定有对应的对讲机.
要创建新通信子,我们的过程是这样的:
(1)获取一个旧的通信组(通常是利用COMM_WORLD获取)
(2)对这个通信组进行添加和删减,以达到我们的目标
(3)通过旧通信子的Create方法创建新通信子
下面我们会在6个进程中,让后3个进程单独创建一个新的通信组.
代码:
运行结果:
代码解释:
第8行获取了全局通信子的通信组,第9行调用了Excl方法,得到了一个剔除了前3个进程的新通信组(具体的含义会在下一篇笔记说到).
然后我们用Create()方法,把group作为参数送进去,就会得到一个新的通信子new_comm.
注意到,对于第3,4,5个进程来说,它们会得到新通信子.但是,对第0,1,2个进程来说,它们不应该得到新通信子.
那样的话,是否需要全部进程都执行Create()这个方法?
答案是一定要的.实际上这个方法有一个barrier的过程,也就是全局同步.
在comm这个通信子里的所有进程,必须都调用Create()这个方法,MPI才会生成并返回新的通信子.
不相信的话,你可以让某个进程不调用Create(),结果是其他进程会被阻塞住.
那么新问题又到了,对第0,1,2个进程来说,它们得到的new_comm是个什么玩意?
没错,从代码第13行你已经发现了,它会是一个空通信子COMM_NULL.
总的来说就是,旧通信子的所有进程都调用Create()方法.对于在新通信组的进程会得到新通信子,而不在新通信组的进程会得到空通信子.
空通信子就是这样使用的,并且如果你像普通通信子一样调用它的方法,会抛出异常.
从输出结果可以看到新通信子对它的进程重新分配了rank.
也就是说,对某个进程,它在不同的通信组会有不同的rank,这也是把它称为rank而不是id的原因.
因此,在不同通信组进行通信的时候,这个进程使用的rank是不同的.这有点像,在不同的场合,人有不同的身份.
通信组示意图:
本文介绍了MPI中的通信子和通信组概念,包括通信子作为对讲机的角色,通信组的划分,如COMM_WORLD、COMM_SELF和COMM_NULL。详细阐述了如何创建新通信组,通过Get_group()获取Group对象,再利用Create()方法创建新通信子,并通过示例代码展示了在多个进程中创建新通信组的过程,强调了全局同步的重要性以及空通信子的使用场景。
2260

被折叠的 条评论
为什么被折叠?



