TBB :: parallel_for创建了太多的类/正文副本?

时间:2014-05-28 15:36:23

标签: c++ multithreading tbb

我遵循了TBB的基本parallel_for example。文档说明:

  

模板函数parallel_for要求body对象具有复制构造函数,该构造函数被调用以为每个工作线程创建单独的副本(或多个副本)。

我的算法每个并发工作者需要一些内存才能运行。我现在在复制构造函数中分配内存。它有效,但这些是我的8线程机器上的数字: 在0-10000的范围内,我得到大约2000个工作块(调用operator()),复制构造函数被调用大约300次!这就是问题:300个内存分配,只需要8个。我检查过只有8个线程正在运行,并且肯定不会同时使用超过8个类副本。

假设副本数量与线程数相关,我完全错了吗?有没有更好的方法来分配内存?

#include "tbb/tbb.h"

using namespace tbb;

class ApplyFoo {
    float *const my_a;
public:
    void operator()( const blocked_range<size_t>& r ) const {
        float *a = my_a;
        for( size_t i=r.begin(); i!=r.end(); ++i ) 
           Foo(a[i]); // Foo uses the allocated memory
    }
    ApplyFoo( float a[] ) :
        my_a(a)
    {}

    // the Copy-Constructor is called work every 
    ApplyFoo( const ApplyFoo& other ) :
        my_a(a)
    {
      // Allocate some memory here...
    }

    ~ApplyFoo() 
    {
      // Free the memory here...
    }
};

void ParallelApplyFoo( float a[], size_t n ) {
    parallel_for(blocked_range<size_t>(0,n), ApplyFoo(a));
}

1 个答案:

答案 0 :(得分:1)

  

假设副本数量与线程数相关,我完全错了吗?

您可以假设使用的默认分区程序(auto_partitioner)具有相关性,但乘数足够大并且取决于运行时条件,因此副本数量可以与子范围数量一样大。所以,毫无疑问。

但是,可以通过指定gain-size:

来控制子范围的数量
size_t p = task_scheduler_init::default_num_threads();
size_t grainsize = 2*n/p-1;
parallel_for(blocked_range<size_t>(0,n,grainsize), ApplyFoo(a));

这里的计算2*n/p-1是因为在TBB中,粒度不是可能的子范围的最小尺寸,而是用于决定是否分割的阈值。

此外,对于具有parallel_for正文副本数量的分区程序的完全可预测行为(独立于运行时条件),请改为使用simple_partitioner

parallel_for(blocked_range<size_t>(0,n), ApplyFoo(a), simple_partitioner());

但是,它可能导致大范围和小粒度的额外开销,因为它不会聚合范围。

  

有更好的方法来分配内存吗?

是的,粒度不是一个好方法,因为它可以防止TBB调度程序更好地进行负载平衡。我建议改用thread local storage containers。与基于编译器的TLS不同,可以遍历这些值以便在一个地方清理内存,即使原始线程已经消失。