Ubuntu中的OpenMP:并行程序在双核处理器上运行,速度比单线程慢两倍.为什么



我从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为您决定!);除非你的线程团队有一个专用任务的主线程,在这种情况下,分配一个额外的线程可能是有价值的
在我尝试应用OMP进行并行化的所有情况下,大约70%的情况较慢。在 的情况下,加速是明显的
  • 粗粒度并行性(您的示例位于频谱的细粒度端)
  • 没有共享数据

您面临的问题是错误的内存共享。每个线程都应该有自己私有的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;
}

最新更新