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ツカエ、という感じ。今時、gccもVC++もサポートしてたと思うし。
タスク並列の方も、あんまり変わらず:
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マクロ)を使っているので、シンタックス的にはずっと簡潔になっている。
まぁ、オーバヘッドは頑張って小さくしているように見えるけど、本当にこんなんで書くんかいな。