約20個のList
を持つクラスから、メモリに大きなproperties
があります。
このリストを1つのproperty
だけに基づいてフィルタリングしたいのですが、特定のタスクでは、そのproperty
のリストだけが必要です。したがって、私のクエリは次のようになります。
data.Select(x => x.field).Where(x => x == "desired value").ToList()
最初にSelect
を使用するか、Where
を使用するか、どちらがパフォーマンスを向上させますか?
data.Where(x => x.field == "desired value").Select(x => x.field).ToList()
これがdata type
に関連しているかどうかを教えてください。データをメモリに保持しているのか、フィールドのタイプです。これらのオブジェクトは他のタスクにも必要であるため、最初からメモリにロードする前にフィルタリングすることはできません。
[最初に選択]を使用するか、[Where]を使用すると、どちらがパフォーマンスが向上します。
Where
最初のアプローチは、コレクションを最初にフィルタリングしてから、filtered値に対してのみSelect
を実行するため、パフォーマンスが向上します。 。
数学的に言えば、Where
-最初のアプローチはN + N'
操作、ここでN'
は、Where
条件に該当するコレクションアイテムの数です。
つまり、N + 0 = N
最小の操作(アイテムがこのWhere
条件を通過しない場合)およびN + N = 2 * N
最大での操作(すべてのアイテムが条件に合格した場合)。
同時に、Select
最初のアプローチは常に正確に2 * N
操作。これは、すべてのオブジェクトを反復処理してプロパティを取得してから、すべてのオブジェクトを反復処理してそれらをフィルタリングするためです。
私は自分の答えを証明するためにベンチマークを完了しました。
結果:
Condition value: 50
Where -> Select: 88 ms, 10500319 hits
Select -> Where: 137 ms, 20000000 hits
Condition value: 500
Where -> Select: 187 ms, 14999212 hits
Select -> Where: 238 ms, 20000000 hits
Condition value: 950
Where -> Select: 186 ms, 19500126 hits
Select -> Where: 402 ms, 20000000 hits
ベンチマークを何度も実行すると、Where -> Select
アプローチヒットは時々変化しますが、Select -> Where
アプローチは常に2N
オペレーション。
IDEOneのデモ:
コード:
class Point
{
public int X { get; set; }
public int Y { get; set; }
}
class Program
{
static void Main()
{
var random = new Random();
List<Point> points = Enumerable.Range(0, 10000000).Select(x => new Point { X = random.Next(1000), Y = random.Next(1000) }).ToList();
int conditionValue = 250;
Console.WriteLine($"Condition value: {conditionValue}");
Stopwatch sw = new Stopwatch();
sw.Start();
int hitCount1 = 0;
var points1 = points.Where(x =>
{
hitCount1++;
return x.X < conditionValue;
}).Select(x =>
{
hitCount1++;
return x.Y;
}).ToArray();
sw.Stop();
Console.WriteLine($"Where -> Select: {sw.ElapsedMilliseconds} ms, {hitCount1} hits");
sw.Restart();
int hitCount2 = 0;
var points2 = points.Select(x =>
{
hitCount2++;
return x.Y;
}).Where(x =>
{
hitCount2++;
return x < conditionValue;
}).ToArray();
sw.Stop();
Console.WriteLine($"Select -> Where: {sw.ElapsedMilliseconds} ms, {hitCount2} hits");
Console.ReadLine();
}
}
これらの質問もあなたにとって興味深いものになる可能性があります。これらはSelect
およびWhere
とは関係ありませんが、LINQ注文のパフォーマンスに関するものです。
答えは、コレクションの状態によって異なります。
更新:
@YeldarKurmangaliyevは、具体的な例とベンチマークを使用して回答を作成しました。私は彼の主張を検証するために同様のコードを実行しましたそして私たちの結果は正反対ですそしてそれは私が彼と同じテストを実行したがオブジェクトがそれほど単純ではないためです彼がテストを実行するために使用したPoint
タイプとして。
クラスの名前をPoint
からEnumerableClass
に変更したことを除けば、コードは彼のコードと非常によく似ています。
以下に、私がEnumerableClass
クラスを構成するために使用したクラスを示します。
public class EnumerableClass
{
public int X { get; set; }
public int Y { get; set; }
public String A { get; set; }
public String B { get; set; }
public String C { get; set; }
public String D { get; set; }
public String E { get; set; }
public Frame F { get; set; }
public Gatorade Gatorade { get; set; }
public Home Home { get; set; }
}
public class Home
{
private Home(int rooms, double bathrooms, Stove stove, InternetConnection internetConnection)
{
Rooms = rooms;
Bathrooms = (decimal) bathrooms;
StoveType = stove;
Internet = internetConnection;
}
public int Rooms { get; set; }
public decimal Bathrooms { get; set; }
public Stove StoveType { get; set; }
public InternetConnection Internet { get; set; }
public static Home GetUnitOfHome()
{
return new Home(5, 2.5, Stove.Gas, InternetConnection.Att);
}
}
public enum InternetConnection
{
Comcast = 0,
Verizon = 1,
Att = 2,
Google = 3
}
public enum Stove
{
Gas = 0,
Electric = 1,
Induction = 2
}
public class Gatorade
{
private Gatorade(int volume, Color liquidColor, int bottleSize)
{
Volume = volume;
LiquidColor = liquidColor;
BottleSize = bottleSize;
}
public int Volume { get; set; }
public Color LiquidColor { get; set; }
public int BottleSize { get; set; }
public static Gatorade GetGatoradeBottle()
{
return new Gatorade(100, Color.Orange, 150);
}
}
public class Frame
{
public int X { get; set; }
public int Y { get; set; }
private Frame(int x, int y)
{
X = x;
Y = y;
}
public static Frame GetFrame()
{
return new Frame(5, 10);
}
}
クラスFrame
、Gatorade
、およびHome
には、それぞれのタイプのインスタンスを返す静的メソッドがあります。
以下はメインプログラムです:
public static class Program
{
const string Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
private static readonly Random Random = new Random();
private static string RandomString(int length)
{
return new string(Enumerable.Repeat(Chars, length)
.Select(s => s[Random.Next(s.Length)]).ToArray());
}
private static void Main()
{
var random = new Random();
var largeCollection =
Enumerable.Range(0, 1000000)
.Select(
x =>
new EnumerableClass
{
A = RandomString(500),
B = RandomString(1000),
C = RandomString(100),
D = RandomString(256),
E = RandomString(1024),
F = Frame.GetFrame(),
Gatorade = Gatorade.GetGatoradeBottle(),
Home = Home.GetUnitOfHome(),
X = random.Next(1000),
Y = random.Next(1000)
})
.ToList();
const int conditionValue = 250;
Console.WriteLine(@"Condition value: {0}", conditionValue);
var sw = new Stopwatch();
sw.Start();
var firstWhere = largeCollection
.Where(x => x.Y < conditionValue)
.Select(x => x.Y)
.ToArray();
sw.Stop();
Console.WriteLine(@"Where -> Select: {0} ms", sw.ElapsedMilliseconds);
sw.Restart();
var firstSelect = largeCollection
.Select(x => x.Y)
.Where(y => y < conditionValue)
.ToArray();
sw.Stop();
Console.WriteLine(@"Select -> Where: {0} ms", sw.ElapsedMilliseconds);
Console.ReadLine();
Console.WriteLine();
Console.WriteLine(@"First Where's first item: {0}", firstWhere.FirstOrDefault());
Console.WriteLine(@"First Select's first item: {0}", firstSelect.FirstOrDefault());
Console.WriteLine();
Console.ReadLine();
}
}
結果:
テストを複数回実行したところ、
。Select()。Where()より良いパフォーマンス。Where()。 Select()。
コレクションサイズが1000000の場合。
これは、すべての
EnumerableClass
オブジェクトのY
値を5に強制した最初のテスト結果であるため、すべてのアイテムが渡されましたWhere:
Condition value: 250
Where -> Select: 149 ms
Select -> Where: 115 ms
First Where's first item: 5
First Select's first item: 5
これは、すべての
EnumerableClass
オブジェクトのY
値を251に強制したため、アイテムが渡されなかった2番目のテスト結果ですWhere:
Condition value: 250
Where -> Select: 110 ms
Select -> Where: 100 ms
First Where's first item: 0
First Select's first item: 0
明らかに、結果はコレクションのstateに大きく依存しているため、:
私が何度も言及しているコレクションのstateには、次のものが含まれます。
回答に対するコメントへの回答:
さらに、@ Enigmativityは、Whereを最初に置くかSelectを最初にするかを知るために、Whereの結果を事前に知ることはキャッチであると述べました。 -22。理想的かつ理論的には、彼は正しく、当然のことながら、この状況はコンピュータサイエンスの別のドメインで見られます スケジューリング 。
最適なスケジューリングアルゴリズムは Shortest Job First で、実行時間が最短になるジョブを最初にスケジュールします。しかし、特定の仕事が完了するまでにどれくらいの時間がかかるかを誰かがどうやって知るのでしょうか?まあ、答えはそれです:
次の最短ジョブは、実行時間の正確な見積もりが可能な特殊な環境で使用されます。
したがって、一番上で言ったように(これは私の答えの最初の短いバージョンでもありました)、この質問に対する正しい答えは、コレクションの現在の状態。
一般に、
そうすれば、この回答の冒頭に記載されているガイドラインが役に立ちます。