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

345

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

Desde que li que um forloop funciona mais rápido do que um foreachloop há muito tempo, presumi que fosse verdade para todas as coleções, coleções genéricas, todas as matrizes etc.

Vasculhei o Google e encontrei alguns artigos, mas a maioria deles é inconclusiva (leia os comentários nos artigos) e é aberta.

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

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

  1. para iterar uma matriz de mais de 1000 strings - foré melhor queforeach
  2. para iterar sobre IListcadeias (não genéricas) - foreaché melhor quefor

Algumas referências encontradas na web para o mesmo:

  1. Artigo grandioso original de Emmanuel Schanzer
  2. CodeProject FOREACH vs. PARA
  3. Blog - Para foreachou não foreach, eis a questão
  4. Fórum ASP.NET - NET 1.1 C # forvsforeach

[Editar]

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

Binoj Antony
fonte
3
A diferença ainda existe. As matrizes em particular devem ser tão rápidas no foreach, mas, para todo o resto, os loops simples são mais rápidos. É claro que, na maioria das vezes, isso não fará diferença e, é claro, um compilador JIT inteligente poderia, em teoria, eliminar a diferença.
jalf
3
Sem contexto, não sei exatamente o que você está fazendo, mas o que acontece quando você se depara com uma matriz parcialmente preenchida?
22610 Chris Cudmore
6
A propósito, 2 milhões de acessos / mês não é nada assustador. É menos do que um acerto por segundo, em média.
Mehrdad Afshari
37
Nota importante : Essa pergunta foi mesclada ontem com uma questão totalmente independente sobre ser forçado a usar em foreachvez de forem C #. Se você vê aqui respostas que não fazem nenhum sentido, é por isso. Culpe o moderador, não as respostas infelizes.
TED
7
@TED Oh eu queria saber onde todo o "seu chefe é um idiota", comenta onde provenientes de, graças
Gaspa79

Respostas:

350

Patrick Smacchia escreveu um blog sobre este mês passado, com as seguintes conclusões:

  • para loops na lista são um pouco mais de duas vezes mais baratos que os loops foreach na lista.
  • Fazer um loop na matriz é cerca de duas vezes mais barato do que fazer um loop na lista.
  • Como conseqüência, o loop no array usando for é 5 vezes mais barato do que o loop no List usando foreach (que eu acredito que é o que todos nós fazemos).
Ian Nelson
fonte
130
No entanto, nunca se esqueça: "A otimização prematura é a raiz de todo mal".
Oorang
18
@Hardwareguy: Depois de saber que é quase imperceptivelmente mais rápido, por que você não deveria começar a usá-lo em geral? Não leva tempo extra.
DevinB
47
@devinb, usar "for" é mais difícil do que usar "foreach", pois adiciona código, outra variável, uma condição que você precisa verificar etc. Quantas vezes você já viu um erro de um por um no loop "foreach" ?
#
35
@ Hardwareguy, deixe-me ver se entendi direito. Leva 5 vezes mais tempo para percorrer uma lista foreachdo que para percorrer uma matriz com for, e você está chamando isso de insignificante? Esse tipo de diferença de desempenho pode importar para o seu aplicativo, e pode não, mas eu não descartaria isso imediatamente.
23411 Robert Harvey
44
Lendo as postagens do blog, parece que os testes foram executados no Debug e não no Release, o que pode ter um fator. Além disso, a diferença é específica apenas para a sobrecarga do loop. Isso não afeta o tempo para executar o corpo do loop, o que geralmente ocorre muito mais do que o tempo necessário para passar para o próximo elemento da lista. É uma boa informação saber quando você identificou que há claramente um problema e mediu a diferença no seu aplicativo especificamente e há uma melhoria notável, mas definitivamente não há conselhos gerais para remover todos os foreachs.
Davy8
164

