Intel Threading Building Block

GPLになったそうなので、見てみる。(昔見たような気がしたけど、すっかり内容を忘れていた…)
…んー、個人的には微妙。

チュートリアルから引用すると、例えば、

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

というループを並列化するためには、

#include "tbb/blocked_range.h" 
 
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) 
    {} 
}; 
#include "tbb/parallel_for.h" 
 
void ParallelApplyFoo( float a[], size_t n ) { 
    parallel_for(blocked_range<size_t>(0,n,IdealGrainSize), ApplyFoo(a) ); 
} 

と、こうなる。ナガスギ。
ループのボディを、別Class中でoperator()の中に定義して、呼び出し側は、parallel_for中で、そのClassを使うという形。
C++で、言語を拡張せずに、Threadを利用するためには、こうせざるを得ないのかも知れないけど…

なんというか、ループの並列化がしたいんだったら、OpenMPツカエ、という感じ。今時、gccVC++もサポートしてたと思うし。

タスク並列の方も、あんまり変わらず:

long ParallelFib( long n ) { 
    long sum; 
    FibTask& a = *new(task::allocate_root()) FibTask(n,&sum); 
    task::spawn_root_and_wait(a); 
    return sum; 
} 
class FibTask: public task { 
public: 
    const long n; 
    long* const sum; 
    FibTask( long n_, long* sum_ ) : 
        n(n_), sum(sum_) 
    {} 
    task* execute() {  // Overrides virtual function task::execute 
        if( n<CutOff ) { 
            *sum = SerialFib(n); 
        } else { 
            long x, y; 
            FibTask& a = *new( allocate_child() ) FibTask(n-1,&x); 
            FibTask& b = *new( allocate_child() ) FibTask(n-2,&y); 
            // Set ref_count to "two children plus one for the wait". 
            set_ref_count(3); 
            // Start b running. 
            spawn( b ); 
            // Start a running and wait for all children (a and b). 
            spawn_and_wait_for_all( a ); 
            // Do the sum 
            *sum = x+y; 
        } 
        return NULL; 
    } 
}; 

長いヨ!

PARDSはfork()(とCPPマクロ)を使っているので、シンタックス的にはずっと簡潔になっている。

まぁ、オーバヘッドは頑張って小さくしているように見えるけど、本当にこんなんで書くんかいな。