我将首先给出一些关于我遇到的问题的背景,以便您知道我正在尝试做什么。我一直在帮助某个软件工具的开发,发现我们可以从使用OpenMP并行化这个软件中的一些最大的循环中获得巨大的好处。我们实际上成功地实现了循环的并行化,并且只有两个内核,循环的执行速度提高了30%,这是一个不错的改进。另一方面,我们注意到一个使用递归调用遍历树结构的函数中出现了一个奇怪的现象。当OpenMP打开时,程序实际上在这里变慢了,并且这个函数的执行时间增加了一倍以上。我们认为可能是树结构对于并行化来说不够平衡,并注释掉了这个函数中的OpenMP编译指示。不过,这似乎对执行时间没有影响。为了支持OpenMP,我们目前使用的是带有-fopenmp标志的GCC编译器4.4.6。现在的问题是:
如果我们不在代码中使用任何omp编译指示,那么一切都运行得很好。但是,如果我们只在程序的main函数的开头添加以下代码,则树遍历函数的执行时间会加倍,从35秒增加到75秒:
//beginning of main function
...
#pragma omp parallel
{
#pragma omp single
{}
}
//main function continues
...
有谁知道为什么会发生这种情况吗?我不明白为什么仅仅因为使用了OpenMP编译指示,程序就会变得如此缓慢。如果我们去掉所有的omp编译指示,树遍历函数的执行时间再次下降到35秒。我猜这是某种编译器错误,因为我现在没有其他的解释。
发布于 2011-05-09 23:09:59
并不是所有可以并行化的东西都应该并行化。如果您使用的是单线程,那么只有一个线程执行它,其余的线程必须等待,直到区域完成。他们可以旋转-等待或睡眠。大多数实现都以自旋等待开始,希望单个区域不会花费太长时间,并且等待线程可以比休眠更快地看到完成。旋转等待会消耗大量的处理器周期。您可以尝试指定等待应该是被动的-但这只在OpenMP V3.0中出现,并且只是对实现的一个提示(因此它可能没有任何效果)。基本上,除非你在并行区域中有大量的工作可以补偿单线程,否则单线程将大大增加并行开销,并很可能使其并行化成本过高。
发布于 2011-05-10 08:33:18
首先,OpenMP通常会降低第一次尝试时的性能。如果你不能从里到外理解它,使用omp并行可能会很棘手。如果你能告诉我更多关于程序结构的信息,特别是以下由?注释的问题,我也许能帮上忙。
//beginning of main function
...
#pragma omp parallel
{
???? What goes here, is this a loop? if so, for loop, while loop?
#pragma omp single
{
???? What goes here, how long does it run?
}
}
//main function continues
....
???? Does performance of this code reduce or somewhere else?
谢谢。
发布于 2011-05-11 16:44:33
我做了更多的测试,并编写了一个小测试程序来测试这个问题是否与内存操作有关。我无法在我的小测试程序中复制空的并行-单个区域导致程序减速的问题,但我可以通过并行化一些malloc调用来复制减速。
在具有2个CPU核心的64位Windows7上运行测试程序时,与不使用OpenMP支持的情况下运行程序相比,使用-fopenmp标志和g++编译器并运行编译后的程序没有明显的速度减慢。
然而,在同一台计算机上的Kubuntu 11.04 64位上执行相同的操作,将执行提升到非OpenMP版本的4倍以上。此问题似乎只出现在Unix系统上,而不出现在Windows上。
下面是我的测试程序的源代码。我还上传了win和unix版本的压缩源代码,以及支持和不支持OpenMP的win和unix版本的汇编源代码。这个压缩包可以在这里下载http://www.2shared.com/file/0thqReHk/omp_speed_test_2011_05_11.html
#include <stdio.h>
#include <windows.h>
#include <list>
#include <sys/time.h>
//#include <cstdlib>
using namespace std;
int main(int argc, char* argv[])
{
// #pragma omp parallel
// #pragma omp single
// {}
int start = GetTickCount();
/*
struct timeval begin, end;
int usecs;
gettimeofday(&begin, NULL);
*/
list<void *> pointers;
#pragma omp parallel for default(shared)
for(int i=0; i< 10000; i++)
//pointers.push_back(calloc(20000, sizeof(void *)));
pointers.push_back(malloc(20000));
for(list<void *>::iterator i = pointers.begin(); i!= pointers.end(); i++)
free(*i);
/*
gettimeofday(&end, NULL);
if (end.tv_usec < begin.tv_usec) {
end.tv_usec += 1000000;
begin.tv_sec += 1;
}
usecs = (end.tv_sec - begin.tv_sec) * 1000000;
usecs += (end.tv_usec - begin.tv_usec);
*/
printf("It took %d milliseconds to finish the memory operations", GetTickCount() - start);
//printf("It took %d milliseconds to finish the memory operations", usecs/1000);
return 0;
}
现在仍然没有回答的是,我可以做些什么来避免在Unix平台上出现这样的问题。
https://stackoverflow.com/questions/5938243
复制相似问题