Where と FindAll on List の速度の違いは誰でも知っています。Where は IEnumerable の一部であり、FindAll は List の一部であることを知っています。何が速いのか知りたいだけです。
5 に答える
List<T> クラスの FindAll メソッドは、実際に新しいリスト オブジェクトを構築し、それに結果を追加します。IEnumerable<T> の Where 拡張メソッドは、単に既存のリストを反復処理し、(列挙子自体以外に) 何も作成または追加せずに、一致する結果の列挙を生成します。
小規模なセットを考えると、この 2 つは同等のパフォーマンスを発揮する可能性があります。ただし、結果を含めるために作成された新しいリストは、追加の結果を含めるために動的に拡張する必要があるため、より大きなセットを指定すると、Where は FindAll よりも優れているはずです。FindAll のメモリ使用量も、一致する結果の数が増加するにつれて指数関数的に増加し始めます。ここで、Where は一定の最小限のメモリ使用量を持つ必要があります (それ自体で...結果に対して行うことはすべて除外します)。
FindAll は、新しいリストを作成する必要があるため、明らかに Where よりも遅くなります。
とにかく、Jon Hanna のコメントを本当に考慮する必要があると思います。おそらく、結果に対していくつかの操作を行う必要があり、多くの場合、リストは IEnumerable よりも便利です。
私は小さなテストを書いたので、コンソール アプリ プロジェクトに貼り付けるだけです。関数の実行、結果収集の操作(「実際の」使用のパフォーマンスを取得し、コンパイラが未使用のデータなどを最適化しないことを確認するため)の時間/ティックを測定します-私はC#が初めてで、そうではありませんそれがどのように機能するかはまだわかりません、申し訳ありません)。
注意: WhereIENumerable() を除くすべての測定関数は、要素の新しいリストを作成します。私は何か間違ったことをしているかもしれませんが、明らかに IEnumerable を反復すると、リストを反復するよりもはるかに時間がかかります。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
namespace Tests
{
public class Dummy
{
public int Val;
public Dummy(int val)
{
Val = val;
}
}
public class WhereOrFindAll
{
const int ElCount = 20000000;
const int FilterVal =1000;
const int MaxVal = 2000;
const bool CheckSum = true; // Checks sum of elements in list of resutls
static List<Dummy> list = new List<Dummy>();
public delegate void FuncToTest();
public static long TestTicks(FuncToTest function, string msg)
{
Stopwatch watch = new Stopwatch();
watch.Start();
function();
watch.Stop();
Console.Write("\r\n"+msg + "\t ticks: " + (watch.ElapsedTicks));
return watch.ElapsedTicks;
}
static void Check(List<Dummy> list)
{
if (!CheckSum) return;
Stopwatch watch = new Stopwatch();
watch.Start();
long res=0;
int count = list.Count;
for (int i = 0; i < count; i++) res += list[i].Val;
for (int i = 0; i < count; i++) res -= (long)(list[i].Val * 0.3);
watch.Stop();
Console.Write("\r\n\nCheck sum: " + res.ToString() + "\t iteration ticks: " + watch.ElapsedTicks);
}
static void Check(IEnumerable<Dummy> ieNumerable)
{
if (!CheckSum) return;
Stopwatch watch = new Stopwatch();
watch.Start();
IEnumerator<Dummy> ieNumerator = ieNumerable.GetEnumerator();
long res = 0;
while (ieNumerator.MoveNext()) res += ieNumerator.Current.Val;
ieNumerator=ieNumerable.GetEnumerator();
while (ieNumerator.MoveNext()) res -= (long)(ieNumerator.Current.Val * 0.3);
watch.Stop();
Console.Write("\r\n\nCheck sum: " + res.ToString() + "\t iteration ticks :" + watch.ElapsedTicks);
}
static void Generate()
{
if (list.Count > 0)
return;
var rand = new Random();
for (int i = 0; i < ElCount; i++)
list.Add(new Dummy(rand.Next(MaxVal)));
}
static void For()
{
List<Dummy> resList = new List<Dummy>();
int count = list.Count;
for (int i = 0; i < count; i++)
{
if (list[i].Val < FilterVal)
resList.Add(list[i]);
}
Check(resList);
}
static void Foreach()
{
List<Dummy> resList = new List<Dummy>();
int count = list.Count;
foreach (Dummy dummy in list)
{
if (dummy.Val < FilterVal)
resList.Add(dummy);
}
Check(resList);
}
static void WhereToList()
{
List<Dummy> resList = list.Where(x => x.Val < FilterVal).ToList<Dummy>();
Check(resList);
}
static void WhereIEnumerable()
{
Stopwatch watch = new Stopwatch();
IEnumerable<Dummy> iEnumerable = list.Where(x => x.Val < FilterVal);
Check(iEnumerable);
}
static void FindAll()
{
List<Dummy> resList = list.FindAll(x => x.Val < FilterVal);
Check(resList);
}
public static void Run()
{
Generate();
long[] ticks = { 0, 0, 0, 0, 0 };
for (int i = 0; i < 10; i++)
{
ticks[0] += TestTicks(For, "For \t\t");
ticks[1] += TestTicks(Foreach, "Foreach \t");
ticks[2] += TestTicks(WhereToList, "Where to list \t");
ticks[3] += TestTicks(WhereIEnumerable, "Where Ienum \t");
ticks[4] += TestTicks(FindAll, "FindAll \t");
Console.Write("\r\n---------------");
}
for (int i = 0; i < 5; i++)
Console.Write("\r\n"+ticks[i].ToString());
}
}
class Program
{
static void Main(string[] args)
{
WhereOrFindAll.Run();
Console.Read();
}
}
}
結果 (ティック) - チェックサムが有効 (結果に対する一部の操作)、モード: デバッグなしでリリース (CTRL+F5):
- 16,222,276 (for ->list)
- 17,151,121 (foreach -> list)
- 4,741,494 (where ->list)
- 27,122,285 (where ->ienum)
- 18,821,571 (findall ->list)
CheckSum 無効 (返されたリストをまったく使用しない):
- 10,885,004 (for ->list)
- 11,221,888 (foreach ->list)
- 18,688,433 (where ->list)
- 1,075 (where ->ienum)
- 13,720,243 (findall ->list)
実際の結果を得るには、より多くの反復が必要です。
UPDATE(コメントから):そのコードを見て、私は同意します。
元の答え:
.FindAll()
より高速である必要があります。リストのサイズを既に知っていることと、単純なfor
ループで内部配列をループすることを利用します。 .Where()
列挙子 (この場合はシールされたフレームワーク クラス) を起動WhereIterator
し、より具体的な方法で同じジョブを実行する必要があります。
ただし、 .Where() は列挙可能であり、メモリ内に List を積極的に作成して埋めるわけではないことに注意してください。これはストリームに似ているため、非常に大きなものでのメモリ使用量には大きな違いがあります。また、4.0 で .Where() アプローチを使用すると、結果を並列に使用してはるかに高速に開始できます。
Where
よりもはるかに高速ですFindAll
。リストがどれほど大きくても、Where
まったく同じ時間がかかります。
もちろんWhere
、クエリを作成するだけです。FindAll
リストを作成するのとは異なり、実際には何もしません。
jrista からの回答は理にかなっています。ただし、新しいリストは同じオブジェクトを追加するため、既存のオブジェクトを参照して成長するだけであり、それほど遅くはないはずです。3.5/Linq 拡張が可能である限り、とにかく良いままです。2.0 で制限すると、FindAll ははるかに理にかなっています