我将首先介绍一下我遇到的问题,以便了解我正在尝试做什么。我一直在帮助开发某个软件工具,并发现我们可以通过使用OpenMP并行化该软件中的一些最大循环来获益。我们实际上成功并行化了循环,只有两个核心,循环执行速度提高了30%,这是一个很好的改进。另一方面,我们注意到函数中的一个奇怪的现象,它使用递归调用遍历树结构。这个程序实际上在OpenMP打开时放慢了速度,这个函数的执行时间增加了一倍。我们认为树结构可能不够平衡以进行并行化,并在此函数中注释掉了OpenMP pragma。这似乎对执行时间没有影响。我们目前正在使用带有-fopenmp标志的GCC编译器4.4.6来支持OpenMP。这是当前的问题:
如果我们在代码中不使用任何omp编译指示,则所有运行正常。但是如果我们只在程序的main函数的开头添加以下内容,那么tree travelsal函数的执行时间将从35秒增加到75秒:
//beginning of main function
...
#pragma omp parallel
{
#pragma omp single
{}
}
//main function continues
...
有没有人知道为什么会这样?我不明白为什么程序因使用OpenMP pragma而大大减慢。如果我们取消所有omp pragma,树遍历函数的执行时间将再次下降到35秒。我猜这是某种编译器错误,因为我现在没有其他解释。
答案 0 :(得分:1)
并非所有可以并行化的东西都应该并行化。如果您使用单个,则只有一个线程执行它,其余线程必须等到该区域完成。他们可以旋转等待或睡觉。大多数实现都以旋转等待开始,希望单个区域不会花费太长时间,等待线程可以比睡眠时更快地看到完成。旋转等待耗费了大量的处理器周期。您可以尝试指定等待应该是被动的 - 但这仅在OpenMP V3.0中,并且只是对实现的提示(因此它可能没有任何效果)。基本上,除非你在并行区域有大量工作可以补偿单个,否则单个将大大增加并行开销,并且很可能使并行化太昂贵。
答案 1 :(得分:1)
首先,OpenMP通常会在首次尝试时降低性能。如果你不了解omp并行,那么使用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?
感谢。
答案 2 :(得分:1)
我做了一些测试并做了一个小测试程序来测试问题是否与内存操作有关。我无法复制一个空的并行单一区域的问题,导致程序在我的小测试程序中减速,但我能够通过并行化一些malloc调用来复制减速。
在带有2个CPU内核的Windows 7 64位上运行测试程序时,与运行没有OpenMP的程序相比,使用-fopenmp标志和gcc(g ++)编译器并运行已编译的程序没有明显的减速支持。
在同一台计算机上的64位Kubuntu 11.04上执行相同操作,但是将执行时间提高到非OpenMP版本的4倍以上。这个问题似乎只出现在Unix系统上,而不是出现在Windows上。
我的测试程序的来源如下。我还为win和unix版本上传了zipped-source,以及在有和没有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平台上出现这些问题..
答案 3 :(得分:1)
谢谢大家。我们今天能够通过与TCMalloc联系来解决这个问题,TCMalloc是ejd提供的解决方案之一。执行时间立即下降,与非线程版本相比,我们能够将执行时间提高约40%。我们使用了2个核心。似乎在Unix上使用带有GCC的OpenMP时,您还应该选择标准内存管理解决方案的替代品。否则程序可能会变慢。