|
|
連載:C# 4入門
第2回 タスク並列ライブラリ
株式会社ピーデー 川俣 晶
2010/08/20 |
|
|
本当にコアを活用しているの?
本当にコアを活用しているのか気になる人のために、簡単な検証プログラムを用意した(リスト4)。このプログラムは、単純にループで時間を消費する機能を2回呼び出して時間を計る。ただし、1回目は従来型のシーケンシャルな呼び出しで、2回目はパラレルな呼び出しである。
using System;
using System.Threading.Tasks;
class Program
{
private const int count = 1000000000;
private static void taro()
{
Console.WriteLine("Taro is busy now");
for (int i = 0; i < count; i++)
;
Console.WriteLine("Taro Done");
}
private static void hanako()
{
Console.WriteLine("Hanako is busy now");
for (int i = 0; i < count; i++)
;
Console.WriteLine("Hanako Done");
}
static void Main(string[] args)
{
DateTime start1 = DateTime.Now;
taro();
hanako();
Console.WriteLine(DateTime.Now - start1);
DateTime start2 = DateTime.Now;
Parallel.Invoke(taro, hanako);
Console.WriteLine(DateTime.Now - start2);
}
}
|
|
リスト4 |
Taro is busy now
Taro Done
Hanako is busy now
Hanako Done
00:00:05.9710000
Taro is busy now
Hanako is busy now
Hanako Done
Taro Done
00:00:03.0320000
|
|
リスト4の実行結果 |
デバッグ・ビルドを行い、2コアのCPUで実行。後半の順番は相前後するかもしれない。 |
見てのとおり、パラレルに実行させた方が約半分の時間で終わっている。2つのコアに処理が分散されたことが、この時間差から分かるだろう。
ちなみに、無駄なカウントを行って時間をつぶすことは、8bit CPUの時代には一般的なプログラミング・テクニックであった。しかし、いまでは悪いやり方として避けるべき方法論である。CPUは有限の資源だから、時間を待つだけならThread.Sleepメソッドなどを使い、使用していない時間をほかのプロセスに渡すべきである。しかし、今回は「無駄な仕事をさせることで、処理がコア間で分散していることを示す」ためにあえて良くない方法を使っている。見習うべきお手本ではないので、注意していただきたい。
foreach文をパラレルに
実際に使用する場合は、foreach文やfor文を並列化することで、即座にパフォーマンスアップできることがある。以下は、foreach文とParallel.ForEachメソッドを比較した例である。
using System;
using System.Threading.Tasks;
class Program
{
static void Main(string[] args)
{
int [] a = {1,2,3,4,5,6,7,8,9};
foreach (var n in a) Console.Write("{0} ",n);
Console.WriteLine("by serial");
Parallel.ForEach(a, (n) => Console.Write("{0} ", n));
Console.WriteLine("by parallel");
}
}
|
|
リスト5 |
1 2 3 4 5 6 7 8 9 by serial
1 2 3 4 6 7 8 9 5 by parallel
|
|
リスト5の実行結果 |
後半の順番は不定である。 |
ここで注意すべき点は、Parallel.ForEachメソッドの実行結果は、foreach文の実行結果と必ずしも同じではないという点だ。
もし、順番に意味があればパラレル処理することで狂ってしまうこともあり得る。また、ある値に対する処理が、それまでの値の処理に依存するような場合も処理できない可能性がある。つまり、内容次第で置き換えができないケースもある。パラレルはどれほど簡単にプログラミングできても、実はアルゴリズムの選定から勝負は始まっているのだ。
for文をパラレルに
単純に数値を数えるfor文も、簡単に並列に置き換えられる。
using System;
using System.Threading.Tasks;
class Program
{
static void Main(string[] args)
{
for (int i = 0; i < 10; i++) Console.Write("{0} ", i);
Console.WriteLine("by serial");
Parallel.For(0, 10, (n) => Console.Write("{0} ", n));
Console.WriteLine("by parallel");
}
}
|
|
リスト6 |
0 1 2 3 4 5 6 7 8 9 by serial
0 5 1 2 4 8 9 3 6 7 by parallel
|
|
リスト6の実行結果 |
後半の順番は不定である。 |
ただし、すべてのfor文をパラレルに置き換えられるわけではない。また、do文やwhile文はパラレルに置き換えられない。繰り返しを始める前に回数が決まっている場合だけ並列化ができる。パラレルに処理するということは、「終わりである」と判定する前に、それ以降の値の処理が走ってしまう可能性があるからである。
また同じ理由で、break文による繰り返しの打ち切りもできない。打ち切る前に、打ち切った後で処理されないはずの処理が先行して走っていることがあるからである。
Insider.NET 記事ランキング
本日
月間