使用openmp时奇怪的减速

时间:2012-07-13 10:03:19

标签: c++ visual-studio-2010 openmp

我试图通过并行化矩阵乘法来提高相当复杂的迭代算法的性能,矩阵乘法在每次迭代时被调用。 该算法需要500次迭代并且大约10秒。但在并行化矩阵乘法后,它会减慢到13秒。 然而,当我单独测试相同尺寸的矩阵乘法时,速度有所增加。 (我说的是100x100矩阵。)

最后,我关闭了算法内部的任何并行化,并在每次迭代时添加了以下代码,这些代码完全没有任何内容,并且可能不会花费很长时间:

int j;

#pragma omp parallel for private(j)

for (int i = 0; i < 10; i++)
j = i;

与没有这段代码的相同算法相比,再次减速30%。

因此,在主算法中使用openmp 500次调用任何并行化都会减慢速度。这种行为对我来说很奇怪,任何人都有任何线索是什么问题?

主要算法由桌面应用程序调用,由VS2010,Win32 Release编译。 我使用Intel Core i3(并行化创建4个线程),64位Windows 7。

这是一个程序结构:

int internal_method(..)

{
...//no openmp here


 // the following code does nothing, has nothing to do with the rest of the program  and shouldn't take long,
 // but somehow adding of this code caused a 3 sec slowdown of the Huge_algorithm()
 double sum;
 #pragma omp parallel for private(sum)
 for (int i = 0; i < 10; i++)
    sum = i*i*i / (1.0 + i*i*i*i);

...//no openmp here
}


int Huge_algorithm(..)
{

 ...//no openmp here

    for (int i = 0; i < 500; i++)
    {
     .....// no openmp

     internal_method(..);

     ......//no openmp
    }

...//no openmp here
}

所以,最后一点是: 单独调用并行代码500次(当省略算法的其余部分时)花费不到0.01秒,但是当你在一个巨大的算法中调用它500次时,它会导致整个算法延迟3秒。 而我不明白的是小并行部分如何影响算法的其余部分?

2 个答案:

答案 0 :(得分:2)

对于10次迭代和一个简单的赋值,我想与计算本身相比,OpenMP开销太多了。这里看起来很轻巧的实际上是管理和同步多个线程,这些线程甚至可能来自线程池。可能存在一些锁定,我不知道MSVC在估计是否要并行化方面有多好。

尝试使用更大的循环体或更大量的迭代(例如1024 * 1024次迭代,仅适用于初学者)。


示例OpenMP Magick:

#pragma omp parallel for private(j)
for (int i = 0; i < 10; i++)
    j = i;

这可能是大致由编译器扩展到:

const unsigned __cpu_count = __get_cpu_count();
const unsigned __j  = alloca (sizeof (unsigned) * __cpu_count);
__thread *__threads = alloca (sizeof (__thread) * __cpu_count);
for (unsigned u=0; u!=__cpu_count; ++u) {
    __init_thread (__threads+u);
    __run_thread ([u]{for (int i=u; i<10; i+=__cpu_count)
                          __j[u] = __i;}); // assume lambdas
}

for (unsigned u=0; u!=__cpu_count; ++u)
    __join (__threads+u);

__init_thread()__run_thread()__join()是调用某些系统调用的非平凡函数。

如果使用线程池,您可以用alloca()左右替换第一个__pick_from_pool()

(请注意,名称和发出的代码,都是虚构的,实际实现看起来会有所不同)


关于您更新的问题:

您似乎以错误的粒度进行并行化。在线程中放置尽可能多的工作量,而不是

 for (...) {
     #omp parallel ...
     for (...) {} 
 }

 #omp parallel ...
 for (...) {
     for (...) {} 
 }

经验法则:每个线程保持足够大的工作负载,以减少相对开销。

答案 1 :(得分:0)

也许只是j = i对于core-cpu bandwith来说并不是高收益。也许你应该尝试更多的计算方法。 (用于实现i * i * i * i * i * i并将其除以i + i + i)

你在多核cpu或gpu上运行吗?