En este código, cuando creo un objeto en el main
método y luego llamo a ese método de objetos: ff.twentyDivCount(i)
(se ejecuta en 16010 ms), se ejecuta mucho más rápido que llamarlo con esta anotación: twentyDivCount(i)
(se ejecuta en 59516 ms). Por supuesto, cuando lo ejecuto sin crear un objeto, hago que el método sea estático, por lo que se puede llamar en 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: Hasta ahora parece que diferentes máquinas producen resultados diferentes, pero usando JRE 1.8. * Es donde el resultado original parece reproducirse consistentemente.
+PrintCompilation +PrintInlining
muestraRespuestas:
Usando JRE 1.8.0_45 obtengo resultados similares.
Investigación:
-XX:+UnlockDiagnosticVMOptions -XX:+PrintCompilation -XX:+PrintInlining
opciones de VM muestra que ambos métodos se compilan y se insertanmain
es muy diferente, y el método de instancia se optimiza de manera más agresiva, especialmente en términos de desenrollado de buclesLuego realicé su prueba nuevamente pero con diferentes configuraciones de desenrollado de bucle para confirmar la sospecha anterior. Ejecuté tu código con:
-XX:LoopUnrollLimit=0
y ambos métodos se ejecutan lentamente (similar al método estático con las opciones predeterminadas).-XX:LoopUnrollLimit=100
y ambos métodos se ejecutan rápidamente (similar al método de instancia con las opciones predeterminadas).Como conclusión, parece que, con la configuración predeterminada, el JIT del hotspot 1.8.0_45 no puede desenrollar el ciclo cuando el método es estático (aunque no estoy seguro de por qué se comporta de esa manera). Otras JVM pueden producir resultados diferentes.
fuente
Solo una suposición no probada basada en la respuesta de Assylias.
La JVM usa un umbral para el desenrollado de bucles, que es algo así como 70. Por alguna razón, la llamada estática es un poco más grande y no se desenrolla.
Actualizar resultados
LoopUnrollLimit
siguiente 52, ambas versiones son lentas.Esto es extraño ya que mi conjetura fue que la llamada estática es solo un poco más grande en la representación interna y el OP encontró un caso extraño. Pero la diferencia parece ser de unos 20, lo que no tiene 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 aquellos que deseen experimentar, mi versión puede ser útil.
fuente
NON_STATIC
ySTATIC
, pero mi conclusión fue correcta. Arreglado ahora, gracias.Cuando se ejecuta en modo de depuración, los números son los mismos para la instancia y los casos estáticos. Eso significa además que el JIT vacila en compilar el código en código nativo en el caso estático de la misma manera que lo hace en el caso del método de instancia.
¿Por qué lo hace? Es difícil de decir; probablemente haría lo correcto si esta fuera una aplicación más grande ...
fuente
Simplemente modifiqué la prueba ligeramente y obtuve los siguientes resultados:
Salida:
Dynamic Test: 465585120 232792560 232792560 51350 ms Static Test: 465585120 232792560 232792560 52062 ms
NOTA
Mientras los probaba por separado, obtuve ~ 52 segundos para dinámica y ~ 200 segundos para estática.
Este es el 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"); } }
También cambié el orden de la prueba a:
public static void main(String[] args) { System.out.println("Static Test: " ); staticTest(); System.out.println("Dynamic Test: " ); dynamicTest(); }
Y tengo esto:
Static Test: 465585120 232792560 232792560 188945 ms Dynamic Test: 465585120 232792560 232792560 50106 ms
Como puede ver, si se llama dinámico antes que estático, la velocidad de estático disminuyó drásticamente.
Basado en este punto de referencia:
REGLA DE ORO:
Java: cuando usar métodos estáticos
fuente
Por favor, inténtalo:
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; } }
fuente