No .NET, qual loop é executado mais rapidamente, ‘for’ ou ‘foreach’?

Em C # / VB.NET / .NET, qual loop é executado mais rapidamente, for ou foreach ?

Desde que eu li que um loop for trabalha mais rápido do que um loop foreach há muito tempo atrás eu assumi que era verdade para todas as collections, collections genéricas, todos os arrays, etc.

Eu vasculhei o Google e encontrei alguns artigos, mas a maioria deles é inconclusiva (leia comentários nos artigos) e está em aberto.

O ideal seria ter cada cenário listado e a melhor solução para o mesmo.

Por exemplo (apenas um exemplo de como deveria ser):

  1. para iterar uma matriz de mais de 1000 strings – for é melhor que foreach
  2. para iterar em strings IList (não genéricas) – foreach é melhor que for

Algumas referências encontradas na web para o mesmo:

  1. Original grand old article de Emmanuel Schanzer
  2. CodeProject FOREACH vs. PARA
  3. Blog – Para foreach ou não foreach , essa é a questão
  4. Fórum ASP.NET – NET 1.1 C # for vs foreach

[Editar]

Além do aspecto de legibilidade, estou realmente interessado em fatos e números. Existem aplicativos nos quais a última milha de otimização de desempenho espremida é importante.

Patrick Smacchia blogou sobre isso no mês passado, com as seguintes conclusões:

  • For loops on List são um pouco mais de 2 vezes mais baratas do que foreach loops on List.
  • Loop on array é cerca de 2 vezes mais barato que looping na List.
  • Como consequência, o looping em array usando é 5 vezes mais barato que o looping em List usando foreach (que, acredito, é o que todos nós fazemos).

Loops foreach demonstram intenção mais específica do que for loops .

Usar um loop foreach demonstra para qualquer pessoa usando seu código que você está planejando fazer algo para cada membro de uma coleção, independentemente de seu lugar na coleção. Ele também mostra que você não está modificando a coleção original (e lança uma exceção se você tentar).

A outra vantagem do foreach é que ele funciona em qualquer IEnumerable , onde, como for apenas faz sentido para IList , onde cada elemento realmente tem um índice.

No entanto, se você precisar usar o índice de um elemento, é claro que você deve usar um loop for . Mas se você não precisa usar um índice, ter um é apenas bagunçar seu código.

Não há implicações significativas de desempenho, tanto quanto sei. Em algum momento no futuro, pode ser mais fácil adaptar código usando o foreach para rodar em múltiplos núcleos, mas isso não é algo para se preocupar agora.

Primeiro, uma contra-reivindicação à resposta de Dmitry . Para matrizes, o compilador C # emite basicamente o mesmo código para foreach como seria para um loop equivalente. Isso explica porque, para este benchmark, os resultados são basicamente os mesmos:

 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 

Em seguida, confirme que o ponto de Greg sobre o tipo de coleção é importante – altere a matriz para uma List acima e obtenha resultados radicalmente diferentes. Não só é significativamente mais lento em geral, mas foreach torna-se significativamente mais lento do que acessar por índice. Dito isto, eu ainda prefiro quase sempre preferir um loop for onde ele torna o código mais simples – porque a legibilidade é quase sempre importante, enquanto a micro-otimização raramente é.

Sempre que houver discussões sobre desempenho, você só precisará escrever um pequeno teste para poder usar resultados quantitativos para dar suporte ao seu caso.

