问题 并行任务使用boost :: thread比使用ppl或OpenMP获得更好的性能


我有一个可以并行化的C ++程序。我正在使用Visual Studio 2010,32位编译。

简而言之,该计划的结构如下

#define num_iterations 64 //some number

struct result
{ 
    //some stuff
}

result best_result=initial_bad_result;

for(i=0; i<many_times; i++)
{ 
    result *results[num_iterations];


    for(j=0; j<num_iterations; j++)
    {
        some_computations(results+j);
    }

    // update best_result; 
}

由于每个 some_computations() 是独立的(一些全局变量读取,但没有修改全局变量)我并行内部 for-循环。

我的第一次尝试是 提高::螺纹

 thread_group group;
 for(j=0; j<num_iterations; j++)
 {
     group.create_thread(boost::bind(&some_computation, this, result+j));
 } 
 group.join_all();

结果很好,但我决定尝试更多。

我试过了 OpenMP的 图书馆

 #pragma omp parallel for
 for(j=0; j<num_iterations; j++)
 {
     some_computations(results+j);
 } 

结果比...差 boost::thread的。

然后我尝试了 PPL 库和使用 parallel_for()

 Concurrency::parallel_for(0,num_iterations, [=](int j) { 
     some_computations(results+j);
 })

结果是最糟糕的。

我发现这种行为非常令人惊讶。由于OpenMP和ppl是为并行化而设计的,我希望得到更好的结果 boost::thread。我错了吗?

为什么是 boost::thread 给我更好的结果?


6525
2018-03-04 16:38


起源

你能否量化“更好”,例如提供执行时间与线程数量?同 boost::thread 你正在创建64个线程。 OpenPM使用一组工作线程,其数量默认为虚拟CPU的数量。 PPL还使用线程池,并且比OpenMP具有更高的开销,因为它还实现了工作平衡。 - Hristo Iliev
我为每次尝试使用相同的数字(32或64),正如您所指出的,使用OpenMP和ppl我可以获得更好的结果,设置线程数等于核心数。我会尽力。 - 888
现在几乎不可能回答这个问题。什么是 some_computations 在做什么?我在某处可能存在争用(可能会以不同的方式触及不同的库,例如,如果openmp实际上具有较低的开销,但是您对共享缓存行有大量写入,导致缓存失效的狂热实际上可能会使其变慢)?为每个变体运行并行化块需要多长时间 - Grizzly


答案:


OpenMP或PPL不会让人感到悲观。他们只是按照他们的说法去做,但是当你尝试对paralellize循环时,你应该考虑一些事情。

如果没有看到你如何实现这些东西,很难说真正的原因是什么。

此外,如果每次迭代中的操作都依赖于同一循环中的任何其他迭代,那么这将产生争用,这将减慢速度。你还没有表现出什么 some_operation 函数实际上是这样做的,因此很难判断是否存在数据依赖性。

可以真正并行化的循环必须能够使每个迭代运行完全独立于所有其他迭代,在任何迭代中都不会访问共享内存。因此,最好将内容写入局部变量,然后在最后复制。

不是全部 循环可以并行化,它非常依赖于正在完成的工作类型。

例如,有助于并行化的东西是在屏幕缓冲区的每个像素上进行的工作。每个像素完全独立于所有其他像素,因此,线程可以进行循环的一次迭代并完成工作,而不需要等待迭代之间的循环内的共享存储器或数据依赖性。

此外,如果你有一个连续的数组,这个数组可能部分在缓存行中,如果你在线程A中编辑元素5然后在线程B中更改元素6,你可能会得到缓存争用,这也会减慢事情的速度,因为这些将驻留在同一缓存行中。一种现象称为 虚假分享

在进行循环并行化时,需要考虑很多方面。


10
2018-03-04 16:44



你的功能 some_operation 将偏移量放入数组中,并在多个线程之间共享该数组。我不知道PPL或OpenMP可以使你没有写入该数组的任何garantuees,或者其他任何东西正在写入该数组。因此,我的答案不会改变。 - Tony The Lion
你的第一段不是真的。 OpenMP和PPL都不关心你对共享变量所做的事情,并且它们的工作方式没有任何悲观或乐观。两者都是命令式编程概念,这意味着如果被告知,编译器会使代码并行,而不是将表达式视为提示。对共享变量的正确处理完全由程序员完成。 - Hristo Iliev


简而言之, openMP 主要基于共享内存,具有额外的任务管理和内存管理成本。 ppl 旨在处理通用数据结构和算法的通用模式,它带来了额外的复杂性成本。它们都有额外的CPU成本,但你的简单下降 boost线程不(boost 线程只是简单的API包装)。这就是为什么他们都比你的慢 boost 版。并且,由于示例计算彼此独立,没有同步, openMP 应该接近 boost 版。

它发生在简单的场景中,但是,对于复杂的场景,复杂的数据布局和算法,它应该依赖于上下文。


3
2018-03-09 15:14



OpenMP不是为消息传递而设计的,MPI是通过masseges的。 - Moss
@Moss,谢谢,我混淆了OpenMP和MPI。 OpenMP是基于共享内存的。 - Peixu Zhu