TBB之parallel_for

下面是串行代碼:

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

tbb::parallel_for把迭代空間分成若干塊,在每個塊上運行獨立的線程。第一步形成Body,把它作用在塊上,這個Body是一個STL風格的對象,叫做body對象,這個對象裏面有個operator()處理一個塊,下面的代碼是生成body對象:

#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) {}
};

注意operator()參數,庫提供的一個blocked_range模板類,它描述的是類型T的一維迭代空間,parallel_for也和其他類型的迭代空間工作,TBB也提供blocked_range2d的2維空間,你能定義自己的空間在3.4章節中。

parallel_for需要body對象有一個拷貝構造函數,它被調用創建一個拷貝爲每個工作線程,它也調用析構函數摧毀這些拷貝,在大多情況,隱式產生的拷貝構造函數和析構函數能夠工作正常。

因爲body對象一定被拷貝,它的operator()不應該改變body裏的成員變量,否者這個改變可能或者不可能對其他線程是可見的(產生歧義),作爲一個細微的提醒,parallel_for需要body對象的operator()聲明成const。

operator()加載my_a成局部變量a。雖然這不是強制的,但是有2個原因需要這樣做:

  • Style。它讓body看起來更像原始的。
  • Performance。有時頻繁訪問局部變量可以幫助編譯器優化循環,因爲局部變量對編譯器來說常常是更容易跟蹤的。

一旦你有了body對象,就可以調用模板函數parallel_for:

#include "tbb/tbb.h"
void ParallelApplyFoo( float a[], size_t n ) {
    parallel_for(blocked_range<size_t>(0,n), ApplyFoo(a));
}
發佈了27 篇原創文章 · 獲贊 26 · 訪問量 14萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章