¿Qué fragmento de código ofrecerá un mejor rendimiento? Los siguientes segmentos de código se escribieron en C #.
1.
for(int counter=0; counter<list.Count; counter++)
{
list[counter].DoSomething();
}
2.
foreach(MyType current in list)
{
current.DoSomething();
}
c#
performance
for-loop
foreach
Kthevar
fuente
fuente
list
realmente tenga uncount
miembro en lugar deCount
.Respuestas:
Bueno, depende en parte del tipo exacto de
list
. También dependerá del CLR exacto que esté utilizando.Si es significativo de alguna manera o no, dependerá de si está haciendo un trabajo real en el ciclo. En casi todos los casos, la diferencia con el rendimiento no será significativa, pero la diferencia con la legibilidad favorece el
foreach
ciclo.Yo personalmente usaría LINQ para evitar el "si" también:
EDITAR: Para aquellos de ustedes que afirman que iterar sobre un
List<T>
conforeach
produce el mismo código que elfor
bucle, aquí hay evidencia de que no es así:Produce IL de:
El compilador trata las matrices de manera diferente, convirtiendo un
foreach
bucle básicamente en unfor
bucle, pero noList<T>
. Aquí está el código equivalente para una matriz:Curiosamente, no puedo encontrar esto documentado en la especificación C # 3 en ninguna parte ...
fuente
List<T>
.foreach
en cuenta que sobre una matriz es equivalente a defor
todos modos. Siempre codifique primero para la legibilidad, luego solo micro-optimice cuando tenga evidencia de que brinda un beneficio de rendimiento medible.Un
for
bucle se compila en un código aproximadamente equivalente a esto:Donde un
foreach
bucle se compila en un código aproximadamente equivalente a esto:Como puede ver, todo dependerá de cómo se implemente el enumerador versus cómo se implemente el indexador de listas. Como resultado, el enumerador para tipos basados en matrices normalmente se escribe de esta manera:
Entonces, como puede ver, en este caso no hará mucha diferencia, sin embargo, el enumerador de una lista vinculada probablemente se vería así:
En .NET encontrará que la clase LinkedList <T> ni siquiera tiene un indexador, por lo que no podrá hacer su bucle for en una lista enlazada; pero si pudiera, el indexador tendría que escribirse así:
Como puede ver, llamar a esto varias veces en un bucle será mucho más lento que usar un enumerador que pueda recordar dónde está en la lista.
fuente
Una prueba fácil de semi-validar. Hice una pequeña prueba, solo para ver. Aquí está el código:
Y aquí está la sección de foreach:
Cuando reemplacé el for con un foreach, el foreach fue 20 milisegundos más rápido, consistentemente . El for fue de 135-139ms mientras que el foreach fue de 113-119ms. Cambié de un lado a otro varias veces, asegurándome de que no fuera un proceso que simplemente se inició.
Sin embargo, cuando eliminé el foo y la instrucción if, el for fue más rápido en 30 ms (foreach fue de 88 ms y for fue de 59 ms). Ambos eran cascos vacíos. Supongo que el foreach realmente pasó una variable donde el for solo estaba incrementando una variable. Si agregué
Luego, el for se vuelve lento en unos 30 ms. Supongo que esto tuvo que ver con la creación de foo y tomando la variable en la matriz y asignándola a foo. Si solo accede a intList [i], entonces no tiene esa penalización.
Honestamente ... esperaba que foreach fuera un poco más lento en todas las circunstancias, pero no lo suficiente como para importar en la mayoría de las aplicaciones.
editar: aquí está el nuevo código usando las sugerencias de Jons (134217728 es el mayor int que puede tener antes de que se lance la excepción System.OutOfMemory):
Y aquí están los resultados:
Generando datos. Cálculo de bucle for: 2458ms Cálculo de bucle foreach: 2005ms
Al intercambiarlos para ver si se trata del orden de las cosas, se obtienen los mismos resultados (casi).
fuente
Nota: esta respuesta se aplica más a Java que a C #, ya que C # no tiene un indexador activado
LinkedLists
, pero creo que el punto general aún se mantiene.Si el código
list
con el que está trabajando es aLinkedList
, el rendimiento del código indexador ( acceso al estilo de matriz ) es mucho peor que el de usar elIEnumerator
deforeach
, para listas grandes.Cuando accede al elemento 10.000 en a
LinkedList
usando la sintaxis del indexador:,list[10000]
la lista enlazada comenzará en el nodo principal y atravesará elNext
puntero diez mil veces, hasta que alcance el objeto correcto. Obviamente, si haces esto en un bucle, obtendrás:Cuando llama
GetEnumerator
(implícitamente usando laforach
-sintaxis), obtendrá unIEnumerator
objeto que tiene un puntero al nodo principal. Cada vez que llamaMoveNext
, ese puntero se mueve al siguiente nodo, así:Como puede ver, en el caso de
LinkedList
s, el método del indexador de matriz se vuelve más y más lento, cuanto más largo es el bucle (tiene que pasar por el mismo puntero principal una y otra vez). Mientras que elIEnumerable
justo opera en tiempo constante.Por supuesto, como dijo Jon, esto realmente depende del tipo de
list
, silist
no es unaLinkedList
, sino una matriz, el comportamiento es completamente diferente.fuente
LinkedList<T>
documentos en MSDN, y tiene una API bastante decente. Lo más importante es que no tiene unget(int index)
método, como lo tiene Java. Aún así, supongo que el punto sigue siendo válido para cualquier otra estructura de datos similar a una lista que exponga un indexador que es más lento que uno específicoIEnumerator
.Como han mencionado otras personas, aunque el rendimiento en realidad no importa mucho, el foreach siempre será un poco más lento debido al uso de
IEnumerable
/IEnumerator
en el ciclo. El compilador traduce la construcción en llamadas en esa interfaz y para cada paso se llama a una función + una propiedad en la construcción foreach.Esta es la expansión equivalente de la construcción en C #. Puede imaginarse cómo puede variar el impacto en el rendimiento según las implementaciones de MoveNext y Current. Mientras que en un acceso a una matriz, no tiene esas dependencias.
fuente
List<T>
aquí, todavía existe el acierto (posiblemente en línea) de llamar al indexador. No es como si fuera un acceso a una matriz de metal desnudo.Después de leer suficientes argumentos de que "el bucle foreach debería ser preferido por legibilidad", puedo decir que mi primera reacción fue "¿qué"? La legibilidad, en general, es subjetiva y, en este caso particular, aún más. Para alguien con experiencia en programación (prácticamente, todos los lenguajes antes de Java), los bucles for son mucho más fáciles de leer que los bucles foreach. Además, las mismas personas que afirman que los bucles foreach son más legibles, también son partidarios de linq y otras "características" que hacen que el código sea difícil de leer y mantener, algo que prueba el punto anterior.
Sobre el impacto en el rendimiento, consulte la respuesta a esta pregunta.
EDITAR: Hay colecciones en C # (como el HashSet) que no tienen indexador. En estas colecciones, foreach es la única manera de iterar y es el único caso creo que se debe utilizar más de .
fuente
Hay otro hecho interesante que puede pasarse por alto fácilmente al probar la velocidad de ambos bucles: el uso del modo de depuración no permite que el compilador optimice el código con la configuración predeterminada.
Esto me llevó al resultado interesante de que foreach es más rápido que en el modo de depuración. Mientras que el primero es más rápido que el foreach en el modo de lanzamiento. Obviamente, el compilador tiene mejores formas de optimizar un bucle for que un bucle foreach que compromete varias llamadas a métodos. Por cierto, un bucle for es tan fundamental que es posible que incluso lo optimice la propia CPU.
fuente
En el ejemplo que proporcionó, definitivamente es mejor usar un
foreach
bucle en lugar de unfor
bucle.La
foreach
construcción estándar puede ser más rápida (1,5 ciclos por paso) que una simplefor-loop
(2 ciclos por paso), a menos que el bucle se haya desenrollado (1,0 ciclos por paso).Así que para el código de todos los días, el rendimiento no es una razón para utilizar los más complejos
for
,while
odo-while
construcciones.Consulte este enlace: http://www.codeproject.com/Articles/146797/Fast-and-Less-Fast-Loops-in-C
fuente
puede leer sobre esto en Deep .NET - parte 1 Iteración
cubre los resultados (sin la primera inicialización) desde el código fuente .NET hasta el desmontaje.
por ejemplo - Iteración de matriz con un bucle foreach:
y - lista de iteraciones con foreach loop:
y los resultados finales:
fuente