I was wondering about the benefits of using Linq over Lambda or vice versa for data filtering. I noticed that some of Scott Gu's recent MVC posts contain examples like:
var products = from p in northwind.Products where p.Category.CategoryName == "Beverages" select p;
Personally, I prefer the Lambda construct:
var products = northwind.Products.Where(p => p.Category.CategoryName == "Beverages");
This is largely a personal preference issue, but being regularly involved in architectural discussion you often get asked to justify your personal preferences. So I checked the interweb for some metrics and came across an older post about LINQ over loops.
Back in 2007, HÃ¥vard Stranden posted some performance metrics for LINQ vs Loop using a Visual Studio 2008 beta. His test concluded that there was a performance hit in using LINQ over loops but that it was within acceptable ranges. Anders Hejlsberg and others pointed out that the test would benefit from removing some unnecessary casting.
I have modified the tests to remove the unnecessary casting, include metrics for using Lambda, and take advantage of the improvements to the framework in Visual Studio 2008 SP1. I experimented with several different approaches because the various methods have dependencies and return constraints. LINQ and looping depend on IEnumerable collections (IEnumerable
The test results show that the framework is now seriously biased in favour of Lambda implementations. Presumably, because of changes to Generic internals.
The test code as well as the results on my PC are shown below.
Test:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
namespace IterationPerformanceTest
{
class Program
{
public static void Main(string[] args)
{
Trace.Listeners.Add(new TextWriterTraceListener(File.CreateText("Output.txt")));
RunTests();
Trace.Flush();
}
static void RunTests()
{
const int SIZE = 10000;
const int RUNS = 1000;
Stopwatch stopwatch = new Stopwatch();
Random r = new Random();
int[] ints = new int[SIZE];
for (int i = 0; i < SIZE; i++)
ints[i] = r.Next(int.MinValue, int.MaxValue);
ListintList = new List (ints);
//LINQ
stopwatch.Start();
for (int t = 0; t < RUNS; ++t)
{
IEnumerableresult = (from int i in ints where i < 10 select i);
}
stopwatch.Stop();
Trace.WriteLine(string.Format("LINQ: {0}, avg. {1}", stopwatch.Elapsed, new TimeSpan(stopwatch.ElapsedTicks / RUNS)));
stopwatch.Reset();
//LAMBDA
stopwatch.Start();
for (int t = 0; t < RUNS; ++t)
{
IEnumerableresult = (intList.Where(i => i < 10));
}
stopwatch.Stop();
Trace.WriteLine(string.Format("LAMBDA: {0}, avg. {1}", stopwatch.Elapsed, new TimeSpan(stopwatch.ElapsedTicks / RUNS)));
stopwatch.Reset();
//LOOP
stopwatch.Start();
for (int t = 0; t < RUNS; ++t)
{
foreach (var i in ints)
{
bool match = (i < 10);
}
}
stopwatch.Stop();
Trace.WriteLine(string.Format("Loop: {0}, avg. {1}", stopwatch.Elapsed, new TimeSpan(stopwatch.ElapsedTicks / RUNS)));
}
}
}
Results:
LINQ: 00:00:00.0019557, avg. 00:00:00.0004552
LAMBDA: 00:00:00.0000574, avg. 00:00:00.0000133
Loop: 00:00:00.0601094, avg. 00:00:00.0139906