测试或等待前的非阻塞通信缓冲区操作
Non-blocking communication buffer manipulation before test or wait
MPI 标准规定,一旦为非阻塞通信函数提供了缓冲区,应用程序就不能使用它,直到操作完成(即,直到成功的 TEST 或 WAIT 函数之后)。
这是否也适用于以下情况:
我有一个缓冲区,它的每一部分都会进入不同的处理器,例如,它的一部分将从处理器本身可用的数据中复制。
是否允许我在每个处理器上 MPI_Irecv
缓冲区中与其他处理器不同的部分,复制处理器中可用的部分,然后 MPI_Isend
应该交给其他人的数据,做我的其他事情计算,MPI_Waitall
所以我的发送和接收完成了吗?
n=0;
for (i = 0; i < size; i++) {
if (i != rank) {
MPI_Irecv(&recvdata[i*100], 100, MPI_INT, i, i, comm, &requests[n]);
n++;
}
}
process(&recvdata[rank*100], 100);
for (i = 0; i < size; i++) {
if (i != rank) {
MPI_Isend(&senddata[i*100], 100, MPI_INT, i, rank, comm, &requests[n]);
n++;
}
}
MPI_Waitall(n, requests, statuses);
我不是 100% 确定我理解你的问题,所以我先重申一下问题:
If I have a large array of data, can I create nonblocking calls to receive data from subsets of the array and then send the data back out to other processes?
答案是肯定的,只要您同步接收和发送即可。请记住,在使用 MPI_WAIT
完成调用之前,来自 MPI_IRECV
的数据不会到达,因此在此之前您不能将其发送到另一个进程。否则,发送端将发送当时缓冲区中的任何垃圾。
因此您的代码可以看起来像这样并且是安全的:
for (i = 0; i < size; i++)
MPI_Irecv(&data[i*100], 100, MPI_INT, i, 0, comm, &requests[i]);
/* No touching data in here */
MPI_Waitall(i, requests, statuses);
/* You can touch data here */
for (i = 0; i < size; i++)
MPI_Isend(&data[i*100], 100, MPI_INT, i+1, 0, comm); /* i+1 is wherever you want to send the data */
/* No touching data in here either */
MPI_Waitall(i, requests, statuses);
在整个 MPI 标准中,使用术语 locations 而不是术语 variables 以防止此类混淆。只要未完成的 MPI 操作在不相交的 内存位置 集合上运行,MPI 库就不会关心内存来自何处。不同的内存位置可能是不同的变量或大数组的不同元素。事实上,整个进程内存可以被认为是一个大的匿名字节数组。
在很多情况下,给定一组不同的变量声明可以实现相同的内存布局。例如,对于大多数 x86/x64 C/C++ 编译器,以下两组局部变量声明将导致相同的堆栈布局:
int a, b; int d[3];
int c;
| .... | | .... | |
+--------------+ +--------------+ |
| a | | d[2] | |
+--------------+ +--------------+ | lower addresses
| b | | d[1] | |
+--------------+ +--------------+ |
| c | | d[0] | \|/
+--------------+ +--------------+ V
在那种情况下:
int a, b;
int c;
MPI_Irecv(&a, 1, MPI_INT, ..., &req[0]);
MPI_Irecv(&c, 1, MPI_INT, ..., &req[1]);
MPI_Waitall(2, &req, MPI_STATUSES_IGNORE);
相当于:
int d[3];
MPI_Irecv(&d[2], 1, MPI_INT, ..., &req[0]);
MPI_Irecv(&d[0], 1, MPI_INT, ..., &req[1]);
MPI_Waitall(2, &req, MPI_STATUSES_IGNORE);
在第二种情况下,虽然d[0]
和d[2]
属于同一个变量,但是&d[0]
和&d[2]
指定不同并且 - 结合..., 1, MPI_INT, ...
- 不相交的内存位置。
无论如何,请确保您不会同时读取和写入同一内存位置。
下面是 Wesley Bland 给出的示例的更复杂版本。它通过使用 MPI_Waitsome
来重叠发送和接收操作:
MPI_Request rreqs[size], sreqs[size];
for (i = 0; i < size; i++)
MPI_Irecv(&data[i*100], 100, MPI_INT, i, 0, comm, &rreqs[i]);
while (1)
{
int done_idx[size], numdone;
MPI_Waitsome(size, rreqs, &numdone, done_idx, MPI_STATUSES_IGNORE);
if (numdone == MPI_UNDEFINED)
break;
for (i = 0; i < numdone; i++)
{
int id = done_idx[i];
process(&data[id*100], 100);
MPI_Isend(&data[id*100], 100, MPI_INT, id, 0, comm, &sreqs[id]);
}
}
MPI_Waitall(size, sreqs, MPI_STATUSES_IGNORE);
在那种特殊情况下,使用 size
单独的数组可能会导致代码更具可读性。
MPI 标准规定,一旦为非阻塞通信函数提供了缓冲区,应用程序就不能使用它,直到操作完成(即,直到成功的 TEST 或 WAIT 函数之后)。
这是否也适用于以下情况:
我有一个缓冲区,它的每一部分都会进入不同的处理器,例如,它的一部分将从处理器本身可用的数据中复制。
是否允许我在每个处理器上 MPI_Irecv
缓冲区中与其他处理器不同的部分,复制处理器中可用的部分,然后 MPI_Isend
应该交给其他人的数据,做我的其他事情计算,MPI_Waitall
所以我的发送和接收完成了吗?
n=0;
for (i = 0; i < size; i++) {
if (i != rank) {
MPI_Irecv(&recvdata[i*100], 100, MPI_INT, i, i, comm, &requests[n]);
n++;
}
}
process(&recvdata[rank*100], 100);
for (i = 0; i < size; i++) {
if (i != rank) {
MPI_Isend(&senddata[i*100], 100, MPI_INT, i, rank, comm, &requests[n]);
n++;
}
}
MPI_Waitall(n, requests, statuses);
我不是 100% 确定我理解你的问题,所以我先重申一下问题:
If I have a large array of data, can I create nonblocking calls to receive data from subsets of the array and then send the data back out to other processes?
答案是肯定的,只要您同步接收和发送即可。请记住,在使用 MPI_WAIT
完成调用之前,来自 MPI_IRECV
的数据不会到达,因此在此之前您不能将其发送到另一个进程。否则,发送端将发送当时缓冲区中的任何垃圾。
因此您的代码可以看起来像这样并且是安全的:
for (i = 0; i < size; i++)
MPI_Irecv(&data[i*100], 100, MPI_INT, i, 0, comm, &requests[i]);
/* No touching data in here */
MPI_Waitall(i, requests, statuses);
/* You can touch data here */
for (i = 0; i < size; i++)
MPI_Isend(&data[i*100], 100, MPI_INT, i+1, 0, comm); /* i+1 is wherever you want to send the data */
/* No touching data in here either */
MPI_Waitall(i, requests, statuses);
在整个 MPI 标准中,使用术语 locations 而不是术语 variables 以防止此类混淆。只要未完成的 MPI 操作在不相交的 内存位置 集合上运行,MPI 库就不会关心内存来自何处。不同的内存位置可能是不同的变量或大数组的不同元素。事实上,整个进程内存可以被认为是一个大的匿名字节数组。
在很多情况下,给定一组不同的变量声明可以实现相同的内存布局。例如,对于大多数 x86/x64 C/C++ 编译器,以下两组局部变量声明将导致相同的堆栈布局:
int a, b; int d[3];
int c;
| .... | | .... | |
+--------------+ +--------------+ |
| a | | d[2] | |
+--------------+ +--------------+ | lower addresses
| b | | d[1] | |
+--------------+ +--------------+ |
| c | | d[0] | \|/
+--------------+ +--------------+ V
在那种情况下:
int a, b;
int c;
MPI_Irecv(&a, 1, MPI_INT, ..., &req[0]);
MPI_Irecv(&c, 1, MPI_INT, ..., &req[1]);
MPI_Waitall(2, &req, MPI_STATUSES_IGNORE);
相当于:
int d[3];
MPI_Irecv(&d[2], 1, MPI_INT, ..., &req[0]);
MPI_Irecv(&d[0], 1, MPI_INT, ..., &req[1]);
MPI_Waitall(2, &req, MPI_STATUSES_IGNORE);
在第二种情况下,虽然d[0]
和d[2]
属于同一个变量,但是&d[0]
和&d[2]
指定不同并且 - 结合..., 1, MPI_INT, ...
- 不相交的内存位置。
无论如何,请确保您不会同时读取和写入同一内存位置。
下面是 Wesley Bland 给出的示例的更复杂版本。它通过使用 MPI_Waitsome
来重叠发送和接收操作:
MPI_Request rreqs[size], sreqs[size];
for (i = 0; i < size; i++)
MPI_Irecv(&data[i*100], 100, MPI_INT, i, 0, comm, &rreqs[i]);
while (1)
{
int done_idx[size], numdone;
MPI_Waitsome(size, rreqs, &numdone, done_idx, MPI_STATUSES_IGNORE);
if (numdone == MPI_UNDEFINED)
break;
for (i = 0; i < numdone; i++)
{
int id = done_idx[i];
process(&data[id*100], 100);
MPI_Isend(&data[id*100], 100, MPI_INT, id, 0, comm, &sreqs[id]);
}
}
MPI_Waitall(size, sreqs, MPI_STATUSES_IGNORE);
在那种特殊情况下,使用 size
单独的数组可能会导致代码更具可读性。