好的,所以我想在树状结构中进行多线程深度优先搜索。我正在使用群集中多台计算机的线程(本例中为localhost四核和覆盆子pi 2)。主线程应该启动进程并在树中的第一个分割中,对于它分割成的每个节点,它应该生成一个新线程。然后,这些线程应该能够将他们的发现报告给主人。
我正在尝试动态地执行此操作,而不是为mpiexec提供多个线程,因为我不知道树预先看起来像什么(例如,可能有2或9个拆分)。
我从我正在为这个问题工作的项目中做了一个样本,我的工作如下。它从一串数字中取一位数,每个数字产生一个线程并将数字发送到该线程。
对于主人:
#!/usr/bin/python
from mpi4py import MPI
import datetime, sys, numpy, time
################ Set up MPI variables ################
comm = MPI.COMM_WORLD
rank = comm.Get_rank()
size = comm.Get_size()
name = MPI.Get_processor_name()
status = MPI.Status()
################ Master code ################
script = 'cpi.py'
for d in '34':
try:
print 'Trying to spawn child process...'
icomm = MPI.COMM_SELF.Spawn(sys.executable, args=[script], maxprocs=1, root=0)
spawnrank = icomm.Get_rank()
icomm.send(d, dest=spawnrank, tag=11)
print 'Spawned rank %d.' % spawnrank
except: ValueError('Spawn failed to start.')
solved = False
while solved == False:
#while not comm.Iprobe(source=MPI.ANY_SOURCE, tag=MPI.ANY_TAG):
# print 'spawns doing some work...'
# time.sleep(1)
solved = comm.recv(source=MPI.ANY_SOURCE, tag=22)
print 'received solution: %d' % solved
它正确地产生了工人,他们收到了数字,但没有把它发回给主人。工人的代码如下:
工
#!/usr/bin/python
from mpi4py import MPI
import datetime, sys, numpy
################ Set up MPI variables ################
icomm = MPI.Comm.Get_parent()
comm = MPI.COMM_WORLD
irank = comm.Get_rank()
rank = comm.Get_rank()
running = True
while running:
data = None
data = icomm.recv(source=0, tag=11)
if data:
print 'Trying to send %s from worker rank %d to %d' % (data, rank, irank)
icomm.send(data, dest=0, tag=22)
break
print 'Worker on rank %d done.' % rank
icomm.Disconnect()
它永远不会到达主代码的最后一行。我还在主代码中添加(注释掉)探测器,以检查带有标记22的消息是否在某处悬挂,排除了recv函数中的错误,但探测器从未找到该消息。所以我认为它永远不会发送。
我认为通过打印两个进程的排名他们都使用等级0 这是有道理的,因为它们是在同一台计算机上生成的。但是当我添加一个hostfile和rankfile,试图强制它为奴隶使用另一台计算机时,它给了我以下错误:
[hch-K55A:06917] *** Process received signal ***
[hch-K55A:06917] Signal: Segmentation fault (11)
[hch-K55A:06917] Signal code: Address not mapped (1)
[hch-K55A:06917] Failing at address: 0x3c
[hch-K55A:06917] [ 0] /lib/x86_64-linux-gnu/libpthread.so.0(+0x10340) [0x7f2c0d864340]
[hch-K55A:06917] [ 1] /usr/lib/openmpi/lib/openmpi/mca_rmaps_rank_file.so(orte_rmaps_rank_file_lex+0x4a0) [0x7f2c0abdcb70]
[hch-K55A:06917] [ 2] /usr/lib/openmpi/lib/openmpi/mca_rmaps_rank_file.so(+0x23ac) [0x7f2c0abda3ac]
[hch-K55A:06917] [ 3] /usr/lib/libopen-rte.so.4(orte_rmaps_base_map_job+0x2e) [0x7f2c0dacd05e]
[hch-K55A:06917] [ 4] /usr/lib/libopen-rte.so.4(orte_plm_base_setup_job+0x5a) [0x7f2c0dac580a]
[hch-K55A:06917] [ 5] /usr/lib/openmpi/lib/openmpi/mca_plm_rsh.so(orte_plm_rsh_launch+0x338) [0x7f2c0b80a8c8]
[hch-K55A:06917] [ 6] /usr/lib/libopen-rte.so.4(+0x51ff4) [0x7f2c0dac3ff4]
[hch-K55A:06917] [ 7] /usr/lib/libopen-rte.so.4(opal_event_base_loop+0x31e) [0x7f2c0dae9cfe]
[hch-K55A:06917] [ 8] mpiexec() [0x4047d3]
[hch-K55A:06917] [ 9] mpiexec() [0x40347d]
[hch-K55A:06917] [10] /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf5) [0x7f2c0d4b0ec5]
[hch-K55A:06917] [11] mpiexec() [0x403399]
[hch-K55A:06917] *** End of error message ***
Segmentation fault (core dumped)
使用的命令:mpiexec -np 1 --hostfile hostfile --rankfile rankfile python spawntest.py
HOSTFILE: 本地主机 localhost slots = 1 max-slots = 4 pi2 @ raspi2 slots = 4
Rankfile: rank 0 = localhost slot = 1 等级1 = pi2 @ raspi2 slot = 1-4
所以我的问题如下;如何在主计算机以外的计算机上生成这些线程,同时能够来回发送数据?
答案 0 :(得分:3)
你的主人的代码非常错误,我觉得你缺乏对那里发生的事情的概念性理解。
MPI_COMM_SPAWN
(或其mpi4py对等comm.Spawn()
)产生的作业中的MPI流程不会成为父MPI_COMM_WORLD
的一部分。产生的进程形成一个完全独立的世界通信器,并通过 intercommunicator 与父作业相互链接,这正是spawn返回的内容。在您的情况下,icomm = MPI.COMM_SELF.Spawn(...)
是主进程中的intercommunicator句柄。子作业中的进程使用MPI_COMM_GET_PARENT
(mpi4py中的MPI.Comm.Get_parent()
)获取intercommunicator句柄。由于您正在产生单进程作业:
MPI.COMM_SELF.Spawn(sys.executable, args=[script], maxprocs=1, root=0)
^^^^^^^^^^
在新成立的童工世界沟通者中只有一个流程,因此MPI.COMM_WORLD.Get_rank()
在每个工人中都返回零。
这部分主人的代码是错误的,但由于交流员的实际工作方式,它仍然有效:
spawnrank = icomm.Get_rank() # <--- not what you expect
icomm.send(d, dest=spawnrank, tag=11)
Intercommunicators链接两组不同的进程。其中一个称为本地组,另一个称为远程组。在对讲机上使用MPI_COMM_RANK
(comm.Get_rank()
)时,您可以在本地组中获取呼叫流程的等级。发送或接收时,指定的等级与远程组相关。在您的情况下,产生一个新工作人员会产生以下互通:
mastet's MPI_COMM_SELF child's MPI_COMM_WORLD
| |
+=============|================================|=============+
| +----------V----------+ +-------------V----------+ |
| | group of the master | | group of the child job | |
| | [ 0 ] | | [ 0 ] | |
| +---------------------+ +------------------------+ |
| intercommunicator |
+============================================================+
(上面的传播者显示每个群体来自哪里;传播者本身不是互动者的一部分)
哪个组是本地组,哪个组是远程组取决于调用进程所属的组。主进程的本地组是子作业中的等级的远程组,反之亦然。这里重要的是,每个组的等级为0,因为组中至少有一个进程。您很幸运,主组中只有一个进程,因此icomm.Get_rank()
返回0(并且它将始终返回零,因为主要的本地组来自MPI_COMM_SELF
,它总是包含一个进程),它恰好(始终)是远程(子)组中的有效排名。正确的做法是将邮件发送到您知道在远程组中存在的固定排名,例如排名0
:
icomm = MPI.COMM_SELF.Spawn(sys.executable, args=[script], maxprocs=1, root=0)
icomm.send(d, dest=0, tag=11)
(此代码显式发送到远程组的排名0
,而在此之前,值0
只是一个幸运的巧合)
那就是说,发送部分 - 虽然不正确 - 仍然有效。接收部分没有,有几个原因。首先,您正在使用错误的通信器 - 从MPI_COMM_WORLD
接收不起作用,因为子进程不是它的成员。事实上,MPI中的传播者是不可变的 - 如果不创建新的传播者,就无法添加或删除等级。您应该使用icomm
从工作人员那里接收,就像您使用它发送给他们一样。现在,出现了第二个问题 - 主人中的icomm
被每个新的Spawn
覆盖,因此你实际上失去了与任何子工作进行通信的能力,但最后一个。你需要保留一个句柄列表并附加句柄。
接收部分有点复杂。没有MPI_ANY_COMM
- 您无法接受涵盖所有子工作的接收操作,因为他们都住在他们各自的相互通信器中。您应该使用MPI_IPROBE
在互通者列表上循环,或者(更好)从每个孩子开始非阻塞接收,然后使用MPI_WAIT_SOME
(无论mpi4py等价物是什么)。
使用循环,主代码看起来应该是这样的(注意 - 未经测试的代码,我没有和/或使用mpi4py):
#!/usr/bin/python
from mpi4py import MPI
import datetime, sys, numpy, time
################ Set up MPI variables ################
comm = MPI.COMM_WORLD
rank = comm.Get_rank()
size = comm.Get_size()
name = MPI.Get_processor_name()
status = MPI.Status()
################ Master code ################
icomms = []
script = 'cpi.py'
for d in '34':
try:
print 'Trying to spawn child process...'
icomm = MPI.COMM_SELF.Spawn(sys.executable, args=[script], maxprocs=1, root=0)
icomm.send(d, dest=0, tag=11)
icomms.append(icomm)
print 'Spawned a child.'
except: ValueError('Spawn failed to start.')
solved = False
while not solved and icomms:
for icomm in icomms:
if icomm.Iprobe(source=0, tag=MPI.ANY_TAG):
print 'A child responded...'
solved = icomm.recv(source=0, tag=MPI.ANY_TAG)
icomm.Disconnect()
icomms.remove(icomm)
if solved: break
if not solved:
print 'spawns doing some work...'
time.sleep(1)
# make sure all pending sends get matched
for icomm in icomms:
icomm.recv(source=0, tag=MPI.ANY_TAG)
icomm.Disconnect()
print 'received solution: %d' % solved
我希望你明白这一点。
添加:如果您从生成的作业中生成作业,则新子项无法轻松建立与顶级母版的连接。为此,您应该转向MPI-2客户端/服务器模型支持的一个不起眼的部分,让主服务器打开一个MPI_PORT_OPEN
的端口,然后使用MPI_PUBLISH_NAME
将其注册到MPI命名服务,并且最后使用MPI_COMM_ACCEPT
接收来自任何其他MPI作业的连接。工作人员应使用MPI_LOOKUP_NAME
获取对端口的引用,并使用MPI_COMM_CONNECT
与主作业建立互通。我不知道mpi4py中是否存在这些函数的包装器,如果存在,那么这些函数是如何命名的。