2013-10-19 57 views
1

我最近开始研究OpenMP,因为我将研究一些高度计算昂贵的图像分析项目。我使用带有Intel i7(8核)和mingw64 gcc 4.8.1的Windows 7。我在Code :: Blocks中编写代码,并且为了编译和运行它而设置了一切。在我的代码的几个部分,我会做一些像素操作,我认为这将是一个很好的候选人并行处理。令我惊讶的是,事实证明,顺序比并行处理更快。我为32位和64位以及两台独立的计算机尝试了不同版本的gcc(4.7 - 4.8),但我总是遇到相同的性能问题。然后我试着用我以前在这两台电脑中的一台上运行的旧版Visual Studio 2008运行它,为此我获得了预期的性能提升。因此,我的问题是 - 为什么我无法使用gcc看到相同的效果。有什么我做错了吗?C++ OpenMP和gcc 4.8.1 - 并行化循环时的性能问题

这是一个最小工作示例。

#include <omp.h> 
#include <cstdlib> 
#include <iostream> 

int main(int argc, char * argv[]) 
{ 
    /* process a stack of images - set the number to 1000 for testing */ 
    int imgStack = 1000; 

    double start_t = omp_get_wtime(); 
    for (int img = 0; img < imgStack; img++) 
    { 
     omp_set_num_threads(8); 
     #pragma omp parallel for default(none) 
     for (int y = 0; y < 1000000000; y++) /* increased the number of pixels to make it worthwhile and to see a difference*/ 
     { 
     for (int x = 0; x < 1000000000; x++) 
     { 
      unsigned char pixel[4]; 
      pixel[0] = 1; 
      pixel[1] = 2; 
      pixel[2] = 3; 
      pixel[3] = 4; 

      /* here I would do much more but removed it for testing purposes */ 

     } 
     } 
    } 
    double end_t = (omp_get_wtime() - start_t) * 1000.0; 
    std::cout << end_t << "ms" << std::endl; 

    return 0; 
} 

在建筑日志我有以下

x86_64-w64-mingw32-g++.exe -Wall -O2 -fopenmp -c C:\Code\omptest\main.cpp -o obj\Release\main.o 
x86_64-w64-mingw32-g++.exe -o bin\Release\omptest.exe obj\Release\main.o -s C:\mingw-builds\x64-4.8.1-posix-seh-rev5\mingw64\bin\libgomp-1.dll 

输出以下

for 1 thread : 43ms 
for 8 threads: 594ms 

我也试图关闭优化(-O0)的情况下,编译做一些循环展开。我阅读了关于虚假共享的问题,因此我将循环内的任何变量保留为私有的,以确保这不是问题。我不擅长分析,因此我无法分辨下面会发生什么,例如引起所有线程等待的内部锁。

我不明白我在这里做错了什么。

- 编辑 -

感谢大家。在我的真实代码中,我有一个包含2000个图像的图像堆栈,每个图像大小为2000x2000像素。我试图简化这个例子,这样每个人都可以很容易地重现这个问题,在这个问题中,我简化了它,导致了其他问题的后果。你们都完全正确。 在我的真实代码中,我使用Qt打开并显示了我的图像,以及我自己的图像管理器,该图像管理器加载并遍历堆栈,一次给出一张图像。我认为提供整个样本只会太多而且使事情复杂化(即不提供最低工作示例)。

我将所有变量(imageHeight,imageWidth等)作为常量传递给我的图像作为共享。最初那是一个指向QImage的指针。在循环中,我使用qtimg-> setPixel(...)设置了最终的像素值,似乎MSVC编译器处理与gcc编译器相比的不同。最后,我用一个指向unsigned char数组的指针替换了QImage指针,这给了我预期的性能提升。

@Hristo Iliev:感谢有关线程池的信息。这真的很好知道。

+0

你在内循环中根本没有做任何事情。编译器应该完全优化它,所以你会看到的是设置线程和向他们分发(不)工作的成本。 – pburka

+0

如果'QImage :: setPixel()'使用内部锁,例如为了使操作线程安全,一次从多个线程调用它只会将其执行序列化。 –

回答

1

到由于pixels只被分配到再没有用过,整个内环得到完全由GCC的优化器去除与-O2作为一个可以很容易地通过启用树来验证转储:

; Function <built-in> (main._omp_fn.0, funcdef_no=1036, decl_uid=21657, cgraph_uid=256) 

<built-in> (void * .omp_data_i) 
{ 
<bb 2>: 
    return; 

} 

和你这样做的目的是有效衡量OpenMP运行时开销。

使用-O0所有的代码都保留在原位,并且运行时间按照预期的线程数进行缩放,但我怀疑你是否曾用1000000000 x 1000000000的图像对它进行过测试。

1

鉴于代码示例,我无法重复您的结果。你必须显示你的真实堆栈大小和图像大小。因为如果使用1个线程只能在5ms内完成工作,多线程不会使其更快。启动多个线程会带来很大的开销,特别是当你启动它们时。

+0

多年以来,GCC,MSVC,Intel和其他大多数OpenMP运行库都实现了带有线程池的工作线程。只有第一个平行区域是昂贵的。除非以后需要更多的线程,否则进一步输入并行区域并不像您预期​​的那样昂贵。 –