Qual trecho de código terá melhor desempenho? Os segmentos de código abaixo foram escritos em C #.
1
for(int counter=0; counter<list.Count; counter++)
{
list[counter].DoSomething();
}
2
foreach(MyType current in list)
{
current.DoSomething();
}
c#
performance
for-loop
foreach
Kthevar
fonte
fonte
list
realmente tenha umcount
membro em vez deCount
.Respostas:
Bem, isso depende parcialmente do tipo exato de
list
. Também dependerá do CLR exato que você está usando.Se é de alguma forma significativo ou não, dependerá se você está fazendo algum trabalho real no loop. Em quase todos os casos, a diferença no desempenho não será significativa, mas a diferença na legibilidade favorece o
foreach
loop.Eu pessoalmente usaria o LINQ para evitar o "se" também:
EDIT: Para aqueles de vocês que afirmam que iterar em um
List<T>
comforeach
produz o mesmo código que ofor
loop, aqui está a evidência de que não:Produz IL de:
O compilador trata os arrays de maneira diferente, convertendo um
foreach
loop basicamente em umfor
loop, mas nãoList<T>
. Este é o código equivalente para uma matriz:Curiosamente, não consigo encontrar isso documentado na especificação C # 3 em nenhum lugar ...
fonte
List<T>
.foreach
sobre uma matriz é equivalente afor
qualquer maneira. Sempre codifique primeiro para legibilidade e, em seguida, somente micro-otimize quando tiver evidências de que isso oferece um benefício de desempenho mensurável.Um
for
loop é compilado para um código aproximadamente equivalente a este:Onde como um
foreach
loop é compilado para um código aproximadamente equivalente a este:Como você pode ver, tudo dependeria de como o enumerador é implementado versus como o indexador de listas é implementado. Acontece que o enumerador para tipos baseados em matrizes normalmente é escrito da seguinte forma:
Como você pode ver, neste caso, não fará muita diferença, no entanto, o enumerador de uma lista vinculada provavelmente seria algo assim:
No .NET, você descobrirá que a classe LinkedList <T> nem mesmo tem um indexador, portanto, você não seria capaz de fazer seu loop for em uma lista vinculada; mas se você pudesse, o indexador teria que ser escrito assim:
Como você pode ver, chamar isso várias vezes em um loop será muito mais lento do que usar um enumerador que se lembre de onde ele está na lista.
fonte
Um teste fácil de semivalidar. Fiz um pequeno teste, só para ver. Aqui está o código:
E aqui está a seção foreach:
Quando substituí o for por um foreach - o foreach foi 20 milissegundos mais rápido - de forma consistente . O for foi de 135-139ms, enquanto o foreach foi de 113-119ms. Troquei de um lado para o outro várias vezes, certificando-me de que não era algum processo que apenas começou.
No entanto, quando removi o foo e a instrução if, o for foi 30 ms mais rápido (foreach foi de 88 ms e for foi de 59 ms). Ambos eram conchas vazias. Estou assumindo que o foreach realmente passou uma variável onde o for estava apenas incrementando uma variável. Se eu adicionar
Em seguida, a velocidade de for ficou lenta em cerca de 30ms. Estou assumindo que isso teve a ver com a criação de foo e pegando a variável na matriz e atribuindo-a a foo. Se você acessar apenas intList [i], você não terá essa penalidade.
Com toda a honestidade ... Eu esperava que o foreach fosse um pouco mais lento em todas as circunstâncias, mas não o suficiente para importar na maioria das aplicações.
editar: aqui está o novo código usando as sugestões de Jons (134217728 é o maior int que você pode ter antes que a exceção System.OutOfMemory seja lançada):
E aqui estão os resultados:
Gerando dados. Calculando o loop: 2458ms Calculando o loop foreach: 2005ms
Trocá-los para ver se lida com a ordem das coisas produz os mesmos resultados (quase).
fonte
Observação: essa resposta se aplica mais ao Java do que ao C #, já que o C # não tem um indexador ativado
LinkedLists
, mas acho que o ponto geral ainda é válido.Se o com o qual
list
você está trabalhando for umLinkedList
, o desempenho do código do indexador ( acesso de estilo de matriz ) é muito pior do que usar oIEnumerator
deforeach
, para listas grandes.Quando você acessa o elemento 10.000 em a
LinkedList
usando a sintaxe do indexador :,list[10000]
a lista encadeada começará no nó principal e percorrerá oNext
-pointer dez mil vezes, até atingir o objeto correto. Obviamente, se você fizer isso em um loop, obterá:Quando você chama
GetEnumerator
(usando implicitamente aforach
sintaxe -sint), você obterá umIEnumerator
objeto que tem um ponteiro para o nó principal. Cada vez que você chamaMoveNext
, esse ponteiro é movido para o próximo nó, assim:Como você pode ver, no caso de
LinkedList
s, o método do indexador de array se torna cada vez mais lento, quanto mais tempo você faz o loop (ele tem que passar pelo mesmo ponteiro de cabeça repetidamente). Já oIEnumerable
justo opera em tempo constante.Claro, como Jon disse, isso realmente depende do tipo de
list
, selist
não for umLinkedList
, mas um array, o comportamento é completamente diferente.fonte
LinkedList<T>
documentos no MSDN e tem uma API bastante decente. Mais importante ainda, não tem umget(int index)
método, como o Java. Ainda assim, acho que o ponto ainda é válido para qualquer outra estrutura de dados do tipo lista que exponha um indexador mais lento do que um específicoIEnumerator
.Como outras pessoas mencionaram, embora o desempenho não importe muito, o foreach sempre será um pouco mais lento por causa do uso de
IEnumerable
/IEnumerator
no loop. O compilador traduz a construção em chamadas nessa interface e, para cada etapa, uma função + uma propriedade é chamada na construção foreach.Esta é a expansão equivalente da construção em C #. Você pode imaginar como o impacto no desempenho pode variar com base nas implementações de MoveNext e Current. Considerando que em um acesso de array, você não tem essas dependências.
fonte
List<T>
aqui, então ainda há o hit (possivelmente inline) de chamar o indexador. Não é como se fosse um acesso de array bare metal.Depois de ler argumentos suficientes para que "o loop foreach seja preferido para facilitar a leitura", posso dizer que minha primeira reação foi "o quê"? A legibilidade, em geral, é subjetiva e, neste caso particular, ainda mais. Para alguém com experiência em programação (praticamente todas as linguagens anteriores ao Java), os loops for são muito mais fáceis de ler do que os loops foreach. Além disso, as mesmas pessoas que afirmam que os loops foreach são mais legíveis também são defensores do linq e de outros "recursos" que tornam o código difícil de ler e manter, algo que prova o ponto acima.
Sobre o impacto no desempenho, veja a resposta a esta pergunta.
EDIT: Existem coleções em C # (como o HashSet) que não possuem indexador. Nessas coleções, foreach é a única forma de interagir e é o único caso que eu acho que deveria ser utilizado ao longo de .
fonte
Há um outro fato interessante que pode ser facilmente esquecido ao testar a velocidade de ambos os loops: usar o modo de depuração não permite que o compilador otimize o código usando as configurações padrão.
Isso me levou ao resultado interessante de que foreach é mais rápido do que no modo de depuração. Enquanto o for é mais rápido do que o foreach no modo de liberação. Obviamente, o compilador tem maneiras melhores de otimizar um loop for do que um loop foreach, que compromete várias chamadas de método. Um loop for é, aliás, tão fundamental que é possível que seja até otimizado pela própria CPU.
fonte
No exemplo que você forneceu, é definitivamente melhor usar
foreach
loop em vez defor
loop.A
foreach
construção padrão pode ser mais rápida (1,5 ciclos por etapa) do que uma simplesfor-loop
(2 ciclos por etapa), a menos que o loop tenha sido desenrolado (1,0 ciclo por etapa).Assim, por código de todos os dias, o desempenho não é uma razão para usar as mais complexas
for
,while
oudo-while
construções.Confira este link: http://www.codeproject.com/Articles/146797/Fast-and-Less-Fast-Loops-in-C
fonte
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 - Iteração de matriz com um loop foreach:
e - iteração de lista com loop foreach:
e os resultados finais:
fonte