経緯
PLINQについて速いと噂には聞いていたがなぜ速いのかというところに疑問を
感じ、どんな動きをしているかを簡単に共有できればなと感じて
そもそもPLINQって?
ドキュメントには下記のように記載されています。
データ ソースをセグメントにパーティション分割し、複数のプロセッサで個々のワーカー スレッドの各セグメントに対してクエリを並行実行します。
うーん、並行実行というのはマルチスレッドのことなのかな?
マルチスレッド自体複雑だからな、、、 async/awaitとかもなんだかんだ理解まで時間かかったし、、、
実際にやってみた
前置きが長くなりましたが、実際に今までForeachで処理していた部分をAsPararell().ForAll()を使用して、どのくらい変化が現れるかを実験してみました。
また、実際にプログラムを作成する上で下記記事を参考にさせていただきました、本当にありがとうございます。
(参考記事:PLINQの実行速度と高速な書き方)
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Linq;
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Start");
var app = new Program();
app.Run();
Console.WriteLine("End");
}
public void Run()
{
const int N = 500;
var list = Enumerable.Range(1, N).ToList();
var sw = new System.Diagnostics.Stopwatch();
// for
sw.Restart();
list.ForEach(x => Thread.Sleep(1));
Console.WriteLine("for : " + sw.Elapsed);
// PLINQ
sw.Restart();
list.AsParallel().ForAll(x => Thread.Sleep(1));
Console.WriteLine("PLINQ : " + sw.Elapsed);
}
}
結果がこちらとなります。
$ dotnet run
Start
for : 00:00:00.9243632
PLINQ : 00:00:00.1569520
End
確かに早いですよね。
ここからが本題でこいつがどんな感じで動いているのかを調べてみました。
端的に言うと、マルチスレッドになっているのかどうかとかその辺を見てみました(これだけ早ければマルチスレッドで動いていることはほぼ間違いないのですが・・・)
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Linq;
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Start");
var app = new Program();
app.Run();
Console.WriteLine("End");
}
public void Run()
{
// アウトプットが多いので10に変更
const int N = 10;
var list = Enumerable.Range(1, N).ToList();
// for
list.ForEach(x =>
{
Console.WriteLine("ForEach Start Theread:" + Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(1);
Console.WriteLine("ForEach End Theread:" + Thread.CurrentThread.ManagedThreadId);
});
// PLINQ
list.AsParallel().ForAll(x =>
{
Console.WriteLine("PLINQ Start Theread:" + Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(1);
Console.WriteLine("PLINQ End Theread:" + Thread.CurrentThread.ManagedThreadId);
});
}
}
結果がこちらです。
$ dotnet run
Start
ForEach Start Theread:1
ForEach End Theread:1
ForEach Start Theread:1
ForEach End Theread:1
ForEach Start Theread:1
ForEach End Theread:1
ForEach Start Theread:1
ForEach End Theread:1
ForEach Start Theread:1
ForEach End Theread:1
ForEach Start Theread:1
ForEach End Theread:1
ForEach Start Theread:1
ForEach End Theread:1
ForEach Start Theread:1
ForEach End Theread:1
ForEach Start Theread:1
ForEach End Theread:1
ForEach Start Theread:1
ForEach End Theread:1
PLINQ Start Theread:1
PLINQ Start Theread:5
PLINQ Start Theread:10
PLINQ Start Theread:7
PLINQ Start Theread:4
PLINQ Start Theread:9
PLINQ Start Theread:6
PLINQ Start Theread:8
PLINQ End Theread:6
PLINQ End Theread:10
PLINQ End Theread:4
PLINQ End Theread:9
PLINQ End Theread:1
PLINQ End Theread:7
PLINQ End Theread:5
PLINQ Start Theread:5
PLINQ End Theread:8
PLINQ Start Theread:6
PLINQ End Theread:6
PLINQ End Theread:5
End
見れば一目瞭然ですが、純粋なForEachの場合にはメインスレッドを使用しているのに対して、PLINQの場合にはマルチスレッドで動いており処理が完了し開放されたスレッドを再び利用するため処理の無駄を省き大幅にパフォーマンスが改善されるのですね。
よかったら参考にしてみてください。