替代 MPI_Sendrecv_replace
Alternative to MPI_Sendrecv_replace
我正在尝试获取
的替代代码
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_Send
和 MPI_Recv
但它似乎陷入了僵局。使用 MPI_Send
和 MPI_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_Irecv
和 MPI_Isend
而不是阻塞调用(MPI_Recv
和 MPI_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 调用的所有 return 代码。
此代码:
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_Sendrecv_replace
的语义,这保证了缓冲区中的数据首先被发送,然后才被接收到的数据覆盖。
请注意,检查排名是否不是 MPI_PROC_NULL
是没有意义的,因为从 MPI_PROC_NULL
发送 to/receive 本质上是空操作,并且总是会成功。 MPI_PROC_NULL
语义的关键思想之一是促进不包含此类 if
的对称代码的编写。
尽管 可能最好的方法是遵循 Harald 的 关于使用 MPI_Isend
和 MPI_Irecv
的建议,但有一种替代方法可能不起作用,具体取决于案.
对于较小的缓冲区大小,一些 MPI 实现遵循所谓的 eager 模式。在这种模式下,无论接收者是否已经在等待消息,消息都会被发送。发送缓冲区数据被复制到临时缓冲区,并且由于复制完成后用户的发送缓冲区可用,MPI_Send
returns 在通信实际完成之前.
这有点冒险,因为对于大消息,MPI 通常工作在 rendezvous 模式,实际上 synchronizes 两端,所以以下代码也会产生死锁:
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
中,接收缓冲区(显然)在数据已被接收之前不可用。
有关 MPI performance tips 的更多信息。
我正在尝试获取
的替代代码 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_Send
和 MPI_Recv
但它似乎陷入了僵局。使用 MPI_Send
和 MPI_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_Irecv
和 MPI_Isend
而不是阻塞调用(MPI_Recv
和 MPI_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 调用的所有 return 代码。
此代码:
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_Sendrecv_replace
的语义,这保证了缓冲区中的数据首先被发送,然后才被接收到的数据覆盖。
请注意,检查排名是否不是 MPI_PROC_NULL
是没有意义的,因为从 MPI_PROC_NULL
发送 to/receive 本质上是空操作,并且总是会成功。 MPI_PROC_NULL
语义的关键思想之一是促进不包含此类 if
的对称代码的编写。
尽管 可能最好的方法是遵循 Harald 的 关于使用 MPI_Isend
和 MPI_Irecv
的建议,但有一种替代方法可能不起作用,具体取决于案.
对于较小的缓冲区大小,一些 MPI 实现遵循所谓的 eager 模式。在这种模式下,无论接收者是否已经在等待消息,消息都会被发送。发送缓冲区数据被复制到临时缓冲区,并且由于复制完成后用户的发送缓冲区可用,MPI_Send
returns 在通信实际完成之前.
这有点冒险,因为对于大消息,MPI 通常工作在 rendezvous 模式,实际上 synchronizes 两端,所以以下代码也会产生死锁:
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
中,接收缓冲区(显然)在数据已被接收之前不可用。
有关 MPI performance tips 的更多信息。