2012-03-23 23 views
52
var fillData = new List<int>(); 
for (var i = 0; i < 100000; i++) 
{ 
    fillData.Add(i); 
} 

var stopwatch1 = new Stopwatch(); 
stopwatch1.Start(); 
var autoFill = new List<int>(); 
autoFill.AddRange(fillData); 
stopwatch1.Stop(); 

var stopwatch2 = new Stopwatch(); 
stopwatch2.Start(); 
var manualFill = new List<int>(); 
foreach (var i in fillData) 
{ 
    manualFill.Add(i); 
} 
stopwatch2.Stop(); 

私はstopwach1stopwach2から結果を取るときは、stopwatch1stopwatch2よりも常に低い値を持っています。それはaddrangeが常にforeachよりも高速であることを意味します。 誰にも分かりますか?追加された項目のforeachループを使用するよりも、AddRangeの方が速いのはなぜですか?

AddRange

答えて

76

AddRangeを確認することができます。もしそうであれば、それはforeachループが数回再割り当てする必要がある場合がある...割り当てる必要がどのくらいのスペースので、多くの値が範囲内にあるかを知る、とすることができます。

さらに、でも割り当てた後、List<T>は、基礎となる配列にバルク・コピーを実行するためにIList<T>.CopyToを使用することができます(もちろんIList<T>を実装する範囲については、。)

私はあなたがあなたのテストをしようとすることを見つけることができます疑い再びしかしfillData代わりにList<T>ためEnumerable.Range(0, 100000)を使用して、二人は同じくらいの時間がかかります。

15

ためのチェック・サイズとは、一度だけ内部配列のサイズを増大させます。

55

あなたがAddを使用している場合は、必要に応じて、それは10のデフォルトの開始サイズ(IIRC)から、(倍増)を徐々に内側の配列のサイズを変更しています。あなたが使用している場合:

var manualFill = new List<int>(fillData.Count); 

を私はそれが(これ以上のサイズ変更/データコピーしない)根本的に変更します期待しています。リフレクタから

AddRangeはむしろ倍増に成長しているよりも、内部的にこの処理を行います。それに渡された値がIListIList<T>実装している可能性

ICollection<T> is2 = collection as ICollection<T>; 
if (is2 != null) 
{ 
    int count = is2.Count; 
    if (count > 0) 
    { 
     this.EnsureCapacity(this._size + count); 
     // ^^^ this the key bit, and prevents slow growth when possible ^^^ 
+3

私はいつも 'reflector'コードで回答をupvoteします。 ** + 1 ** – gdoron

4

AddRangeを使用する場合、コレクションは配列のサイズを1倍に増やして値をコピーできます。

foreachステートメントを使用すると、コレクションのサイズを2倍以上にする必要があります。 THRサイズを大きく

は時間がかかり、完全な配列をコピーすることを意味します。

0

AddRange拡張は各項目を反復処理するのではなく、各項目を全体として適用します。 foreachと.AddRangeの両方に目的があります。 Addrangeはあなたの現在の状況のスピードのコンテストに勝つでしょう。ここではそれについて

より:

Addrange Vs Foreach

1

は試してみて、手動で項目を追加する前にintiialリストの容量を初期化します。

var manualFill = new List<int>(fillData.Count); 
2

私は、これはメモリ割り当ての最適化の結果であると仮定します。 AddRangeメモリの場合は が1回だけ割り当てられ、各反復の再割り当て時にforeachが実行されます。

はまた、いくつかの最適化は、AddRange実装であることができるあなたは、いくつかがある見ることができるように一覧AddRangeメソッドのための反射器から(例えばのmemcpy)

6

dissassembly次のコード

ICollection<T> is2 = collection as ICollection<T>; 
if (is2 != null) 
{ 
    int count = is2.Count; 
    if (count > 0) 
    { 
     this.EnsureCapacity(this._size + count); 
     if (index < this._size) 
     { 
      Array.Copy(this._items, index, this._items, index + count, this._size - index); 
     } 
     if (this == is2) 
     { 
      Array.Copy(this._items, 0, this._items, index, index); 
      Array.Copy(this._items, (int) (index + count), this._items, (int) (index * 2), (int) (this._size - index)); 
     } 
     else 
     { 
      T[] array = new T[count]; 
      is2.CopyTo(array, 0); 
      array.CopyTo(this._items, index); 
     } 
     this._size += count; 
    } 
} 

を有しますEnsureCapacity()などの最適化とArray.Copy()の使用

4

これはウェイターにあなたにビールを10回持ってきて、すぐにビールを10ビール持ってくるように頼むようなものです。

あなたは高速です:)

1

foreachループは、ループが1時間と
AddRange()メソッドは、それがあるすべての値を収集します取得しているすべての値が追加されますので、それは何だと思いますか「チャンク」として取得し、そのチャンクを指定された場所に一度に追加します。

これは市場から持ち出すための10項目のリストを持っているように簡単に理解できます。これは、すべてを1つずつ、またはすべてを一度に持っていく方が速いでしょう。

関連する問題