2015-10-26 68 views
10

だが、私はこれらの2つの方法があるとしましょう:PLINQがforループよりも遅いのはなぜですか?

public BigInteger PFactorial(int n) 
{ 
    return Enumerable.Range(1, n) 
        .AsParallel() 
        .Select(i => (BigInteger)i) 
        .Aggregate(BigInteger.One, BigInteger.Multiply); 
} 

public BigInteger Factorial(int n) 
{ 
    BigInteger result = BigInteger.One; 
    for(int i = 1; i <= n; i++) 
     result *= i; 
    return result; 
} 

以下は、私が得た結果だった:

PFactorial(25000) -> 0,9897 seconds 
Factorial(25000) -> 0,9252 seconds 

私はPLINQが原因のスレッドの設定のいくつかのオーバーヘッドがありますが、このような大きなであることを理解しますn私はPLINQがより速くなることを期待していました。ここで

は別の結果である:

PFactorial(50000) -> 4,91035 seconds 
Factorial(50000) -> 4,40056 seconds 
+4

(最小4は、あなたに良い分析を与える)あなたは、マルチコアプロセッサを使用していることを確認し、これは、なぜ我々のベンチマークです。何かを並列化することによってどれだけ多くのものが得られたかを推測するのではなく、テストする必要があります。もちろん、集計は並列化できますが、他のタイプの操作(投影など)ほど効果的ではありません。 – Servy

+3

'集約 'は並行して起こりません。 – Ryan

+0

'Enumerable.Range'はいくつかの追加時間も発生させます... –

答えて

10

パラレルは、骨材と本当に不可能です。私は少なくとも私の心の中でそれを想像することはできません。いずれにせよ、リストをチャンクに分割することで並列化を可能にする必要があります。それらの結果を見つける。最終的にチャンクを乗算します。ここでPLinqの速い方法です。

static public BigInteger PFactorial(int n) 
{ 
    var range = Enumerable.Range(1, n).Select(x => (BigInteger) x).AsParallel(); 
    var lists = range.GroupBy(x => x/(n/Environment.ProcessorCount)).Select(x => x.AsEnumerable()); 
    var results = lists.Select(x => x.Aggregate(BigInteger.One, BigInteger.Multiply)); 
    var result = results.Aggregate(BigInteger.One, BigInteger.Multiply); 
    return result; 
} 

テスト

PFactorial(50000) -> 1,41 seconds 
Factorial(50000) -> 2,69 seconds 

編集:あなたは、それは完全に並列化を使用することができ、種子を使用いけないし、あなたが(少し速い)上記のようにほとんど同じ結果を取得する場合ServyとChatzigiannakisで述べたように。

return Enumerable.Range(1,n).Select(x => (BigInteger)x).AsParallel().Aggregate(BigInteger.Multiply); 
+1

'Aggregate'は、絶対に*シードがあるときではなく、並行して作業を行うことができます。それはもちろん、あなたがそれをやっているのと同じように仕事を並列化します。つまり、仕事をチャンクに分割し、それらを並行して集計し、中間結果を組み合わせます。 – Servy

+0

私は種を使わずに試しました。私はまだ通常のFactorialと同じ結果を得ています。情報に感謝します。それを見てください。 @Servy –

+1

'ParallelEnumerable.Aggregate'は、連想集計関数と非連想集計関数を内部的に区別し、前者を並列に実行します。不特定のままにすると、私はそれが連想(および可換)と仮定していると信じています。 –

1

pLinQが常にLinQより高速であるとは考えないでください。多くの条件に基づいたPLinQの実行時間

PLinQを使用するのは、要素が増え、CPU集約的なクエリがある場合のみです。私は、サイクル遅延としてCPU負荷をエミュレートする関数にSystem.Threading.Thread.Sleep(1)を使用し、その関数をLinQとPlinQから10000回呼び出すことを提案します。違いを見ることができます。見つけてくださいsample here

現在のファクトリは実際にはCPU集中型タスクを実行しておらず、PLinQはクエリを複数のコアで実行するように分割し、個々のコアの結果を1つの出力に集約し、その時は普通。

また

+0

* "現在のファンクショナルはCPU集約的な仕事を実際に行っていません" * Um ...はい、そうです。 – Ryan

+0

@minitechそうではありません。いくつかの数値を掛け合わせるだけです。これは平均(4900ms/50000)0.098msの乗算のようです。 Parallelにとっては、バッチ処理、起動スレッド、すべてのラッピングなどを処理するために必要なすべての作業を考慮して、何らかの利得を実現することは、それほど大きな作業ではありません。 – NPSF3000

+2

@ NPSF3000:受け入れられた答えによって証明されるように、それは利益を実現することができます。 – Ryan

関連する問題