Recentemente, percebi que declarar um array contendo 64 elementos é muito mais rápido (> 1000 vezes) do que declarar o mesmo tipo de array com 65 elementos.
Aqui está o código que usei para testar isso:
public class Tests{
public static void main(String args[]){
double start = System.nanoTime();
int job = 100000000;//100 million
for(int i = 0; i < job; i++){
double[] test = new double[64];
}
double end = System.nanoTime();
System.out.println("Total runtime = " + (end-start)/1000000 + " ms");
}
}
Este é executado em cerca de 6 ms, se eu substituir new double[64]
com new double[65]
leva aproximadamente 7 segundos. Esse problema se torna exponencialmente mais grave se o trabalho for espalhado por mais e mais threads, que é de onde meu problema se origina.
Esse problema também ocorre com diferentes tipos de matrizes, como int[65]
ou String[65]
. Esse problema não ocorre com strings grandes:, String test = "many characters";
mas começa a ocorrer quando é alterado paraString test = i + "";
Eu queria saber por que esse é o caso e se é possível contornar esse problema.
System.nanoTime()
deve ser preferidoSystem.currentTimeMillis()
para benchmarking.byte
vez dedouble
.Respostas:
Você está observando um comportamento que é causado pelas otimizações feitas pelo compilador JIT de seu Java VM. Esse comportamento é reproduzível acionado com matrizes escalares de até 64 elementos e não é acionado com matrizes maiores que 64.
Antes de entrar em detalhes, vamos dar uma olhada mais de perto no corpo do loop:
O corpo não tem efeito (comportamento observável) . Isso significa que não faz diferença fora da execução do programa se esta instrução é executada ou não. O mesmo é válido para todo o loop. Portanto, pode acontecer que o otimizador de código traduza o loop em algo (ou nada) com o mesmo comportamento funcional e de temporização diferente.
Para benchmarks, você deve pelo menos seguir as duas diretrizes a seguir. Se você tivesse feito isso, a diferença teria sido significativamente menor.
Agora vamos entrar em detalhes. Não é surpresa que haja uma otimização que é acionada para matrizes escalares não maiores que 64 elementos. A otimização faz parte da análise Escape . Ele coloca pequenos objetos e pequenos arrays na pilha em vez de alocá-los no heap - ou, melhor ainda, otimizá-los inteiramente. Você pode encontrar algumas informações sobre isso no seguinte artigo de Brian Goetz escrito em 2005:
A otimização pode ser desabilitada com a opção de linha de comando
-XX:-DoEscapeAnalysis
. O valor mágico 64 para matrizes escalares também pode ser alterado na linha de comando. Se você executar seu programa da seguinte maneira, não haverá diferença entre arrays com 64 e 65 elementos:Dito isso, desencorajo fortemente o uso de tais opções de linha de comando. Duvido que faça uma grande diferença em uma aplicação realista. Eu só o usaria se estivesse absolutamente convencido da necessidade - e não com base nos resultados de alguns pseudo benchmarks.
fonte
Existem várias maneiras pelas quais pode haver uma diferença, com base no tamanho de um objeto.
Como nosid afirmou, o JITC pode estar (provavelmente está) alocando pequenos objetos "locais" na pilha, e o corte de tamanho para "pequenos" arrays pode ser de 64 elementos.
Alocar na pilha é significativamente mais rápido do que alocar no heap e, mais precisamente, a pilha não precisa ser coletada como lixo, portanto, a sobrecarga de GC é bastante reduzida. (E para este caso de teste, a sobrecarga de GC é provavelmente de 80-90% do tempo total de execução.)
Além disso, uma vez que o valor é alocado na pilha, o JITC pode realizar a "eliminação do código morto", determinar que o resultado do
new
nunca seja usado em qualquer lugar e, depois de garantir que não haja efeitos colaterais que seriam perdidos, elimine anew
operação inteira , e então o próprio loop (agora vazio).Mesmo que o JITC não faça alocação de pilha, é inteiramente possível que objetos menores que um certo tamanho sejam alocados em um heap de maneira diferente (por exemplo, de um "espaço" diferente) do que objetos maiores. (Normalmente, isso não produziria diferenças de tempo tão dramáticas, no entanto.)
fonte