2012-03-14 16 views
2

シングルパス版よりもGroupBy()がマルチパスResultSelectorの方が高速に動作するように見えます。GroupBy()を複数回通過させると、1回のパスよりも高速になりますか?

public class DummyItem 
    { 
     public string Category { get; set; } 
     public decimal V1 { get; set; } 
     public decimal V2 { get; set; } 
    } 

私は100,000件のエントリを持つ配列は、いくつかのランダムなデータを作成し、次のクエリ反復:

アプローチ1:このクラスを考えると

カテゴリの複数のパスが

を合計します
var q = randomData.GroupBy(
    x => x.Category, 
    (k, l) => new DummyItem 
    { 
     Category = k, 
     V1 = l.Sum(x => x.V1), // Iterate the items for this category 
     V2 = l.Sum(x => x.V2), // Iterate them again 
    } 
); 

各カテゴリのV1とV2を合計すると、内側の列挙型を二重に扱うように見えます。

したがって、私は次の代替案をまとめました。これは、1回のパスでカテゴリの合計を計算することでパフォーマンスが向上すると仮定しています。

アプローチ2:カテゴリのシングルパスが

var q = randomData.GroupBy(
    x => x.Category, 
    (k, l) => l.Aggregate(// Iterate the inner list once per category 
      new decimal[2], 
      (t,d) => 
      { 
       t[0] += d.V1; 
       t[1] += d.V2; 
       return t; 
      }, 
      t => new DummyItem{ Category = k, V1=t[0], V2=t[1] } 
    ) 
); 

かなり典型的な結果合計:

'Multiple pass': iterations=5 average=2,961 ms each 
'Single pass': iterations=5 average=5,146 ms each 

を信じられないほど、アプローチ2は2倍の長い私が多数実行したアプローチ1.として取りV *特性の数、異なるカテゴリーの数および他の要因を変化させるベンチマーク。パフォーマンスの違いの大きさが異なるが、アプローチ2は常にアプローチよりも実質的に遅いです。1.

は、私がここに根本的な何かが足りないのですか?アプローチ1はアプローチ2よりも速いのですか?から

(私は...手のひらを顔に当てるが来て感じる)


* UPDATE *

私はそれが(GROUPBYを削除する価値があるだろうと思ったJirkaの答え@の後)大きなリストの単純な集計が期待通りに実行されたかどうかを確認します。タスクは、100,000のランダムな行の同じリスト上の2つの10進変数の合計を単純に計算することでした。

SUM:

decimal t1 = 0M; decimal t2 = 0M; foreach(var item in randomData) { t1 += item.V1; t2 += item.V2; } 

ベースラインのForEach

結果は驚きを続けました。私は必要な出力を得る最速の方法を信じています。

SUM:マルチパス

x = randomData.Sum(x => x.V1); 
y = randomData.Sum(x => x.V2); 

SUM:次のようにSINGLEPASS

var result = randomData.Aggregate(new DummyItem(), (t, x) => 
{ 
    t.V1 += x.V1; 
    t.V2 += x.V2; 
    return t; 
}); 

結果は以下のとおりであった:

'SUM: ForEach': iterations=10 average=1,793 ms each 
'SUM: Multipass': iterations=10 average=2,030 ms each 
'SUM: Singlepass': iterations=10 average=5,714 ms each 

は、驚くべきことに、問題は何の関係もありません明らかにGroupByと一緒に。この振る舞いは、一般的にデータ集約と一致しています。 1回のパスでデータ集約を行う方が良いとの私の前提は、間違っている(おそらく私のDBルーツの二日酔いです)。

(手のひらを顔に当てる

@Jirkaは明らかにマルチパスアプローチをoccuringでライニング指摘したように、それがベースライン「のForEach」よりもわずかに遅いことを意味します。シングルパスに最適化しようとする私の素朴な試みは、ほぼ3倍遅くなりました!

メモリ内のリストを扱うときには、リスト内の項目で何をしたいのかは、繰り返しオーバーヘッドよりもはるかに大きな要因になります。

+0

追加のご意見をお寄せいただきありがとうございます。あなたの直感を捨てないでください。シングルパスアルゴリズムは、約1 MBを超えるデータのパフォーマンス上の利点があります。しかしここでは、この利点は、最も内側の(ボトルネック)ループで発生するメソッド呼び出しによって小さくなりました。 –

答えて

1

集計では、プロセス内で99,999個の(インラインではないメソッド呼び出しの)アクティベーションレコードを作成する必要があります。これはシングルパスの利点を相殺します。

Count、Sum、Averageなどを、一般的なケースでAggregateが実行できる最適な特殊ケースと考えてください。

+1

ありがとう@Jirka。配列はAggregateのシードとして一度だけ割り当てられません。いくつかのテストでは、これはわずか4回(すなわち、4つのカテゴリのみ)であった。各カテゴリの列挙型を反復するとき、配列は単に更新されます。 –

+1

@degorolls - あなたは正しいです、私は監督には申し訳ありません。私は自分の答えを修正した。 –

+0

魅力的!ありがとう@ Jirka。私はかなり基本的な誤解を訂正しました... –

関連する問題