あなたの「問題」は、意味がないときにPLINQを使用しています
PLINQが常に高速になるとは限りません。PLINQは常にオーバーヘッドを追加します。
CPU命令に関して; 実行する必要のある作業の量(Xと呼びます)が何であれ、Xを超える命令を実行することになります。PLINQは、スレッドを開始し、スレッドに作業を委任し、結果を作業可能な形式に戻すために、多くの追加作業を実行します。
これを行うことの利点は、複数のCPU/コアで作業できることです。時々それはより速いです。実行しているCPU作業の量がオーバーヘッドに比べて少ない場合、処理速度は遅くなります。
コードを実行すると、次の出力が得られます。
順次実行2ミリ秒
並列実行40ミリ秒
また、PLINQコードによって作成されている8つのワーカースレッドを確認できます。これらの8つのスレッドは、2ミリ秒に相当する計算で多くのオーバーヘッドを表します。Parallel Executionベンチマークを2回実行すると、オーバーヘッドがどれだけあるかを知ることができます。ワーカースレッドはぶらぶらします。これが2回目の実行での私の出力です:
順次実行2ミリ秒
並列#1実行40ミリ秒
並列#2実行3ミリ秒
2回目ははるかに高速です。しかし、それでも何もしないよりも遅いです。なぜなら、ワーカースレッドが既に作成されている場合でも、PLINQは、スレッド間で操作を分割し、アクセス可能な形式で結果を取得するための作業を行う必要があるためです。
実行する必要のある作業が多いほど、オーバーヘッドの影響は少なくなります。この例では、Whereラムダを呼び出された静的関数に置き換えIsValid
、%2を1回だけではなく500回計算します。
static bool IsValid(int input)
{
int result=0;
for(int i =0;i<500;i++)
result = input%2;
return result == 0;
}
今-私の実行時間は次のとおりです。
順次実行36ミリ秒
並列#1実行47ミリ秒
並列#2実行9ミリ秒
PLINQは、最初の実行ではまだ低速ですが、2番目の実行では大幅に高速であることがわかります。ループを500から5000に増やすことでCPUの作業を増やす場合(私のマシンでは)、PLINQが勝ちます。
TL;DR-あなたは正しくやっています。PLINQをより速く選択するのに十分な作業を行っていないだけです。
これが私がやったことの全体のソースコードです:
static void Main(string[] args)
{
Stopwatch sw = new Stopwatch();
int[] vals = Enumerable.Range(0, Int16.MaxValue).ToArray();
sw.Start();
int[] x1 = vals.Where(IsValid).ToArray();
sw.Stop();
Console.WriteLine("Sequential Execution {0} milliseconds", sw.ElapsedMilliseconds);
sw.Restart();
int[] x2 = vals.AsParallel().Where(IsValid).ToArray();
sw.Stop();
Console.WriteLine("Parallel #1 Execution {0} milliseconds", sw.ElapsedMilliseconds);
sw.Restart();
int[] x3 = vals.AsParallel().Where(IsValid).ToArray();
sw.Stop();
Console.WriteLine("Parallel #2 Execution {0} milliseconds", sw.ElapsedMilliseconds);
Console.Read();
}
static bool IsValid(int input)
{
int result=0;
for(int i =0;i<5000;i++)
result = input%2;
return result == 0;
}