Tuesday 24 February 2009

LINQ vs Lambda vs loop - A performance test

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) and return the same, Lamda depends on Genercic Lists (IList) and returns the same. To account for this, I decided that the methods could have their test data provided in the format they need it in before the stopwatch starts. This decision favours Lambda but real world scenarios where Lambda expressions are used also rely on this constraint. For the same reason, I removed the List instantiation in Stranden's loop test because this handicaps the loop implementation since a real world looping example doesn't need to instantiate a List in order to filter on some property data.


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);
List intList = new List(ints);

//LINQ
stopwatch.Start();
for (int t = 0; t < RUNS; ++t)
{
IEnumerable result = (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)
{
IEnumerable result = (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

Thursday 19 February 2009

Running your .Net Web Application under a custom user account on Windows Server 2003

Some web applications will benefit from running under a different user account to the default 'Network Service' account. I had to set this up today to support some server side Visio COM execution.


The user account is specified under the Identity tab for the Application Pool properties and whatever user you use will need access to the following resources & groups:



  • Add to group: IIS_WPG

  • Run %windir%\Microsoft.NET\Framework\v2.0.50727\aspnet_regiis -ga domain\username

  • Local Security Policy > Local Policies > User Rights Assignment > Log on as a service