fastest - foreach c# slow




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

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 foreachmuito tempo atrás eu assumi que era verdade para todas as coleções, coleções 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 aplicações em que a última milha de otimização de desempenho comprimida é importante.


"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.


É 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 coleções podem ter diferentes características de desempenho.


A menos que você esteja em um processo específico de otimização de velocidade, 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 classes de coleção, então o foreach é uma boa opção fácil. E se é um intervalo inteiro que você está interagindo, então provavelmente é mais limpo.


As diferenças de velocidade em um loop foreach e foreach são pequenas quando você percorre 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, escolha 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<int> 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++)

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 um array de tamanho 100, 10000 e 100000 separadamente usando for e foreach loop para medir o tempo.

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

Depois da 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,

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

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 recursão . Isso ensinaria a ele, e também é um bom exercício mental para você.


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

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


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 - ele 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 vinculada, o MoveNext é barato, mas o indexador pode ter o custo O (N), tornando o custo real de um loop "for" O (N * N).



Jeffrey Richter no TechEd 2005:

"Eu vim a aprender ao longo dos anos que 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 elenco der o nome do método Dispose, dentro do loop ele chama a propriedade Current e o método MoveNext repetidamente dentro do loop, objetos estão sendo criados embaixo das 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 em vez de uma coleção usando quadrado notação de colchetes, apenas fazendo indexação, isso é muito mais rápido, e não cria objetos no heap ... "

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


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

Tipicamente você sempre tem que usar foreach quando você não tem um índice numérico explícito, e você sempre tem que usar quando você não possui uma coleção iterável (por exemplo, iterando sobre uma grade de array 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 em 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.


Nos casos em que você trabalha com uma coleção de objetos, 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 .


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 baratos do que foreach loops on List.
  • Loop on array é cerca de 2 vezes mais barato que looping em 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).

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<double> 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. Tendo dito isso, eu ainda preferiria quase sempre que fosse para um loop for, o que torna o código mais simples - porque a legibilidade é quase sempre importante, enquanto a micro-otimização raramente é.


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 métodos 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 métodos altamente usados, pode fazer sentido otimizá-los extensivamente).


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 lista vinculada pode ser terrível pelo indexador, mas pode ser rápida pelo foreach . Na verdade, o padrão LinkedList<T> nem oferece um indexador por esse motivo.


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


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

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

por exemplo - Array Iteration com um loop foreach:

e - listar iteração com loop foreach:

e os resultados finais:


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 acesso de indexador mais rápido ou uma implementação de acesso 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 acesso do enumerador fosse pelo menos tão lento ou mais lento do que o acesso direto ao índice, mas não muito.

É claro que esta resposta não leva em conta quaisquer otimizações que o compilador possa implementar.


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

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

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





for-loop