c# - sintaxis - linq para principiantes
¿El orden de las funciones de LINQ es importante? (7)
Obviamente, los resultados tendrían que ser idénticos aún ...
Tenga en cuenta que esto no es cierto en realidad, en particular, las dos líneas siguientes darán resultados diferentes (para la mayoría de los proveedores / conjuntos de datos):
myCollection.OrderBy(o => o).Distinct();
myCollection.Distinct().OrderBy(o => o);
Básicamente, como dice la pregunta ... ¿importa el orden de las funciones de LINQ en términos de rendimiento ? Obviamente, los resultados tendrían que ser idénticos aún ...
Ejemplo:
myCollection.OrderBy(item => item.CreatedDate).Where(item => item.Code > 3);
myCollection.Where(item => item.Code > 3).OrderBy(item => item.CreatedDate);
Ambos me devuelven los mismos resultados, pero están en un orden LINQ diferente. Me doy cuenta de que reordenar algunos artículos dará lugar a resultados diferentes, y no estoy preocupado por ellos. Lo que más me preocupa es saber si, al obtener los mismos resultados, ordenar puede afectar el rendimiento. Y no solo en las 2 llamadas LINQ que hice (OrderBy, Where), sino en cualquier llamada LINQ.
Depende de la relevancia. Suponga que si tiene muy pocos elementos con Código = 3, entonces el próximo pedido funcionará en un pequeño conjunto de colección para obtener el orden por fecha.
Mientras que si tiene muchos elementos con el mismo CreatedDate, el próximo pedido funcionará en un conjunto de colección más grande para obtener el pedido por fecha.
Entonces, en ambos casos, habrá una diferencia en el rendimiento
Dependerá del proveedor de LINQ en uso. Para LINQ to Objects, eso definitivamente podría marcar una gran diferencia. Supongamos que tenemos realmente:
var query = myCollection.OrderBy(item => item.CreatedDate)
.Where(item => item.Code > 3);
var result = query.Last();
Eso requiere que toda la colección se ordene y luego se filtre. Si tuviéramos un millón de artículos, de los cuales solo uno tuviera un código superior a 3, estaríamos desperdiciando mucho tiempo ordenando resultados que se descartarían.
Compare eso con la operación inversa, filtrando primero:
var query = myCollection.Where(item => item.Code > 3)
.OrderBy(item => item.CreatedDate);
var result = query.Last();
Esta vez solo estamos ordenando los resultados filtrados, que en el caso de muestra de "solo un elemento que coincida con el filtro" serán mucho más eficientes, tanto en tiempo como en espacio.
También podría hacer una diferencia en si la consulta se ejecuta correctamente o no. Considerar:
var query = myCollection.Where(item => item.Code != 0)
.OrderBy(item => 10 / item.Code);
var result = query.Last();
Está bien, sabemos que nunca dividiremos por 0. Pero si realizamos el pedido antes del filtrado, la consulta emitirá una excepción.
En su ejemplo particular, puede hacer una diferencia en el rendimiento.
Primera consulta: su llamada OrderBy
necesita iterar a través de toda la secuencia fuente, incluidos aquellos elementos donde Code
es 3 o menos. La cláusula Where
también necesita iterar toda la secuencia ordenada.
Segunda consulta: la llamada Where
limita la secuencia solo a aquellos elementos donde Code
es mayor que 3. La llamada OrderBy
solo necesita atravesar la secuencia reducida devuelta por la llamada Where
.
Sí.
Pero exactamente cuál es la diferencia de rendimiento depende de cómo el árbol de expresiones subyacente es evaluado por el proveedor de LINQ.
Por ejemplo, su consulta puede ejecutarse más rápido la segunda vez (con la cláusula WHERE primero) para LINQ-to-XML, pero más rápida la primera vez para LINQ-to-SQL.
Para saber exactamente cuál es la diferencia de rendimiento, lo más probable es que desee perfilar su aplicación. Sin embargo, como siempre sucede con estas cosas, la optimización prematura no suele valer la pena; es muy posible que descubras otros problemas además del rendimiento de LINQ.
Vale la pena señalar que debe tener cuidado al considerar cómo optimizar una consulta LINQ. Por ejemplo, si usa la versión declarativa de LINQ para hacer lo siguiente:
public class Record
{
public string Name { get; set; }
public double Score1 { get; set; }
public double Score2 { get; set; }
}
var query = from record in Records
order by ((record.Score1 + record.Score2) / 2) descending
select new
{
Name = record.Name,
Average = ((record.Score1 + record.Score2) / 2)
};
Si, por algún motivo, decidiera "optimizar" la consulta almacenando primero el promedio en una variable, no obtendría los resultados deseados:
// The following two queries actually takes up more space and are slower
var query = from record in Records
let average = ((record.Score1 + record.Score2) / 2)
order by average descending
select new
{
Name = record.Name,
Average = average
};
var query = from record in Records
let average = ((record.Score1 + record.Score2) / 2)
select new
{
Name = record.Name,
Average = average
}
order by average descending;
Sé que no muchas personas usan LINQ declarativo para objetos, pero es una buena fuente de reflexión.
En Linq-To-Objects:
La ordenación es bastante lenta y usa memoria O(n)
. Where
por otro lado, es relativamente rápido y usa memoria constante. Así que hacer Where
primero será más rápido, y para colecciones grandes significativamente más rápido.
La presión de memoria reducida también puede ser significativa, ya que las asignaciones en el montón de objetos grandes (junto con su colección) son relativamente caros en mi experiencia.