En .NET, ¿qué bucle se ejecuta más rápido, 'for' o 'foreach'?
¿ En C#/VB.NET/.NET,
qué bucle se ejecuta más rápido for
o foreach
?
Desde que leí que un for
bucle funciona más rápido que un foreach
bucle hace mucho tiempo, asumí que era válido para todas las colecciones, generic collections
todas arrays
, etc.
Busqué en Google y encontré algunos artículos, pero la mayoría de ellos no son concluyentes (lea los comentarios de los artículos) y son abiertos.
Lo ideal sería tener cada escenario enumerado y la mejor solución para el mismo.
Por ejemplo (solo un ejemplo de como debería ser):
- para iterar más
array
de 1000 cadenas,for
es mejor queforeach
- para iterar sobre
IList
cadenas (no genéricas) -foreach
es mejor quefor
Algunas referencias encontradas en la web sobre el mismo:
- Gran artículo original antiguo de Emmanuel Schanzer
- CodeProject FOREACH vs. PARA
- Blog - Hacer
foreach
o noforeach
, esa es la cuestión - Foro ASP.NET - NET 1.1 C#
for
vs.foreach
[Editar]
Aparte del aspecto de legibilidad, estoy realmente interesado en hechos y cifras. Hay aplicaciones en las que el último kilómetro de optimización del rendimiento sí importa.
Patrick Smacchia escribió en su blog sobre esto el mes pasado, con las siguientes conclusiones:
- Los bucles for en List son un poco más de 2 veces más baratos que los bucles foreach en List.
- Hacer un bucle en una matriz es aproximadamente 2 veces más barato que hacerlo en una lista.
- Como consecuencia, hacer un bucle en una matriz usando for es 5 veces más barato que hacer un bucle en una Lista usando foreach (que creo que es lo que todos hacemos).
Primero, una contrademanda a la respuesta de Dmitry (ahora eliminada) . Para las matrices, el compilador de C# emite prácticamente el mismo código que foreach
lo haría para un for
bucle equivalente. 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
A continuación, valide el punto de Greg acerca de que el tipo de colección es importante: cambie la matriz a a List<double>
en lo anterior y obtendrá resultados radicalmente diferentes. No sólo es significativamente más lento en general, sino que foreach se vuelve significativamente más lento que el acceso por índice. Dicho esto, casi siempre preferiría foreach a un bucle for que simplifica el código, porque la legibilidad casi siempre es importante, mientras que la microoptimización rara vez lo es.
foreach
Los bucles demuestran una intención más específica que for
los bucles .
El uso de un foreach
bucle le demuestra a cualquiera que use su código que está planeando hacer algo con cada miembro de una colección, independientemente de su lugar en la colección. También muestra que no estás modificando la colección original (y genera una excepción si lo intentas).
La otra ventaja foreach
es que funciona en cualquiera IEnumerable
, donde for
solo tiene sentido para IList
, donde cada elemento en realidad tiene un índice.
Sin embargo, si necesita utilizar el índice de un elemento, entonces, por supuesto, se le debería permitir utilizar un for
bucle. Pero si no necesita utilizar un índice, tener uno sólo saturará su código.
Hasta donde yo sé, no hay implicaciones significativas en el rendimiento. En algún momento en el futuro, podría ser más fácil adaptar el código para foreach
ejecutarlo en múltiples núcleos, pero eso no es algo de qué preocuparse en este momento.
Cada vez que haya discusiones sobre el rendimiento, sólo necesita escribir una pequeña prueba para poder utilizar resultados cuantitativos para respaldar su caso.
Utilice la clase StopWatch y repita algo unos millones de veces para mayor precisión. (Esto podría resultar 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
Cruzamos los dedos y los resultados de esto muestran que la diferencia es insignificante, y también podrías hacer lo que resulte en el código más fácil de mantener.