En C # / VB.NET / .NET, ¿qué bucle se ejecuta más rápido, for
o foreach
?
Desde que leí que un bucle for
funciona más rápido que un bucle foreach
hace mucho tiempo, asumí que era válido para todas las colecciones, colecciones genéricas, todas las matrices, etc.
Recorrí Google y encontré algunos artículos, pero la mayoría no son concluyentes (leer comentarios sobre los artículos) y abiertos.
Lo que sería ideal es tener cada escenario en la lista y la mejor solución para el mismo.
Por ejemplo (solo un ejemplo de cómo debería ser):
for
es mejor que foreach
IList
(no genéricas): foreach
es mejor que for
Algunas referencias encontradas en la web para el mismo:
foreach
o no para foreach
, esa es la pregunta for
vs foreach
[Editar]
Además del aspecto de legibilidad, me interesan los hechos y las cifras. Hay aplicaciones en las que importa la última milla de optimización del rendimiento.
Patrick Smacchia escribió en su blog sobre este último mes, con las siguientes conclusiones:
- para bucles en la lista son un poco más de 2 veces más baratos que los bucles foreach en la lista.
- Looping en array es aproximadamente 2 veces más económico que el bucle en List.
- Como consecuencia, el bucle en el uso de array es 5 veces más económico que el bucle en List usando foreach (que creo que es lo que todos hacemos).
foreach
bucles foreach
demuestran una intención más específica que for
bucles .
El uso de un bucle foreach
demuestra a cualquiera que use su código que planea hacer algo con cada miembro de una colección, independientemente de su lugar en la colección. También muestra que no está modificando la colección original (y arroja una excepción si lo intenta).
La otra ventaja de foreach
es que funciona en cualquier IEnumerable
, donde solo tiene sentido para IList
, donde cada elemento tiene un índice.
Sin embargo, si necesita usar el índice de un elemento, entonces, por supuesto, debería poder usar un ciclo for
. Pero si no necesita usar un índice, tener uno solo está abarrotando su código.
No hay implicaciones de rendimiento significativas por lo que yo sé. En algún momento en el futuro, podría ser más fácil adaptar el código usando foreach
para ejecutar en múltiples núcleos, pero eso no es algo de lo que preocuparse en este momento.
Primero, una contrademanda a la respuesta de Dmitry . Para las matrices, el comstackdor de C # emite en gran medida el mismo código para foreach
que lo haría para un ciclo equivalente for
. Eso explica por qué para este punto de referencia, los resultados son básicamente los mismos:
using System; using System.Diagnostics; using System.Linq; class Test { const int Size = 1000000; const int Iterations = 10000; static void Main() { double[] data = new double[Size]; Random rng = new Random(); for (int i=0; i < data.Length; i++) { data[i] = rng.NextDouble(); } double correctSum = data.Sum(); Stopwatch sw = Stopwatch.StartNew(); for (int i=0; i < Iterations; i++) { double sum = 0; for (int j=0; j < data.Length; j++) { sum += data[j]; } if (Math.Abs(sum-correctSum) > 0.1) { Console.WriteLine("Summation failed"); return; } } sw.Stop(); Console.WriteLine("For loop: {0}", sw.ElapsedMilliseconds); sw = Stopwatch.StartNew(); for (int i=0; i < Iterations; i++) { double sum = 0; foreach (double d in data) { sum += d; } if (Math.Abs(sum-correctSum) > 0.1) { Console.WriteLine("Summation failed"); return; } } sw.Stop(); Console.WriteLine("Foreach loop: {0}", sw.ElapsedMilliseconds); } }
Resultados:
For loop: 16638 Foreach loop: 16529
Luego, la validación de que el punto de Greg sobre el tipo de colección es importante: cambie la matriz a una List
en la anterior, y obtendrá resultados radicalmente diferentes. No solo es significativamente más lento en general, sino que foreach se vuelve significativamente más lento que el acceso por índice. Habiendo dicho eso, casi siempre preferiría foreach a un bucle for donde simplifique el código, porque la legibilidad casi siempre es importante, mientras que la microoptimización rara vez lo es.
Cada vez que hay argumentos sobre el rendimiento, solo necesita escribir una pequeña prueba para que pueda usar resultados cuantitativos para respaldar su caso.
Use la clase StopWatch y repita algo unos pocos millones de veces, para mayor precisión. (Esto podría ser difícil sin un bucle for):
using System.Diagnostics; //... Stopwatch sw = new Stopwatch() sw.Start() for(int i = 0; i < 1000000;i ++) { //do whatever it is you need to time } sw.Stop(); //print out sw.ElapsedMilliseconds
Los dedos cruzaron los resultados de este espectáculo que la diferencia es insignificante, y también podría hacer cualquier resultado en el código más fácil de mantener
Siempre estará cerca. Para una matriz, a veces es un poco más rápido, pero foreach
es más expresivo y ofrece LINQ, etc. En general, quédese con foreach
.
Además, foreach
puede optimizarse en algunos escenarios. Por ejemplo, una lista vinculada podría ser terrible para el indexador, pero podría ser rápida por foreach
. En realidad, la LinkedList
estándar ni siquiera ofrece un indexador por este motivo.
Supongo que probablemente no sea significativo en el 99% de los casos, entonces, ¿por qué elegirías el más rápido en lugar del más apropiado (como el más fácil de entender / mantener)?
Es poco probable que haya una gran diferencia de rendimiento entre los dos. Como siempre, cuando te enfrentas a un “¿qué es más rápido?” pregunta, siempre debes pensar “Puedo medir esto”.
Escriba dos bucles que hagan lo mismo en el cuerpo del bucle, ejecútelos y cronometralos, y vea cuál es la diferencia de velocidad. Haga esto con un cuerpo casi vacío y un cuerpo de bucle similar al que realmente estará haciendo. También pruébelo con el tipo de colección que está utilizando, ya que diferentes tipos de colecciones pueden tener diferentes características de rendimiento.
Hay muy buenas razones para preferir los bucles foreach
en vez for
bucles. Si puede usar un ciclo foreach
, su jefe tiene razón en que debería hacerlo.
Sin embargo, no todas las iteraciones simplemente pasan por una lista en orden uno a uno. Si él está prohibiendo , sí, eso está mal.
Si yo fuera tú, lo que haría es convertir todos tus circuitos naturales en repetición . Eso lo enseñaría, y también es un buen ejercicio mental para ti.
Jeffrey Richter en TechEd 2005:
“He aprendido que a lo largo de los años el comstackdor de C # es básicamente un mentiroso para mí”. .. “Miente sobre muchas cosas”. .. “Como cuando haces un ciclo foreach …” … “… esa es una pequeña línea de código que escribes, pero lo que el comstackdor C # escupe para hacer eso es fenomenal. try / finally block in all, dentro del bloque finally arroja su variable a una interfaz IDisposable, y si el elenco lo activa llama al método Dispose, dentro del bucle llama a la propiedad Current y al método MoveNext repetidamente dentro del bucle, se están creando objetos debajo de las coberturas. Mucha gente usa Foreach porque es muy fácil de codificar, muy fácil de hacer … “…” foreach no es muy bueno en términos de rendimiento, si itera sobre una colección en lugar de usar cuadrado la notación de corchetes, simplemente haciendo índice, es mucho más rápido y no crea ningún objeto en el montón … ”
Transmisión por Internet a petición: http://msevents.microsoft.com/CUI/WebCastEventDetails.aspx?EventID=1032292286&EventCategory=3&culture=en-US&CountryCode=US
Esto es ridículo. No hay una razón convincente para prohibir el for-loop, en cuanto a rendimiento u otros.
Vea el blog de Jon Skeet para obtener un punto de referencia de rendimiento y otros argumentos.
En casos en los que trabaje con una colección de objetos, foreach
es mejor, pero si incrementa un número, un bucle for
es mejor.
Tenga en cuenta que en el último caso, podría hacer algo como:
foreach (int i in Enumerable.Range(1, 10))...
Pero ciertamente no funciona mejor, en realidad tiene un peor rendimiento en comparación con un.
Esto debería salvarte:
public IEnumerator For(int start, int end, int step) { int n = start; while (n <= end) { yield n; n += step; } }
Utilizar:
foreach (int n in For(1, 200, 4)) { Console.WriteLine(n); }
Para obtener una mayor ganancia, puede tomar tres delegates como parámetros.
Las diferencias de velocidad en un bucle for
y foreach
son pequeñas cuando se navega por estructuras comunes como matrices, listas, etc., y hacer una consulta LINQ
sobre la colección es casi siempre un poco más lento, ¡aunque es más agradable escribir! Como dijeron los otros carteles, opta por la expresividad en lugar de un milisegundo de rendimiento extra.
Lo que no se ha dicho hasta ahora es que cuando se comstack un bucle foreach
, el comstackdor lo optimiza basándose en la colección sobre la que se está iterando. Eso significa que cuando no esté seguro de qué bucle utilizar, debe usar el bucle foreach
: generará el mejor bucle para usted cuando se compile. Es más legible también.
Otra ventaja clave con el ciclo foreach
es que si la implementación de su colección cambia (de una array
int a una List
por ejemplo), entonces su bucle foreach
no requerirá ningún cambio de código:
foreach (int i in myCollection)
Lo anterior es lo mismo sin importar de qué tipo sea su colección, mientras que en su ciclo for
, lo siguiente no se comstackrá si cambió myCollection
de una array
a una List
:
for (int i = 0; i < myCollection.Length, i++)
“¿Hay algún argumento que pueda usar para ayudarme a convencerlo de que el bucle for es aceptable de usar?”
No, si su jefe está microgestionando al nivel de decirle qué construcciones de lenguaje de progtwigción usar, realmente no hay nada que pueda decir. Lo siento.
Probablemente dependa del tipo de colección que está enumerando y de la implementación de su indexador. Sin embargo, en general, es probable que usar foreach
sea un mejor enfoque.
Además, funcionará con cualquier IEnumerable
, no solo con indexadores.
Cada construcción de lenguaje tiene un tiempo y un lugar apropiados para su uso. Hay una razón por la cual el lenguaje C # tiene cuatro declaraciones de iteración separadas: cada una tiene un propósito específico y tiene un uso apropiado.
Recomiendo sentarse con su jefe y tratar de explicar de manera racional por qué un bucle for
tiene un propósito. Hay momentos en los que un bloque de iteración describe más claramente un algoritmo que una iteración foreach
. Cuando esto es cierto, es apropiado usarlos.
También le diría a su jefe: el rendimiento no es, y no debería ser un problema de ninguna manera práctica, es más una cuestión de expresión, el algoritmo de una manera sucinta, significativa y fácil de mantener. Las micro-optimizaciones como esta pierden por completo el punto de optimización del rendimiento, ya que cualquier beneficio real del rendimiento vendrá del rediseño algorítmico y refactorización, no de la reestructuración del ciclo.
Si, después de una discusión racional, todavía existe esta visión autoritaria, depende de usted cómo proceder. Personalmente, no me gustaría trabajar en un entorno donde se desaliente el pensamiento racional, y consideraría mudarme a otro puesto bajo un empleador diferente. Sin embargo, recomiendo encarecidamente la discusión antes de enojarme, puede haber un simple malentendido.
Esto tiene las mismas dos respuestas que la mayoría de las preguntas “qué es más rápido”:
1) Si no mides, no sabes.
2) (Porque …) Depende.
Depende de qué tan caro es el método “MoveNext ()”, en relación con qué tan caro es el método “this [int index]”, para el tipo (o tipos) de IEnumerable que se repetirán.
La palabra clave “foreach” es una abreviatura de una serie de operaciones: llama a GetEnumerator () una vez en IEnumerable, llama a MoveNext () una vez por iteración, realiza una comprobación de tipo, etc. Lo más probable que afecte las mediciones de rendimiento es el costo de MoveNext () ya que se invoca O (N) veces. Tal vez es barato, pero tal vez no lo es.
La palabra clave “para” parece más predecible, pero dentro de la mayoría de los bucles “para” encontrará algo así como “colección [índice]”. Esto parece una simple operación de indexación de matriz, pero en realidad es una llamada a método, cuyo costo depende por completo de la naturaleza de la colección sobre la que se está iterando. Probablemente sea barato, pero tal vez no lo sea.
Si la estructura subyacente de la colección es esencialmente una lista enlazada, MoveNext es muy barato, pero el indexador puede tener un costo O (N), lo que representa el verdadero costo de un bucle “for” O (N * N).
Es lo que haces dentro del ciclo que afecta el rendimiento, no el constructo de bucle real (suponiendo que tu caso no sea trivial).
Si for
es más rápido que foreach
es realmente además del punto. Dudo seriamente que elegir uno sobre el otro tenga un impacto significativo en su desempeño.
La mejor forma de optimizar su aplicación es a través del perfil del código real. Eso identificará los métodos que representan la mayor cantidad de trabajo / tiempo. Optimiza esos primero. Si el rendimiento aún no es aceptable, repita el procedimiento.
Como regla general, recomendaría mantenerme alejado de las micro optimizaciones ya que raramente producirán ganancias significativas. La única excepción es cuando se optimizan las rutas calientes identificadas (es decir, si su perfil identifica algunos métodos muy utilizados, puede tener sentido optimizarlos ampliamente).
Los dos correrán casi exactamente de la misma manera. Escribe un código para usar ambos, luego muéstrale el IL. Debería mostrar cálculos comparables, lo que significa que no hay diferencia en el rendimiento.
Encontré el ciclo foreach
que itera a través de una List
más rápido . Vea los resultados de mi prueba a continuación. En el siguiente código itero una array
de tamaño 100, 10000 y 100000 separadamente usando for
y foreach
loop para medir la hora.
private static void MeasureTime() { var array = new int[10000]; var list = array.ToList(); Console.WriteLine("Array size: {0}", array.Length); Console.WriteLine("Array For loop ......"); var stopWatch = Stopwatch.StartNew(); for (int i = 0; i < array.Length; i++) { Thread.Sleep(1); } stopWatch.Stop(); Console.WriteLine("Time take to run the for loop is {0} millisecond", stopWatch.ElapsedMilliseconds); Console.WriteLine(" "); Console.WriteLine("Array Foreach loop ......"); var stopWatch1 = Stopwatch.StartNew(); foreach (var item in array) { Thread.Sleep(1); } stopWatch1.Stop(); Console.WriteLine("Time take to run the foreach loop is {0} millisecond", stopWatch1.ElapsedMilliseconds); Console.WriteLine(" "); Console.WriteLine("List For loop ......"); var stopWatch2 = Stopwatch.StartNew(); for (int i = 0; i < list.Count; i++) { Thread.Sleep(1); } stopWatch2.Stop(); Console.WriteLine("Time take to run the for loop is {0} millisecond", stopWatch2.ElapsedMilliseconds); Console.WriteLine(" "); Console.WriteLine("List Foreach loop ......"); var stopWatch3 = Stopwatch.StartNew(); foreach (var item in list) { Thread.Sleep(1); } stopWatch3.Stop(); Console.WriteLine("Time take to run the foreach loop is {0} millisecond", stopWatch3.ElapsedMilliseconds); }
Después de la sugerencia de @jgauffin utilicé el código @johnskeet y descubrí que el ciclo for
con array
es más rápido que seguirlo,
Vea los resultados de mi prueba y el código a continuación,
private static void MeasureNewTime() { var data = new double[Size]; var rng = new Random(); for (int i = 0; i < data.Length; i++) { data[i] = rng.NextDouble(); } Console.WriteLine("Lenght of array: {0}", data.Length); Console.WriteLine("No. of iteration: {0}", Iterations); Console.WriteLine(" "); double correctSum = data.Sum(); Stopwatch sw = Stopwatch.StartNew(); for (int i = 0; i < Iterations; i++) { double sum = 0; for (int j = 0; j < data.Length; j++) { sum += data[j]; } if (Math.Abs(sum - correctSum) > 0.1) { Console.WriteLine("Summation failed"); return; } } sw.Stop(); Console.WriteLine("For loop with Array: {0}", sw.ElapsedMilliseconds); sw = Stopwatch.StartNew(); for (var i = 0; i < Iterations; i++) { double sum = 0; foreach (double d in data) { sum += d; } if (Math.Abs(sum - correctSum) > 0.1) { Console.WriteLine("Summation failed"); return; } } sw.Stop(); Console.WriteLine("Foreach loop with Array: {0}", sw.ElapsedMilliseconds); Console.WriteLine(" "); var dataList = data.ToList(); sw = Stopwatch.StartNew(); for (int i = 0; i < Iterations; i++) { double sum = 0; for (int j = 0; j < dataList.Count; j++) { sum += data[j]; } if (Math.Abs(sum - correctSum) > 0.1) { Console.WriteLine("Summation failed"); return; } } sw.Stop(); Console.WriteLine("For loop with List: {0}", sw.ElapsedMilliseconds); sw = Stopwatch.StartNew(); for (int i = 0; i < Iterations; i++) { double sum = 0; foreach (double d in dataList) { sum += d; } if (Math.Abs(sum - correctSum) > 0.1) { Console.WriteLine("Summation failed"); return; } } sw.Stop(); Console.WriteLine("Foreach loop with List: {0}", sw.ElapsedMilliseconds); }
porque tiene una lógica más simple de implementar, por lo que es más rápido que foreach.
A menos que esté en un proceso de optimización de velocidad específico, yo diría que use el método que produzca el código más fácil de leer y mantener.
Si un iterador ya está configurado, como con una de las clases de colección, entonces el foreach es una buena opción fácil. Y si se trata de un rango entero que está iterando, entonces probablemente sea más limpio.
Jeffrey Richter habló sobre la diferencia de rendimiento entre for y foreach en un podcast reciente: http://pixel8.infragistics.com/shows/everything.aspx#Episode:9317
En la mayoría de los casos, realmente no hay diferencia.
Por lo general, siempre debe utilizar foreach cuando no tiene un índice numérico explícito, y siempre debe usarlo para cuando realmente no tiene una colección iterable (p. Ej. Iterar sobre una cuadrícula de matriz bidimensional en un triángulo superior) . Hay algunos casos en los que puede elegir.
Se podría argumentar que los bucles pueden ser un poco más difíciles de mantener si los números mágicos comienzan a aparecer en el código. Debería estar en lo cierto al estar molesto por no poder usar un bucle for y tener que construir una colección o usar un lambda para crear una subcolección en su lugar simplemente porque los bucles for han sido prohibidos.
Realmente se joda con su cabeza y elija un cierre IQueryable .foreach en su lugar:
myList.ForEach (c => Console.WriteLine (c.ToString ());
LOL
No esperaría que nadie encuentre una “gran” diferencia de rendimiento entre los dos.
Supongo que la respuesta depende de si la colección a la que intenta acceder tiene una implementación de acceso a indexador más rápida o una implementación de acceso a IEnumerator más rápida. Dado que IEnumerator a menudo usa el indexador y solo contiene una copia de la posición del índice actual, esperaría que el acceso del enumerador fuera al menos tan lento o más lento que el acceso directo al índice, pero no mucho.
Por supuesto, esta respuesta no tiene en cuenta ninguna optimización que el comstackdor pueda implementar.
Keep in mind that the for-loop and foreach-loop are not always equivalent. List enumerators will throw an exception if the list changes, but you won’t always get that warning with a normal for loop. You might even get a different exception if the list changes at just the wrong time.
It seems a bit strange to totally forbid the use of something like a for loop.
There’s an interesting article here that covers a lot of the performance differences between the two loops.
I would say personally I find foreach a bit more readable over for loops but you should use the best for the job at hand and not have to write extra long code to include a foreach loop if a for loop is more appropriate.
I did test it a while ago, with the result that a for
loop is much faster than a foreach
loop. The cause is simple, the foreach
loop first needs to instantiate an IEnumerator
for the collection.