Saltar al contenido

Rendimiento entre iterar a través de IEnumerable y lista

Entiende el código bien previamente a utilizarlo a tu trabajo y si ttienes algo que aportar puedes decirlo en los comentarios.

Solución:

enumerando un IEnumerable es de 2 a 3 veces más lento que enumerar el mismo List directamente. Esto se debe a una sutileza sobre cómo C# selecciona su enumerador para un tipo determinado.

List expone 3 enumeradores:

  1. List.Enumerator List.GetEnumerator()
  2. IEnumerator IEnumerable.GetEnumerator()
  3. IEnumerator IEnumerable.GetEnumerator()

Cuando C# compila un foreach bucle, seleccionará el enumerador en el orden anterior. Tenga en cuenta que un tipo no necesita implementar IEnumerable o IEnumerable para ser enumerable, solo necesita un método llamado GetEnumerator() que devuelve un enumerador.

Ahora, List.GetEnumerator() tiene la ventaja de estar tipificado estáticamente, lo que hace que todas las llamadas sean List.Enumerator.get_Current y List.Enumerator.MoveNext() static-enlazado en lugar de virtual.

10 millones de iteraciones (coreclr):

for(int i ...)               73 ms
foreach(... List)        215 ms
foreach(... IEnumerable) 698 ms
foreach(... IEnumerable)   1028 ms
for(int *p ...)              50 ms

10 millones de iteraciones (marco):

for(int i ...)              210 ms
foreach(... List)        252 ms
foreach(... IEnumerable) 537 ms
foreach(... IEnumerable)    844 ms
for(int *p ...)             202 ms

Descargo de responsabilidad

Debo señalar que la iteración real en una lista rara vez es el cuello de botella. Tenga en cuenta que son cientos de milisegundos en millones de iteraciones. Cualquier trabajo en el bucle más complicado que unas pocas operaciones aritméticas será abrumadoramente más costoso que la iteración misma.

Listes un IEnumerable. Cuando estás iterando a través de tu Listestá realizando la misma secuencia de operaciones que para cualquier otro IEnumerable:

  • obtener un IEnumerator.
  • Invocar IEnumerator.MoveNext() en su enumerador.
  • toma el IEnumerator.Current elemento de la interfaz IEnumerator mientras MoveNext() devoluciones true.
  • Deseche el IEnumerator.

lo que sabemos sobre List es que es una colección en memoria, por lo que el MoveNext() la función en su enumerador va a ser muy barata. Parece que su colección da un enumerador cuyo MoveNext() El método es más costoso, quizás porque está interactuando con algún recurso externo, como una conexión de base de datos.

Cuando usted llama ToList() en tu IEnumerable, está ejecutando una iteración completa de su colección y cargando todos los elementos en la memoria con esa iteración. Vale la pena hacerlo si espera iterar a través de la misma colección varias veces. Si espera iterar a través de la colección solo una vez, entonces ToList() es un false economía: todo lo que hace es crear una colección en memoria que luego tendrá que ser recolectada como basura.

List es una implementación de IEnumerable interfaz. Usar el foreach sintaxis, no necesita una List tipo o un IEnumerable tipo, pero es necesario que utilice un tipo con un GetEnumerator() método. Cita de documentos de Microsoft:

Él foreach declaración no se limita a esos tipos. Puede usarlo con una >instancia de cualquier tipo que cumpla con las siguientes condiciones:

  • Un tipo tiene el método público GetEnumerator sin parámetros cuyo tipo de retorno es clase, estructura o tipo de interfaz. A partir de C# 9.0, el método GetEnumerator puede ser el método de extensión de un tipo.
  • El tipo de valor devuelto del método GetEnumerator tiene la propiedad pública Current y el método MoveNext público sin parámetros cuyo tipo de valor devuelto es booleano.

Considerando por ejemplo un contexto LINQ, realizando una consulta, usando un IEnumerable tiene la ventaja de una ejecución diferida de la consulta (la consulta se ejecutará solo cuando sea necesario), pero, utilizando la ToList() estás solicitando que la consulta sea ejecutada (o evaluada) inmediatamente y quieres tus resultados en memoria, guardándolos en una lista, para luego realizar algunas operaciones sobre ellos, como cambiar algunos valores.

Sobre el rendimiento, es depende en lo que estás tratando de hacer. No sabemos qué operaciones está realizando (como obtener datos de una base de datos), qué tipos de recopilación está utilizando, etc.

ACTUALIZAR

La razón por la que tiene un tiempo diferente entre la iteración de la colección IEnumerable y la iteración de la colección List es, como dije, que tiene una ejecución diferida de la consulta cuando está invocando:

IEnumerable entities = from entity in Session.Query
                                             select entity;

Eso significa que la consulta se ejecuta solo cuando está iterando sobre la colección IEnumerable. Esto no sucede cuando estás llamando al ToList() método en entities.ToList(); por las razones que describí anteriormente.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *