我最近开始研究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:感谢有关线程池的信息。这真的很好知道。
你在内循环中根本没有做任何事情。编译器应该完全优化它,所以你会看到的是设置线程和向他们分发(不)工作的成本。 – pburka
如果'QImage :: setPixel()'使用内部锁,例如为了使操作线程安全,一次从多个线程调用它只会将其执行序列化。 –