在函数 MPI_Iallgatherv 中
int MPI_Iallgatherv(const void *sendbuf, int sendcount, MPI_Datatype sendtype,
void *recvbuf, const int recvcounts[], const int displs[],
MPI_Datatype recvtype, MPI_Comm comm, MPI_Request *request)
参数recvcounts[]是一个输入参数,因此据我所知,程序应该提前知道必须从每个进程接收多少个元素。可以设置大于实际发送的元素数量的值,如下例所示
#include <stdio.h>
#include <stdlib.h>
#include <mpi.h>
int main(int argc, char* argv[])
{
MPI_Init(&argc, &argv);
int size;
MPI_Comm_size(MPI_COMM_WORLD, &size);
if(size != 3) {
printf("This application must run with 3 MPI processes.\n");
MPI_Abort(MPI_COMM_WORLD, EXIT_FAILURE);
}
int rank;
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
int counts[3] = {1000, 1000, 1000}; // Max n. of elems that can be received
int displs[3] = {0, 1000, 2000}; // Displacements
int buffer[3000]; // Receiving buffer
// Buffer containing our data to send
int send_values[1000]; // buffer for data to be sent
int send_count;
switch(rank) {
case 0:
{
send_count = 1;
send_values[0] = 3;
break;
}
case 1:
{
send_count = 2;
send_values[0] = 1;
send_values[1] = 4;
break;
}
case 2:
{
send_count = 3;
send_values[0] = 1;
send_values[1] = 5;
send_values[2] = 9;
break;
}
}
MPI_Request recv_mpi_request;
MPI_Iallgatherv(send_values, send_count, MPI_INT, buffer, counts, displs,
MPI_INT, MPI_COMM_WORLD, &recv_mpi_request);
MPI_Status status;
MPI_Waitall(1, &recv_mpi_request, &status);
printf("Values gathered on process %d:", rank);
for(int i=0; i<3; i++) {
for(int j=0; j<3; j++) {
printf(" %d", buffer[i*1000+j]);
}
printf("\t");
}
printf("\n");
MPI_Finalize();
return EXIT_SUCCESS;
}
但是我找不到任何优雅的方法来检索每个进程实际发送的元素数量,而且我想知道通信所需的时间是否会受到我使用的预定义计数值远大于其计数的事实的影响实际值。我知道原则上我可以使用两种单独的 MPI 通信,一种用于获取计数,每个进程一个条目,第二种用于传输具有先前获得的大小的数据。然而,每个 MPI 传输都有一个与大小无关的时间开销,并且对于我正在处理的应用程序来说是不可忽略的。我想知道是否有一种合理的方法可以通过一次调用 MPI_Iallgatherv 来获取实际计数的数量。有人可以帮助我吗?
正如 Gilles 在评论中指出的那样,所有形式的 MPI Allgather 都需要计数。
阻塞情况的解决方案是首先对计数执行
MPI_Allgather
,然后对结果计数向量执行 MPI_Allgatherv
。
我不知道不涉及泛化请求和必要的进度线程的完全非阻塞实现。如果您愿意生成一个 Pthread,您可以在通用请求回调中放置一个 allgather + allgatherv 实现。
如果您对没有输入计数的
MPI_Igatherv
感兴趣,您可以让所有非根进程执行 Isend,然后执行 Ibcast,并让根循环遍历 (M)Probe + (M)Recv,然后 Ibcast 结果回到源头。这不会特别有效,而且几乎肯定你最好先进行计数收集。