2011-06-05 9 views
5
var res = new int[1000000].Skip(999999).First(); 

Sería genial si esta consulta solo utilizara el indexador en lugar de recorrer 999999 entradas.¿Por qué no se optimiza Skip() en LINQ a los objetos?

He echado un vistazo a System.Core.dll y he notado que, a diferencia de Skip(), el método de extensión Count() está optimizado. Si el IEnumerable implementa ICollection, simplemente llama a la propiedad Count.

Respuesta

3

Si nos fijamos en my answer a una pregunta similar, parece como si debe ser fácil para proporcionar un no-ingenua (es decir, emite excepciones apropiadas) la optimización de Skip para cualquier IList:

public static IEnumerable<T> Skip<T>(this IList<T> source, int count) 
    { 
     using (var e = source.GetEnumerator()) 
      while (count < source.Count && e.MoveNext()) 
       yield return source[count++]; 
    } 

Por supuesto, su ejemplo utiliza una matriz. Como las matrices no arrojan excepciones durante la iteración, incluso hacer algo tan complicado como mi función sería innecesario. Por lo tanto, se podría concluir que la EM no lo optimizó porque no lo pensó o no creía que fuera un caso lo suficientemente común como para merecer la pena optimizarlo.

+0

También pensé sobre el uso indebido de MoveNext() del iterador de listas, pero parece más como un truco. Una gran idea de todos modos. – codymanix

3

Voy a dejar que Jon Skeet responder a esta:

Si nuestra secuencia es una lista, que sólo puede saltar directamente a la parte derecha de la misma y el rendimiento de los artículos uno a la vez. Eso suena genial, pero ¿y si la lista cambia (o incluso se trunca) mientras estamos iterando sobre ella? Una implementación que trabaje con el iterador simple generalmente arrojaría una excepción, ya que el cambio invalidaría el iterador. Esto es definitivamente un cambio de comportamiento. Cuando escribí por primera vez sobre Skip, incluí esto como una optimización "posible" y, de hecho, lo activé en el código fuente de Edulinq. Ahora creo que es un error y lo he eliminado por completo.

...

El problema con estos dos "optimizaciones" es discutible que se están aplicando optimizaciones basados ​​en listas dentro de un bloque iterador utilizado para ejecución diferida. La optimización de listas ya sea por adelantado en el punto de la llamada al método inicial o dentro de un operador de ejecución inmediata (Count, ToList, etc.) está bien, porque suponemos que la secuencia no cambiará durante el curso de la ejecución del método. No podemos hacer esa suposición con un bloque de iterador, porque el flujo del código es muy diferente: nuestro código se visita repetidamente en función del uso que hace la persona que llama de MoveNext().

https://msmvps.com/blogs/jon_skeet/archive/2011/01/26/reimplementing-linq-to-objects-part-40-optimization.aspx

+1

Su presupuesto es sobre listas; El ejemplo de OP es sobre matrices. Como las matrices son de longitud fija, no se pueden cambiar durante la iteración y su enumerador no arroja excepciones. – Gabe

+0

@Gabe - En realidad, el título de ese artículo es "¿Qué no podemos optimizar en LINQ to Objects?" así que supongo que no estoy de acuerdo con usted, ya que este artículo trata sobre el uso de Skip() en Linq para los objetos – IAmTimCorey

+0

Siempre he pensado que LINQ IEnumerables debe considerarse de solo lectura. Yo todavía no lo entiendo. La versión optimizada de Skip aún devolverá un Enumerador (comenzando en el índice n), comportándose así correctamente si la Lista cambia ... – codymanix

Cuestiones relacionadas