WhereとFindAll on Listの速度の違いは誰でも知っています。どこがIEnumerableの一部で、FindAllがListの一部であるか知っていますが、何が速いのか知りたいだけです。
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();
}
}
}
Results(ticks)-CheckSumが有効(結果に対するいくつかの操作)、モード:デバッグせずに解放(CTRL + F5):
チェックサムが無効になっています(返されたリストをまったく使用していません):
実際の結果を得るには、より多くの反復が必要になるため、結果は少し異なる場合があります。
.FindAll()
の方が高速である必要があります。これは、リストのサイズをすでに認識しており、単純なfor
ループで内部配列をループすることを利用しています。 .Where()
は、列挙子(この場合はWhereIterator
と呼ばれる封印されたフレームワーククラス)を起動し、より具体的な方法で同じジョブを実行する必要があります。
ただし、.Where()は列挙可能であり、メモリ内に積極的にリストを作成してそれを埋めないことに注意してください。これはストリームに似ているため、非常に大きなものでのメモリ使用量には大きな違いがあります。また、4.0のthere .Where()アプローチを使用すると、結果を並列方式ではるかに速く開始できます。
Where
はFindAll
よりもはるかに高速です。リストがどれほど大きい場合でも、Where
にはまったく同じ時間がかかります。
もちろん、Where
はクエリを作成するだけです。リストを作成するFindAll
とは異なり、実際には何もしません。