前日にLINQのパフォーマンスについて議論した際、自分はLINQが性能劣化していると考えていたが、実際にはLINQも特定の面では優れた性能を発揮するものであることを学んだ。ただし、LINQ to SQLについては別として、以下に簡単なパフォーマンステストのコードを示す。このテストではDateTimeではなくStopwatchを使用して計測している点に注意が必要である。初心者の私は最初はDateTimeを使っていて、LINQのパフォーマンスが悪いと思っていたが、Stopwatchを使用することで真実に気づいた。LINQに対する偏見を持っている方々は、LINQを理解し直す価値があるかもしれない。もし誤解があれば、皆様のご指摘を歓迎する。
class Program<br></br> {<br></br> static void Main(string[] args)<br></br> {<br></br> test();<br></br> }<br></br><br></br> static void test()<br></br> {<br></br> List<DataItem> items = new List<DataItem>();<br></br> for (int i = 0; i < 10000000; i++)<br></br> {<br></br> DataItem data=new DataItem();<br></br> data.Label = "テストデータ" + i;<br></br> data.Index = i;<br></br> items.Add(data);<br></br> }<br></br><br></br> Stopwatch watch = new Stopwatch();<br></br><br></br><br></br> #region forループ<br></br> watch.Start();<br></br> List<DataItem> result1 = new List<DataItem>();<br></br><br></br> foreach (DataItem item in items)<br></br> {<br></br> if (item.Index >= 52 && item.Index < 850) { result1.Add(item); }<br></br> }<br></br> watch.Stop();<br></br><br></br> Console.Write("一致件数" + result1.Count + "、forループ時間:");<br></br> Console.WriteLine(watch.Elapsed.Ticks);<br></br> #endregion<br></br><br></br><br></br> #region LINQ<br></br> watch = new Stopwatch();<br></br> watch.Start();<br></br> var result2 = items.Where(product => product.Index >= 52 && product.Index < 850);<br></br> watch.Stop();<br></br><br></br><br></br> Console.Write("一致件数" + result2.Count() + "、LINQ時間:");<br></br> Console.WriteLine(watch.Elapsed.Ticks);<br></br> #endregion<br></br><br></br><br></br> #region デリゲート<br></br> watch = new Stopwatch();<br></br> watch.Start();<br></br> List<DataItem> result3 = items.FindAll(delegate(DataItem item)<br></br> {<br></br> return item.Index >= 52 && item.Index < 850;<br></br> });<br></br> watch.Stop();<br></br><br></br> Console.Write("一致件数" + result3.Count() + "、デリゲート時間:");<br></br> Console.WriteLine(watch.Elapsed.Ticks);<br></br> #endregion<br></br><br></br><br></br> Console.Read(); <br></br> }<br></br><br></br> public class DataItem<br></br> {<br></br> public string Label { get; set; }<br></br> public int Index { get; set; }<br></br> }<br></br> }
テスト結果のスクリーンショットは以下の通り:
誤解がある場合はご指摘いただきたい。
園内のコメントをもとにコードを再構成した。シンプルな内容でも、間違いを指摘してもらうことは成長にとって重要であり、皆様の丁寧な指摘に感謝する。
再構成後のコードは以下の通り:
View Code ``` class Program { static void Main(string[] args) { test(); } static void test() { List<DataItem> items = new List<DataItem>(); for (int i = 0; i < 10000000; i++) { DataItem data=new DataItem(); data.Label = "テストデータ" + i; data.Index = i; items.Add(data); } Stopwatch watch = new Stopwatch(); #region forループ watch.Start(); List<DataItem> result1 = new List<DataItem>(); int matchedCount = 0; foreach (DataItem item in items) { if (item.Index >= 52 && item.Index < 850) { result1.Add(item); } } matchedCount = result1.Count; watch.Stop(); Console.Write("一致件数" + matchedCount + "、forループ時間:"); Console.WriteLine(watch.Elapsed.Ticks); #endregion #region LINQ watch = new Stopwatch(); watch.Start(); var result2 = items.Where(product => product.Index >= 52 && product.Index < 850); //items.Where(product => product.Index >= 52 && product.Index < 850).ToArray().Count(); int count2 = result2.Count(); watch.Stop(); Console.Write("一致件数" + count2 + "、LINQ時間:"); Console.WriteLine(watch.Elapsed.Ticks); #endregion #region デリゲート watch = new Stopwatch(); watch.Start(); List<DataItem> result3 = items.FindAll(delegate(DataItem item) { return item.Index >= 52 && item.Index < 850; }); int count3 = result3.Count(); watch.Stop(); Console.Write("一致件数" + count3 + "、デリゲート時間:"); Console.WriteLine(watch.Elapsed.Ticks); #endregion Console.Read(); } public class DataItem { public string Label { get; set; } public int Index { get; set; } } }
テスト結果
第3回目のコード更新では順序の影響を排除した:
View Code ```
class Program<br></br> {<br></br> static void Main(string[] args)<br></br> {<br></br> List<DataItem> items = new List<DataItem>();<br></br> for (int i = 0; i < 10000000; i++)<br></br> {<br></br> DataItem data = new DataItem();<br></br> data.Label = "テストデータ" + i;<br></br> data.Index = i;<br></br> items.Add(data);<br></br> }<br></br><br></br> test_linq(items);<br></br><br></br> test_foreach(items);<br></br><br></br> test_delegate(items);<br></br><br></br> <br></br> Console.Read();<br></br> }<br></br><br></br> static void test_foreach(List<DataItem> items)<br></br> {<br></br> Stopwatch watch = new Stopwatch();<br></br> #region foreachループ<br></br> watch.Start();<br></br> List<DataItem> result1 = new List<DataItem>();<br></br><br></br> int matchedCount = 0;<br></br> foreach (DataItem item in items)<br></br> {<br></br> if (item.Index >= 52 && item.Index < 850) { result1.Add(item); }<br></br> }<br></br> matchedCount = result1.Count;<br></br> watch.Stop();<br></br><br></br> Console.Write("一致件数" + matchedCount + "、foreachループ時間:");<br></br> Console.WriteLine(watch.Elapsed.Ticks);<br></br> #endregion<br></br> }<br></br> static void test_linq(List<DataItem> items)<br></br> {<br></br> Stopwatch watch = new Stopwatch();<br></br> #region LINQ<br></br> watch = new Stopwatch();<br></br> watch.Start();<br></br> var result2 = items.Where(product => product.Index >= 52 && product.Index < 850);<br></br> //items.Where(product => product.Index >= 52 && product.Index < 850).ToArray().Count();<br></br> int count2 = result2.Count();<br></br> watch.Stop();<br></br><br></br><br></br> Console.Write("一致件数" + count2 + "、LINQ時間:");<br></br> Console.WriteLine(watch.Elapsed.Ticks);<br></br> #endregion<br></br> }<br></br> static void test_delegate(List<DataItem> items)<br></br> {<br></br> Stopwatch watch = new Stopwatch();<br></br> #region デリゲート<br></br> watch = new Stopwatch();<br></br> watch.Start();<br></br> List<DataItem> result3 = items.FindAll(delegate(DataItem item)<br></br> {<br></br> return item.Index >= 52 && item.Index < 850;<br></br> });<br></br> int count3 = result3.Count();<br></br> watch.Stop();<br></br><br></br> Console.Write("一致件数" + count3 + "、デリゲート時間:");<br></br> Console.WriteLine(watch.Elapsed.Ticks);<br></br> #endregion<br></br> }<br></br><br></br> public class DataItem<br></br> {<br></br> public string Label { get; set; }<br></br> public int Index { get; set; }<br></br> }<br></br> }
上記のスクリーンショットを見ると、大きな差異はほとんどない。
結論:LINQの遅延評価機能を理解していなかったため、Count操作がLINQの実際のクエリ実行を引き起こしていた。まだ深く理解していないが、いずれ理解できるだろう。
ブログアドレス: http://www.jqpress.com/post/43.aspx