Use a class StopWatch e repita algo alguns milhões de vezes, para precisão. (Isso pode ser difícil sem um loop 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 

Os dedos cruzaram os resultados deste show que a diferença é insignificante, e você também pode fazer o que resultar no código mais sustentável

Sempre estará perto. Para um array, às vezes é um pouco mais rápido, mas foreach é mais expressivo, e oferece LINQ, etc. Em geral, fique com foreach .

Além disso, foreach pode ser otimizado em alguns cenários. Por exemplo, uma linked list pode ser terrível pelo indexador, mas pode ser rápida pelo foreach . Na verdade, o LinkedList padrão nem oferece um indexador por esse motivo.

Meu palpite é que provavelmente não será significativo em 99% dos casos, então por que você escolheria o mais rápido em vez do mais apropriado (como o mais fácil de entender / manter)?

É improvável que haja uma enorme diferença de desempenho entre os dois. Como sempre, quando se depara com um “que é mais rápido?” pergunta, você deve sempre pensar “eu posso medir isso”.

Escreva dois loops que fazem a mesma coisa no corpo do loop, execute e tempo os dois, e veja qual é a diferença de velocidade. Faça isso com um corpo quase vazio e um corpo semelhante ao que você realmente estará fazendo. Experimente também com o tipo de coleção que você está usando, porque diferentes tipos de collections podem ter diferentes características de desempenho.

Há boas razões para preferir foreach loops over for loops. Se você pode usar um loop foreach , seu chefe está certo que você deveria.

No entanto, nem toda iteração está simplesmente passando por uma lista na ordem, uma por uma. Se ele está proibindo , sim, isso está errado.

Se eu fosse você, o que eu faria seria transformar todos os seus loops naturais em recursion . Isso ensinaria a ele, e também é um bom exercício mental para você.

Jeffrey Richter no TechEd 2005:

“Eu tenho vindo a aprender ao longo dos anos o compilador C # é basicamente um mentiroso para mim.” .. “Mentiras sobre muitas coisas.” .. “Como quando você faz um loop foreach …” .. “… é uma pequena linha de código que você escreve, mas o que o compilador C # cospe para fazer isso é fenomenal. Ele coloca um tente / finalmente bloqueie lá dentro, dentro do bloco finally ele coloca sua variável em uma interface IDisposable, e se o casting der o nome do método Dispose, dentro do loop ele chama a propriedade Current e o método MoveNext repetidamente dentro do loop, objects estão sendo criados sob as capas.Muitas pessoas usam foreach porque é muito fácil codificar, muito fácil de fazer .. “..” foreach não é muito bom em termos de desempenho, se você iterou sobre uma coleção ao invés de usar quadrado notação de suporte, apenas fazendo indexação, isso é muito mais rápido, e não cria objects no heap … ”

Webcast sob demanda: http://msevents.microsoft.com/CUI/WebCastEventDetails.aspx?EventID=1032292286&EventCategory=3&culture=en-US&CountryCode=US

Isto é ridículo. Não há nenhuma razão convincente para proibir o loop, o desempenho ou outro.

Veja o blog de Jon Skeet para um benchmark de desempenho e outros argumentos.

Nos casos em que você trabalha com uma coleção de objects, foreach é melhor, mas se você incrementar um número, um loop for é melhor.

Note que no último caso, você poderia fazer algo como:

 foreach (int i in Enumerable.Range(1, 10))... 

Mas certamente não funciona melhor, na verdade tem um desempenho pior em comparação com um for .

Isso deve economizar você:

 public IEnumerator For(int start, int end, int step) { int n = start; while (n < = end) { yield n; n += step; } } 

Usar:

 foreach (int n in For(1, 200, 4)) { Console.WriteLine(n); } 

Para maior vitória, você pode tomar três delegates como parâmetros.

As diferenças de velocidade em um loop foreach e foreach são pequenas quando você faz um loop por estruturas comuns, como arrays, listas, etc., e fazer uma consulta LINQ sobre a coleção é quase sempre um pouco mais lenta, embora seja mais agradável escrever! Como os outros pôsteres disseram, vá para a expressividade em vez de um milésimo de segundo de desempenho extra.

O que não foi dito até agora é que quando um loop foreach é compilado, ele é otimizado pelo compilador baseado na coleção em que ele está se repetindo. Isso significa que quando você não tem certeza de qual loop usar, você deve usar o loop foreach – ele irá gerar o melhor loop para você quando for compilado. É mais legível também.

Outra vantagem importante com o loop foreach é que, se a implementação da sua coleção for alterada (de uma array int para uma List por exemplo), o loop foreach não exigirá alterações de código:

 foreach (int i in myCollection) 

O acima é o mesmo, não importa o tipo de sua coleção, ao passo que em seu loop for , o seguinte não será compilado se você alterou myCollection de uma array para uma List :

 for (int i = 0; i < myCollection.Length, i++) 

“Há algum argumento que eu possa usar para me ajudar a convencê-lo de que o loop for é aceitável de usar?”

Não, se o seu chefe está microgerenciando ao nível de dizer o que a linguagem de programação constrói para usar, não há realmente nada que você possa dizer. Desculpa.

Isso provavelmente depende do tipo de coleta que você está enumerando e da implementação de seu indexador. Em geral, porém, usar foreach provavelmente será uma abordagem melhor.

Além disso, funcionará com qualquer IEnumerable – não apenas coisas com indexadores.

Cada construção de linguagem tem um tempo e lugar apropriados para uso. Há uma razão pela qual a linguagem C # tem quatro instruções de iteração separadas – cada uma está lá para um propósito específico e tem um uso apropriado.

Eu recomendo sentar com seu chefe e tentar explicar racionalmente porque um loop for tem um propósito. Há momentos em que um bloco de iteração descreve mais claramente um algoritmo do que uma iteração foreach . Quando isso é verdade, é apropriado usá-los.

Gostaria também de salientar ao seu chefe – Performance não é, e não deve ser um problema de qualquer forma prática – é mais uma questão de expressão do algoritmo de uma forma sucinta, significativa e sustentável. Micro-otimizações como essa perdem completamente o ponto de otimização de desempenho, já que qualquer benefício de desempenho real virá do redesenho algorítmico e da refatoração, e não da reestruturação de loop.

Se, depois de uma discussão racional, ainda houver essa visão autoritária, cabe a você decidir como proceder. Pessoalmente, eu não ficaria feliz trabalhando em um ambiente onde o pensamento racional é desencorajado, e consideraria mudar para outra posição sob um empregador diferente. No entanto, eu recomendo fortemente a discussão antes de ficar chateado – pode haver apenas um simples mal-entendido no lugar.

Isso tem as mesmas duas respostas que a maioria das perguntas “o que é mais rápido”:

1) Se você não mede, você não sabe.

