Conozco la diferencia entre IQueryable e IEnumerable, y sé que las colecciones son compatibles con Linq To Objects a través de la interfaz IEnumerable.Por qué IQueryable es dos veces más rápido que IEnumerable al usar Linq To Objects
Lo que me desconcierta es que las consultas se ejecutan dos veces más rápido cuando la colección se convierte a IQueryable.
Deje l ser un objeto relleno de tipo Lista, a continuación, una consulta LINQ es tiempo dos veces más rápido si la lista l se convierte en un IQueryable través l.AsQueryable().
He escrito una prueba simple con VS2010SP1 y .NET 4.0 que demuestra esto:
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));
}
Al ejecutar esta prueba (con voluntad numTests == 1 y 10) produce el siguiente resultado:
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
Repitiendo la prueba pero cambiando el orden (es decir, primero midiendo IQuerable y luego IEnumerable) da resultados diferentes!
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
Aquí están mis preguntas:
- ¿Qué estoy haciendo mal?
- ¿Por qué es IEnumerable más rápido si la prueba se ejecuta después de IQueryable test?
- ¿Por qué es IQueryable más rápido cuando el no. de carreras de prueba se incrementa?
- ¿Hay una penalización involucrada al usar IQueryable en lugar de IEnumerable?
Hago estas preguntas porque me preguntaba cuál utilizar para mi interfaz de repositorio. En este momento consultan las colecciones en la memoria (Linq to Objects), pero en el futuro este podría ser ser un origen de datos SQL. Si diseño las clases de repositorio ahora con IQueryable puedo cambiar sin más tarde a Linq a SQL. Sin embargo, si se aplica una penalización de rendimiento, entonces, si metemos a IEnumerable, si bien no hay SQL involucrado, parece ser más inteligente.
No está especificando si está construyendo en modo de liberación o depuración, y no está "imprimiendo" las funciones primero para que pueda ver ruido de fluctuación. (Creo). A la larga, una diferencia de unos pocos ms más de 10 000 000 iteraciones en realidad no parece ser un gran problema. – asawyer
Estaba construyendo en modo de depuración. Cambiar al modo de lanzamiento y agregar una ejecución de "inicialización" (es decir, ejecutar y materializar cada consulta una vez) ayudó: Ahora el código ** IEnumerable ** se ejecuta un poco más rápido que el código ** IQueryable ** (11ms vs 12ms). Y eso es exactamente lo que esperaba. Así que mi código de prueba fue defectuoso. Gracias por los consejos! – rbu
"Puedo cambiar luego sin problemas a Linq a SQL" ... Me encantaría saber cómo te fue. –