Primeiro, uma reconvenção à resposta de Dmitry (agora excluída) . Para matrizes, o compilador C # emite praticamente o mesmo código foreachque seria para um forloop equivalente . Isso explica por que, para esse 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 se o argumento de Greg sobre o tipo de coleção é importante - altere a matriz para a List<double>no exemplo acima e você obtém resultados radicalmente diferentes. Não é apenas significativamente mais lento em geral, mas o foreach se torna significativamente mais lento do que o acesso por índice. Dito isto, eu quase sempre preferiria foreach a um loop for, onde ele torna o código mais simples - porque a legibilidade é quase sempre importante, enquanto a micro-otimização raramente é.

Jon Skeet
fonte
"mudar a matriz para um List <double> no exemplo acima, e você obter resultados radicalmente diferentes" Muito interessante, eu não tinha pensado nisso
johnc
5
Dadas as estranhas diferenças de resultados entre os meus testes e benchmarks de outras pessoas, eu acho que isso vai merecer um post ...
Jon Skeet
11
Qual você quase sempre prefere entre matrizes e List<T>? A legibilidade também supera a micro-otimização nesse caso?
JohnB 27/05
12
@ JohnB: Sim - quase sempre prefiro List<T>arrays. As exceções são char[]e byte[]são tratadas com mais frequência como "pedaços" de dados, em vez de coleções normais.
Jon Skeet
Surpreendente, estou obtendo uma diferença ainda mais agressiva na minha máquina, quase 10% a favor do foreach em matrizes simples. Estou supondo amplamente que tudo isso deriva da instabilidade, não precisa se preocupar com a variável extra, verificações vinculadas, etc., seria muito interessante se alguém tivesse uma explicação profunda disso.
Tamir Daniely
162

foreachloops demonstram intenção mais específica do que forloops .

O uso de um foreachloop demonstra para qualquer pessoa que use seu código que você está planejando fazer algo para cada membro de uma coleção, independentemente do seu lugar na coleção. Também mostra que você não está modificando a coleção original (e lança uma exceção, se tentar).

A outra vantagem foreachdisso é que ele funciona em qualquer um IEnumerable, onde forapenas faz sentido IList, onde cada elemento realmente possui um índice.

No entanto, se você precisar usar o índice de um elemento, é claro que poderá usar um forloop. Mas se você não precisar usar um índice, ter um está apenas sobrecarregando seu código.

Não há implicações significativas de desempenho, até onde eu saiba. Em algum momento no futuro, pode ser mais fácil adaptar o código foreachpara rodar em vários núcleos, mas isso não é algo para se preocupar no momento.

