我从wikipedia获得代码:
#include <stdio.h>
#include <omp.h>
#define N 100
int main(int argc, char *argv[])
{
float a[N], b[N], c[N];
int i;
omp_set_dynamic(0);
omp_set_num_threads(10);
for (i = 0; i < N; i++)
{
a[i] = i * 1.0;
b[i] = i * 2.0;
}
#pragma omp parallel shared(a, b, c) private(i)
{
#pragma omp for
for (i = 0; i < N; i++)
c[i] = a[i] + b[i];
}
printf ("%fn", c[10]);
return 0;
}
我试图用gcc4.5在我的Ubuntu 11.04中编译和运行它(我的配置:Intel C2D T7500M 2.2GHz, 2048Mb RAM),这个程序的工作速度比单线程慢两倍。为什么?
非常简单的答案:增加n。并将线程数设置为与处理器数量相等。
对于您的机器,100是一个非常低的数字。试试更高的数量级。另一个问题是:如何测量计算时间?通常需要花费程序时间才能得到可比较的结果。
我认为编译器在非smp情况下优化了for循环(例如使用SSE指令),而在OMP变体中则不能。
使用gcc -S(或objdump -S)来查看不同变体的程序集。
无论如何,您可能需要注意共享变量,因为它们需要同步,这会使事情变得非常慢。如果你可以"智能"块(查看调度pragma),你可能会减少争用,但是再次:
- 验证发出的代码
- 概要文件
- 不要低估单线程代码的效率(因为缓存局部性和缺乏上下文切换)
- 设置线程数为cpu数(让openMP为您决定!);除非你的线程团队有一个专用任务的主线程,在这种情况下,分配一个额外的线程可能是有价值的
- 粗粒度并行性(您的示例位于频谱的细粒度端)
- 没有共享数据
您面临的问题是错误的内存共享。每个线程都应该有自己私有的c[i]。
#pragma omp parallel shared(a, b) private(i, c)运行下面的代码,看看有什么不同。
1)。OpenMP有一个开销,所以运行时必须大于这个开销才能看到好处。
2)。不要自己设置线程的数量。通常我使用默认线程。但是,如果您的处理器具有超线程,则可以通过将线程数设置为与内核数相等来获得更好的性能。对于超线程,默认的线程数将是内核数的两倍。例如,在我的机器上,我有四个内核,默认的线程数是8个。通过将它设置为4,在某些情况下我得到更好的结果,而在其他情况下我得到更差的结果。
3)。c中有一些虚假共享,但只要N
足够大(它需要克服开销),虚假共享就不会造成太大的问题。你可以调整块的大小,但我不认为它会有帮助。
4)。缓存的问题。您至少有四个级别的内存(这些值适用于我的系统):L1 (32Kb)、L2(256Kb)、L3(12Mb)和主内存(>>12Mb)。当你进入更高的层次时,并行的好处就会减少。然而,在下面的示例中,我将N设置为1亿个浮点数,即4亿字节或大约381Mb,并且使用多线程仍然要快得多。尝试调整N
,看看会发生什么。例如,尝试将N
设置为缓存级别/4(一个浮点数为4字节)(数组a和b也需要在缓存中,因此您可能需要将N设置为缓存级别/12)。但是,如果N
太小,您将与OpenMP开销作斗争(这就是您问题中的代码所做的)。
#include <stdio.h>
#include <omp.h>
#define N 100000000
int main(int argc, char *argv[]) {
float *a = new float[N];
float *b = new float[N];
float *c = new float[N];
int i;
for (i = 0; i < N; i++) {
a[i] = i * 1.0;
b[i] = i * 2.0;
}
double dtime;
dtime = omp_get_wtime();
for (i = 0; i < N; i++) {
c[i] = a[i] + b[i];
}
dtime = omp_get_wtime() - dtime;
printf ("time %f, %fn", dtime, c[10]);
dtime = omp_get_wtime();
#pragma omp parallel for private(i)
for (i = 0; i < N; i++) {
c[i] = a[i] + b[i];
}
dtime = omp_get_wtime() - dtime;
printf ("time %f, %fn", dtime, c[10]);
return 0;
}