I know the difference between IQueryable and IEnumerable, and I know that collections are supported by Linq To Objects through the IEnumerable interface.
What puzzles me is that queries run twice as fast when the collection is converted to IQueryable.
Let l be a filled List object, then linq query is twice as fast if list l is converted to IQueryable via l.AsQueryable () .
I wrote a simple test with VS2010SP1 and .NET 4.0 that demonstrates this:
private void Test() { const int numTests = 1; const int size = 1000 * 1000; var l = new List<int>(); var resTimesEnumerable = new List<long>(); var resTimesQueryable = new List<long>(); System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch(); for ( int x=0; x<size; x++ ) { l.Add( x ); } Console.WriteLine( "Testdata size: {0} numbers", size ); Console.WriteLine( "Testdata iterations: {0}", numTests ); for ( int n = 0; n < numTests; n++ ) { sw.Restart(); var result = from i in l.AsEnumerable() where (i % 10) == 0 && (i % 3) != 0 select i; result.ToList(); sw.Stop(); resTimesEnumerable.Add( sw.ElapsedMilliseconds ); } Console.WriteLine( "TestEnumerable" ); Console.WriteLine( " Min: {0}", Enumerable.Min( resTimesEnumerable ) ); Console.WriteLine( " Max: {0}", Enumerable.Max( resTimesEnumerable ) ); Console.WriteLine( " Avg: {0}", Enumerable.Average( resTimesEnumerable ) ); for ( int n = 0; n < numTests; n++ ) { sw.Restart(); var result = from i in l.AsQueryable() where (i % 10) == 0 && (i % 3) != 0 select i; result.ToList(); sw.Stop(); resTimesQueryable.Add( sw.ElapsedMilliseconds ); } Console.WriteLine( "TestQuerable" ); Console.WriteLine( " Min: {0}", Enumerable.Min( resTimesQueryable ) ); Console.WriteLine( " Max: {0}", Enumerable.Max( resTimesQueryable ) ); Console.WriteLine( " Avg: {0}", Enumerable.Average( resTimesQueryable ) ); }
Running this test (using numTests == 1 and 10) produces the following result:
Testdata size: 1000000 numbers Testdata iterations: 1 TestEnumerable Min: 44 Max: 44 Avg: 44 TestQuerable Min: 37 Max: 37 Avg: 37 Testdata size: 1000000 numbers Testdata iterations: 10 TestEnumerable Min: 22 Max: 29 Avg: 23,9 TestQuerable Min: 12 Max: 22 Avg: 13,9
Repeating the test, but changing the order (i.e. the first dimension is IQuerable and then IEnumerable) gives distinctive results!
Testdata size: 1000000 numbers Testdata iterations: 1 TestQuerable Min: 75 Max: 75 Avg: 75 TestEnumerable Min: 25 Max: 25 Avg: 25 Testdata size: 1000000 numbers Testdata iterations: 10 TestQuerable Min: 12 Max: 28 Avg: 14 TestEnumerable Min: 22 Max: 26 Avg: 23,4
Here are my questions:
- What am I doing wrong?
- Why is IEnumerable faster if the test runs after the IQueryable test?
- Why is iQueryable faster when not. test runs increasing?
- Is there a penalty associated with using IQueryable instead of IEnumerable ?
I ask these questions because I am wondering which one to use for my repository interface. Right now they are querying collections in memory (Linq to Objects), but in the future it might be an SQL data source. If I now create repository classes using IQueryable , I can safely switch to Linq later on SQL. However, if enforcement exists, then sticking to IEnumerable while SQL is not involved seems wiser.