Ctford
fonte
23
ctford: Não, não é. O compilador certamente não pode reorganizar elementos no foreach. foreachnão está relacionado à programação funcional. É totalmente um paradigma imperativo de programação. Você está atribuindo mal coisas acontecendo no TPL e no PLINQ foreach.
Mehrdad Afshari
13
@BlueTrin: Certamente garante o pedido (a seção 8.8.4 das especificações do C # define formalmente foreachcomo o equivalente a um whileloop). Eu acho que sei que @ctford está se referindo. A biblioteca paralela de tarefas permite que a coleção subjacente forneça elementos em uma ordem arbitrária (chamando .AsParallelum enumerável). foreachnão faz nada aqui e o corpo do loop é executado em um único thread . A única coisa que é paralelizada é a geração da sequência.
Mehrdad Afshari
6
Enumerable.Select tem uma sobrecarga que permite obter o índice do item, portanto, mesmo a necessidade de um índice não exige o uso de for. Consulte msdn.microsoft.com/en-us/library/bb534869.aspx
TrueWill
5
O ForEach é útil para facilitar a leitura e salvar a digitação. Os custos são importantes; alterar 2 ou 3 for-loops em uma interface do usuário do designer de documentos que eu criei de (para cada obj na lista) para (para i = 0 para list.count-1) reduziu o tempo de resposta de 2-3 segundos por edição para aproximadamente. 5 segundos por edição em um documento pequeno, percorrendo algumas centenas de objetos. Agora, mesmo para grandes documentos, não há aumento no tempo de repetir tudo. Não tenho ideia de como isso aconteceu. O que eu sei é que a alternativa era um esquema complicado para fazer um loop apenas de um subconjunto dos objetos. Vou fazer a troca de 5 minutos a qualquer dia! - não micro-otimização.
FastAl
5
@FastAl A diferença entre foreache o fordesempenho das listas normais é de frações de segundo para iterar milhões de itens. Portanto, seu problema certamente não estava diretamente relacionado ao desempenho de cada um, pelo menos não para algumas centenas de objetos. Soa como uma implementação de enumerador quebrada em qualquer lista que você estava usando.
Mike Marynowski
53

Sempre que houver argumentos sobre desempenho, basta escrever um pequeno teste para poder usar resultados quantitativos para apoiar seu caso.

Use a classe StopWatch e repita algo alguns milhões de vezes, para maior 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 disso e mostram que a diferença é insignificante, e você também pode fazer o que resultar no código mais sustentável

Rob Fonseca-Ensor
fonte
13
Mas você não pode comparar o desempenho de for e foreach. Eles devem ser usados ​​em diferentes circunstâncias.
Michael Krelin - hacker
7
Concordo com você, Michael, você não deve escolher qual usar com base no desempenho - você deve escolher o que faz mais sentido! Mas se o seu chefe diz "Não utilizar para porque é mais lento do que foreach", então esta é a única maneira de convencê-lo de que a diferença é insignificante
Rob Fonseca-Ensor
"(Isso pode ser difícil sem um loop for)" Ou você pode usar um loop while.
jonescb
49

Sempre estará perto. Para uma matriz, às vezes for é um pouco mais rápido, mas foreaché mais expressivo, oferece LINQ etc. etc. Em geral, atenha-se a foreach.

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

Marc Gravell
fonte
Então você LinkedList<T>está dizendo que é mais magro do que List<T>? E se eu sempre vou usar foreach(em vez de for), é melhor usar LinkedList<T>?
JohnB
2
@ JohnB - não mais magro; apenas diferente. Por exemplo, cada nó em uma lista vinculada tem referências adicionais que não são necessárias para uma matriz plana (que também sustenta List<T>). É mais que mais barato inserir / remover .
Marc Gravell
36

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

Brian Rasmussen
fonte
6
@klew, se você criar um perfil do seu código, não precisará adivinhar quais 20% precisam ser o mais rápido possível. Você provavelmente descobriria também que o número real de loops que precisam ser rápidos é muito menor. Além disso, você está realmente dizendo que o ato de fazer um loop é onde você gasta seu tempo, em oposição ao que você realmente faz nesse loop?
#
32

É improvável que haja uma enorme diferença de desempenho entre os dois. Como sempre, quando confrontado 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 cronometre os dois e veja qual é a diferença de velocidade. Faça isso com um corpo quase vazio e um corpo em loop semelhante ao que você realmente estará fazendo. Tente também com o tipo de coleção que você está usando, porque diferentes tipos de coleções podem ter características de desempenho diferentes.

Greg Hewgill
fonte
32

Existem boas razões para preferir foreach loops a forloops. Se você pode usar um foreachloop, seu chefe está certo que deveria.

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

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

TED
fonte
Como a recursão se compara aos forloops e foreachloops em termos de desempenho?
JohnB 27/05
Depende. Se você usa recursão de cauda e seu compilador é inteligente o suficiente para perceber, ele pode ser idêntico. OTOH: Se isso não acontecer e você fizer algo estúpido, como passar muitos dados não essenciais (imutáveis) como parâmetros ou declarar grandes estruturas na pilha como locais, pode ser realmente muito lento (ou até ficar sem memória RAM).
TED
Ahhh. Entendo por que você perguntou isso agora. Esta resposta foi para uma pergunta totalmente diferente. Por algum motivo bizarro, Jonathan Sampson fundiu os dois ontem. Ele realmente não deveria ter feito isso. As respostas mescladas não farão nenhum sentido aqui.
TED
18

Jeffrey Richter no TechEd 2005:

"Aprendi ao longo dos anos que o compilador C # é basicamente um mentiroso para mim." .. "Encontra-se sobre muitas coisas." .. "Como quando você faz um loop foreach ..." .. "... essa é uma pequena linha de código que você escreve, mas o que o compilador C # divulga para fazer isso é fenomenal. tente / finalmente bloqueie lá dentro, dentro do bloco finalmente, lança sua variável para uma interface IDisposable e, se o elenco for bem-sucedido, chama o método Dispose, dentro do loop chama a propriedade Current e o método MoveNext repetidamente dentro do loop, objetos estão sendo criados por baixo das capas. Muitas pessoas usam o foreach porque é muito fácil de codificar, muito fácil de fazer .. ".." foreach não é muito bom em termos de desempenho,

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

Max Toro
fonte
12

Isto é ridículo. Não há motivo convincente para proibir o loop for, em termos de desempenho ou outro.

Consulte o blog de Jon Skeet para obter uma referência de desempenho e outros argumentos.

Rik
fonte
2
Link atualizado: codeblog.jonskeet.uk/2009/01/29/…
Matt
A construção de loop mais rápida depende do que você precisa repetir. Outro blog que avalia várias iterações em vários tipos de objetos , como DataRows e objetos personalizados. Ele também inclui o desempenho da construção de loop While e não apenas as construções de loop for e foreach.
Free Coder 24 de
11

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

Observe que, no último caso, você pode fazer algo como:

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

Mas certamente não tem um desempenho melhor, na verdade, tem desempenho pior comparado a um for.

Meta-Cavaleiro
fonte
"Melhor" é discutível: é mais lento e o depurador dnspy ​​não entra em um foreach C # (embora o depurador VS2017 o faça). Às vezes, mais legível, mas se você suporta idiomas sem ele, pode ser irritante.
Zeek2
10

Isso deve te salvar:

public IEnumerator<int> 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 uma vitória maior, você pode levar três delegados como parâmetros.

akuhn
fonte
11
Uma pequena diferença é que um forloop geralmente é escrito para excluir o final do intervalo (por exemplo 0 <= i < 10). Parallel.Fortambém faz isso para mantê-lo facilmente intercambiável com um forloop comum .
Groo 24/07
9

você pode ler sobre isso no Deep .NET - parte 1 Iteração

abrange os resultados (sem a primeira inicialização) do código-fonte .NET até a desmontagem.

por exemplo - Iteração de matriz com um loop foreach: insira a descrição da imagem aqui

e - liste a iteração com o loop foreach: insira a descrição da imagem aqui

e os resultados finais: insira a descrição da imagem aqui

insira a descrição da imagem aqui

Ou Yaacov
fonte
8

As diferenças de velocidade em um loop for- e um foreachloop são pequenas quando você percorre estruturas comuns como matrizes, listas, etc.LINQ consulta sobre a coleção é quase sempre um pouco mais lento, embora seja mais agradável para escrever! Como os outros pôsteres disseram, busque expressividade em vez de um milissegundo de desempenho extra.

O que não foi dito até agora é que, quando um foreachloop é compilado, ele é otimizado pelo compilador com base na coleção em que ele está interagindo. Isso significa que, quando você não tiver certeza de qual loop usar, deverá usar oforeach -lo - ele gerará o melhor loop para você quando for compilado. É mais legível também.

Outra vantagem importante do foreachloop é que, se a implementação da sua coleção mudar (de um int arraypara um List<int>por exemplo), seu foreachloop não exigirá nenhuma alteração no código:

foreach (int i in myCollection)

O acima é o mesmo, independentemente do tipo de sua coleção, enquanto no seu forloop, o seguinte não será gerado se você alterar myCollectionde um arraypara um List:

for (int i = 0; i < myCollection.Length, i++)
Alex York
fonte
7

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

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

Ken
fonte
7

Provavelmente depende do tipo de coleção que você está enumerando e da implementação de seu indexador. Em geral, porém, o uso foreachprovavelmente será uma abordagem melhor.

Além disso, funcionará com qualquer um IEnumerable- e não apenas com indexadores.

Andrew Kennan
fonte
7

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 quanto o método "MoveNext ()" é caro, em relação à forma como o método "this [int index]" é caro, para o tipo (ou tipos) de IEnumerable que você estará repetindo.

A palavra-chave "foreach" é uma abreviação de uma série de operações - 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 afetar as medições de desempenho é o custo de MoveNext (), uma vez que isso é invocado O (N) vezes. Talvez seja barato, mas talvez não seja.

A palavra-chave "for" parece mais previsível, mas na maioria dos loops "for" você encontrará algo como "coleção [índice]". Parece uma operação simples de indexação de matriz, mas na verdade é uma chamada de método, cujo custo depende inteiramente da natureza da coleção que você está repetindo. Provavelmente é barato, mas talvez não seja.

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

NSFW
fonte
6

Cada construção de idioma tem um horário e local apropriados para uso. Há uma razão pela qual a linguagem C # possui quatro instruções de iteração separadas - cada uma existe para uma finalidade específica e tem um uso apropriado.

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

Eu também apontaria para o seu chefe - o desempenho não é e não deve ser um problema de maneira prática - é mais uma questão de expressar o algoritmo de maneira sucinta, significativa e sustentável. Micro-otimizações como essa perdem completamente o ponto de otimização de desempenho, pois qualquer benefício real de desempenho virá do redesenho e refatoração algorítmica, e não da reestruturação em loop.

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

Reed Copsey
fonte
5

É o que você faz dentro do loop que afeta o desempenho, não a construção real do loop (supondo que seu caso não seja trivial).

Martin Wickman
fonte
5

Se foré mais rápido do que foreachrealmente está além do ponto. Eu duvido seriamente que escolher um sobre o outro tenha um impacto significativo no seu desempenho.

A melhor maneira de otimizar seu aplicativo é através da criação de perfil do código real. Isso identificará os métodos que representam mais trabalho / tempo. Otimize aqueles primeiro. Se o desempenho ainda não for aceitável, repita o procedimento.

Como regra geral, eu recomendaria ficar longe das micro otimizações, pois elas raramente geram ganhos significativos. A única exceção é ao otimizar os caminhos ativos identificados (ou seja, se sua criação de perfil identificar alguns métodos altamente usados, pode fazer sentido otimizá-los extensivamente).

Brian Rasmussen
fonte
Se o único tipo de otimização que eu precisava fazer nos projetos em que trabalhava fosse micro otimizações, eu seria um feliz campista. Infelizmente, isso nunca é o caso.
Yannick Motton
2
foré marginalmente mais rápido que foreach. Eu seriamente contra essa afirmação. Isso depende totalmente da coleção subjacente. Se uma classe de lista vinculada fornecer um indexador com um parâmetro inteiro, eu esperaria que o uso de um forloop nele fosse O (n ^ 2) enquanto o foreachesperado fosse O (n).
Mehrdad Afshari
@Merhdad: Na verdade, esse é um bom argumento. Eu estava pensando no caso regular de indexar uma lista (ou seja, matriz). Vou reformular para refletir isso. Obrigado.
22711 Brian Rasmussen
@Mehrdad Afshari: A indexação de uma coleção por número inteiro pode ser muito mais lenta que a enumeração. Mas você está realmente comparando o uso for e uma pesquisa do indexador ao uso foreachpor si só. Acho que a resposta de @Brian Rasmussen está correta que, além de qualquer uso em uma coleção, forsempre será um pouco mais rápida que foreach. No entanto, foralém de uma pesquisa de coleção sempre será mais lenta do que foreachpor si só.
22611 Daniel Pryden
@ Daniel: Ou você tem uma matriz simples, para a qual ambos irão gerar código idêntico, ou há um indexador envolvido quando você usa a forinstrução. O forloop simples com uma variável de controle inteiro não é comparável foreach, então está pronto. Entendo o que @Brian significa e está correto como você diz, mas a resposta pode ser enganosa. Re: seu último ponto: não, na verdade, formais List<T>ainda é mais rápido que foreach.
Mehrdad Afshari
4

Os dois serão executados quase exatamente da mesma maneira. Escreva algum código para usar os dois e mostre a ele a IL. Ele deve mostrar cálculos comparáveis, o que significa que não há diferença no desempenho.

cjk
fonte
O compilador reconhece loops foreach usados ​​em matrizes / ILists etc e os altera para loops for.
Callum Rogers
3
Mostre a ele linhas de prova ininteligível de que está tudo bem e peça sua prova de que não está bem.
Cjk
3

O for tem uma lógica mais simples de implementar, por isso é mais rápido que o foreach.

Tarik
fonte
3

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

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

GeekyMonkey
fonte
3

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

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

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

Cade Roux
fonte
3

Parece um pouco estranho proibir totalmente o uso de algo como um loop for.

Há um artigo interessante aqui que aborda muitas diferenças de desempenho entre os dois loops.

Eu diria que pessoalmente acho o foreach um pouco mais legível para loops, mas você deve usar o melhor para o trabalho em questão e não precisa escrever um código extra longo para incluir um loop foreach se um loop for for mais apropriado.

colethecoder
fonte
Citações essenciais do artigo ao qual você vincula: "... se você planeja escrever código de alto desempenho que não seja para coleções, use para loop. Mesmo para coleções, o foreach pode parecer útil ao usar, mas não é tão eficiente."
22412 NickFitz
3

Eu encontrei o foreachloop que iterava através de um List mais rápido . Veja meus resultados de teste abaixo. No código abaixo eu iterar um arrayde tamanho 100, 10000 e 100000 usando separadamente fore foreachcircuito 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 forloop com arrayé mais rápido que o seguinte,

  • Foreach loop com matriz.
  • For loop com lista.
  • Foreach loop com lista.

Veja meus resultados e código de teste 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);
    }
