Consider the following C# code:
IEnumerable numbers = Enumerable.Range(0, 10);
var evens = from num in numbers where num % 2 == 0 select num;
Is this pure syntactic sugar to allow me to write a for
or foreach
loop as a one-liner? Are there any compiler optimizations under the covers that make the list comprehension above more efficient than the loop construct? How does this work under the hood?
As Jason said, your code is equivalent to:
Enumerable.Range(0, 10).Where(n => n % 2 == 0);
Note the lambda will be transformed to a function call which is done for every element. This is probably the largest part of the overhead. I did a test, which indicates LINQ is about 3 times slower (mono gmcs version 1.2.6.0) on this exact task
Time for 10000000 for loop reps: 00:00:17.6852560 Time for 10000000 LINQ reps: 00:00:59.0574430 Time for 1000000 for loop reps: 00:00:01.7671640 Time for 1000000 LINQ reps: 00:00:05.8868350
EDIT: Gishu reports that VS2008 and framework v3.5 SP1 gives:
Time for 1000000 loop reps: :00.3724585 Time for 1000000 LINQ reps: :00.5119530
LINQ is about 1.4 times slower there.
It compares a for-loop and a list to LINQ (and whatever structure it uses internally). Either way, it converts the result to an array (necessary to force LINQ to stop being "lazy"). Both versions repeat:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
public class Evens
{
private static readonly int[] numbers = new int[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
private static int MAX_REPS = 1000000;
public static void Main()
{
Stopwatch watch = new Stopwatch();
watch.Start();
for(int reps = 0; reps < MAX_REPS; reps++)
{
List<int> list = new List<int>(); // This could be optimized with a default size, but we'll skip that.
for(int i = 0; i < numbers.Length; i++)
{
int number = numbers[i];
if(number % 2 == 0)
list.Add(number);
}
int[] evensArray = list.ToArray();
}
watch.Stop();
Console.WriteLine("Time for {0} for loop reps: {1}", MAX_REPS, watch.Elapsed);
watch.Reset();
watch.Start();
for(int reps = 0; reps < MAX_REPS; reps++)
{
var evens = from num in numbers where num % 2 == 0 select num;
int[] evensArray = evens.ToArray();
}
watch.Stop();
Console.WriteLine("Time for {0} LINQ reps: {1}", MAX_REPS, watch.Elapsed);
}
}
Past performance tests on similar tasks (e.g. LINQ vs Loop - A performance test) corroborate this.