Neste código, quando eu crio um objeto no main
método e chamo esse método de objetos: ff.twentyDivCount(i)
(executa em 16010 ms), ele é executado muito mais rápido do que chamá-lo usando esta anotação: twentyDivCount(i)
(executa em 59516 ms). Claro, quando eu o executo sem criar um objeto, eu torno o método estático, para que ele possa ser chamado no main.
public class ProblemFive {
// Counts the number of numbers that the entry is evenly divisible by, as max is 20
int twentyDivCount(int a) { // Change to static int.... when using it directly
int count = 0;
for (int i = 1; i<21; i++) {
if (a % i == 0) {
count++;
}
}
return count;
}
public static void main(String[] args) {
long startT = System.currentTimeMillis();;
int start = 500000000;
int result = start;
ProblemFive ff = new ProblemFive();
for (int i = start; i > 0; i--) {
int temp = ff.twentyDivCount(i); // Faster way
// twentyDivCount(i) - slower
if (temp == 20) {
result = i;
System.out.println(result);
}
}
System.out.println(result);
long end = System.currentTimeMillis();;
System.out.println((end - startT) + " ms");
}
}
EDITAR: Até agora, parece que máquinas diferentes produzem resultados diferentes, mas usando o JRE 1.8. * É onde o resultado original parece ser reproduzido de forma consistente.
+PrintCompilation +PrintInlining
mostraRespostas:
Usando o JRE 1.8.0_45, obtenho resultados semelhantes.
Investigação:
-XX:+UnlockDiagnosticVMOptions -XX:+PrintCompilation -XX:+PrintInlining
opções de VM mostra que ambos os métodos são compilados e embutidosmain
é muito diferente, com o método de instância sendo otimizado de forma mais agressiva, especialmente em termos de desenrolamento de loopEm seguida, executei seu teste novamente, mas com configurações de desenrolamento de loop diferentes para confirmar a suspeita acima. Eu executei seu código com:
-XX:LoopUnrollLimit=0
e ambos os métodos são executados lentamente (semelhante ao método estático com as opções padrão).-XX:LoopUnrollLimit=100
e ambos os métodos são executados rapidamente (semelhante ao método de instância com as opções padrão).Como conclusão, parece que, com as configurações padrão, o JIT do hotspot 1.8.0_45 não é capaz de desenrolar o loop quando o método é estático (embora eu não saiba por que ele se comporta dessa forma). Outras JVMs podem produzir resultados diferentes.
fonte
Apenas uma suposição não comprovada com base em uma resposta de assilias.
A JVM usa um limite para o desenrolamento do loop, que é algo como 70. Por alguma razão, a chamada estática é um pouco maior e não é desenrolada.
Resultados de atualização
LoopUnrollLimit
abaixo de 52, ambas as versões são lentas.Isso é estranho, pois meu palpite era que a chamada estática é apenas um pouco maior na representação interna e o OP atingiu um caso estranho. Mas a diferença parece ser de cerca de 20, o que não faz sentido.
-XX:LoopUnrollLimit=51 5400 ms NON_STATIC 5310 ms STATIC -XX:LoopUnrollLimit=52 1456 ms NON_STATIC 5305 ms STATIC -XX:LoopUnrollLimit=71 1459 ms NON_STATIC 5309 ms STATIC -XX:LoopUnrollLimit=72 1457 ms NON_STATIC 1488 ms STATIC
Para aqueles que desejam experimentar, minha versão pode ser útil.
fonte
NON_STATIC
eSTATIC
, mas minha conclusão estava certa. Consertado agora, obrigado.Quando isso é executado no modo de depuração, os números são os mesmos para a instância e os casos estáticos. Isso significa ainda que o JIT hesita em compilar o código para o código nativo no caso estático da mesma maneira que faz no caso do método de instância.
Por que isso acontece? É difícil dizer; provavelmente faria a coisa certa se este fosse um aplicativo maior ...
fonte
Acabei de ajustar um pouco o teste e obtive os seguintes resultados:
Resultado:
Dynamic Test: 465585120 232792560 232792560 51350 ms Static Test: 465585120 232792560 232792560 52062 ms
NOTA
Enquanto os testava separadamente, obtive ~ 52 segundos para dinâmico e ~ 200 segundos para estático.
Este é o programa:
public class ProblemFive { // Counts the number of numbers that the entry is evenly divisible by, as max is 20 int twentyDivCount(int a) { // Change to static int.... when using it directly int count = 0; for (int i = 1; i<21; i++) { if (a % i == 0) { count++; } } return count; } static int twentyDivCount2(int a) { int count = 0; for (int i = 1; i<21; i++) { if (a % i == 0) { count++; } } return count; } public static void main(String[] args) { System.out.println("Dynamic Test: " ); dynamicTest(); System.out.println("Static Test: " ); staticTest(); } private static void staticTest() { long startT = System.currentTimeMillis();; int start = 500000000; int result = start; for (int i = start; i > 0; i--) { int temp = twentyDivCount2(i); if (temp == 20) { result = i; System.out.println(result); } } System.out.println(result); long end = System.currentTimeMillis();; System.out.println((end - startT) + " ms"); } private static void dynamicTest() { long startT = System.currentTimeMillis();; int start = 500000000; int result = start; ProblemFive ff = new ProblemFive(); for (int i = start; i > 0; i--) { int temp = ff.twentyDivCount(i); // Faster way if (temp == 20) { result = i; System.out.println(result); } } System.out.println(result); long end = System.currentTimeMillis();; System.out.println((end - startT) + " ms"); } }
Eu também alterei a ordem do teste para:
public static void main(String[] args) { System.out.println("Static Test: " ); staticTest(); System.out.println("Dynamic Test: " ); dynamicTest(); }
E eu tenho isso:
Static Test: 465585120 232792560 232792560 188945 ms Dynamic Test: 465585120 232792560 232792560 50106 ms
Como você pode ver, se dynamic for chamado antes de static, a velocidade de static diminuiu drasticamente.
Com base nesta referência:
REGRA DO POLEGAR:
Java: quando usar métodos estáticos
fonte
Tente por favor:
public class ProblemFive { public static ProblemFive PROBLEM_FIVE = new ProblemFive(); public static void main(String[] args) { long startT = System.currentTimeMillis(); int start = 500000000; int result = start; for (int i = start; i > 0; i--) { int temp = PROBLEM_FIVE.twentyDivCount(i); // faster way // twentyDivCount(i) - slower if (temp == 20) { result = i; System.out.println(result); System.out.println((System.currentTimeMillis() - startT) + " ms"); } } System.out.println(result); long end = System.currentTimeMillis(); System.out.println((end - startT) + " ms"); } int twentyDivCount(int a) { // change to static int.... when using it directly int count = 0; for (int i = 1; i < 21; i++) { if (a % i == 0) { count++; } } return count; } }
fonte