アグリゲーターのようなプロジェクトの1つで、Webからのフィード、ポッドキャストなどを解析します。
多数のリソースがある場合、シーケンシャルアプローチを使用すると、すべてのリソースを処理するのにかなりの時間がかかります(ネットワークの問題などが原因で)。
foreach(feed in feeds)
{
read_from_web(feed)
parse(feed)
}
したがって、並行性を実装したいので、基本的にThreadPoolsを使用してワーカースレッドで処理するか、TPLに依存してソートするかを決定できませんでした。
ThreadPoolsは確かにワーカースレッドでジョブを処理し、期待どおりのものを取得します(マルチコアCPU環境では、他のコアも使用されます)。
しかし、TPLは推奨される方法であるため、TPLも検討したいのですが、少し心配です。まず第一に、TPLはThreadPoolを使用しますが、意思決定の層を追加します。私は、シングルコア環境が存在するという条件を主に心配しています。私が間違っていない場合、TPLは最初に使用可能なCPUコアの数に等しいワーカースレッドの数で始まります。 IPLの場合、TPLがシーケンシャルアプローチと同様の結果をもたらすことを恐れています。
IOにバインドされた操作(私の場合はWebからリソースを読み取る)の場合、ThreadPoolを使用して物事を制御するのが最善ですか、それともTPLに依存する方がよいでしょうか? TPLはIOバウンドシナリオでも使用できますか?
更新:私の主な懸念は-シングルコアCPU上環境はTPLがシーケンシャルアプローチのように動作するか、それとも並行性を提供しますか?私はすでに Microsoft .NETでの並列プログラミング を読んでいるので book ですが、これに対する正確な答えが見つかりませんでした。
注:これは、以前の質問[ スレッドの同時実行と並列処理を一緒に使用することは可能ですか? ]の言い換えであり、非常に誤った表現でした。
そこで、代わりにこのためのテストを作成し、実際のデータで確認することにしました。
テスト凡例
テスト結果
シングルコアCPU [Win7-32]-VMWareの下で実行-
Test Environment: 1 physical cpus, 1 cores, 1 logical cpus.
Will be parsing a total of 10 feeds.
________________________________________________________________________________
Itr. Seq. PrlEx TPL TPool
________________________________________________________________________________
#1 10.82s 04.05s 02.69s 02.60s
#2 07.48s 03.18s 03.17s 02.91s
#3 07.66s 03.21s 01.90s 01.68s
#4 07.43s 01.65s 01.70s 01.76s
#5 07.81s 02.20s 01.75s 01.71s
#6 07.67s 03.25s 01.97s 01.63s
#7 08.14s 01.77s 01.72s 02.66s
#8 08.04s 03.01s 02.03s 01.75s
#9 08.80s 01.71s 01.67s 01.75s
#10 10.19s 02.23s 01.62s 01.74s
________________________________________________________________________________
Avg. 08.40s 02.63s 02.02s 02.02s
________________________________________________________________________________
シングルコアCPU [WinXP]-VMWareの下で実行-
Test Environment: 1 physical cpus, NotSupported cores, NotSupported logical cpus.
Will be parsing a total of 10 feeds.
________________________________________________________________________________
Itr. Seq. PrlEx TPL TPool
________________________________________________________________________________
#1 10.79s 04.05s 02.75s 02.13s
#2 07.53s 02.84s 02.08s 02.07s
#3 07.79s 03.74s 02.04s 02.07s
#4 08.28s 02.88s 02.73s 03.43s
#5 07.55s 02.59s 03.99s 03.19s
#6 07.50s 02.90s 02.83s 02.29s
#7 07.80s 04.32s 02.78s 02.67s
#8 07.65s 03.10s 02.07s 02.53s
#9 10.70s 02.61s 02.04s 02.10s
#10 08.98s 02.88s 02.09s 02.16s
________________________________________________________________________________
Avg. 08.46s 03.19s 02.54s 02.46s
________________________________________________________________________________
デュアルコアCPU [Win7-64]
Test Environment: 1 physical cpus, 2 cores, 2 logical cpus.
Will be parsing a total of 10 feeds.
________________________________________________________________________________
Itr. Seq. PrlEx TPL TPool
________________________________________________________________________________
#1 07.09s 02.28s 02.64s 01.79s
#2 06.04s 02.53s 01.96s 01.94s
#3 05.84s 02.18s 02.08s 02.34s
#4 06.00s 01.43s 01.69s 01.43s
#5 05.74s 01.61s 01.36s 01.49s
#6 05.92s 01.59s 01.73s 01.50s
#7 06.09s 01.44s 02.14s 02.37s
#8 06.37s 01.34s 01.46s 01.36s
#9 06.57s 01.30s 01.58s 01.67s
#10 06.06s 01.95s 02.88s 01.62s
________________________________________________________________________________
Avg. 06.17s 01.76s 01.95s 01.75s
________________________________________________________________________________
クアッドコアCPU [Win7-64]-HyprerThreadingサポート-
Test Environment: 1 physical cpus, 4 cores, 8 logical cpus.
Will be parsing a total of 10 feeds.
________________________________________________________________________________
Itr. Seq. PrlEx TPL TPool
________________________________________________________________________________
#1 10.56s 02.03s 01.71s 01.69s
#2 07.42s 01.63s 01.71s 01.69s
#3 11.66s 01.69s 01.73s 01.61s
#4 07.52s 01.77s 01.63s 01.65s
#5 07.69s 02.32s 01.67s 01.62s
#6 07.31s 01.64s 01.53s 02.17s
#7 07.44s 02.56s 02.35s 02.31s
#8 08.36s 01.93s 01.73s 01.66s
#9 07.92s 02.15s 01.72s 01.65s
#10 07.60s 02.14s 01.68s 01.68s
________________________________________________________________________________
Avg. 08.35s 01.99s 01.75s 01.77s
________________________________________________________________________________
要約
自分でテストを実行する
ソース here をダウンロードして、自分で実行できます。結果を投稿できる場合は、それらも追加します。
更新:ソースリンクを修正しました。
IOにバインドされたタスクのスループットを最大化しようとしている場合は、絶対にmust従来の非同期TPLベースの作業での処理モデル(APM)API。 APM APIは、非同期IOコールバックが保留中の場合、CPUスレッドのブロックを解除する唯一の方法です。TPLは、 TaskFactory::FromAsync
ヘルパーメソッド を提供します。 APMとTPLコードを組み合わせます。
これら2つのプログラミングモデルを組み合わせて非同期nirvanaを実現する方法の詳細については、MSDNの TPLおよび従来の.NET非同期プログラミング というタイトルの.NET SDKのこのセクションを参照してください。
TPLが、独自のスレッドプールを作成するときに持っているコントロールの一部を削除するのは正しいことです。しかし、これは、深く掘り下げたくない場合にのみ正しいです。 TPLを使用すると、TPLスレッドプールの一部ではなく、目的を十分に果たすことができる長期実行タスクを作成できます。無料で読むことができる出版された本 Microsoft .NETでの並列プログラミング は、TPLがどのように使用されるかについて、より多くの洞察を与えてくれます。 Paralle.For、Tasks explicit パラメータに割り当てるスレッド数を指定するオプションが常にあります。これに加えて、完全に制御したい場合は、TPLスケジューラを独自のスケジューラに置き換えることができます。
独自の タスクスケジューラ をTPLタスクに割り当てることができます。ただし、デフォルトの work stealing oneは非常に賢い方法です。
IPLの場合、TPLがシーケンシャルアプローチと同様の結果をもたらすことを恐れています。
そうなると思います。ボトルネックは何ですか?解析またはダウンロードですか?マルチスレッドは、Webからのダウンロードにはあまり役立ちません。
タスクパラレルライブラリを使用して、トリミング、ダウンロードした画像のマスクまたはエフェクトの適用、ポッドキャストからのサンプルのカットなどを行います。これはよりスケーラブルです。
しかし、それは桁違いのスピードアップではありません。リソースをいくつかの機能の実装、テストに費やしてください。
PS。 「わあ、私の関数は0.9秒ではなく0.7秒で実行される」;)
URLへの呼び出しを並列化すると、コアが1つしかない場合でも、アプリケーションが改善されると思います。このコードを見てください:
var client = new HttpClient();
var urls = new[]{"a", "url", "to", "find"};
// due to the EAP pattern, this will run in parallel.
var tasks = urls.Select(c=> client.GetAsync(c));
var result = Tasks.WhenAll(task).ContinueWith(a=> AnalyzeThisWords(a.Result));
result.Wait(); // don't know if this is needed or it's correct to call wait
この場合のマルチスレッドと非同期の違いは、コールバック/完了の方法です。
EAPを使用する場合、タスクの数はスレッドの数とは関係ありません。
GetAsyncタスクに依存しているため、httpクライアントはネットワークストリーム(ソケット、tcpクライアントなど)を使用し、BeginRead/EndReadが完了するとイベントを発生するように通知します。したがって、この時点ではスレッドは関与していません。
完了が呼び出された後、新しいスレッドが作成される可能性がありますが、新しいスレッドを作成する、既存のスレッドを使用する、または呼び出し元のスレッドを使用するタスクをインライン化するのは、TaskScheduler(GetAsync/ContinueWith呼び出しで使用)に依存します。
AnalyzeThisWords
が長時間ブロックされると、ContinueWithの「コールバック」がスレッドプールワーカーから実行されるため、ボトルネックが発生し始めます。