Estou executando o seguinte código Java em um laptop com Intel Core i7 de 2,7 GHz. Eu pretendia deixá-lo medir quanto tempo leva para terminar um loop com 2 ^ 32 iterações, que esperava ser cerca de 1,48 segundos (4 / 2,7 = 1,48).
Mas, na verdade, leva apenas 2 milissegundos, em vez de 1,48 s. Estou me perguntando se isso é resultado de alguma otimização JVM subjacente?
public static void main(String[] args)
{
long start = System.nanoTime();
for (int i = Integer.MIN_VALUE; i < Integer.MAX_VALUE; i++){
}
long finish = System.nanoTime();
long d = (finish - start) / 1000000;
System.out.println("Used " + d);
}
javap -v
para ver.javac
faz muito pouca otimização real e deixa a maior parte para o compilador JIT.Respostas:
Existem duas possibilidades acontecendo aqui:
O compilador percebeu que o loop é redundante e não fez nada para otimizá-lo.
O JIT (compilador just-in-time) percebeu que o loop é redundante e não fazia nada, então o otimizou.
Os compiladores modernos são muito inteligentes; eles podem ver quando o código é inútil. Tente colocar um loop vazio em GodBolt e olhar para a saída, em seguida, ative as
-O2
otimizações, você verá que a saída é algo na linha deGostaria de esclarecer algo, em Java a maioria das otimizações são feitas pelo JIT. Em algumas outras linguagens (como C / C ++), a maioria das otimizações é feita pelo primeiro compilador.
fonte
Parece que foi otimizado pelo compilador JIT. Quando eu o desligo (
-Djava.compiler=NONE
), o código fica muito mais lento:Eu coloquei o código do OP dentro de
class MyClass
.fonte
Vou apenas declarar o óbvio - que se trata de uma otimização JVM que acontece, o loop simplesmente será removido. Aqui está um pequeno teste que mostra a grande diferença
JIT
quando ativado / ativado apenas paraC1 Compiler
e desativado em tudo.Isenção de responsabilidade: não escreva testes como este - isso é apenas para provar que a "remoção" do loop real acontece no
C2 Compiler
:Os resultados mostram que dependendo de qual parte do
JIT
está habilitado, o método fica mais rápido (muito mais rápido que parece que está fazendo "nada" - remoção de loop, o que parece estar acontecendo noC2 Compiler
- que é o nível máximo):fonte
Conforme já apontado, o compilador JIT (just-in-time) pode otimizar um loop vazio para remover iterações desnecessárias. Mas como?
Na verdade, existem dois compiladores JIT: C1 e C2 . Primeiro, o código é compilado com o C1. C1 coleta as estatísticas e ajuda a JVM a descobrir que, em 100% dos casos, nosso loop vazio não muda nada e é inútil. Nesta situação C2 entra em cena. Quando o código é chamado com muita frequência, ele pode ser otimizado e compilado com o C2 usando as estatísticas coletadas.
Como exemplo, testarei o próximo snippet de código (meu JDK está configurado para slowdebug build 9-internal ):
Com as seguintes opções de linha de comando:
E existem diferentes versões do meu método de execução , compilado com o C1 e C2 apropriadamente. Para mim, a variante final (C2) é mais ou menos assim:
É um pouco confuso, mas se você olhar de perto, você notará que não há um loop de longa duração aqui. Existem 3 blocos: B1, B2 e B3 e as etapas de execução podem ser
B1 -> B2 -> B3
ouB1 -> B3
. OndeFreq: 1
- frequência estimada normalizada de execução de um bloco.fonte
Você está medindo o tempo que leva para detectar que o loop não faz nada, compila o código em um thread de segundo plano e elimina o código.
Se você executar isso com,
-XX:+PrintCompilation
poderá ver que o código foi compilado em segundo plano para o compilador de nível 3 ou C1 e depois de alguns loops para o nível 4 de C4.Se você alterar o loop para usar um,
long
ele não ficará tão otimizado.em vez disso você consegue
fonte
long
contador impediria a mesma otimização de acontecer?int
nota de tipo char e short forem efetivamente iguais no nível de código de bytes.Você considera o tempo de início e término em nanossegundos e divide por 10 ^ 6 para calcular a latência
deve ser
10^9
porque1
segundo =10^9
nanossegundo.fonte