我正在尝试ISend()
两个数组:arr1,arr2
和一个n
大小为arr1,arr2
. 我从这篇文章中了解到,发送包含所有三个的结构不是一种选择,因为n
仅在运行时才知道。显然,我需要n
先被接收,否则接收过程将不知道要接收多少元素。在不使用 blkcing 的情况下实现这一目标的最有效方法是什么Send()
?
3 回答
发送数组的大小是多余的(而且效率低下),因为 MPI 提供了一种无需接收即可探测传入消息的方法,这提供了足够的信息以正确分配内存。使用 执行探测MPI_PROBE
,看起来很像MPI_RECV
,除了它不接受与缓冲区相关的参数。探测操作返回一个状态对象,然后可以查询给定 MPI 数据类型的元素数量,这些元素可以从消息的内容中提取MPI_GET_COUNT
,因此显式发送元素数量变得多余。
这是一个有两个等级的简单示例:
if (rank == 0)
{
MPI_Request req;
// Send a message to rank 1
MPI_Isend(arr1, n, MPI_DOUBLE, 1, 0, MPI_COMM_WORLD, &req);
// Do not forget to complete the request!
MPI_Wait(&req, MPI_STATUS_IGNORE);
}
else if (rank == 1)
{
MPI_Status status;
// Wait for a message from rank 0 with tag 0
MPI_Probe(0, 0, MPI_COMM_WORLD, &status);
// Find out the number of elements in the message -> size goes to "n"
MPI_Get_count(&status, MPI_DOUBLE, &n);
// Allocate memory
arr1 = malloc(n*sizeof(double));
// Receive the message. ignore the status
MPI_Recv(arr1, n, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
}
MPI_PROBE
也接受通配符等级MPI_ANY_SOURCE
和通配符标签MPI_ANY_TAG
。然后可以查阅状态结构中的相应条目,以找出实际的发件人等级和实际的消息标签。
探测消息大小的工作原理是每条消息都带有一个标头,称为信封。信封由发送者等级、接收者等级、消息标签和通信者组成。它还携带有关总消息大小的信息。信封作为两个通信进程之间初始握手的一部分发送。
首先,您需要将内存(完整内存 = n = 元素)分配给排名为 0 的 arr1 和 arr2。即您的前端处理器。
根据编号将数组分成几部分。处理器。确定每个处理器的元素计数。
将此元素计数从等级 0 发送到其他处理器。
第二次发送用于数组,即 arr1 和 arr2
在其他处理器中,根据从主处理器接收到的元素计数,即rank = 0,分配arr1 和arr2。接收到元素计数后,接收分配的内存中的两个数组。
这是一个示例 C++ 实现,但 C 将遵循相同的逻辑。也只需将 Send 与 Isend 互换。
#include <mpi.h>
#include <iostream>
using namespace std;
int main(int argc, char*argv[])
{
MPI::Init (argc, argv);
int rank = MPI::COMM_WORLD.Get_rank();
int no_of_processors = MPI::COMM_WORLD.Get_size();
MPI::Status status;
double *arr1;
if (rank == 0)
{
// Setting some Random n
int n = 10;
arr1 = new double[n];
for(int i = 0; i < n; i++)
{
arr1[i] = i;
}
int part = n / no_of_processors;
int offset = n % no_of_processors;
// cout << part << "\t" << offset << endl;
for(int i = 1; i < no_of_processors; i++)
{
int start = i*part;
int end = start + part - 1;
if (i == (no_of_processors-1))
{
end += offset;
}
// cout << i << " Start: " << start << " END: " << end;
// Element_Count
int e_count = end - start + 1;
// cout << " e_count: " << e_count << endl;
// Sending
MPI::COMM_WORLD.Send(
&e_count,
1,
MPI::INT,
i,
0
);
// Sending Arr1
MPI::COMM_WORLD.Send(
(arr1+start),
e_count,
MPI::DOUBLE,
i,
1
);
}
}
else
{
// Element Count
int e_count;
// Receiving elements count
MPI::COMM_WORLD.Recv (
&e_count,
1,
MPI::INT,
0,
0,
status
);
arr1 = new double [e_count];
// Receiving FIrst Array
MPI::COMM_WORLD.Recv (
arr1,
e_count,
MPI::DOUBLE,
0,
1,
status
);
for(int i = 0; i < e_count; i++)
{
cout << arr1[i] << endl;
}
}
// if(rank == 0)
delete [] arr1;
MPI::Finalize();
return 0;
}
@Histro 我想说的是,Irecv/Isend 是一些由 MPI lib 操作的函数。您提出的问题完全取决于您在发送/接收后所做的其余代码。有2种情况:
Master 和 Worker 您将部分问题(比如数组)发送给工作人员(除 0=Master 之外的所有其他等级)。worker 做了一些工作(在数组上),然后将结果返回给 master。然后,主人将结果相加,并将新的工作传达给工人。现在,在这里您希望主人等待所有工人返回他们的结果(修改后的数组)。因此,您不能使用 Isend 和 Irecv,而是在我的代码和相应的 recv 中使用的多次发送。如果您的代码朝这个方向发展,您想使用 B_cast 和 MPI_Reduce。
懒惰 的主人 主人分工,不关心工人的结果。假设您想为相同的数据编写不同类型的模式。就像给定某个城市的人口特征一样,你想计算出有多少人在 18 岁以上,有多少人有工作,有多少人在某家公司工作。现在这些结果彼此没有任何关系。在这种情况下,您不必担心工作人员是否收到数据。master可以继续执行剩下的代码。这是可以安全使用 Isend/Irecv 的地方。