クエリ式のパフォーマンス
きっかけは↓の記事なんですけども、クエリ式のパフォーマンスについての話を少々。
クエリ式で総当たり - NyaRuRuが地球にいたころ
先に、今日の話のまとめに相当するソースファイルを掲示↓。
http://ufcpp.net/study/csharp/source/Comprehension.cs
C# 3.0 のクエリ式は、ラムダ式とかIQueryableを駆使して色々と面白いことができるんで、高いポテンシャルを秘めてるんですけども、ここで話すのはもうちょっと単純な場合について。
クエリ式は、LINQ to object(単純な IEnumerable に対するクエリ)に話を限定して、from, where select くらいしか使わないような単純な場合には、foreach, if, yield return ですべて置き換え可能なんですよね。例えば、
var points = from x in Enumerable.Range(0, 100) from y in Enumerable.Range(0, 100) where x % 2 != 0 where y % 3 != 0 select new { x, y };
みたいな場合、
static IEnumerable<Point> Points() { foreach (var x in Enumerable.Range(0, 100) foreach (var y in Enumerable.Range(0, 100) if (x % 2 != 0) if (y % 3 != 0) yield return new Point(x, y); }
と書ける。イテレータは匿名メソッドで書けない(=匿名型を使えない)っていう欠点はありますが、パフォーマンスは下側のコードの方がよかったりします。
あと、条件式はできる限り前にあった方がパフォーマンスがよかったりします。例えば、上のコードよりも、
var points = from x in Enumerable.Range(0, 100) where x % 2 != 0 from y in Enumerable.Range(0, 100) where y % 3 != 0 select new { x, y };
static IEnumerable<Point> Points() { foreach (var x in Enumerable.Range(0, 100) if (x % 2 != 0) foreach (var y in Enumerable.Range(0, 100) if (y % 3 != 0) yield return new Point(x, y); }
の方がパフォーマンスはいいです。2つ目のループが回る回数が減るんで。が、from が一番上に固まってる場合と比べて、思った以上に見た目が悪い・・・
まあ、結論としては、IQueryable / ラムダ式を使って、from, where, select を foreach, if, yield return に展開&クエリの順序最適化をかけるようなライブラリが欲しいなぁ。