C语言 优化任何大整数的 MPI 代码



我正在尝试使用蒙特卡罗方法计算圆周率的估计值。 我使用的方法是将圆周刻入正方形。

这是我解决问题的简单代码:

#define _XOPEN_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <mpi.h>
#define SEED time(NULL)
void pi_mpi_version(void) {
unsigned int seed;
long long int all_point;
double x, y, start, end;
int rank, size;
long long int i, points = 0, all_intern;
MPI_Init(NULL, NULL);
start = MPI_Wtime();
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Comm_size(MPI_COMM_WORLD, &size);
seed = SEED + rank;
for ( i = 0; i < 1000000000; i++ ) {
x = (double) rand_r(&seed) / RAND_MAX;
y = (double) rand_r(&seed) / RAND_MAX;
if ( x * x + y * y <= 1.0 ) points++;
}
MPI_Reduce(&points, &all_intern, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD);
all_point = 1000000000 * size;
MPI_Barrier(MPI_COMM_WORLD);
end = MPI_Wtime();
if ( rank == 0 ) {
printf("All intern: %lldn", all_intern);
printf("All points: %lldn", all_point);
printf("u03C0 u2248 %Lfn", (long double) all_intern / all_point * 4.0);
printf("Time elapsed: %.4fn", end - start);
}
MPI_Finalize();
}

我尝试将所有变量设置为long long int但输入1000000000,如代码中一样,返回负数。输入高达100000000它似乎有效。

这实际上是一个比你想象的更有趣的问题。您的结果是两个问题的组合:溢出和类型混淆。

首先是溢出部分: 大于 2147483647 的数字不能使用有符号 32 位整数表示,这是许多C++编译器中int的默认大小,也是 MPI 用于MPI_INT的内部表示的大小。因此,只要您的 MPI 秩超过 2-3,1000000000 * pi / 4级中的部分计数仍然可以表示为int,但它们的总和不能 - 结果溢出并变成负数。生产线也是如此

1000000000 * size

没有任何后缀,大小和 1000000000 都是类型int,他们的产品也是如此。因此,此计算将溢出,只有这样,结果才会存储在较大的long long int all_points变量中。 简单修复:将计算替换为

1000000000LL * size

这强制第一个整数的类型为long long int,因此整个计算不会溢出。

但是您的代码还有第二个更严重的问题:类型混淆。

MPI_Reduce的发送和接收缓冲区都需要一个void*值,这意味着您可以使用任何指针作为参数。现在的类型混淆是MPI_Reduce期望你传递一个指向int的指针,因为你传递了MPI_INT作为类型参数。但是您传递的指针指向一个long long int.您需要改用的是MPI_LONG_LONG_INT,它对应于long long int类型。

如果你想知道为什么你的代码仍然适用于较小的迭代计数,你可以阅读整数的二进制表示,特别是小端/大端。

最新更新