TBB使用教程

官方tutorial https://software.intel.com/en-us/tbb-user-guide

1.parallel_for:

常规方式(非lambda函数):

1.1串行版本:

void SerialApplyFoo( float a[], size_t n ) {
    for( size_t i=0; i!=n; ++i )
        Foo(a[i]);
}

并行版本:

首先定义一个类:需要实现operator () 重载和构造函数
operator()中执行具体操作(和串行版本执行的操作一样),注意必须是const类型
构造函数中用于将外部需要操作的变量转换为类中的成员变量。
block_range& t 是当前线程中迭代的空间,是需处理的全部数据的一部分。

#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]);
    }
    ApplyFoo( float a[] ) :
        my_a(a)
    {}
};

使用方法:

#include "tbb/tbb.h" 
void ParallelApplyFoo( float a[], size_t n ) {
    parallel_for(blocked_range<size_t>(0,n), ApplyFoo(a));
}

1.2 lambda函数版本:

#include "tbb/tbb.h"
using namespace tbb;
void ParallelApplyFoo( float* a, size_t n ) {
   parallel_for( blocked_range<size_t>(0,n), 
      [=](const blocked_range<size_t>& r) {
                      for(size_t i=r.begin(); i!=r.end(); ++i) 
                          Foo(a[i]); 
                  }
    );
}

和串行版相比需要修改的内容很少
parallel_for的第二个参数是一个lambda函数,[=]表示对定义在函数外部的变量的传递方法,[=]表示值传递,[&]表示引用传递。

1.3 设置并行粒度

#include "tbb/tbb.h"
void ParallelApplyFoo( float a[], size_t n ) {
    parallel_for(blocked_range<size_t>(0,n,G), ApplyFoo(a), 
                 simple_partitioner());
}

block_range的第三个参数可以设置粒度G,parallel_for的第三个参数设置分配任务方法
分割粒度G小,则并行程度相对高,但会做更多并行前的预处理任务。每个并行子任务至少要执行100,000个时钟周期才能保证较高效率。当不能确定时,可以先设为100000,再用二分的方法向下调节找到最优值。

分配方法和粒度的关系:(默认为simple_partition)

simple_partitioner Chunksize bounded by grain size. g/2 ≤ chunksize ≤ g
auto_partitioner (default)[4]
Automatic chunk size. g/2 ≤ chunksize
affinity_partitioner Automatic chunk size, cache affinity and uniform distribution of iterations.
static_partitioner Deterministic chunk size, cache affinity and uniform distribution of iterations without load balancing. max(g/3, problem_size/num_of_resources) ≤ chunksize

2. parallel_reduce

当各个线程的工作需要汇总时需要使用parallel_reduce

2.1 串行版本

float SerialSumFoo( float a[], size_t n ) {
    float sum = 0;
    for( size_t i=0; i!=n; ++i )
        sum += Foo(a[i]);
    return sum;
}

2.2 并行版本同样需要定义一个类,并需要实现以下方法:
1) operator() 重载:具体执行的操作,和parallel_for不同的是不需要是const型了

2) split拷贝构造函数:

SumFoo( SumFoo& x, split ) : my_a(x.my_a), my_sum(0) {}

该函数在当前线程将任务分配给子线程时执行
split是tbb定义好的元素,用于区别构造函数

3) join 方法:

 void join( const SumFoo& y ) {my_sum+=y.my_sum;}

该函数用于合并两个线程计算的结果

4) 构造函数:用于将外部数据转换为类的成员变量。

完整代码如下:

class SumFoo {
    float* my_a;
public:
    float my_sum; 
    void operator()( const blocked_range<size_t>& r ) {
        float *a = my_a;
        float sum = my_sum;
        size_t end = r.end();
        for( size_t i=r.begin(); i!=end; ++i ) 
            sum += Foo(a[i]); 
        my_sum = sum;    
    }

    SumFoo( SumFoo& x, split ) : my_a(x.my_a), my_sum(0) {}

    void join( const SumFoo& y ) {my_sum+=y.my_sum;}

    SumFoo(float a[] ) :
        my_a(a), my_sum(0)
    {}
};

调用方法是:

float ParallelSumFoo( const float a[], size_t n ) {
    SumFoo sf(a);
    parallel_reduce( blocked_range<size_t>(0,n), sf );
    return sf.my_sum;
}

在实现operator时有个问题,因为同一个对象可能会负责多个子空间的处理,所以在每次执行operator时不能将之前已有累积值的成员变量赋为0,如下面的代码

class SumFoo {
    ...
public:
    float my_sum; 
    void operator()( const blocked_range<size_t>& r ) {
        ...
        float sum = 0;  // WRONG – should be 'sum = my_sum".
        ...
        for( ... ) 
  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值