与ppl或OpenMP相比,使用boost::thread可以获得更好的并行任务性能

Parallel tasks get better performances with boost::thread than with ppl or OpenMP

本文关键字:更好 性能 并行任务 thread 相比 OpenMP 使用 boost ppl      更新时间:2023-10-16

我有一个可以并行化的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 -循环。

我的第一次尝试是boost::thread

 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给我更好的结果?

OpenMP或PPL不做悲观的事情。它们只是按照要求去做,但是当你尝试并行循环时,你应该考虑一些事情。

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

同样,如果每个迭代中的操作都依赖于同一循环中的任何其他迭代,那么这将产生争用,这将减慢速度。您还没有显示some_operation函数的实际功能,因此很难判断是否存在数据依赖关系。

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

不是所有的循环都可以并行化,这取决于所做的工作的类型。

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

同样,如果你有一个连续的数组,这个数组可能部分在缓存行,如果你在线程a中编辑元素5,然后在线程B中修改元素6,你可能会得到缓存争用,这也会减慢速度,因为它们会驻留在同一个缓存行中。虚假分享

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

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

它出现在简单的场景中,但是,对于复杂的场景,具有复杂的数据布局和算法,它应该与上下文相关。