TPLによる並行動作
.NET Framework 4.0で新たに提供されるTPL(Task Parallel Library)は名前空間 System.Threading.Tasks にあり、コア・ライブラリ:mscorlib に含まれるので参照設定の追加の必要はありません。今回は最もシンプルな並列化メソッド Parallel.For を使います。
計算コビト:MandelbrotPlotterにある計算ルーチンは次のようなコードです:
public void Execute() {
int w = paper_.GetWidth();
int h = paper_.GetHeight();
for ( int r = 0; r < w; ++r ) {
for (int i = 0; i < h; ++i ) {
paper_.SetPixel(r, i, Calculate(paper_.GetPoint(r, i)));
}
}
}
ご覧の通り、二重のfor-loopになっています。外側のfor-loopを Parallel.For に置き換えます。Parallel.For の引数はループの始点と終点、そしてloop変数を引数とする(戻り値のない)delegate:System.Action<T>です。第3引数に与えるdelegateにラムダ式を使うと、上記コードは簡単に置き換えられます:
public void Execute() {
int w = paper_.GetWidth();
int h = paper_.GetHeight();
System.Threading.Tasks.Parallel.For(0, w, // 始点と終点(0以上w未満)
// inner-loopのラムダ式表現
(int r) => {
for ( int i = 0; i < h; ++i ) {
paper_.SetPixel(r, i, Calculate(paper_.GetPoint(r, i)));
}
});
}
たったこれだけ。Parallel.ForはCPUコア数その他の条件に応じて適切な数のスレッドを起こし、その中でdelegateを評価します。生成スレッド数の決定はライブラリが面倒みてくれます。
添付のソリューションには C#, VB.NET, C++/CLI版を用意しています。僕のマシンはクロック2GHz強のAthlon dual-coreなんですけど、実行結果はC#版で500[ms]程度でした。こんなもんでしょうかね。
quad-coreやHyper-Threadingによる仮想quad-core/octa-coreのリッチなCPUをお持ちの方はぜひお試しください。論理コア数に応じた速度向上が見込まれるはずです。コア数に応じてスケーラブルにパフォーマンスが上がるのは気持ちのいいものです。
おまけ:PPL(Parallel Patterns Library)によるnative-C++の並列化
Visual C++ 10.0(2010)には.NET 4.0におけるTPLと同様のnativeなライブラリ:PPL(Parallel Patterns Library)が追加されています。せっかくだからこちらも使ってみました。
PPLを使ったお試しコードを書いているとき、残念なことに気付きました。PPLを使うには#include <ppl.h>が必要なのですが、こいつを#includeしたコードを/clrオプション付けてC++/CLIとしてコンパイルすると:
fatal error C1189: #error : ERROR: Concurrency Runtime is not supported when compiling /clr.
というエラー。PPLはC++/CLIでは使えないようです。
ここまできてPPLを諦めるのも気持ちのいいものじゃありませんから、アプリケーションの構成を変更して無理やり(?)使ってみました。native-C++でなら問題なく
void MandelbrotPlotter::Execute() {
int w = paper_->GetWidth();
int h = paper_->GetHeight();
Concurrency::parallel_for(0, w, // 0以上w未満に対し
[&,this](int r) { // ラムダ式を並行評価する
for ( int i = 0; i < h; ++i ) {
paper_->SetPixel(r, i, Calculate(paper_->GetPoint(r,i),limit_));
}
});
}
}
と書けるので、SectionPaperとMandelbrotPlotterをC++で実装し、native-DLLに仕立ててC++/CLIで実装したMailFormから利用します。添付ソリューション:Mandelbrot_CPPCLI_and_CPP をお試しください。
nativeC++版の実行速度は.NETのなんと5倍:100[ms]を叩き出しました。nativeの面目躍如ってとこですか。