2) (Porque …) Depende.

Depende de quão caro é o método “MoveNext ()”, em relação a quão caro é o método “this [int index]”, para o tipo (ou tipos) de IEnumerable sobre o qual você estará repetindo.

A palavra-chave “foreach” é uma forma abreviada de uma série de operações – ela chama GetEnumerator () uma vez no IEnumerable, chama MoveNext () uma vez por iteração, faz alguma verificação de tipo e assim por diante. A coisa com maior probabilidade de impactar as medições de desempenho é o custo de MoveNext () desde que seja chamado O (N) vezes. Talvez seja barato, mas talvez não seja.

A palavra-chave “for” parece mais previsível, mas dentro da maioria dos loops “for” você encontrará algo como “collection [index]”. Isso parece uma simples operação de indexação de matriz, mas na verdade é uma chamada de método, cujo custo depende inteiramente da natureza da coleção sobre a qual você está interagindo. Provavelmente é barato, mas talvez não seja.

Se a estrutura subjacente da coleção é essencialmente uma lista encadeada, o MoveNext é barato, mas o indexador pode ter o custo O (N), tornando o custo real de um loop “for” O (N * N).

É o que você faz dentro do loop que afeta a performance, não o constructo de looping real (assumindo que seu caso não é trivial).

Se é mais rápido do que foreach é realmente além do ponto. Eu duvido seriamente que escolher um sobre o outro cause um impacto significativo no seu desempenho.

A melhor maneira de otimizar sua aplicação é através do perfil do código real. Isso identificará os methods que respondem pela maior parte do tempo / trabalho. Otimize os primeiros. Se o desempenho ainda não for aceitável, repita o procedimento.

Como regra geral, eu recomendaria evitar as micro otimizações, pois elas raramente renderiam ganhos significativos. A única exceção é ao otimizar caminhos ativos identificados (ou seja, se o seu perfil identifica alguns methods altamente usados, pode fazer sentido otimizá-los extensivamente).

Os dois vão correr quase exatamente da mesma maneira. Escreva algum código para usar ambos, depois mostre a ele o IL. Ele deve mostrar cálculos comparáveis, ou seja, sem diferença no desempenho.

Eu encontrei o loop foreach que iterar através de uma List mais rapidamente . Veja os resultados do meu teste abaixo. No código abaixo, eu faço uma iteração de uma array de tamanho 100, 10000 e 100000 separadamente usando for e foreach loop para medir o tempo.

insira a descrição da imagem aqui

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

ATUALIZADA

Após a sugestão @jgauffin, usei o código @johnskeet e descobri que o loop for com array é mais rápido que o seguinte,

  • Foreach loop com array.
  • Para loop com lista.
  • Foreach loop com lista.

Veja os resultados do meu teste e código abaixo,

insira a descrição da imagem aqui

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

para tem lógica mais simples de implementar, então é mais rápido que foreach.

A menos que você esteja em um processo de otimização de velocidade específico, eu diria usar o método que produzir o mais fácil de ler e manter o código.

Se um iterador já estiver configurado, como em uma das classs de coleção, o foreach é uma boa opção fácil. E se é um intervalo inteiro que você está interagindo, então provavelmente é mais limpo.

Jeffrey Richter falou sobre a diferença de desempenho entre foreach e foreach em um podcast recente: http://pixel8.infragistics.com/shows/everything.aspx#Episode:9317

Na maioria dos casos, não há diferença.

Normalmente, você sempre precisa usar foreach quando não tiver um índice numérico explícito, e sempre terá que usá-lo quando não tiver uma coleção iterável (por exemplo, iterar sobre uma grade de matriz bidimensional em um triângulo superior) . Existem alguns casos em que você tem uma escolha.

Pode-se argumentar que os loops podem ser um pouco mais difíceis de manter se números mágicos começarem a aparecer no código. Você deve estar certo de ficar aborrecido por não poder usar um loop for e ter que construir uma coleção ou usar um lambda para construir uma subcoleção, apenas porque os loops foram banidos.

Realmente fode com a cabeça e vai para um fechamento IQueryable .foreach em vez disso:

myList.ForEach (c => Console.WriteLine (c.ToString ());

RI MUITO

Eu não esperaria que alguém encontrasse uma diferença de desempenho “enorme” entre os dois.

Eu acho que a resposta depende se a coleção que você está tentando acessar tem uma implementação de access de indexador mais rápido ou uma implementação de access de IEnumerator mais rápida. Como o IEnumerator geralmente usa o indexador e apenas mantém uma cópia da posição do índice atual, eu esperaria que o access do enumerador fosse pelo menos tão lento ou mais lento do que o access direto ao índice, mas não muito.

É claro que esta resposta não leva em conta quaisquer otimizações que o compilador possa 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.