我必须编写一个MPI库,其中每个进程都在执行一些独立的任务,但是应该对可以从其他进程无法预测地发送的一些消息做出反应。 这些消息的发送和接收都是库的一部分,我不能假设库函数将被频繁调用以跟踪立即发送的进度或检查接收队列。如果接收进程正在进行某些计算,则发送过程可能会被阻塞一段不可预测的时间。
我目前感兴趣的解决方案是让每个MPI进程产生一个固定在自己的CPU上的pthread线程,使用循环中的阻塞接收来接收这些消息。我担心,我的实验表明这个线程花费了一半的CPU时间(我希望阻塞接收能以某种方式与内核一起工作以避免这种情况)。
我通过在进程的一个线程中使用伪计算函数,在另一个进程中使用阻塞接收,以及发送要由第一个进程接收的消息的另一个进程来测量此行为,但仅在计算完成时,在计算之后和发送消息之前由屏障强制执行。每个进程只有一个线程参与屏障,因此它可以工作。这可以确保接收线程在另一个进行计算时确实卡在等待消息。然后我测量计算时间。设置如下:
+ +
| P0 | P1
+--+--+ |
| | |
compute() | | Recv(1) |
| | |
+--------------------+ Barrier
| | |
| | | Send(0)
| | |
+ + +
我尝试将阻塞接收更改为MPI_Iprobe循环,这会使CPU产生到另一个线程,这样如果没有要接收的消息就不会占用太多CPU时间,因为我使用了sleep(0)
充当pthread_yield
或sched_yield
的函数需要权限才能将调度策略更改为实时策略我不确定是否需要。
然后使用nanosleep
函数来控制间隔。
简单版本如下:
int flag;
while (1)
{
MPI_Iprobe(1, 0, comm, &flag, MPI_STATUS_IGNORE);
if (flag == 1) break;
sleep(0);
}
MPI_Recv(NULL, 0, MPI_INT, 1, 0, comm, MPI_STATUS_IGNORE);
似乎来解决我的问题。在我的实验中,计算线程接近相同的时间量,就像没有其他线程一样,相比之下,如果我只是使用阻塞接收MPI_Recv
或者我没有使用{{1 }}
这里是我用来测量它的代码:
sleep(0)
由于我没有使用MPI的经验,也没有使用它与线程,这个解决方案对我来说似乎很脆弱,我不知道我是否可以依赖它。
我使用Linux内核版本4.4.0在Ubuntu 16.04上使用mpich 3.2
这个问题主要是就此问题和我目前的解决方案征求意见或讨论。如果需要,我可以解释更多我的测试方法或提供更多代码。
答案 0 :(得分:0)
由于示例中compute
和thread_recv_message
之间没有数据依赖性,因此很难说出接收到的数据到底在做什么。我也不确定句子片段“应该对某些可能从其他进程中不可预测地发送的消息做出反应”中使用的“不可预测”的特定含义。
如果您确定肯定会在某点上将排名 x 发送到排名为 y 的数据,则将MPI_Irecv
与MPI_Test
会在不阻止线程调用或完成接收请求的情况下实现这种通信方式。您可以将这些Irecv
和Test
调用与您的计算交织在一起,或者每2或64或128个计算循环迭代调用一次,或者进行适当的调用。
如果接收等级不预先知道它将从哪个等级接收或数据大小,那么您可能要使用MPI_Probe
或MPI_Iprobe
并使用返回的{ {1}}结构。与我对MPI_Status
所描述的类似,对Iprobe
的调用可以与您的计算交错进行。
还可以使用Irecv
或MPI_Alltoall
之类的集合来提供与许多行列MPI_Allgather
相似的功能,例如,通过交换包含要发送的字节数的数组,并在随后的点对点呼叫中在等级对之间接收。如果您可以保证所有等级最终都能到达集体召唤,则此方法可以很好地利用仅MPI内部可用的实现细节。您还可以使用等效的非阻塞集合(Probes
,Ialltoall
等)将这一步骤与您的计算重叠。