Intel Threading Building Blocks(下文简称TBB)是一个C++的并行编程模板库,它能使你的程序充分利用多核CPU的性能优势。
配置(以VC2005为例)
开始使用TBB
一个使用TBB库的程序样子应该是这样地:- #include <tbb/task_scheduler_init.h>
- #include ...//其它头文件
- int main()
- {
- tbb::task_scheduler_init init;
- ...//代码
- return 0;
- }
并行排序parallel_sort
- template<typename RandomAccessIterator>
- void parallel_sort(RandomAccessIterator begin,
- RandomAccessIterator end);
- template<typename RandomAccessIterator, typename Compare>
- void parallel_sort(RandomAccessIterator begin,
- RandomAccessIterator end,
- const Compare& comp );
- #include <tbb/task_scheduler_init.h>
- #include <tbb/parallel_sort.h>
- #include <math.h>
- int main()
- {
- //准备排序原料
- const int N = 100000;
- float a[N];
- float b[N];
- for( int i = 0; i < N; i++ ) {
- a[i] = sin((double)i);
- b[i] = cos((double)i);
- }
- //TBB初始化
- tbb::task_scheduler_init init;
- //排序
- tbb::parallel_sort(a, a + N);
- //倒序
- tbb::parallel_sort(b, b + N, std::greater<float>( ));
- return 0;
- }
使用并行for循环
- int main()
- {
- for(int i=0; i<10; i++)
- {
- //处理比较耗时的操作,这里用暂停300ms模拟
- Sleep(300);
- std::cout << i;
- }
- return 0;
- }
- #include <tbb/task_scheduler_init.h>
- #include <tbb/blocked_range.h>
- #include <tbb/parallel_for.h>
- struct OpFor{
- void operator()(const tbb::blocked_range<int> &r) const
- {
- for(int i=r.begin(); i!=r.end(); ++i)
- {
- //处理比较耗时的操作,这里用暂停300ms模拟
- Sleep(300);
- std::cout << i;
- }
- }
- };
- int main()
- {
- tbb::task_scheduler_init init;
- OpFor f;
- tbb::parallel_for(tbb::blocked_range<int>(0,10), f);
- return 0;
- }
blocked_range( Value begin_, Value end_, size_type grainsize_=1 );parallel_for 函数启动并行循环,上例中,一个更好的方式是:
tbb::parallel_for(tbb::blocked_range<int>(0,10), f, tbb::auto_partitioner());下面这段代码for循环里的代码块是可重入的,前一次循环和下一次循环没有关系。
- char buf[] = "hello world";
- for(int i=0; i<sizeof(buf); i++)
- buf[i] = toupper(buf[i]);
- int sum=0;
- for(int i=0; i<10; i++)
- sum += i;
使用parallel_reduce来适应更多循环
上面提到一个不可重入的累加的例子,不能使用 parallel_for 并行执行,TBB用另一种方法让这种形式的循环也能并行执行。见下图:代码:
- #include <tbb/task_scheduler_init.h>
- #include <tbb/blocked_range.h>
- #include <tbb/parallel_reduce.h>
- struct OpSum{
- int m_sum;
- void operator()(const tbb::blocked_range<int> &r)
- {
- for(int i=r.begin(); i!=r.end(); ++i)
- {
- Sleep(300);
- m_sum += i;
- }
- }
- // 合并支线
- void join(OpSum& x){m_sum += x.m_sum;}
- // 分出一个支线,如果要访问x,应该保证原子操作
- OpSum( OpSum& x, tbb::split)
- : m_sum(0) {;}
- OpSum()
- :m_sum(0) {;}
- };
- int main()
- {
- tbb::task_scheduler_init init;
- OpSum x;
- tbb::parallel_reduce(tbb::blocked_range<int>(0,10), x, tbb::auto_partitioner());
- return 0;
- }
在循环中实时添加源数据parallel_do
- #include <tbb/task_scheduler_init.h>
- #include <tbb/parallel_do.h>
- #include <functional>
- void fun(char x)
- {
- Sleep(300); //暂停300ms,模拟长时间运算。
- std::cout << x;
- }
- int main()
- {
- tbb::task_scheduler_init init;
- char data[]="abcdefg";
- tbb::parallel_do(data, data+strlen(data), std::ptr_fun(fun));
- return 0;
- }
- #include <tbb/task_scheduler_init.h>
- #include <tbb/parallel_do.h>
- #include <functional>
- void fun(char x)
- {
- Sleep(300); //暂停300ms,模拟长时间运算。
- std::cout << x;
- }
- struct CFunor{
- void operator()(char x, tbb::parallel_do_feeder<char>& feeder) const
- {
- fun(x);
- //再加点数据(大写的)
- if(x>='a') feeder.add( x - 'a' + 'A' );
- }
- };
- int main()
- {
- tbb::task_scheduler_init init;
- char data[]="abcdefg";
- tbb::parallel_do(data, data+strlen(data), CFunor());
- return 0;
- }
流水线pipeline
- #include <tbb/task_scheduler_init.h>
- #include <tbb/pipeline.h>
- struct MyFilter1 : tbb::filter{
- MyFilter1(int count)
- :m_nCount(count),tbb::filter(true) // bool is_serial_
- {
- }
- virtual void* operator()(void*)
- {
- // 模拟读取数据,这里直接返回存放当前线程ID的一个内存空间
- if(m_nCount>0)
- {
- m_nCount--;
- LPDWORD pdwThread = new DWORD;
- *pdwThread = ::GetCurrentThreadId();
- return pdwThread;
- }
- else
- {
- return NULL;
- }
- }
- private:
- int m_nCount;
- };
- struct MyFilter2 : tbb::filter{
- MyFilter2()
- :tbb::filter(false) // bool is_serial_
- {
- }
- virtual void* operator()(void* item)
- {
- // 模拟处理数据,这里叠加一个当前线程ID
- LPDWORD lpPrev = (LPDWORD)item;
- LPDWORD pdwThreads = new DWORD[2];
- pdwThreads[0] = *lpPrev;
- pdwThreads[1] = ::GetCurrentThreadId();
- delete lpPrev; //把前面传过来的内存块删除
- Sleep(1000); //模拟长时间处理
- return pdwThreads;
- }
- };
- struct MyFilter3 : tbb::filter{
- MyFilter3()
- :tbb::filter(true) // bool is_serial_
- {
- }
- virtual void* operator()(void* item)
- {
- //模拟最后一步处理,一般是输出,这里直接向控制台输出
- //因为MyFilter3指定了串行工作,所以可以放心地用cout输出
- LPDWORD lpPrev = (LPDWORD)item;
- std::cout << "MyFilter1:" << std::hex << lpPrev[0];
- std::cout << ";MyFilter2:" << lpPrev[1];
- std::cout << ";MyFilter3:" << ::GetCurrentThreadId() << std::endl;
- delete []lpPrev; //把前面传过来的内存块删除
- return NULL;
- }
- };
- int main()
- {
- tbb::task_scheduler_init init;
- tbb::pipeline pipeline;
- MyFilter1 f1(10);
- MyFilter2 f2;
- MyFilter3 f3;
- pipeline.add_filter(f1);
- pipeline.add_filter(f2);
- pipeline.add_filter(f3);
- pipeline.run(5);
- return 0;
- }
tbb::task_scheduler_init init(20);