Diganta Kumar
fonte
3
Este é um teste muito ruim. a) você faz poucas iterações para obter uma resposta conclusiva; b) Esse Thread.Sleep não espera realmente um milissegundo. Use o mesmo método que Jon Skeet usou em sua resposta.
jgauffin
11
99.99% do tempo é definitivamente gasto no thread.sleep (o que não garante a rapidez com que ele retornará, exceto pelo menos antes desse período). O loop é muito rápido e o sono é muito lento; você não usa o posterior para testar o primeiro.
Ronan Thibaudau
3

Você pode realmente estragar a cabeça dele e optar por um fechamento .foreach IQueryable:

myList.ForEach(c => Console.WriteLine(c.ToString());
Tad Donaghe
fonte
3
Eu substituiria sua linha de código por myList.ForEach(Console.WriteLine).
Mehrdad Afshari
2

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

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

Obviamente, essa resposta não leva em conta nenhuma otimização que o compilador possa implementar.

JohannesH
fonte
O compilador C # faz muito pouca otimização, ele realmente deixa isso para o JITter.
LJS
Bem, o JITter é um compilador ... Certo?
JohannesH
2

Lembre-se de que o loop for e foreach nem sempre são equivalentes. Os enumeradores de lista lançarão uma exceção se a lista for alterada, mas você nem sempre receberá esse aviso com um loop for normal. Você pode até receber uma exceção diferente se a lista mudar no momento errado.

Craig Gidney
fonte
Se a lista estiver mudando de baixo de você, não será possível confiar no enumerador que está fazendo o loop foreach para lhe dizer isso. Não será verificado novamente depois que o valor retornar para você, resultando em uma corrida.
hoodaticus