2009-06-25 20 views

Respuesta

55

Escriba el código más claro que pueda, y luego haga una evaluación comparativa y perfil para descubrir cualquier problema de rendimiento. Si tiene tiene problemas de rendimiento, puede experimentar con códigos diferentes para determinar si es más rápido o no (midiendo todo el tiempo con datos tan realistas como sea posible) y luego juzgar si la mejora en el rendimiento vale la pena la legibilidad golpeó.

Un enfoque foreach directo será más rápido que LINQ en muchos casos. Por ejemplo, considere:

var query = from element in list 
      where element.X > 2 
      where element.Y < 2 
      select element.X + element.Y; 

foreach (var value in query) 
{ 
    Console.WriteLine(value); 
} 

Ahora bien, hay dos cláusulas where y una cláusula de select, por lo que cada elemento eventual tiene que pasar a través de tres iteradores. (. Es evidente que los dos cláusulas donde se podrían combinar en este caso, pero estoy haciendo un punto general)

Ahora compararlo con el código directa:

foreach (var element in list) 
{ 
    if (element.X > 2 && element.Y < 2) 
    { 
     Console.WriteLine(element.X + element.Y); 
    } 
} 

Eso hará correr más rápido, ya que tiene menos aros para atravesar. Lo más probable es que la salida de la consola empequeñezca el costo del iterador, y ciertamente preferiría la consulta LINQ.

EDIT: Para responder sobre "foreach anidados" bucles ... normalmente están representados aquellos con SelectMany o una segunda cláusula from:

var query = from item in firstSequence 
      from nestedItem in item.NestedItems 
      select item.BaseCount + nestedItem.NestedCount; 

Aquí sólo estamos agregando un único repetidor adicional, porque somos d ya utilizar un iterador adicional por artículo en la primera secuencia debido al ciclo anidado foreach. Todavía hay un poco de sobrecarga, incluida la sobrecarga de hacer la proyección en un delegado en lugar de "en línea" (algo que no mencioné antes), pero aún así no será muy diferente al rendimiento anidado-foreach.

Esto no quiere decir que no puedas disparar en el pie con LINQ, por supuesto. Puede escribir preguntas estupendamente ineficientes si no involucra su cerebro primero, pero eso no es exclusivo de LINQ ...

+13

La primera frase debe ser impreso como una bandera y colgado en cada departamento de programación. –

+1

Jon ... Eres increíble ... ¡Gracias de corazón! –

11

Es más complejo en eso. En definitiva, gran parte de LINQ-to-Objects es (detrás de escena) un bucle foreach, pero con la sobrecarga adicional de un poco de bloques de abstracción/iterador/etc. Sin embargo, a menos que haga cosas muy diferentes en sus dos versiones (foreach vs. LINQ), ambos deberían ser O (N).

La verdadera pregunta es: ¿hay una mejor manera de escribir su algoritmo específico que significa que foreach sería ineficaz? ¿Y puede LINQ hacerlo por ti?

Por ejemplo, LINQ facilita los datos de hash/grupo/clasificación.

22

Si lo hace

foreach(Customer c in Customer) 
{ 
    foreach(Order o in Orders) 
    { 
    //do something with c and o 
    } 
} 

que llevará a cabo Customer.Count * Orden.Contar iteraciones


si lo hace

var query = 
    from c in Customer 
    join o in Orders on c.CustomerID equals o.CustomerID 
    select new {c, o} 

foreach(var x in query) 
{ 
    //do something with x.c and x.o 
} 

que llevará a cabo iteraciones Customer.Count + Order.Count, porque Enumerable.Join se implementa como un HashJoin.

+0

excelente respuesta con una buena explicación –

+4

Eso es porque está ejecutando dos algoritmos diferentes; estás comparando manzanas con naranjas. No veo cómo esto es relevante para la pregunta planteada. – mquander

+3

Su foreach anidados es en realidad equivalente a SelectMany, no unirse a - es decir, desde C en cliente o en las órdenes ... (sin unión) –

2

Se ha dicho antes, pero que merece la repetición.

Desarrolladores nunca se sabe dónde está el cuello de botella es hasta que se agoten las pruebas de rendimiento.

Lo mismo es cierto para comparar la técnica de la A a la técnica de B. A menos que haya una gran diferencia a continuación sólo hay que probarlo. Puede ser obvio si tienes un escenario O (n) vs O (n^x), pero dado que el material LINQ es principalmente compilación de brujería, merece un perfil.

Además, a menos que su proyecto esté en producción y haya perfilado el código y encontrado que ese bucle está ralentizando su ejecución, déjelo como lo prefiera para la legibilidad y el mantenimiento. La optimización prematura es el diablo.

+1

Si bien es cierto que no puede anticipar realmente los cuellos de botella de rendimiento, también es cierto que la mayoría de los problemas de rendimiento están diseñados, se encuentran muy tarde en el ciclo de vida de desarrollo y, por lo tanto, son difíciles de descifrar. Hay mucho para decir para siempre tener un ojo abierto a las implicaciones de rendimiento de las decisiones de diseño e implementación que está haciendo, en lugar de codificar alegremente esperando que todo esté bien. – Mike

2

Una gran ventaja es que el uso de consultas de Linq-To-Objects le da la capacidad de pasar fácilmente la consulta a PLinq y hacer que el sistema realice automáticamente la operación en la cantidad correcta de hilos para el sistema actual.

Si está utilizando esta técnica en grandes conjuntos de datos, que es una muy susceptibles a una gran victoria para muy pocos problemas.

+0

Es cierto, pero también hay equivalentes paralelos propuestos para foreach también. http://www.danielmoth.com/Blog/2009/01/parallelising-loops-in-net-4.html – jpierson