MPI_Sendrecv_替换的替代方案
我正在尝试获得一个替代代码来MPI_Sendrecv_替换的替代方案,c,mpi,C,Mpi,我正在尝试获得一个替代代码来 if(rank %2==0 && rightNeighbour != MPI_PROC_NULL) MPI_Sendrecv_replace(&bufferRight[0], len, MPI_DOUBLE, rightNeighbour, 1, rightNeighbour, 1, MPI_COMM_WORLD, &status); else if(rank%2 ==1 &&
if(rank %2==0 && rightNeighbour != MPI_PROC_NULL)
MPI_Sendrecv_replace(&bufferRight[0], len, MPI_DOUBLE, rightNeighbour, 1,
rightNeighbour, 1, MPI_COMM_WORLD, &status);
else if(rank%2 ==1 && leftNeighbour != MPI_PROC_NULL)
MPI_Sendrecv_replace(&bufferLeft[0], len, MPI_DOUBLE, leftNeighbour, 1,
leftNeighbour, 1, MPI_COMM_WORLD, &status);
if (rank % 2 == 1 && rightNeighbour != MPI_PROC_NULL)
MPI_Sendrecv_replace(&bufferRight[0], len, MPI_DOUBLE, rightNeighbour, 1,
rightNeighbour, 1, MPI_COMM_WORLD, &status);
else if (rank % 2 == 0 && leftNeighbour != MPI_PROC_NULL)
MPI_Sendrecv_replace(&bufferLeft[0], len, MPI_DOUBLE, leftNeighbour, 1,
leftNeighbour, 1, MPI_COMM_WORLD, &status);
使用MPI\u-Send
和MPI\u-Recv
但它似乎是死锁。使用MPI\u Send
和MPI\u Recv
有没有简单的方法可以实现同样的效果
我试过使用
if(rank %2==0 && rightNeighbour != MPI_PROC_NULL){
MPI_Recv(&bufferRight[0], len, MPI_DOUBLE, rightNeighbour, 1,
MPI_COMM_WORLD, &status);
MPI_Send(&bufferRight[0], len, MPI_DOUBLE, rightNeighbour, 1,
MPI_COMM_WORLD);
}
最好使用
MPI\u-Irecv
和MPI\u-Isend
而不是阻塞调用(MPI\u-Recv
和MPI\u-Send
)。然后,在发出通信例程之后,只需使用MPI_Waitall
等待请求(或两次调用MPI_wait
)。但是,要做到这一点,您不能使用同一个缓冲区(即替换),您需要有两个单独的缓冲区,否则它们会损坏,因为缓冲区可能在实际发送之前替换内容
让A
作为传入缓冲区,而B
作为传出缓冲区,您的代码应该如下所示
if(rank %2==0 && rightNeighbour != MPI_PROC_NULL){
MPI_Request req[2];
MPI_Status status[2];
MPI_Irecv (&A, len, MPI_DOUBLE, rightNeighbour, 1,
MPI_COMM_WORLD, &req[0]);
MPI_Isend (&B, len, MPI_DOUBLE, rightNeighbour, 1,
MPI_COMM_WORLD, &req[1]);
/* A */
MPI_Waitall (2, req, status);
}
请注意,在
/*A*/
中,您可以利用通信飞行时进行一些计算。此外,代码中省略了错误检查——您最好检查MPI调用的所有返回代码。您最好使用MPI\u-Irecv
和MPI\u-Isend
而不是阻塞调用(MPI\u-Recv
和MPI\u-Send
)。然后,在发出通信例程之后,只需使用MPI_Waitall
等待请求(或两次调用MPI_wait
)。但是,要做到这一点,您不能使用同一个缓冲区(即替换),您需要有两个单独的缓冲区,否则它们会损坏,因为缓冲区可能在实际发送之前替换内容
让A
作为传入缓冲区,而B
作为传出缓冲区,您的代码应该如下所示
if(rank %2==0 && rightNeighbour != MPI_PROC_NULL){
MPI_Request req[2];
MPI_Status status[2];
MPI_Irecv (&A, len, MPI_DOUBLE, rightNeighbour, 1,
MPI_COMM_WORLD, &req[0]);
MPI_Isend (&B, len, MPI_DOUBLE, rightNeighbour, 1,
MPI_COMM_WORLD, &req[1]);
/* A */
MPI_Waitall (2, req, status);
}
请注意,在/*A*/
中,您可以利用通信飞行时进行一些计算。此外,代码中省略了错误检查——您最好检查MPI调用的所有返回代码。此代码:
if(rank % 2 == 0 && rightNeighbour != MPI_PROC_NULL)
MPI_Sendrecv_replace(&bufferRight[0], len, MPI_DOUBLE, rightNeighbour, 1,
rightNeighbour, 1, MPI_COMM_WORLD, &status);
else if(rank % 2 == 1 && leftNeighbour != MPI_PROC_NULL)
MPI_Sendrecv_replace(&bufferLeft[0], len, MPI_DOUBLE, leftNeighbour, 1,
leftNeighbour, 1, MPI_COMM_WORLD, &status);
相当于:
if(rank % 2 == 0)
{
MPI_Send(&bufferRight[0], len, MPI_DOUBLE, rightNeighbour, 1,
MPI_COMM_WORLD);
MPI_Recv(&bufferRight[0], len, MPI_DOUBLE, rightNeighbour, 1,
MPI_COMM_WORLD, &status);
}
else if(rank % 2 == 1)
{
double *temp = malloc(len * sizeof(double));
MPI_Recv(temp, len, MPI_DOUBLE, leftNeighbour, 1,
MPI_COMM_WORLD, &status);
MPI_Send(&bufferLeft[0], len, MPI_DOUBLE, leftNeighbour, 1,
MPI_COMM_WORLD, &status);
memcpy(&bufferLeft[0], temp, len * sizeof(double));
free(temp);
}
请注意,发送和接收呼叫的顺序在奇数列中是相反的。此外,接收使用临时缓冲区来实现MPI\u Sendrecv\u replace
的语义,该语义保证首先发送缓冲区中的数据,然后才用接收到的数据覆盖
请注意,检查列组是否不是MPI\u PROC\u NULL
是毫无意义的,因为从MPI\u PROC\u NULL
发送到/接收基本上是无操作的,并且总是成功的。MPI\u PROC\u NULL
语义的一个关键思想是,如果,则有助于编写不包含此类的对称代码。此代码:
if(rank % 2 == 0 && rightNeighbour != MPI_PROC_NULL)
MPI_Sendrecv_replace(&bufferRight[0], len, MPI_DOUBLE, rightNeighbour, 1,
rightNeighbour, 1, MPI_COMM_WORLD, &status);
else if(rank % 2 == 1 && leftNeighbour != MPI_PROC_NULL)
MPI_Sendrecv_replace(&bufferLeft[0], len, MPI_DOUBLE, leftNeighbour, 1,
leftNeighbour, 1, MPI_COMM_WORLD, &status);
相当于:
if(rank % 2 == 0)
{
MPI_Send(&bufferRight[0], len, MPI_DOUBLE, rightNeighbour, 1,
MPI_COMM_WORLD);
MPI_Recv(&bufferRight[0], len, MPI_DOUBLE, rightNeighbour, 1,
MPI_COMM_WORLD, &status);
}
else if(rank % 2 == 1)
{
double *temp = malloc(len * sizeof(double));
MPI_Recv(temp, len, MPI_DOUBLE, leftNeighbour, 1,
MPI_COMM_WORLD, &status);
MPI_Send(&bufferLeft[0], len, MPI_DOUBLE, leftNeighbour, 1,
MPI_COMM_WORLD, &status);
memcpy(&bufferLeft[0], temp, len * sizeof(double));
free(temp);
}
请注意,发送和接收呼叫的顺序在奇数列中是相反的。此外,接收使用临时缓冲区来实现MPI\u Sendrecv\u replace
的语义,该语义保证首先发送缓冲区中的数据,然后才用接收到的数据覆盖
请注意,检查列组是否不是MPI\u PROC\u NULL
是毫无意义的,因为从MPI\u PROC\u NULL
发送到/接收基本上是无操作的,并且总是成功的。MPI_PROC_NULL
语义的一个关键思想是,如果的话,可以方便地编写不包含此类的对称代码。尽管最好的方法可能是遵循Harald关于使用MPI Isend
和MPI Irecv
的建议,根据具体情况,有一种选择可能不起作用
对于较小的缓冲区大小,一些MPI实现遵循所谓的“急切模式”。在这种模式下,无论接收者是否已经在等待消息,都会发送消息。发送缓冲区数据被复制到临时缓冲区,由于用户的发送缓冲区在复制完成后可用,MPI_send
在通信实际完成之前返回
这有点冒险,因为对于大型消息,MPI通常在会合模式下工作,这实际上会同步两端,因此以下代码也会产生死锁:
if(rank %2==0 && rightNeighbour != MPI_PROC_NULL){
MPI_Send(&bufferRight[0], len, MPI_DOUBLE, rightNeighbour, 1,
MPI_COMM_WORLD);
MPI_Recv(&bufferRight[0], len, MPI_DOUBLE, rightNeighbour, 1,
MPI_COMM_WORLD, &status);
}
然而,在MPI_Recv
中,接收缓冲区(显然)在数据接收之前不可用
有关的其他信息。尽管最好的方法可能是遵循Harald关于使用MPI Isend
和MPI Irecv
的建议,但根据具体情况,有一种方法可能不起作用
对于较小的缓冲区大小,一些MPI实现遵循所谓的“急切模式”。在这种模式下,无论接收者是否已经在等待消息,都会发送消息。发送缓冲区数据被复制到临时缓冲区,由于用户的发送缓冲区在复制完成后可用,MPI_send
在通信实际完成之前返回
这有点冒险,因为对于大型消息,MPI通常在会合模式下工作,这实际上会同步两端,因此以下代码也会产生死锁:
if(rank %2==0 && rightNeighbour != MPI_PROC_NULL){
MPI_Send(&bufferRight[0], len, MPI_DOUBLE, rightNeighbour, 1,
MPI_COMM_WORLD);
MPI_Recv(&bufferRight[0], len, MPI_DOUBLE, rightNeighbour, 1,
MPI_COMM_WORLD, &status);
}
然而,在MPI_Recv
中,接收缓冲区(显然)在数据接收之前不可用
有关的其他信息。@\hr当(秩%2==0)
时,您为什么不使用临时缓冲区?@Manolete:因为MPI\u Send
是一个阻塞操作-它仅在缓冲区的内容被MPI消耗后返回,因此将同一缓冲区传递给MPI\u Recv
是安全的,这将覆盖以前的内容。@_hr当(秩%2==0)
时,为什么不使用临时缓冲区?@Manolete:因为MPI\u Send
是一个阻塞操作-它只在