【发布时间】:2016-12-20 10:51:50
【问题描述】:
过去几周我一直在学习如何实现 MPI,但我很难理解如何为 MPI_Allgatherv 设置一些输入参数。我将使用一个玩具示例,因为我需要在这里采取一些小步骤。我所做的一些研究列在这篇文章的末尾(包括我之前的问题,这导致我提出了这个问题)。首先,快速总结一下我要完成的工作:
--总结-- 我正在使用 std::vector A,让多个处理器在 A 的不同部分工作,然后获取 A 的更新部分并将这些更新重新分配给所有处理器。因此,所有处理器都以 A 的副本开始,更新 A 的部分,并以完全更新的 A 副本结束。--End--
假设我有一个 std::vector
for (int i = 0; i < 5; i++)
{
mydata[i] = (i+1)*1.1;
}
现在假设我在 2 个节点上运行我的代码 (int tot_proc = 2)。我使用“int id_proc”识别“当前”节点,因此,根处理器的 id_proc = 0。由于 mydata 中的元素数量是奇数,我无法在处理器之间平均分配工作。假设我总是将工作分解如下:
if (id_proc < tot_proc - 1)
{
//handle mydata.size()/tot_proc elements
}
else
{
//handle whatever is left over
}
在本例中,这意味着: id_proc = 0 将适用于 mydata[0] 和 mydata[1](2 个元素,因为 5/2 = 2)......并且...... id_proc = 1 将适用于 mydata[2] - mydata[4](3 个元素,因为 5 /2 + 5%2 = 3)
一旦每个处理器都处理了它们各自的 mydata 部分,我想使用 Allgatherv 将结果合并在一起,以便每个处理器上的 mydata 包含所有更新的值。我们知道 Allgatherv 有 8 个参数:(1) 正在发送的元素/数据的起始地址,(2) 正在发送的元素数量,(3) 类型正在发送的数据,在此示例中为 MPI_DOUBLE,(4)您希望接收数据的位置的地址(未提及“起始”地址),(5)正在接收的元素数量,(6)内存中相对于参数#4中接收位置的“位移”,(7)接收的数据类型,再次是MPI_DOUBLE,以及(8)您正在使用的通信器,在我的例子中就是MPI_COMM_WORLD。
现在是混乱开始的地方。由于处理器 0 处理前两个元素,处理器 1 处理后 3 个元素,因此处理器 0 需要发送前两个元素,处理器 1 需要发送最后 3 个元素。对我来说,这表明 Allgatherv 的前两个参数应该是:
处理器 0:MPI_Allgatherv(&mydata[0],2,…
处理器 1:MPI_Allgatherv(&mydata[2],3,…
(Q1) 我说的对吗?如果是这样,我的下一个问题是关于参数 2 的格式。假设我创建了一个 std::vector sendcount 使得 sendcount[0] = 2 和 sendcount[1] = 3。
(Q2) 参数 2 是否需要引用 sendcount 的第一个位置,还是我需要将引用发送到与每个处理器相关的位置?换句话说,我应该做哪些:
Q2 - 选项 1
处理器 0:MPI_Allgatherv(&mydata[0], &sendcount[0],…
处理器 1:MPI_Allgatherv(&mydata[2], &sendcount[0],…
Q2 - 选项 2
处理器 0:MPI_Allgatherv(&mydata[0], &sendcount[id_proc], ... (这里 id_proc = 0)
处理器 1:MPI_Allgatherv(&mydata[2], &sendcount[id_proc], ... (这里 id_proc = 1)
...关于论点 4。由于我将 mydata 的不同部分收集回自身,我怀疑这个论点看起来类似于论点 1。即它应该类似于 &mydata[?]。 (Q3)这个参数是否可以简单地引用 mydata 的开头(即 &mydata[0]),还是我必须像对参数 1 所做的那样更改索引? (Q4) 想象一下我使用了 3 个处理器。这意味着处理器 1 将发送位于向量“中间”的 mydata[2] 和 mydata[3]。由于向量的元素是连续的,因此必须拆分处理器 1 正在接收的数据(一些在前面,而 mydata[4] 在后面)。我是否必须在这个论点中解释这种分裂,如果是,如何解释?
...让我更困惑的是论点 5,但今天早上我有了一个想法。使用玩具示例:如果处理器 0 发送 2 个元素,那么它将接收 3 个元素,对吗?同样,如果处理器 1 发送 3 个元素,那么它接收 2 个元素。(Q5)所以,如果我要创建一个 std::vector recvcount,我不能将它初始化为:
for (int i = 0; i < tot_proc; i++)
{
recvcount[i] = mydata.size() - sendcount[i];
}
如果这是真的,那么我是将其作为 &recvcount[0] 还是 &recvcount[id_proc] 传递给 Allgatherv(类似于参数 2)?
最后,参数 6。我知道这与我对参数 4 的输入相关。我的猜测如下:如果我要在所有处理器上将 &mydata[0] 作为参数 4 传递,那么位移就是位置数在内存中,我需要移动才能到达实际需要接收数据的第一个位置。例如,
处理器 0:MPI_Allgatherv( ... , &mydata[0], ... , 2, ... );
处理器 1:MPI_Allgatherv( ... , &mydata[0], ... , 0, ... );
(Q5) 我是否认为上面两行的意思是“处理器 0 将接收从位置 &mydata[0+2] 开始的数据。处理器 1 将接收从位置 &mydata[0+0] 开始的数据。” ??当需要像第四季度那样拆分数据时会发生什么?最后,由于我将向量的一部分收集回自身(通过覆盖它来用更新的 mydata 替换 mydata),因此这告诉我除根进程之外的所有处理器都将从 &mydata[0] 开始接收数据。 (Q6)如果是这样,那么不是根的所有处理器的位移不应该为0吗?
我读过的一些链接: Difference between MPI_allgather and MPI_allgatherv Difference between MPI_Allgather and MPI_Alltoall functions? Problem with MPI_Gatherv for std::vector C++: Using MPI's gatherv to concatenate vectors of differing lengths http://www.mcs.anl.gov/research/projects/mpi/www/www3/MPI_Allgatherv.html https://computing.llnl.gov/tutorials/mpi/#Routine_Arguments
我之前在 stackoverflow 上的帖子: MPI C++ matrix addition, function arguments, and function returns
我读过的大多数教程等只是掩盖了 Allgatherv。
【问题讨论】: