que estoy haciendo algunas pruebas de rendimiento y notó que una expresión LINQ como¿Por qué LINQ .Where (predicate) .First() es más rápido que .Primero (predicado)?
result = list.First(f => f.Id == i).Property
es más lento que
result = list.Where(f => f.Id == i).First().Property
Esto parece contrario a la intuición. Hubiera pensado que la primera expresión sería más rápida porque puede detener la iteración sobre la lista tan pronto como se satisfaga el predicado, mientras que yo hubiera pensado que la expresión .Where()
podría iterar en toda la lista antes de llamar al .First()
en el subconjunto resultante. Incluso si este último hace un cortocircuito, no debería ser más rápido que usar First directamente, pero lo es.
A continuación se muestran dos pruebas de unidad realmente simples que ilustran esto. Cuando se compila con optimización en TestWhereAndFirst es aproximadamente 30% más rápido que TestFirstOnly en .Net y Silverlight 4. He intentado hacer que el predicado arroje más resultados, pero la diferencia de rendimiento es la misma.
¿Alguien puede explicar por qué .First(fn)
es más lento que ? Veo un resultado contrario a la intuición similar con .Count(fn)
en comparación con .Where(fn).Count()
.
private const int Range = 50000;
private class Simple
{
public int Id { get; set; }
public int Value { get; set; }
}
[TestMethod()]
public void TestFirstOnly()
{
List<Simple> list = new List<Simple>(Range);
for (int i = Range - 1; i >= 0; --i)
{
list.Add(new Simple { Id = i, Value = 10 });
}
int result = 0;
for (int i = 0; i < Range; ++i)
{
result += list.First(f => f.Id == i).Value;
}
Assert.IsTrue(result > 0);
}
[TestMethod()]
public void TestWhereAndFirst()
{
List<Simple> list = new List<Simple>(Range);
for (int i = Range - 1; i >= 0; --i)
{
list.Add(new Simple { Id = i, Value = 10 });
}
int result = 0;
for (int i = 0; i < Range; ++i)
{
result += list.Where(f => f.Id == i).First().Value;
}
Assert.IsTrue(result > 0);
}
¿Cómo lo calcula? –
Sin embargo, su pensamiento inicial es incorrecto: LINQ hace cálculos perezosos, por lo que cuando se llama a 'First()' consultará (el valor de retorno) 'Where (...)' para una sola coincidencia y nunca solicitará otra. Por lo tanto, se examinará exactamente la misma cantidad de elementos que cuando se llama 'Primero (...)' (es decir, directamente con un predicado). – Jon
Obtengo el mismo resultado, '.Where(). First()' es .021 segundos y '.First()' es .037 segundos. Esto es con una simple lista de 'int's. – Ryan