Я знаю разницу между IQueryable и IEnumerable, и я знаю, что коллекции поддерживаются Linq To Objects через интерфейс IEnumerable.
Что меня озадачивает, так это то, что запросы выполняются в два раза быстрее, когда коллекция преобразуется в IQueryable.
Пусть l— заполненный объект типа List, тогда запрос linq выполняется в два раза быстрее, если список lпреобразуется в IQueryableчерез l.AsQueryable().
Я написал простой тест с VS2010SP1 и .NET 4.0, который демонстрирует это:
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 ) );
}
Выполнение этого теста (с will numTests== 1 и 10) дает следующий результат:
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
Повторение тест, но переключение порядка (т.е. сначала измерение IQuerable, а затем IEnumerable) дает разные результаты!
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
Вот мои вопросы:
Я задаю эти вопросы, потому что мне интересно, какой из них использовать для моего интерфейса репозитория.Прямо сейчас они запрашивают коллекции в памяти (Linq to Objects), но в будущем этот можетстать источником данных SQL. Если я создам классы репозитория сейчас с помощью IQueryable, я смогу безболезненно переключиться позже на Linq to SQL. Однако, если есть потеря производительности, то придерживаться IEnumerable, пока не задействован SQL, кажется более мудрым.