为什么这个 OpenMP 程序每次都给我不同的答案?
Why is this OpenMP program giving me different answers every time?
我正在尝试使用多线程制作这个程序运行。
#include <stdio.h>
#include <time.h>
#include <omp.h>
#define NUM_THREADS 4
static long num_steps = 1000000000;
int main()
{
int i;
double x, pi, sum = 0.0;
double step = 1.0/(double)num_steps;
clock_t start = clock(), diff;
#pragma omp parallel for num_threads(NUM_THREADS) reduction (+:sum)
for (i = 0; i < num_steps; i++)
{
x = (i+0.5)*step;
sum += 4.0/(1.0 + x*x);
}
#pragma omp ordered
pi = step*sum;
printf("pi = %.15f\n %d iterations\n", pi, num_steps);
diff = clock() - start;
int msec = diff * 1000 / CLOCKS_PER_SEC;
printf("Time taken %d seconds %d milliseconds", msec/1000, msec%1000);
return 0;
}
通过添加 #pragma omp parallel for num_threads(NUM_THREADS) reduction (+:sum)
。我在 for 循环之后也有 #pragma omp ordered
,我认为我实际上不需要,因为在所有线程完成 for 循环之前,任何线程都不应该继续。这个对吗?这是否也是为什么与 运行 将其作为单线程程序相比,我的性能只提高了大约一秒钟的原因?与我的 7 秒相比,这是 6 秒。
我无法回答的是,为什么每次我 运行 这个程序都会给我不同的圆周率答案?
您的问题来自于您忘记声明 x
private
。
如果将 OpenMP 指令更改为:
#pragma omp parallel for num_threads(NUM_THREADS) reduction(+:sum) private(x)
您的代码生效。
但是,这里还有两个问题:
#pragma omp ordered
没有意义,因为您不在 parallel
区域。你应该删除它。
- 在多线程代码中使用
clock()
测量时间是危险的,不是因为该函数不是线程安全的,而是因为它 returns CPU 当前线程 和 其子线程的时间 ,而不是 已用时间 。因此,无论是否激活 OpenMP,您通常最终得到的结果几乎相同,人们想知道为什么他们的代码没有任何加速......所以除非你有充分的理由使用 clock()
,否则请使用omp_get_wtime()
相反。
除了 Gilles 指出的错误之外,这里还有一个更根本的问题。
跨并行线程的减少不必是确定性的。每线程贡献的组合顺序可以随着代码的每次执行而改变。如果您不知道为什么这很重要,请去阅读“What Every Computer Scientist Should Know About Floating-Point Arithmetic”
如果您还没有明白这一点,请考虑三个线程在支持三位精度的十进制算术机上进行求和归约。
假设我们正在累积集合 (100, -100, 0.1) 如果我们按顺序添加它们,我们将有 100 - 100 = 0 + 0.1 = 0.1,但是如果我们按顺序添加它们 (100, 0.1, - 100),我们将得到 100 + 0.1 = 100(三位有效数字,记住!)-100 == 0
如果您使用的是 Intel 编译器,则可以设置一个环境变量来请求确定性缩减 (KMP_DETERMINISTIC_REDUCTION
),但是它只会在使用相同数量的线程时强制执行确定性。它不会在具有不同线程数的运行之间强制执行它。 (这样做需要对每线程贡献的累积执行一个命令,这将需要不同的代码生成和一些线程间同步)。
我正在尝试使用多线程制作这个程序运行。
#include <stdio.h>
#include <time.h>
#include <omp.h>
#define NUM_THREADS 4
static long num_steps = 1000000000;
int main()
{
int i;
double x, pi, sum = 0.0;
double step = 1.0/(double)num_steps;
clock_t start = clock(), diff;
#pragma omp parallel for num_threads(NUM_THREADS) reduction (+:sum)
for (i = 0; i < num_steps; i++)
{
x = (i+0.5)*step;
sum += 4.0/(1.0 + x*x);
}
#pragma omp ordered
pi = step*sum;
printf("pi = %.15f\n %d iterations\n", pi, num_steps);
diff = clock() - start;
int msec = diff * 1000 / CLOCKS_PER_SEC;
printf("Time taken %d seconds %d milliseconds", msec/1000, msec%1000);
return 0;
}
通过添加 #pragma omp parallel for num_threads(NUM_THREADS) reduction (+:sum)
。我在 for 循环之后也有 #pragma omp ordered
,我认为我实际上不需要,因为在所有线程完成 for 循环之前,任何线程都不应该继续。这个对吗?这是否也是为什么与 运行 将其作为单线程程序相比,我的性能只提高了大约一秒钟的原因?与我的 7 秒相比,这是 6 秒。
我无法回答的是,为什么每次我 运行 这个程序都会给我不同的圆周率答案?
您的问题来自于您忘记声明 x
private
。
如果将 OpenMP 指令更改为:
#pragma omp parallel for num_threads(NUM_THREADS) reduction(+:sum) private(x)
您的代码生效。
但是,这里还有两个问题:
#pragma omp ordered
没有意义,因为您不在parallel
区域。你应该删除它。- 在多线程代码中使用
clock()
测量时间是危险的,不是因为该函数不是线程安全的,而是因为它 returns CPU 当前线程 和 其子线程的时间 ,而不是 已用时间 。因此,无论是否激活 OpenMP,您通常最终得到的结果几乎相同,人们想知道为什么他们的代码没有任何加速......所以除非你有充分的理由使用clock()
,否则请使用omp_get_wtime()
相反。
除了 Gilles 指出的错误之外,这里还有一个更根本的问题。
跨并行线程的减少不必是确定性的。每线程贡献的组合顺序可以随着代码的每次执行而改变。如果您不知道为什么这很重要,请去阅读“What Every Computer Scientist Should Know About Floating-Point Arithmetic”
如果您还没有明白这一点,请考虑三个线程在支持三位精度的十进制算术机上进行求和归约。 假设我们正在累积集合 (100, -100, 0.1) 如果我们按顺序添加它们,我们将有 100 - 100 = 0 + 0.1 = 0.1,但是如果我们按顺序添加它们 (100, 0.1, - 100),我们将得到 100 + 0.1 = 100(三位有效数字,记住!)-100 == 0
如果您使用的是 Intel 编译器,则可以设置一个环境变量来请求确定性缩减 (KMP_DETERMINISTIC_REDUCTION
),但是它只会在使用相同数量的线程时强制执行确定性。它不会在具有不同线程数的运行之间强制执行它。 (这样做需要对每线程贡献的累积执行一个命令,这将需要不同的代码生成和一些线程间同步)。