2016-10-24 12 views
1

最近、私は目的のために通常のforループよりも優れたパフォーマンスを示すParralel.Forループを見つけました。多くの方法でC#でParallel.Forのパフォーマンスを改善する

これは私がそれを使用する方法です:

Parallel.For(0, values.Count, i =>Products.Add(GetAllProductByID(values[i]))); 

それは十分に速く、より速く私のアプリケーションの仕事をしたが、まだありません。あなたに私の質問は:

  1. Parallel.ForeachはParallel.Forより高速ですか?
  2. Parralel.Forループを組み合わせて、より高速に(つまり、より多くのCPUパワーを使用して)実行できる「ハイブリッド」メソッドがありますか?はいの場合、どうですか?

誰かが私を助けてくれますか?あなたはパラレルとを果たしにしたい場合は、私は例えばパラレルLINQのPLINQ)の代わりに、Parallel.For/Parallel.ForEachを、使用することをお勧め

+3

' *危険な*の場合、 'Products'は' List 'であり、スレッドセーフではありません。 –

+2

CPUボトルネックがありますか?あなたのコードをプロファイリングして、あなたのプログラムで最も多くの時間を取っていることを確認しましたか? – Dismissile

+2

私は彼らがあなたを助けるようなやり方で異なって動作するのではないかと疑います。あなたのプロセスはCPUバインドで、I/Oバウンドではないのですか? 1回のI/O操作ですべてのデータを戻すために、すべてのIDを1つのクエリに渡すことはできませんか? –

答えて

1

あなたの実装をチューニングしてみてくださいWith方法(例えばWithDegreeOfParallelism)の助けを借りて

var Products = Enumerable 
    .Range(0, values.Count) 
    .AsParallel() 
    //.WithDegreeOfParallelism(10) // <- if you want, say 10 threads 
    .Select(i => GetAllProductByID(values[i])) 
    .ToList(); // <- this is thread safe now 

+0

これを私がやっているeBay API呼び出しの観点に入れてみましょう。私が10本のスレッドをセットすると、10回のコールで10回のコールが同時に行われることになります。または、その値が10に設定されている場合、1つのスレッドで10以上のコールを実行できますか? :D – User987

+0

@ User987:10スレッドはすべて、いわゆるスレッドプル*から取得されます。 10スレッドとは、タスクを解決するときに最大10スレッドが使用されることを意味します(実行するアイテムが10個未満の場合は10スレッド未満になります)。確かに、あなたは10以上のアイテムを持つことができます。 –

+0

聖なる男、あなたは天才です!このメソッドは、現在のParralel.Forループより少なくとも2倍速いだけでなく、スレッドセーフでもあります。私はちょうど私の古い方法、巨大なデータの約44秒を使用して結果を得るために必要な時間を測定しました。あなたのコードでは、結果を得るのに20秒未満がかかります。あなたは天才です!! :D – User987

1

2つの関連概念があります。非同期プログラミングとマルチスレッドです。基本的には、「並行」または非同期で処理を行うには、新しいスレッドを作成するか、同じスレッドで非同期で動作させることができます。

どちらの方法でも、race conditionsを防ぐには、何らかの仕組みが必要です。次のようにWikipediaの記事から私は、競合状態が定義されているにリンク:

競合状態やレースの危険が出力順序に依存している電子、 ソフトウェアやその他のシステムの動作です か他の制御できない事象のタイミング。プログラマが意図した順序でイベント が発生しない場合、バグになります。

少数の人々がコメントで述べたように、あなたは、スレッドセーフであることが、標準のListクラスに依存することはできません - あなたが複数のスレッドからそれを更新している場合、すなわち、それは予期しない動作をする可能性があります。マイクロソフトでは、非同期または複数のスレッドから更新する場合に、予期したように動作する特別な「ビルトイン」コレクションクラス(System.Collections.Concurrent namespace)を提供しています。

よく文書化されたライブラリ(Microsoftの一般的にはドキュメントの中ではかなり良い)については、問題のクラスまたはメソッドがスレッドセーフであるかどうかを明示的に述べることがよくあります。例えば、System.Collections.Generic.Listためdocumentationで、それは次のように述べている:(Visual BasicではShared)

この型のpublic staticメンバーは、スレッド 安全です。どのインスタンスメンバーもスレッドセーフであるとは限りません。

非同期プログラミング(vs。マルチスレッド)、私の標準的なイラストは次のようなものです:10人のレストランがあるとします。ウェイターが来たら、彼が注文した最初の人は準備ができていません。しかし、他の9人はそうです。したがって、ウェイターは他の9人に注文を求め、元の人に戻ってくる。 (それは間違いなく、元の人が注文する準備ができているのを待つために2番目のウェイターを手に入れてしまうので、とにかく時間を節約することはできません)。これは、async/awaitが通常動作する方法です(例外的に、Thread.Run(...)のようなTask Parallelライブラリ呼び出しのいくつかは、実際には他のスレッドで実行されています - 例では2番目のウェイターを持ち込みます)どのドキュメントがどのドキュメントであるかをチェックします)。

基本的には、同じスレッド上で非同期に(または新しいスレッドを作成して)選択するのは、I/Oバインドの何かをしようとしているかどうかによって決まります。結果)またはCPUにバインドされています。

Ebayの結果を待つことを主目的としているのであれば、マルチスレッドを使用した場合のパフォーマンス上のメリットがほとんどない場合と同じスレッドで非同期で作業するほうがよいでしょう。私たちのアナロジーに戻って考えてみましょう。最初の男が注文する準備が整うのを待つために2番目のウェイターを連れて来ることは、ウェイターが彼に戻ってくるだけでは必ずしも良いことではありません。

私はこの構文が完全でない場合ので、私を許してIDEの前に座っていないんだけど、ここで何ができるかのおおよそのアイデアです:Products.Add`がうまくでき

public async Task GetResults(int[] productIDsToGet) { 
    var tasks = new List<Task>(); 
    foreach (int productID in productIDsToGet) { 
     Task task = GetResultFromEbay(productID); 
     tasks.Add(task); 
    } 

    // Wait for all of the tasks to complete 
    await Task.WhenAll(tasks); 
} 

private async Task GetResultFromEbay(int productIdToGet) { 
    // Get result asynchronously from eBay 
} 
+1

ありがとう私はあなたにソリューションを試してみます:) – User987

関連する問題