为什么执行此 C MPI 应用程序进行 Pi 近似时的结果总是相同?
Why is the result when executing this C MPI application for Pi approximation always the same?
这个用于 Pi 近似的 C MPI 应用程序总是根据问题大小打印出相同的结果,即随机生成的点数 (npts
)。
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "mpi.h"
int main(int argc, char *argv[]) {
int myid,nprocs;
double PI25DT = 3.141592653589793238462643;
long long npts = 1e10;
long i,mynpts;
long double f,sum,mysum;
long double xmin,xmax,x;
MPI_Init(&argc,&argv);
MPI_Comm_size(MPI_COMM_WORLD,&nprocs);
MPI_Comm_rank(MPI_COMM_WORLD,&myid);
if (myid == 0) {
mynpts = npts - (nprocs-1)*(npts/nprocs);
} else {
mynpts = npts/nprocs;
}
mysum = 0.0;
xmin = 0.0;
xmax = 1.0;
srand(myid);
for (i=0; i<mynpts; i++) {
x = (long double) rand()/RAND_MAX*(xmax-xmin) + xmin;
mysum += 4.0/(1.0 + x*x);
}
MPI_Reduce(&mysum,&sum,1,MPI_LONG_DOUBLE,MPI_SUM,0,MPI_COMM_WORLD);
if (myid == 0) {
f = sum/npts;
printf("PI calculated with %lld points = %.16f \n",npts,f);
printf("Error is: %.16f \n",fabs(f-PI25DT));
}
MPI_Finalize();
}
这是输出。我认为每个 运行 应用程序的结果应该有所不同。我在一个有 128 个节点的集群上执行它。
$ mpicc pi.c -o /mnt/cluster_128/pi
$ mpirun -np 128 --hostfile hosts_4cores_128.mpi /mnt/cluster_128/pi
PI calculated with 10000000000 points = 3.1415901444578158
Error is: 0.0000025091319773
$ mpirun -np 128 --hostfile hosts_4cores_128.mpi /mnt/cluster_128/pi
PI calculated with 10000000000 points = 3.1415901444578158
Error is: 0.0000025091319773
$ mpirun -np 128 --hostfile hosts_4cores_128.mpi /mnt/cluster_128/pi
PI calculated with 10000000000 points = 3.1415901444578158
Error is: 0.0000025091319773
$ mpirun -np 128 --hostfile hosts_4cores_128.mpi /mnt/cluster_128/pi
PI calculated with 10000000000 points = 3.1415901444578158
Error is: 0.0000025091319773
您正在此处播种 PRNG:
srand(myid);
myid
是您调用 MPI_Comm_rank()
设置的值,您只对 myid == 0
的结果感兴趣,因此这始终是相同的值。播种相同的值会产生相同的 "random" 数字序列。
改用播种的常用习语:
#include <time.h>
#include <stdlib.h>
[...]
srand(time(0));
您的代码仅在 if (myid == 0)
显示您的 RNG 种子无用时才打印任何结果。
我建议
srand((unsigned)time(NULL));
现有的答案已经指出了正确的方向,但我认为他们的解释并不完全正确。
如前所述,问题出在 RNG 种子上。您正在使用 myid
作为 srand
的种子,它始终是 0
和 N-1
之间的数字(N
等级数,即数字你传递给 mpiexec -np N executable
命令)。这是确定性的:它不会因不同的执行而改变。
因此,所有 N 个进程都将创建相同的种子集,并且对于相同数量的进程,生成的随机数将相同:
- 对于单个进程(N=1):种子值始终为 0
- 对于五个进程(N=5):种子值在每个进程中总是0、1、2、3和4。等等。
MPI_Reduce
集体收集每个进程的所有部分结果(由于随机数相同,它们产生相同的值)并在根进程中打印总和(myid=0
).具有 myid
的进程是否打印结果并不重要,因为还有其他 N-1 个具有不同 myid
值的进程对正在打印的最终结果有贡献。
2 个进程的小示例,myid
:
等级 0: srand(0)
, 计算部分和 (mysum
), 在 [=23] 中累加其他进程的部分和=] 变量(您在 MPI_Reduce
第 7 个参数中指定 0
作为根进程。打印结果(myid==0
是 true
)。
等级 1:srand(1)
,计算部分和 (mysum
) 将其发送到 MPI_Reduce
根进程 ( 0
)。不打印结果(myid==0
是 false
)。
对于 MPI,您需要了解的最重要的一点是,您通常会使用多个进程执行单个程序,并且这些进程中的每一个都会获得不同的 environment(输出参数MPI_Comm_rank
).
这个用于 Pi 近似的 C MPI 应用程序总是根据问题大小打印出相同的结果,即随机生成的点数 (npts
)。
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "mpi.h"
int main(int argc, char *argv[]) {
int myid,nprocs;
double PI25DT = 3.141592653589793238462643;
long long npts = 1e10;
long i,mynpts;
long double f,sum,mysum;
long double xmin,xmax,x;
MPI_Init(&argc,&argv);
MPI_Comm_size(MPI_COMM_WORLD,&nprocs);
MPI_Comm_rank(MPI_COMM_WORLD,&myid);
if (myid == 0) {
mynpts = npts - (nprocs-1)*(npts/nprocs);
} else {
mynpts = npts/nprocs;
}
mysum = 0.0;
xmin = 0.0;
xmax = 1.0;
srand(myid);
for (i=0; i<mynpts; i++) {
x = (long double) rand()/RAND_MAX*(xmax-xmin) + xmin;
mysum += 4.0/(1.0 + x*x);
}
MPI_Reduce(&mysum,&sum,1,MPI_LONG_DOUBLE,MPI_SUM,0,MPI_COMM_WORLD);
if (myid == 0) {
f = sum/npts;
printf("PI calculated with %lld points = %.16f \n",npts,f);
printf("Error is: %.16f \n",fabs(f-PI25DT));
}
MPI_Finalize();
}
这是输出。我认为每个 运行 应用程序的结果应该有所不同。我在一个有 128 个节点的集群上执行它。
$ mpicc pi.c -o /mnt/cluster_128/pi
$ mpirun -np 128 --hostfile hosts_4cores_128.mpi /mnt/cluster_128/pi
PI calculated with 10000000000 points = 3.1415901444578158
Error is: 0.0000025091319773
$ mpirun -np 128 --hostfile hosts_4cores_128.mpi /mnt/cluster_128/pi
PI calculated with 10000000000 points = 3.1415901444578158
Error is: 0.0000025091319773
$ mpirun -np 128 --hostfile hosts_4cores_128.mpi /mnt/cluster_128/pi
PI calculated with 10000000000 points = 3.1415901444578158
Error is: 0.0000025091319773
$ mpirun -np 128 --hostfile hosts_4cores_128.mpi /mnt/cluster_128/pi
PI calculated with 10000000000 points = 3.1415901444578158
Error is: 0.0000025091319773
您正在此处播种 PRNG:
srand(myid);
myid
是您调用 MPI_Comm_rank()
设置的值,您只对 myid == 0
的结果感兴趣,因此这始终是相同的值。播种相同的值会产生相同的 "random" 数字序列。
改用播种的常用习语:
#include <time.h>
#include <stdlib.h>
[...]
srand(time(0));
您的代码仅在 if (myid == 0)
显示您的 RNG 种子无用时才打印任何结果。
我建议
srand((unsigned)time(NULL));
现有的答案已经指出了正确的方向,但我认为他们的解释并不完全正确。
如前所述,问题出在 RNG 种子上。您正在使用 myid
作为 srand
的种子,它始终是 0
和 N-1
之间的数字(N
等级数,即数字你传递给 mpiexec -np N executable
命令)。这是确定性的:它不会因不同的执行而改变。
因此,所有 N 个进程都将创建相同的种子集,并且对于相同数量的进程,生成的随机数将相同:
- 对于单个进程(N=1):种子值始终为 0
- 对于五个进程(N=5):种子值在每个进程中总是0、1、2、3和4。等等。
MPI_Reduce
集体收集每个进程的所有部分结果(由于随机数相同,它们产生相同的值)并在根进程中打印总和(myid=0
).具有 myid
的进程是否打印结果并不重要,因为还有其他 N-1 个具有不同 myid
值的进程对正在打印的最终结果有贡献。
2 个进程的小示例,myid
:
等级 0:
srand(0)
, 计算部分和 (mysum
), 在 [=23] 中累加其他进程的部分和=] 变量(您在MPI_Reduce
第 7 个参数中指定0
作为根进程。打印结果(myid==0
是true
)。等级 1:
srand(1)
,计算部分和 (mysum
) 将其发送到MPI_Reduce
根进程 (0
)。不打印结果(myid==0
是false
)。
对于 MPI,您需要了解的最重要的一点是,您通常会使用多个进程执行单个程序,并且这些进程中的每一个都会获得不同的 environment(输出参数MPI_Comm_rank
).