?!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
q行库充分利用多核的优势Q通过q行q算提高E序效率Q本文主要介lc++中两个知名的q行库,一个是intel开发的TBBQ一个是微Y开发的PPL。本文只介绍其基本的常用用法Qƈ行算法和d?/span>
TBBQIntel] Threading Building Blocks Q?/span>
TBB是intel用标准c++写的一个开源的q行计算库。它的目的是提升数据q行计算的能力,可以在他的官|上下蝲最新的库和文档。TBB主要功能Q?/span>
q行法
d调度
q行容器
同步原语
内存分配?/span>
TBBq行法
parallel_forQƈ行方式遍历一个区间?/span>
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_eachQ将法应用于一个区?/span>
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_reduce
cM于map_reduceQ但是有区别。它先将区间自动分组Q对每个分组q行聚合(accumulate)计算Q每l得C个结果,最后将各组的结果进行汇?reduce)。这个算法稍微复杂一点,parallel_reduce(range,identity,func,reduction)Q第一个参数是区间范围Q第二个参数是计的初始|W三个参数是聚合函数Q第四个参数是汇聚参数?/span>
复制代码
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>()
);
}
复制代码
q个Ҏl求和的例子是先自动分l然后对各组中的元素q行聚合累加Q最后将各组l果汇聚相加?/span>
parallel_pipeline:q行的管道过滤器
数据经q一个管道,在数据流动的q程中依ơ要l过一些过滤器的处理,其中有些qo器可能会q行处理数据Q这时就可以用到q行的管道过滤器。D一个例子,比如我要d一个文Ӟ先将文g中的数字提取出来Q再提取出来的数字做一个{换,最后将转换后的数字输出到另外一个文件中。其中读文g和输出文件不能ƈ兴去做,但是中间数字转换的环节可以ƈ行去做的。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) );
W一个参数是最大的q行敎ͼ我们可以通过&q接多个filterQ这些filter是顺序执行的Q前一个filter的输出是下一个filter的输入?/span>
复制代码
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);
}
复制代码
W一个filter生成数据Q如从文件中d数据{)Q第二个filter对生的数据q行转换Q第三个filter是对转换后的数据做篏加。其中第二个filter是可以ƈ行处理的Q通过filter::parallel来指定其处理模式?/span>
parallel_sort:q行排序
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:q行调用Qƈ行调用多个函?/span>
void f();
extern void bar(int);
void RunFunctionsInParallel() {
tbb::parallel_invoke(f, []{bar(2);}, []{bar(3);} );
}
TBBd
task_group表示可以{待或者取消的d集合
task_group g;
g.run([]{TestPrint(); });
g.run([]{TestPrint(); });
g.run([]{TestPrint(); });
g.wait();
PPL(Parallel Patterns Library)
PPL是微软开发的q行计算库,它的功能和TBB是差不多的,但是PPL只能在windows上用。二者在q行法的用上基本上是一L, 但还是有差异的。二者的差异Q?/span>
parallel_reduce的原型有些不同,PPL的paraller_reduce函数多一个参敎ͼ原型为parallel_reduce(begin,end,identity,func,reduction), 比tbb多了一个参敎ͼ但是表达的意思差不多Q一个是区间Q一个是区间q代器?/span>
PPL中没有parallel_pipeline接口?/span>
TBB的task没有PPL的task强大QPPL的task可以铑ּq箋执行q可以组合Q务,TBB的task则不行?/span>
PPLd的链式连l执行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
*/
复制代码
PPLd的组?/span>
1.when_all可以执行一lQ务,所有Q务完成之后将所有Q务的l果q回C个集合中。要求该lQ务中的所有Q务的q回值类型都相同?/span>
复制代码
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_anydl中的某一个Q务执行完成之后,q回一个pairQ键值对是结果和d序号?/span>
复制代码
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.
复制代码
ȝQ?/span>
ppl和tbb两个q行q算库功能相|如果需要跨q_则选择tbb, 否则选择ppl。ppl在Q务调度上比tbb强大Qtbb׃设计上的原因不能做到d的连l执行以及Q务的l合Q但是tbb有跨q_的优ѝ?/span>