web-dev-qa-db-ja.com

IOにバインドされた操作にThreadPoolsまたはTask Parallel Libraryを使用する必要があります

アグリゲーターのようなプロジェクトの1つで、Webからのフィード、ポッドキャストなどを解析します。

多数のリソースがある場合、シーケンシャルアプローチを使用すると、すべてのリソースを処理するのにかなりの時間がかかります(ネットワークの問題などが原因で)。

foreach(feed in feeds)
{
   read_from_web(feed)
   parse(feed)
}

したがって、並行性を実装したいので、基本的にThreadPoolsを使用してワーカースレッドで処理するか、TPLに依存してソートするかを決定できませんでした。

ThreadPoolsは確かにワーカースレッドでジョブを処理し、期待どおりのものを取得します(マルチコアCPU環境では、他のコアも使用されます)。

concurrency

しかし、TPLは推奨される方法であるため、TPLも検討したいのですが、少し心配です。まず第一に、TPLはThreadPoolを使用しますが、意思決定の層を追加します。私は、シングルコア環境が存在するという条件を主に心配しています。私が間違っていない場合、TPLは最初に使用可能なCPUコアの数に等しいワーカースレッドの数で始まります。 IPLの場合、TPLがシーケンシャルアプローチと同様の結果をもたらすことを恐れています。

IOにバインドされた操作(私の場合はWebからリソースを読み取る)の場合、ThreadPoolを使用して物事を制御するのが最善ですか、それともTPLに依存する方がよいでしょうか? TPLはIOバウンドシナリオでも使用できますか?

更新:私の主な懸念は-シングルコアCPU上環境はTPLがシーケンシャルアプローチのように動作するか、それとも並行性を提供しますか?私はすでに Microsoft .NETでの並列プログラミング を読んでいるので book ですが、これに対する正確な答えが見つかりませんでした。

注:これは、以前の質問[ スレッドの同時実行と並列処理を一緒に使用することは可能ですか? ]の言い換えであり、非常に誤った表現でした。

76
HuseyinUslu

そこで、代わりにこのためのテストを作成し、実際のデータで確認することにしました。

テスト凡例

  • Itr:反復
  • Seq:シーケンシャルアプローチ。
  • PrlEx:並列拡張-Parallel.ForEach
  • TPL:タスク並列ライブラリ
  • TPool:ThreadPool

テスト結果

シングルコア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
________________________________________________________________________________

要約

  • シングルコア環境で実行してもマルチコア環境で実行しても、Parallel Extensions、TPL、およびThreadPoolは同じように動作し、おおよその結果が得られます
  • それでも[〜#〜] tpl [〜#〜]にはadvantagesがあります簡単な例外処理、キャンセルのサポート、タスク結果を簡単に返す機能など。 Parallel Extensionsも別の実行可能な代替手段ですが。

自分でテストを実行する

ソース here をダウンロードして、自分で実行できます。結果を投稿できる場合は、それらも追加します。

更新:ソースリンクを修正しました。

106
HuseyinUslu

IOにバインドされたタスクのスループットを最大化しようとしている場合は、絶対にmust従来の非同期TPLベースの作業での処理モデル(APM)API。 APM APIは、非同期IOコールバックが保留中の場合、CPUスレッドのブロックを解除する唯一の方法です。TPLは、 TaskFactory::FromAsyncヘルパーメソッド を提供します。 APMとTPLコードを組み合わせます。

これら2つのプログラミングモデルを組み合わせて非同期nirvanaを実現する方法の詳細については、MSDNの TPLおよび従来の.NET非同期プログラミング というタイトルの.NET SDKのこのセクションを参照してください。

15
Drew Marsh

TPLが、独自のスレッドプールを作成するときに持っているコントロールの一部を削除するのは正しいことです。しかし、これは、深く掘り下げたくない場合にのみ正しいです。 TPLを使用すると、TPLスレッドプールの一部ではなく、目的を十分に果たすことができる長期実行タスクを作成できます。無料で読むことができる出版された本 Microsoft .NETでの並列プログラミング は、TPLがどのように使用されるかについて、より多くの洞察を与えてくれます。 Paralle.For、Tasks explicit パラメータに割り当てるスレッド数を指定するオプションが常にあります。これに加えて、完全に制御したい場合は、TPLスケジューラを独自のスケジューラに置き換えることができます。

2
Alois Kraus

独自の タスクスケジューラ をTPLタスクに割り当てることができます。ただし、デフォルトの work stealing oneは非常に賢い方法です。

1
ehnmark

IPLの場合、TPLがシーケンシャルアプローチと同様の結果をもたらすことを恐れています。

そうなると思います。ボトルネックは何ですか?解析またはダウンロードですか?マルチスレッドは、Webからのダウンロードにはあまり役立ちません。

タスクパラレルライブラリを使用して、トリミング、ダウンロードした画像のマスクまたはエフェクトの適用、ポッドキャストからのサンプルのカットなどを行います。これはよりスケーラブルです。

しかし、それは桁違いのスピードアップではありません。リソースをいくつかの機能の実装、テストに費やしてください。

PS。 「わあ、私の関数は0.9秒ではなく0.7秒で実行される」;)

0
Lukasz Madon

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の「コールバック」がスレッドプールワーカーから実行されるため、ボトルネックが発生し始めます。