使用非阻塞操作计算数组的总和
Calculating sum of array with nonblocking operations
每个进程都需要计算自己的部分和,并将其发送给0进程,然后统计数组的和
我写了这段代码
double* a;
a = new double[N];
for (int i = 0; i < N; i++)
a[i] = 1.0;
int k = (N - 1) / proc_count + 1;
int ibeg = proc_this * k;
int iend = (proc_this + 1) * k - 1;
if (ibeg >= N)
iend = ibeg - 1;
else if(iend >= N)
iend = N - 1;
double s = 0;
for (int i = ibeg; i <= iend; i++)
s += a[i];
MPI_Status* stats = new MPI_Status[proc_count];
MPI_Request* reqs = new MPI_Request[proc_count];
double* inmes = new double[proc_count];
inmes[0] = s;
if (proc_this != 0)
MPI_Isend(&s, 1, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD, &reqs[proc_this]);
else
for (int i = 1; i < proc_count; i++)
MPI_Irecv(&inmes[i], 1, MPI_DOUBLE, i, 0, MPI_COMM_WORLD, &reqs[0]);
MPI_Waitall(proc_count, reqs, stats);
MPI_Finalize();
if (proc_this == 0) {
for (int i = 1; i<proc_count; i++)
inmes[0] += inmes[i];
printf("sum = %f", inmes[0]);
}
delete[] a;
但是一直报错
Fatal error in PMPI_Waitall: Invalid MPI_Request, error stack:
PMPI_Waitall(274): MPI_Waitall(count=1, req_array=00000212B7B24A40, status_array=00000212B7B34740) failed
PMPI_Waitall(250): The supplied request in array element 0 was invalid (kind=3)
你能解释一下我做错了什么吗?
总之,分配完reqs
的所有元素后,立即将其设置为MPI_REQUEST_NULL
。
更长的答案是 MPI 程序 运行 作为一个或多个源程序的多个实例,每个实例(等级)都有自己的一组不共享的变量。当你有:
if (proc_this != 0)
MPI_Isend(&s, 1, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD, &reqs[proc_this]);
else
for (int i = 1; i < proc_count; i++)
MPI_Irecv(&inmes[i], 1, MPI_DOUBLE, i, 0, MPI_COMM_WORLD, &reqs[0]);
您期望结果是 reqs
将充满以下值:
reqs: [ Irecv req | Isend req 1 | Isend req 2 | ... ]
现实情况是您将拥有:
reqs in rank 0: [ Irecv req | ??? | ??? | ... ??? ... ]
reqs in rank 1: [ ??? | Isend req | ??? | ... ??? ... ]
reqs in rank 2: [ ??? | ??? | Isend req | ... ??? ... ]
etc.
其中 ???
代表未初始化的内存。 MPI_Waitall()
是本地操作,它只能看到 reqs
的本地副本。它无法完成其他等级发布的请求。
未初始化的内存中可以有任何值,如果该值导致无效的请求句柄,MPI_Waitall()
将中止并出现错误。如果将所有请求设置为 MPI_REQUEST_NULL
,则不会发生这种情况,因为将忽略空请求。
您的代码中还有语义错误:
MPI_Irecv(&inmes[i], 1, MPI_DOUBLE, i, 0, MPI_COMM_WORLD, &reqs[0]);
将所有接收请求存储在同一位置,每个新请求都会覆盖前一个请求。所以你将永远无法等待除最后一个请求之外的任何请求。
鉴于您在 MPI_Isend()
之后立即调用 MPI_Waitall()
,使用 non-blocking 发送没有任何意义。一个更简洁的代码版本是:
if (proc_this != 0)
MPI_Send(&s, 1, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD);
else {
MPI_Request* reqs = new MPI_Request[proc_count - 1];
for (int i = 1; i < proc_count; i++)
MPI_Irecv(&inmes[i], 1, MPI_DOUBLE, i, 0, MPI_COMM_WORLD, &reqs[i-1]);
MPI_Waitall(proc_count-1, reqs, MPI_STATUSES_IGNORE);
delete [] reqs;
}
每个进程都需要计算自己的部分和,并将其发送给0进程,然后统计数组的和 我写了这段代码
double* a;
a = new double[N];
for (int i = 0; i < N; i++)
a[i] = 1.0;
int k = (N - 1) / proc_count + 1;
int ibeg = proc_this * k;
int iend = (proc_this + 1) * k - 1;
if (ibeg >= N)
iend = ibeg - 1;
else if(iend >= N)
iend = N - 1;
double s = 0;
for (int i = ibeg; i <= iend; i++)
s += a[i];
MPI_Status* stats = new MPI_Status[proc_count];
MPI_Request* reqs = new MPI_Request[proc_count];
double* inmes = new double[proc_count];
inmes[0] = s;
if (proc_this != 0)
MPI_Isend(&s, 1, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD, &reqs[proc_this]);
else
for (int i = 1; i < proc_count; i++)
MPI_Irecv(&inmes[i], 1, MPI_DOUBLE, i, 0, MPI_COMM_WORLD, &reqs[0]);
MPI_Waitall(proc_count, reqs, stats);
MPI_Finalize();
if (proc_this == 0) {
for (int i = 1; i<proc_count; i++)
inmes[0] += inmes[i];
printf("sum = %f", inmes[0]);
}
delete[] a;
但是一直报错
Fatal error in PMPI_Waitall: Invalid MPI_Request, error stack:
PMPI_Waitall(274): MPI_Waitall(count=1, req_array=00000212B7B24A40, status_array=00000212B7B34740) failed
PMPI_Waitall(250): The supplied request in array element 0 was invalid (kind=3)
你能解释一下我做错了什么吗?
总之,分配完reqs
的所有元素后,立即将其设置为MPI_REQUEST_NULL
。
更长的答案是 MPI 程序 运行 作为一个或多个源程序的多个实例,每个实例(等级)都有自己的一组不共享的变量。当你有:
if (proc_this != 0)
MPI_Isend(&s, 1, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD, &reqs[proc_this]);
else
for (int i = 1; i < proc_count; i++)
MPI_Irecv(&inmes[i], 1, MPI_DOUBLE, i, 0, MPI_COMM_WORLD, &reqs[0]);
您期望结果是 reqs
将充满以下值:
reqs: [ Irecv req | Isend req 1 | Isend req 2 | ... ]
现实情况是您将拥有:
reqs in rank 0: [ Irecv req | ??? | ??? | ... ??? ... ]
reqs in rank 1: [ ??? | Isend req | ??? | ... ??? ... ]
reqs in rank 2: [ ??? | ??? | Isend req | ... ??? ... ]
etc.
其中 ???
代表未初始化的内存。 MPI_Waitall()
是本地操作,它只能看到 reqs
的本地副本。它无法完成其他等级发布的请求。
未初始化的内存中可以有任何值,如果该值导致无效的请求句柄,MPI_Waitall()
将中止并出现错误。如果将所有请求设置为 MPI_REQUEST_NULL
,则不会发生这种情况,因为将忽略空请求。
您的代码中还有语义错误:
MPI_Irecv(&inmes[i], 1, MPI_DOUBLE, i, 0, MPI_COMM_WORLD, &reqs[0]);
将所有接收请求存储在同一位置,每个新请求都会覆盖前一个请求。所以你将永远无法等待除最后一个请求之外的任何请求。
鉴于您在 MPI_Isend()
之后立即调用 MPI_Waitall()
,使用 non-blocking 发送没有任何意义。一个更简洁的代码版本是:
if (proc_this != 0)
MPI_Send(&s, 1, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD);
else {
MPI_Request* reqs = new MPI_Request[proc_count - 1];
for (int i = 1; i < proc_count; i++)
MPI_Irecv(&inmes[i], 1, MPI_DOUBLE, i, 0, MPI_COMM_WORLD, &reqs[i-1]);
MPI_Waitall(proc_count-1, reqs, MPI_STATUSES_IGNORE);
delete [] reqs;
}