En .NET, ¿qué bucle se ejecuta más rápido, ‘para’ o ‘foreach’?

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):

  1. para iterar una matriz de más de 1000 cadenas, for es mejor que foreach
  2. para iterar sobre cadenas IList (no genéricas): foreach es mejor que for

Algunas referencias encontradas en la web para el mismo:

  1. Original gran artículo antiguo de Emmanuel Schanzer
  2. CodeProject FOREACH vs. PARA
  3. Blog – Para foreach o no para foreach , esa es la pregunta
  4. Foro ASP.NET – NET 1.1 C # 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.

enter image description here

 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); } 

ACTUALIZADO

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,

  • Foreach loop con matriz.
  • Para bucle con lista.
  • Bucle Foreach con lista.

Vea los resultados de mi prueba y el código a continuación,

enter image description here

 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.