Como faço para escrever um micro-benchmark correto em Java?




jvm benchmarking (8)

Como você escreve (e executa) uma micro-referência correta em Java?

Eu estou olhando aqui para exemplos de código e comentários ilustrando várias coisas para pensar.

Exemplo: o benchmark deve medir o tempo / iteração ou iterações / tempo e por quê?

Relacionado: O benchmarking do cronômetro é aceitável?


O benchmark deve medir o tempo / iteração ou iterações / tempo e por quê?

Depende do que você está tentando testar. Se você estiver interessado em latência, use time / iteration e, se estiver interessado em throughput, use iterações / tempo.


Certifique-se de usar de alguma forma os resultados que são computados no código de referência. Caso contrário, seu código pode ser otimizado.


Dicas sobre como escrever micro benchmarks dos criadores do Java HotSpot :

Regra 0: leia um artigo respeitável sobre JVMs e micro-benchmarking. Uma boa é Brian Goetz, 2005 . Não espere muito de micro-benchmarks; eles medem apenas um intervalo limitado de características de desempenho da JVM.

Regra 1: Sempre inclua uma fase de aquecimento que executa todo o seu kernel de teste, o suficiente para acionar todas as inicializações e compilações antes da (s) fase (s) de tempo. (Poucas iterações são aceitáveis ​​na fase de aquecimento. A regra prática é várias dezenas de milhares de iterações de loop interno.)

Regra 2: Sempre execute com -XX:+PrintCompilation , -verbose:gc , etc., para que você possa verificar se o compilador e outras partes da JVM não estão fazendo um trabalho inesperado durante sua fase de sincronização.

Regra 2.1: Imprima mensagens no início e no final das fases de tempo e aquecimento, para que você possa verificar se não há saída da Regra 2 durante a fase de tempo.

Regra 3: Esteja ciente da diferença entre -client e -server, e OSR e compilações regulares. O sinalizador -XX:+PrintCompilation reporta compilações OSR com um sinal de arroba para indicar o ponto de entrada não inicial, por exemplo: Trouble$1::run @ 2 (41 bytes) . Prefere servidor para cliente e regular para OSR, se você estiver após o melhor desempenho.

Regra 4: Esteja ciente dos efeitos de inicialização. Não imprima pela primeira vez durante a fase de tempo, já que a impressão carrega e inicializa as classes. Não carregue novas classes fora da fase de aquecimento (ou fase final de relatório), a menos que você esteja testando o carregamento da classe especificamente (e, nesse caso, carregue apenas as classes de teste). A regra 2 é a sua primeira linha de defesa contra esses efeitos.

Regra 5: Esteja ciente dos efeitos de desotimização e recompilação. Não tome nenhum caminho de código pela primeira vez na fase de tempo, porque o compilador pode lixo e recompilar o código, com base em uma suposição otimista anterior que o caminho não ia ser usado em tudo. A regra 2 é a sua primeira linha de defesa contra esses efeitos.

Regra 6: Use ferramentas apropriadas para ler a mente do compilador e espere ser surpreendido pelo código que ele produz. Inspecione o código você mesmo antes de formar teorias sobre o que torna algo mais rápido ou mais lento.

Regra 7: Reduza o ruído em suas medições. Execute seu benchmark em uma máquina silenciosa e execute-o várias vezes, descartando outliers. Use -Xbatch para serializar o compilador com o aplicativo e considere configurar -XX:CICompilerCount=1 para impedir que o compilador seja executado em paralelo com ele mesmo. Tente o seu melhor para reduzir a sobrecarga GC, definir Xmx (grande o suficiente) é igual a Xms e use UseEpsilonGC se estiver disponível.

Regra 8: Use uma biblioteca para o seu benchmark, pois é provavelmente mais eficiente e já foi depurado para este único propósito. Tais como JMH , Caliper ou Bill e Paul's Excel UCSD Benchmarks para Java .



Para adicionar ao outro excelente conselho, eu também estaria ciente do seguinte:

Para algumas CPUs (por exemplo, a gama Intel Core i5 com TurboBoost), a temperatura (e o número de núcleos actualmente utilizados, bem como a sua percentagem de utilização) afectam a velocidade do relógio. Como as CPUs são dinamicamente sincronizadas, isso pode afetar seus resultados. Por exemplo, se você tiver um aplicativo de encadeamento único, a velocidade máxima do clock (com o TurboBoost) será maior do que para um aplicativo usando todos os núcleos. Isso pode, portanto, interferir nas comparações de desempenho de single e multi-thread em alguns sistemas. Tenha em mente que a temperatura e a volatilidade também afetam por quanto tempo a frequência do Turbo é mantida.

Talvez um aspecto fundamentalmente importante sobre o qual você tenha controle direto: verifique se está medindo a coisa certa! Por exemplo, se você estiver usando System.nanoTime() para comparar um determinado código, coloque as chamadas para a atribuição em locais que façam sentido para evitar medir coisas que você não está interessado. Por exemplo, não Faz:

long startTime = System.nanoTime();
//code here...
System.out.println("Code took "+(System.nanoTime()-startTime)+"nano seconds");

O problema é que você não está recebendo imediatamente o horário de término quando o código terminou. Em vez disso, tente o seguinte:

final long endTime, startTime = System.nanoTime();
//code here...
endTime = System.nanoTime();
System.out.println("Code took "+(endTime-startTime)+"nano seconds");

Se você está tentando comparar dois algoritmos, faça pelo menos dois benchmarks em cada um, alternando a ordem. ie:

for(i=1..n)
  alg1();
for(i=1..n)
  alg2();
for(i=1..n)
  alg2();
for(i=1..n)
  alg1();

Eu encontrei algumas diferenças notáveis ​​(5-10% às vezes) no tempo de execução do mesmo algoritmo em diferentes passagens ..

Além disso, certifique-se de que n é muito grande, para que o tempo de execução de cada loop seja no mínimo 10 segundos ou mais. Quanto mais iterações, os números mais significativos em seu tempo de referência e mais confiáveis ​​são esses dados.



http://opt.sourceforge.net/ Java Micro Benchmark - controle as tarefas necessárias para determinar as características comparativas de desempenho do sistema de computador em diferentes plataformas. Pode ser usado para guiar decisões de otimização e comparar diferentes implementações Java.





microbenchmark