Sempre me perguntei se, em geral, declarar uma variável descartável antes de um loop, em vez de repetidamente dentro do loop, faz alguma diferença (de desempenho)? Um exemplo (bastante inútil) em Java:
a) declaração antes do loop:
double intermediateResult;
for(int i=0; i < 1000; i++){
intermediateResult = i;
System.out.println(intermediateResult);
}
b) declaração (repetidamente) loop interno:
for(int i=0; i < 1000; i++){
double intermediateResult = i;
System.out.println(intermediateResult);
}
Qual é o melhor, a ou b ?
Suspeito que a declaração variável repetida (exemplo b ) crie mais sobrecarga na teoria , mas que os compiladores sejam inteligentes o suficiente para que isso não importe. O exemplo b tem a vantagem de ser mais compacto e limitar o escopo da variável para onde ela é usada. Ainda assim, tendo a codificar de acordo com o exemplo a .
Edit: Estou especialmente interessado no caso Java.
java
performance
loops
variables
initialization
Rabarberski
fonte
fonte
Respostas:
Qual é o melhor, a ou b ?
De uma perspectiva de desempenho, você teria que medir isso. (E na minha opinião, se você pode medir a diferença, o compilador não é muito bom).
Do ponto de vista da manutenção, b é melhor. Declare e inicialize variáveis no mesmo local, no escopo mais restrito possível. Não deixe um buraco entre a declaração e a inicialização e não polua os namespaces que você não precisa.
fonte
Bem, eu executei seus exemplos A e B 20 vezes cada, repetindo 100 milhões de vezes (JVM - 1.5.0)
A: tempo médio de execução: 0,074 s
B: tempo médio de execução: 0,067 seg
Para minha surpresa, B foi um pouco mais rápido. Agora, tão rápido quanto os computadores, é difícil dizer se você pode medir isso com precisão. Eu codificaria da maneira A também, mas diria que isso realmente não importa.
fonte
Depende do idioma e do uso exato. Por exemplo, em C # 1 não fazia diferença. No C # 2, se a variável local for capturada por um método anônimo (ou expressão lambda em C # 3), poderá fazer uma diferença muito significativa.
Exemplo:
Resultado:
A diferença é que todas as ações capturam a mesma
outer
variável, mas cada uma tem sua própriainner
variável separada .fonte
Outer
ser 9?A seguir, é o que escrevi e compilei no .NET.
É isso que recebo do .NET Reflector quando o CIL é renderizado novamente no código.
Então, ambos parecem exatamente iguais após a compilação. Em idiomas gerenciados, o código é convertido em código CL / byte e, no momento da execução, é convertido em linguagem de máquina. Portanto, na linguagem de máquina, um duplo nem pode ser criado na pilha. Pode ser apenas um registro, pois o código reflete que é uma variável temporária para a
WriteLine
função. Há todo um conjunto de regras de otimização apenas para loops. Portanto, o indivíduo comum não deve se preocupar com isso, especialmente em idiomas gerenciados. Há casos em que você pode otimizar o código de gerenciamento, por exemplo, se precisar concatenar um grande número de cadeias usando juststring a; a+=anotherstring[i]
vsStringBuilder
. Há uma diferença muito grande no desempenho entre os dois. Existem muitos casos em que o compilador não pode otimizar seu código, porque não consegue descobrir o que se pretende em um escopo maior. Mas isso pode otimizar as coisas básicas para você.fonte
Este é um problema no VB.NET. O resultado do Visual Basic não reinicializará a variável neste exemplo:
Isso imprimirá 0 na primeira vez (as variáveis do Visual Basic têm valores padrão quando declaradas!), Mas
i
cada vez depois disso.Se você adicionar um
= 0
, no entanto, obtém o que pode esperar:fonte
Eu fiz um teste simples:
vs
Eu compilei esses códigos com o gcc - 5.2.0. E então eu desmontei o main () desses dois códigos e esse é o resultado:
1º:
vs
2º
Exatamente o mesmo resultado. não é uma prova de que os dois códigos produzem a mesma coisa?
fonte
É dependente da linguagem - o IIRC C # otimiza isso, portanto não há diferença, mas o JavaScript (por exemplo) fará toda a alocação de memória mostrada a cada vez.
fonte
Eu sempre usaria A (em vez de confiar no compilador) e também poderia reescrever para:
Isso ainda restringe
intermediateResult
o escopo do loop, mas não é redefinido durante cada iteração.fonte
Na minha opinião, b é a melhor estrutura. Em a, o último valor de intermediárioResult permanece após a conclusão do loop.
Editar: Isso não faz muita diferença com os tipos de valor, mas os tipos de referência podem ser um pouco pesados. Pessoalmente, gosto que as variáveis sejam desreferenciadas o mais rápido possível para a limpeza, eb faz isso por você,
fonte
sticks around after your loop is finished
- embora isso não importe em uma linguagem como Python, onde os nomes vinculados permanecem até a função terminar.my
palavra - chave), C # e Java para o nome 5 que eu usei.Suspeito que alguns compiladores possam otimizar ambos para que sejam o mesmo código, mas certamente não todos. Então, eu diria que você está melhor com o primeiro. A única razão para o último é se você deseja garantir que a variável declarada seja usada apenas dentro do seu loop.
fonte
Como regra geral, declaro minhas variáveis no escopo mais interno possível. Portanto, se você não estiver usando o intermediárioResult fora do loop, eu usaria o B.
fonte
Um colega de trabalho prefere a primeira forma, dizendo que é uma otimização, preferindo reutilizar uma declaração.
Prefiro o segundo (e tento convencer meu colega de trabalho! ;-)), depois de ler o seguinte:
De qualquer forma, ele se enquadra na categoria de otimização prematura que depende da qualidade do compilador e / ou JVM.
fonte
Há uma diferença no C # se você estiver usando a variável em um lambda, etc. Mas, em geral, o compilador basicamente fará a mesma coisa, assumindo que a variável seja usada apenas dentro do loop.
Dado que eles são basicamente os mesmos: Observe que a versão b torna muito mais óbvio para os leitores que a variável não é e não pode ser usada após o loop. Além disso, a versão b é muito mais facilmente refatorada. É mais difícil extrair o corpo do loop em seu próprio método na versão a. Além disso, a versão b garante que não haja efeito colateral em tal refatoração.
Portanto, a versão a me irrita sem fim, porque não há benefício para ela e torna muito mais difícil argumentar sobre o código ...
fonte
Bem, você sempre pode fazer um escopo para isso:
Dessa forma, você apenas declara a variável uma vez e ela morre quando você sai do loop.
fonte
Eu sempre pensei que, se você declarar suas variáveis dentro do seu loop, estará perdendo memória. Se você tem algo parecido com isto:
Além disso, o objeto não precisa ser criado para cada iteração, mas também deve haver uma nova referência alocada para cada objeto. Parece que se o coletor de lixo estiver lento, você terá várias referências pendentes que precisam ser limpas.
No entanto, se você tiver este:
Então você está apenas criando uma única referência e atribuindo um novo objeto a cada vez. Claro, pode demorar um pouco mais para ficar fora do escopo, mas só há uma referência pendente para lidar.
fonte
Eu acho que depende do compilador e é difícil dar uma resposta geral.
fonte
Minha prática é a seguinte:
se o tipo de variável for simples (int, double, ...) , prefiro a variante b (interna).
Razão: reduzindo o escopo da variável.
se o tipo de variável não for simples (algum tipo de
class
oustruct
) , prefiro a variante a (fora).Razão: redução do número de chamadas de ctor-dtor.
fonte
Do ponto de vista do desempenho, o exterior é (muito) melhor.
Eu executei as duas funções 1 bilhão de vezes cada. outside () levou 65 milissegundos. inside () levou 1,5 segundos.
fonte
Testei para JS com o nó 4.0.0, se alguém estiver interessado. Declarar fora do loop resultou em uma melhoria de desempenho de ~ 0,5 ms, em média, mais de 1000 tentativas com 100 milhões de iterações de loop por tentativa. Então eu vou dizer: vá em frente e escreva da maneira mais legível / sustentável que é B, imo. Eu colocaria meu código em um violino, mas usei o módulo Nó com desempenho agora. Aqui está o código:
fonte
A) é uma aposta segura que B) ......... Imagine se você está inicializando a estrutura em loop em vez de 'int' ou 'float', e daí?
gostar
Você certamente enfrentará problemas com vazamentos de memória !. Portanto, acredito que 'A' é uma aposta mais segura, enquanto 'B' é vulnerável à acumulação de memória, trabalhando em bibliotecas de código-fonte próximo.
fonte
É uma pergunta interessante. Da minha experiência, há uma pergunta definitiva a ser considerada quando você debate esse assunto por um código:
Existe alguma razão para a variável precisar ser global?
Faz sentido declarar a variável apenas uma vez, globalmente, e não muitas vezes localmente, porque é melhor para organizar o código e requer menos linhas de código. No entanto, se ele apenas precisar ser declarado localmente em um método, eu o inicializaria nesse método para que fique claro que a variável é exclusivamente relevante para esse método. Cuidado para não chamar essa variável fora do método em que ela é inicializada se você escolher a última opção - seu código não saberá do que está falando e relatará um erro.
Além disso, como uma observação lateral, não duplique nomes de variáveis locais entre métodos diferentes, mesmo que seus propósitos sejam quase idênticos; isso só fica confuso.
fonte
esta é a melhor forma
1) desta maneira declarada uma vez ambas as variáveis, e não cada uma para o ciclo. 2) a tarefa é mais eficaz do que todas as outras opções. 3) Portanto, a regra da melhor prática é qualquer declaração fora da iteração.
fonte
Tentei a mesma coisa no Go e comparou a saída do compilador usando o
go tool compile -S
go 1.9.4Diferença zero, conforme a saída do montador.
fonte
Eu tive essa mesma pergunta por um longo tempo. Então eu testei um pedaço de código ainda mais simples.
Conclusão: Para esses casos , NÃO há diferença de desempenho.
Caso de loop externo
Caso interno do laço
Eu verifiquei o arquivo compilado no descompilador do IntelliJ e, nos dois casos, obtive o mesmo
Test.class
Também desmontei o código para ambos os casos, usando o método fornecido nesta resposta . Vou mostrar apenas as partes relevantes para a resposta
Caso de loop externo
Caso interno do laço
Se você prestar atenção, apenas o
Slot
designado parai
eintermediateResult
emLocalVariableTable
é trocado como um produto da sua ordem de aparecimento. A mesma diferença no slot é refletida em outras linhas de código.intermediateResult
ainda é uma variável local nos dois casos, portanto não há diferença no tempo de acesso.BÔNUS
Os compiladores fazem uma tonelada de otimização, dê uma olhada no que acontece neste caso.
Zero caso de trabalho
Zero trabalho descompilado
fonte
Mesmo que eu saiba que meu compilador é inteligente o suficiente, não gostarei de confiar nele e usarei a variante a).
A variante b) só faz sentido para mim se você precisar desesperadamente tornar o resultado intermediário indisponível após o corpo do loop. Mas não consigo imaginar uma situação tão desesperadora ...
EDIT: Jon Skeet fez um argumento muito bom, mostrando que a declaração variável dentro de um loop pode fazer uma diferença semântica real.
fonte