Latest Replies
Thursday
Sep252008

Some performance issues and caveats of LINQ

20% of the code executes 80% of the time

As you may already know, Linq to collections generally performs worse, than the hard-coded approach. That happens because we deal with the iterator classes and lazy evaluation.

Most of the time this is not a problem, since the impact on the overall performance is negligible. And the code itself gets much cleaner and easier to read.

However, in the high-stress scenarios (i.e.: in heavy math calculations) improper Linq usage could become a problem. Let's write a couple of micro-benchmarks and compare the performance of hand-written loops with the Linq.

In simple selection and sum Linq works just x1.5 times slower:

// Loop - 01.73 s
for (int j = 0; j < array.Length; j++)
{
  var v = array[j].Value;
  if (v > Math.PI)
  {
    sum1 += v;
  }
}

// LINQ - 02.54 s
sum2 += array.Where(o => o.Value > Math.PI).Sum(o => o.Value);

But the difference could get bigger (x2.6), if we forget, that Linq does the lazy evaluation:

// Loop - 01.77 s
for (int j = 0; j < array.Length; j++)
{
  var v = array[j].Value;
  if (v > Math.PI)
  {
    sum1 += v;
    total1 += 1;
  }
}

// Linq - 04.60 s
var doubles = array.Where(o => o.Value > Math.PI);
total2 += doubles.Count();
sum2 += doubles.Sum(o => o.Value);

One could be tempted to use .ToArray() to force the evaluation once, but this has its own cost and will degrade the performance even more (x3.1):

// Linq - 05.43 s
for (int i = 0; i < 100000; i++)
{
  var doubles = array
    .Select(o => o.Value)
    .Where(d => d > Math.PI)
    .ToArray();
  total2 += doubles.Count();
  sum2 += doubles.Sum();
}

Resume: Linq is sharp. There is a lot of stuff going inside these simple looking extension methods for collections. Just be careful with them, while writing performance-critical code, and you should be fine.

Appendix: these micro-snippets have been executed against array generated like this:

var r = new Random(1);
var array = Enumerable.Range(1, 1000)
  .Select(i => new
  {
    ID = i,
    Name = "Item_" + i,
    Category = "Category_" + (i%10),
    Value = r.NextDouble()*10
  })
  .ToArray();

Note: we use randSeed parameter to create repeatable experiment results.

Every statement was compiled in "Release" mode and executed 100000 times. Total execution time was measured with the Stopwatch class.

« MSBUILD - item list flattening transform | Main | CodeMonkey song »

Reader Comments (2)

The popular Alma MM Roses Handbag fromtag heuer formula|
Louis Vuitton has been given a twist of Stephen Sprouses signature style.

October 19, 2010 | Unregistered Commenterreplica watches

really hard for me
ffxiv gil

October 27, 2010 | Unregistered Commenterdewe

PostPost a New Comment

Enter your information below to add a new comment.

My response is on my own website »
Author Email (optional):
Author URL (optional):
Post:
 
Some HTML allowed: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <code> <em> <i> <strike> <strong>