TBB(Intel Threading Building Blocks)学习笔记

 TBB(Intel Threading Building Blocks)学习笔记
并行与并发是相对的,OS里讲的是并发而在 架构 方面更多的是说并行。并行是分多个层面的,我的认为基本上能够分为这么几个层面:一、指令级的并行;即所谓的微程序、指令流水线等,如今 cp u的一级缓存、二级缓存都很大,因此这个cache的效果仍是比较好的(基于局部性原理)二、线程级的并行;即同一个时刻多个函数在运行(如今的cpu好像都是多核的)三、服务级别的(好比一个游戏服务器中有商店服务、也有战斗服务、聊天服务等 这里的每一个服务可能对应多个逻辑线程)四、节点级别的;即所谓的分布式系统,多个节点互相配合,使整个系统在逻辑上成为一个单一的系统。(google、qq等这些海量访问的服务通通是分布式的)通常来讲,第一个级别的并行直接作在硬件里面,第二个级别的并行会有一些基础的框架,第三和第四个级别的并行就是应用程序本身的架构的问题了。这里面实际上有一个争论:是在算法并行化上面花心思去研究仍是采用分布式的框架来面对问题规模的增加?实际上2者各有利弊,前者能够充分利用已有硬件,可是对程序员的要求较高,维护开发成本高,风险大;后者容易实现可是浪费硬件,在有些状况下不是全部问题均可以用加个机器的方式能够解决的(好比客户端上的多媒体软件,其计算量极大,总不能要求全部用户都升级吧。)

Intel Threading Building Blocks,是为了方便程序员使用多核处理器的C++库,应该是对应上面提到的第二个级别的并行的。

1、TBB应该提供哪些东西?
    用TBB就是为了程序的并行化,那么程序员须要什么样的支持呢?最理想的状况是已有的代码不做任何修改,换一个编译器从新编译一下就OK,如今看来这个还不太现实。要有更好的效率就须要有更多的启发式信息,同时也就要求程序员要了解不少细节。整个程序逻辑没办法自动并行化,那就针对控制流进行并行化吧,因此TBB中提供了 parallel_for、parallel_while、 parallel_re du ce等;(这些是TBB给C++程序员的比较高层的接口)并行确定是多线程,这样的话数据竞争问题就比较棘手,因此TBB提供并发容器;若是以为
TBB提供的这些接口尚未办法解决性能问题,那就能够更深刻的研究使用mut ex 、atomic、task等了;能够看出,TBB从几个层次上为程序员提供了支持。

2、TBB提供的接口
    由底层到高层,task_scheduler--------co nc urrent_container--------parallel_for---pipeline
简单说,TBB帮咱们调度一个个task(比OS的调度要高效),实现高效的并行算法

3、细节
一、parallel_for 适用场合:多个数据或请求彼此没有依赖关系,所要进行的操做是同样的(典型SPMD)
    例子:
    // 典型的c++泛型编程 blocked_range 是要处理的多个数据,3个参数依次是开始的指针(迭代器)、结束指针、每一个任务分配的数据数
    // parallel_forFibBody能够简单理解为一个函数对象(c++里是用运算符重载实现的,即()是通讯的接口)
    parallel_for( blocked_range<int>( 1, my_n, 10 ), parallel_forFibBody(my_s tr eam) );

    struct parallel_forFibBody {
    QueueStream &my_stream;
    //! fill functor arguments
    parallel_forFibBody(QueueStream &s) : my_stream(s) { }
    // 这里是并行的代码
    vo id  operator()( const blocked_range<int> &range ) const {
        int i_end = range.end();
        for( int i = range.begin(); i != i_end; ++i ) {
            my_stream.Queue.push( Matrix1110 ); // push initial matrix
        }
    }
};

二、parallel_reduce 适合于须要汇总的状况,即各个数据的结果须要汇总回来

例子:(注意分发下去和汇总回来的方法)
    float ParallelSumFoo( const float a[], size_t n ) {
    SumFoo sf(a);
    parallel_reduce(blocked_range<size_t>(0,n,IdealGrainSize), sf );
    return sf. su m;
    }

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

        SumFoo( SumFoo& x,  split  ) : my_a(x.my_a), sum(0) {} // 分发任务,注意这个构造器要求是线程安全的

        void  join ( const SumFoo& y ) {sum+=y.sum;} // 收集汇总结果

        SumFoo(float a[] ) :
            my_a(a), sum(0)
        {}
    };
三、parallel_while 有时不知道循环什么时候结束,即便用for的end未知,在这种状况下可使用parallel_while

例子:注意pop_if_present、typedef Item* argument_type、operator()等部分的处理
// 串行版本
void SerialApplyFooToList( Item*root ) {
    for( Item* ptr=root; ptr!=NULL; ptr=ptr->next )
        Foo(pointer->data);
}

// 并行版本
class ItemStream {
    Item* my_ptr;
public:
    bool pop_if_present( Item*& item ) { // 用于提供下一个迭代器
        if( my_ptr ) {
            item = my_ptr;
            my_ptr = my_ptr->next;
            return true;
        } e ls e {
            return false;
        }
};
ItemStream( Item* root ) : my_ptr(root) {}
}

class ApplyFoo {
public:
    void operator()( Item* item ) const { // 要求必定是const的
        Foo(item->data);
    }
    typedef Item* argument_type; // 此句是必须的
};

void ParallelApplyFooToList( Item*root ) {
    // parallel_while是个class
    parallel_while<ApplyFoo> w; //  先创建个对象
    ItemStream stream;
    ApplyFoo body;

    // 第一个参数提供数据指针,第二个参数提供函数体
    w.run( stream, body );
}

4、并发容器
    大部分程序都有容器类,在多线程环境下就有数据污染的问题,为了使并发的线程串行化,通常是使用加锁的办法,若是这个
容器由程序员本身来实现,难度仍是比较大的,这样就须要有线程安全的容器类。
    一、concurrent_hash_map
        hash接口与stl相似
    二、concurrent_vector
        grow_by(n) 插入n个item(动态增加)
        grow_to_at_least()设定容器的大小
        size()  包括正在并发增加的部分 由于有可能会同时取,因此程序员须要本身维护本身的class的线程安全性
         clear () 不是线程安全的
    三、concurrent_queue
        pop_if_present(item) 非阻塞,
        pop() 阻塞,
        concurrent_queue::size() 负数时表示有多少个消费者在等待
         set _capacity()指定队列大小,会使push操做被阻塞
在并行时,paralell_while pipeline 的效率要高于concurrent_queue


5、若是以为TBB的加锁效率不高,能够本身控制锁
    最经常使用的是spin lock
6、整个TBB引擎的核心是 Task Scheduler(基于任务图来实现)
    提升效率的核心是threading stealing,保证cpu的效率

7、小结
    要使用TBB进行并行化,首先程序员要知道哪些是能够并行化;其次,要熟悉TBB并行化的框架(主要是泛型编程);再次,程序员要大概知道
并行算法的执行步骤;最后,利用TBB的组件,实现并行化的算法。整体上来讲,仍是不太好用的

再分享一下我老师大神的人工智能教程吧。零基础!通俗易懂!风趣幽默!还带黄段子!但愿你也加入到咱们人工智能的队伍中来!https://blog.csdn.net/jiangjunshowhtml

相关文章
相关标签/搜索