c++并行计算库TBB和PPL的基本用法

并行库充分利用多核的优点,经过并行运算提升程序效率,本文主要介绍c++中两个知名的并行库,一个是intel开发的TBB,一个是微软开发的PPL。本文只介绍其基本的经常使用用法:并行算法和任务。c++

TBB(Intel® Threading Building Blocks )

TBB是intel用标准c++写的一个开源的并行计算库。它的目的是提高数据并行计算的能力,能够在他的官网上下载最新的库和文档。TBB主要功能:算法

    1. 并行算法
    2. 任务调度
    3. 并行容器
    4. 同步原语
    5. 内存分配器

TBB并行算法

parallel_for:并行方式遍历一个区间。windows

parallel_for(1, 20000, [](int i){cout << i << endl; });
parallel_for(blocked_range<size_t>(0, 20000), [](blocked_range<size_t>& r)
{
    for (size_t i = r.begin(); i != r.end(); ++i)
        cout << i << endl; 
});

parallel_do和parallel_for_each:将算法应用于一个区间数组

vector<size_t> v;
parallel_do(v.begin(), v.end(), [](size_t i){cout << i << endl; });
parallel_for_each(v.begin(), v.end(), [](size_t i){cout << i << endl; });

 parallel_reduceide

  相似于map_reduce,可是有区别。它先将区间自动分组,对每一个分组进行聚合(accumulate)计算,每组获得一个结果,最后将各组的结果进行汇聚(reduce)。这个算法稍微复杂一点,parallel_reduce(range,identity,func,reduction),第一个参数是区间范围,第二个参数是计算的初始值,第三个参数是聚合函数,第四个参数是汇聚参数。函数

float ParallelSum(float array [], size_t n) {
    return parallel_reduce(
        blocked_range<float*>(array, array + n),
        0.f,
        [](const blocked_range<float*>& r, float value)->float {
            return std::accumulate(r.begin(), r.end(), value);
    },
        std::plus<float>()
        );
}

这个对数组求和的例子就是先自动分组而后对各组中的元素进行聚合累加,最后将各组结果汇聚相加。ui

parallel_pipeline:并行的管道过滤器spa

  数据流通过一个管道,在数据流动的过程当中依次要通过一些过滤器的处理,其中有些过滤器可能会并行处理数据,这时就能够用到并行的管道过滤器。举一个例子,好比我要读入一个文件,先将文件中的数字提取出来,再将提取出来的数字作一个转换,最后将转换后的数字输出到另一个文件中。其中读文件和输出文件不能并兴去作,可是中间数字转换的环节能够并行去作的。parallel_pipeline的原型:设计

parallel_pipeline( max_number_of_live_tokens, 
                   make_filter<void,I1>(mode0,g0) &
                   make_filter<I1,I2>(mode1,g1) &
                   make_filter<I2,I3>(mode2,g2) &
                   ... 
                   make_filter<In,void>(moden,gn) );

  第一个参数是最大的并行数,咱们能够经过&链接多个filter,这些filter是顺序执行的,前一个filter的输出是下一个filter的输入。c++11

float RootMeanSquare( float* first, float* last ) {
    float sum=0;
    parallel_pipeline( /*max_number_of_live_token=*/16,       
        make_filter<void,float*>(
            filter::serial,
            [&](flow_control& fc)-> float*{
                if( first<last ) {
                    return first++;
                 } else {
                    fc.stop();
                    return NULL;
                }
            }    
        ) &
        make_filter<float*,float>(
            filter::parallel,
            [](float* p){return (*p)*(*p);} 
        ) &
        make_filter<float,void>(
            filter::serial,
            [&](float x) {sum+=x;}
        )
    );
    return sqrt(sum);
}

  第一个filter生成数据(如从文件中读取数据等),第二个filter对产生的数据进行转换,第三个filter是对转换后的数据作累加。其中第二个filter是能够并行处理的,经过filter::parallel来指定其处理模式。

parallel_sort:并行排序

const int N = 1000000;
float a[N];
float b[N];
parallel_sort(a, a + N);
parallel_sort(b, b + N, std::greater<float>());

parallel_invoke:并行调用,并行调用多个函数

void f();
extern void bar(int);

void RunFunctionsInParallel() {
    tbb::parallel_invoke(f, []{bar(2);}, []{bar(3);} );
}

TBB任务

task_group表示能够等待或者取消的任务集合

task_group g;
g.run([]{TestPrint(); });
g.run([]{TestPrint(); });
g.run([]{TestPrint(); });
g.wait();

 

PPL(Parallel Patterns Library)

  PPL是微软开发的并行计算库,它的功能和TBB是差很少的,可是PPL只能在windows上使用。两者在并行算法的使用上基本上是同样的, 但仍是有差别的。两者的差别:

  1. parallel_reduce的原型有些不一样,PPL的paraller_reduce函数多一个参数,原型为parallel_reduce(begin,end,identity,func,reduction), 比tbb多了一个参数,可是表达的意思差很少,一个是区间,一个是区间迭代器。
  2. PPL中没有parallel_pipeline接口。
  3. TBB的task没有PPL的task强大,PPL的task能够链式连续执行还能够组合任务,TBB的task则不行。

PPL任务的链式连续执行then

int main()
{
    auto t = create_task([]() -> int
    { 
        return 0;
    });

    // Create a lambda that increments its input value.
    auto increment = [](int n) { return n + 1; };

    // Run a chain of continuations and print the result. 
    int result = t.then(increment).then(increment).then(increment).get();
    cout << result << endl;
}
/* Output:
    3
*/

PPL任务的组合

  1.when_all能够执行一组任务,全部任务完成以后将全部任务的结果返回到一个集合中。要求该组任务中的全部任务的返回值类型都相同。

array<task<int>, 3> tasks =
{
    create_task([]() -> int { return 88; }),
    create_task([]() -> int { return 42; }),
    create_task([]() -> int { return 99; })
};

auto joinTask = when_all(begin(tasks), end(tasks)).then([](vector<int> results)
{
    cout << "The sum is " 
          << accumulate(begin(results), end(results), 0)
          << '.' << endl;
});

// Print a message from the joining thread.
cout << "Hello from the joining thread." << endl;

// Wait for the tasks to finish.
joinTask.wait();

2.when_any任务组中的某一个任务执行完成以后,返回一个pair,键值对是结果和任务序号。

array<task<int>, 3> tasks = {
        create_task([]() -> int { return 88; }),
        create_task([]() -> int { return 42; }),
        create_task([]() -> int { return 99; })
    };

    // Select the first to finish.
    when_any(begin(tasks), end(tasks)).then([](pair<int, size_t> result)
    {
        cout << "First task to finish returns "
              << result.first
              << " and has index "
              << result.second<<endl;
    }).wait();
//output: First task to finish returns 42 and has index 1.

 

总结:

  ppl和tbb两个并行运算库功能类似,若是须要跨平台则选择tbb,  不然选择ppl。ppl在任务调度上比tbb强大,tbb因为设计上的缘由不能作到任务的连续执行以及任务的组合,可是tbb有跨平台的优点。

 

若是你以为这篇文章对你有用,能够点一下推荐,谢谢。

c++11 boost技术交流群:296561497,欢迎你们来交流技术。

相关文章
相关标签/搜索