为什么这个 for 循环使用 OpenMP 没有更快?

Why is this for loop not faster using OpenMP?

我从一个更大的二维程序中提取了这个简单的成员函数,它所做的只是一个 for 循环,从三个不同的数组访问并进行数学运算(一维卷积)。我一直在测试使用 OpenMP 使这个特定功能更快:

void Image::convolve_lines()
{
  const int *ptr0 = tmp_bufs[0];
  const int *ptr1 = tmp_bufs[1];
  const int *ptr2 = tmp_bufs[2];
  const int width = Width;
#pragma omp parallel for
  for ( int x = 0; x < width; ++x )
    {
    const int sum = 0
      + 1 * ptr0[x]
      + 2 * ptr1[x]
      + 1 * ptr2[x];
    output[x] = sum;
    }
}

如果我在 debian/wheezy amd64 上使用 gcc 4.7,则整个程序在 8 个 CPU 机器上的执行速度会慢很多。如果我在 debian/jessie amd64(这台机器上只有 4 个 CPU)上使用 gcc 4.9,整个程序的执行差别很小。

time比较: 单核运行:

$ ./test black.pgm out.pgm  94.28s user 6.20s system 84% cpu 1:58.56 total

多核运行:

$ ./test black.pgm out.pgm  400.49s user 6.73s system 344% cpu 1:58.31 total

其中:

$ head -3 black.pgm
P5
65536 65536
255

所以Width在执行过程中被设置为65536

如果有关系,我正在使用 cmake 进行编译:

add_executable(test test.cxx)
set_target_properties(test PROPERTIES COMPILE_FLAGS "-fopenmp" LINK_FLAGS "-fopenmp")

并且CMAKE_BUILD_TYPE设置为:

CMAKE_BUILD_TYPE:STRING=Release

这意味着 -O3 -DNDEBUG

我的问题是,为什么这个 for 循环在使用多核时速度不快?数组上没有重叠,openmp 应该平分内存。我看不出瓶颈来自哪里?

编辑:如评论所述,我将输入文件更改为:

$ head -3 black2.pgm
P5
33554432 128
255

所以Width现在在执行时设置为33554432(应该考虑够了)。现在时间显示:

单核运行:

$ ./test ./black2.pgm out.pgm  100.55s user 5.77s system 83% cpu 2:06.86 total

多核 运行(由于某种原因 cpu% 始终低于 100%,这表明根本没有线程):

$ ./test ./black2.pgm out.pgm  117.94s user 7.94s system 98% cpu 2:07.63 total

我有一些一般性的意见:

1.在优化您的代码之前,请确保数据是 16 字节对齐的。 这对于想要应用的任何优化都非常重要。而如果数据分成3块,最好加入一些dummy元素,让3块的起始地址都是16字节对齐的。通过这样做,CPU 可以轻松地将您的数据加载到缓存行中。

2。在实现 openMP 之前确保简单函数被向量化。大多数情况下,使用 AVX/SSE 指令集应该会给您带来 2 到 8 倍的单线程改进。对于您的情况来说非常简单:创建一个常量 mm256 寄存器并将其设置为值 2,然后将 8 个整数加载到三个 mm256 寄存器。使用Haswell处理器,一加一乘可​​以一起完成。所以理论上,如果可以填充 AVX 管道,循环应该加速 12 倍!

3。有时并行化会降低性能 :现代 CPU 需要数百到数千个时钟周期来预热,进入高性能状态并提高频率。如果任务不够大,很可能任务在 CPU 预热之前完成,并且无法通过并行获得速度提升。不要忘记 openMP 也有开销:线程创建、同步和删除。另一种情况是内存管理不善。数据访问非常分散,所有 CPU 个核心都处于空闲状态并等待来自 RAM 的数据。

我的建议:

你可能想试试intel MKL,不要重新发明轮子。库优化到极致,没有时钟周期浪费。可以 link 使用串行库或并行版本,如果可以通过并行进行,则可以保证速度提升。