tipos - suma de numeros con ciclo for en java
¿Por qué este programa se repite muchas veces tomando tiempo cuando hay `println` después de los bucles? (5)
Aquí está el pequeño código que estoy intentando. Este programa requiere una buena cantidad de tiempo para ejecutarse. Mientras se ejecuta, si trato de matarlo a través del botón de terminar en eclipse, devuelve Terminate Failed
. Puedo matarlo desde el terminal usando kill -9 <PID>
.
Pero, cuando no imprimo el resultado de la variable en la última línea del programa (verifique la porción comentada del código), el programa sale inmediatamente.
Me pregunto :
¿Por qué lleva tiempo ejecutar cuando se imprime el valor del resultado?
Tenga en cuenta que si no imprimovalue
, el mismo ciclo se soluciona inmediatamente.¿Por qué el eclipse no puede matar el programa?
Actualización 1: Parece que JVM optimiza el código durante el tiempo de ejecución (no en tiempo de compilación). Este hilo es útil.
Actualización 2 : Cuando jstack <PID>
el valor del value
, jstack <PID>
no está funcionando. Solo jstack -F <PID>
está funcionando. ¿Alguna razón posible?
public class TestClient {
private static void loop() {
long value =0;
for (int j = 0; j < 50000; j++) {
for (int i = 0; i < 100000000; i++) {
value += 1;
}
}
//When the value is being printed, the program
//is taking time to complete
System.out.println("Done "+ value);
//When the value is NOT being printed, the program
//completes immediately
//System.out.println("Done ");
}
public static void main(String[] args) {
loop();
}
}
Básicamente, JVM es realmente muy inteligente . Puede detectar si está usando cualquier variable o no, y en base a eso, puede eliminar cualquier procesamiento relacionado con eso. Como usted comentó su línea de código que imprime "valor", detecta que esta variable no se usará en ningún lado y no ejecutará el ciclo, ni siquiera una vez .
Pero cuando imprime el valor, tiene que ejecutar sus bucles, que de nuevo es un número muy grande (50000 * 100000000). Ahora, el tiempo de ejecución de este ciclo depende de muchos factores, incluidos, entre otros, el procesador de su máquina, la memoria que se le da a JVM, la carga del procesador, etc.
En lo que concierne a su pregunta sobre el eclipse que no puede terminar, puedo matar fácilmente el programa usando eclipse en mi máquina. Tal vez deberías volver a verificar.
Es a causa de la optimización del compilador / JVM. Cuando está imprimiendo el resultado, el cálculo y, por tanto, el compilador mantendrán el ciclo.
En el lado, el compilador / JVM eliminará el bucle cuando no esté usando nada del bucle.
Esta es una optimización del compilador JIT (no de optimización del compilador de Java).
Si compara el código de bytes generado por el compilador de java para las dos versiones, verá que el bucle está presente en ambos.
Así es como se ve el método decompilado con println:
private static void loop() {
long value = 0L;
for(int j = 0; j < ''썐''; ++j) {
for(int i = 0; i < 100000000; ++i) {
++value;
}
}
System.out.println("Done " + value);
}
Así es como se ve el método descompilado cuando se elimina println:
private static void loop() {
long value = 0L;
for(int j = 0; j < ''썐''; ++j) {
for(int i = 0; i < 100000000; ++i) {
++value;
}
}
}
Como puede ver, los bucles aún están allí.
Sin embargo, puede habilitar el registro del compilador JIT y la impresión de ensamblaje con las siguientes opciones de JVM:
-XX:+UnlockDiagnosticVMOptions -XX:+LogCompilation -XX:+PrintAssembly
También puede necesitar descargar el hsdis-amd64.dylib y ponerlo en su directorio de trabajo (MacOS, HotSpot Java 8)
Después de ejecutar TestClient, debería ver el código producido por el compilador JIT en la consola. Aquí publicaré solo un extracto de la salida.
Versión sin println:
# {method} ''loop'' ''()V'' in ''test/TestClient''
0x000000010e3c2500: callq 0x000000010dc1c202 ; {runtime_call}
0x000000010e3c2505: data32 data32 nopw 0x0(%rax,%rax,1)
0x000000010e3c2510: sub $0x18,%rsp
0x000000010e3c2517: mov %rbp,0x10(%rsp)
0x000000010e3c251c: mov %rsi,%rdi
0x000000010e3c251f: movabs $0x10dc760ec,%r10
0x000000010e3c2529: callq *%r10 ;*iload_3
; - test.TestClient::loop@12 (line 9)
0x000000010e3c252c: add $0x10,%rsp
0x000000010e3c2530: pop %rbp
0x000000010e3c2531: test %eax,-0x1c18537(%rip) # 0x000000010c7aa000
; {poll_return}
0x000000010e3c2537: retq
Versión con println:
# {method} ''loop'' ''()V'' in ''test/TestClient''
0x00000001092c36c0: callq 0x0000000108c1c202 ; {runtime_call}
0x00000001092c36c5: data32 data32 nopw 0x0(%rax,%rax,1)
0x00000001092c36d0: mov %eax,-0x14000(%rsp)
0x00000001092c36d7: push %rbp
0x00000001092c36d8: sub $0x10,%rsp
0x00000001092c36dc: mov 0x10(%rsi),%r13
0x00000001092c36e0: mov 0x8(%rsi),%ebp
0x00000001092c36e3: mov (%rsi),%ebx
0x00000001092c36e5: mov %rsi,%rdi
0x00000001092c36e8: movabs $0x108c760ec,%r10
0x00000001092c36f2: callq *%r10
0x00000001092c36f5: jmp 0x00000001092c3740
0x00000001092c36f7: add $0x1,%r13 ;*iload_3
; - test.TestClient::loop@12 (line 9)
0x00000001092c36fb: inc %ebx ;*iinc
; - test.TestClient::loop@22 (line 9)
0x00000001092c36fd: cmp $0x5f5e101,%ebx
0x00000001092c3703: jl 0x00000001092c36f7 ;*if_icmpge
; - test.TestClient::loop@15 (line 9)
0x00000001092c3705: jmp 0x00000001092c3734
0x00000001092c3707: nopw 0x0(%rax,%rax,1)
0x00000001092c3710: mov %r13,%r8 ;*iload_3
; - test.TestClient::loop@12 (line 9)
0x00000001092c3713: mov %r8,%r13
0x00000001092c3716: add $0x10,%r13 ;*ladd
; - test.TestClient::loop@20 (line 10)
0x00000001092c371a: add $0x10,%ebx ;*iinc
; - test.TestClient::loop@22 (line 9)
0x00000001092c371d: cmp $0x5f5e0f2,%ebx
0x00000001092c3723: jl 0x00000001092c3710 ;*if_icmpge
; - test.TestClient::loop@15 (line 9)
0x00000001092c3725: add $0xf,%r8 ;*ladd
; - test.TestClient::loop@20 (line 10)
0x00000001092c3729: cmp $0x5f5e101,%ebx
0x00000001092c372f: jl 0x00000001092c36fb
0x00000001092c3731: mov %r8,%r13 ;*iload_3
; - test.TestClient::loop@12 (line 9)
0x00000001092c3734: inc %ebp ;*iinc
; - test.TestClient::loop@28 (line 8)
0x00000001092c3736: cmp $0xc350,%ebp
0x00000001092c373c: jge 0x00000001092c376c ;*if_icmpge
; - test.TestClient::loop@7 (line 8)
0x00000001092c373e: xor %ebx,%ebx
0x00000001092c3740: mov %ebx,%r11d
0x00000001092c3743: inc %r11d ;*iload_3
; - test.TestClient::loop@12 (line 9)
0x00000001092c3746: mov %r13,%r8
0x00000001092c3749: add $0x1,%r8 ;*ladd
; - test.TestClient::loop@20 (line 10)
0x00000001092c374d: inc %ebx ;*iinc
; - test.TestClient::loop@22 (line 9)
0x00000001092c374f: cmp %r11d,%ebx
0x00000001092c3752: jge 0x00000001092c3759 ;*if_icmpge
; - test.TestClient::loop@15 (line 9)
0x00000001092c3754: mov %r8,%r13
0x00000001092c3757: jmp 0x00000001092c3746
0x00000001092c3759: cmp $0x5f5e0f2,%ebx
0x00000001092c375f: jl 0x00000001092c3713
0x00000001092c3761: mov %r13,%r10
0x00000001092c3764: mov %r8,%r13
0x00000001092c3767: mov %r10,%r8
0x00000001092c376a: jmp 0x00000001092c3729 ;*if_icmpge
; - test.TestClient::loop@7 (line 8)
0x00000001092c376c: mov $0x24,%esi
0x00000001092c3771: mov %r13,%rbp
0x00000001092c3774: data32 xchg %ax,%ax
0x00000001092c3777: callq 0x0000000109298f20 ; OopMap{off=188}
;*getstatic out
; - test.TestClient::loop@34 (line 13)
; {runtime_call}
0x00000001092c377c: callq 0x0000000108c1c202 ;*getstatic out
; - test.TestClient::loop@34 (line 13)
; {runtime_call}
También debe tener el archivo hotspot.log con los pasos del compilador JIT. Aquí hay un extracto:
<phase name=''optimizer'' nodes=''114'' live=''77'' stamp=''0.100''>
<phase name=''idealLoop'' nodes=''115'' live=''67'' stamp=''0.100''>
<loop_tree>
<loop idx=''119'' >
<loop idx=''185'' main_loop=''185'' >
</loop>
</loop>
</loop_tree>
<phase_done name=''idealLoop'' nodes=''197'' live=''111'' stamp=''0.101''/>
</phase>
<phase name=''idealLoop'' nodes=''197'' live=''111'' stamp=''0.101''>
<loop_tree>
<loop idx=''202'' >
<loop idx=''159'' inner_loop=''1'' pre_loop=''131'' >
</loop>
<loop idx=''210'' inner_loop=''1'' main_loop=''210'' >
</loop>
<loop idx=''138'' inner_loop=''1'' post_loop=''131'' >
</loop>
</loop>
</loop_tree>
<phase_done name=''idealLoop'' nodes=''221'' live=''113'' stamp=''0.101''/>
</phase>
<phase name=''idealLoop'' nodes=''221'' live=''113'' stamp=''0.101''>
<loop_tree>
<loop idx=''202'' >
<loop idx=''159'' inner_loop=''1'' pre_loop=''131'' >
</loop>
<loop idx=''210'' inner_loop=''1'' main_loop=''210'' >
</loop>
<loop idx=''138'' inner_loop=''1'' post_loop=''131'' >
</loop>
</loop>
</loop_tree>
<phase_done name=''idealLoop'' nodes=''241'' live=''63'' stamp=''0.101''/>
</phase>
<phase name=''ccp'' nodes=''241'' live=''63'' stamp=''0.101''>
<phase_done name=''ccp'' nodes=''241'' live=''63'' stamp=''0.101''/>
</phase>
<phase name=''idealLoop'' nodes=''241'' live=''63'' stamp=''0.101''>
<loop_tree>
<loop idx=''202'' inner_loop=''1'' >
</loop>
</loop_tree>
<phase_done name=''idealLoop'' nodes=''253'' live=''56'' stamp=''0.101''/>
</phase>
<phase name=''idealLoop'' nodes=''253'' live=''56'' stamp=''0.101''>
<phase_done name=''idealLoop'' nodes=''253'' live=''33'' stamp=''0.101''/>
</phase>
<phase_done name=''optimizer'' nodes=''253'' live=''33'' stamp=''0.101''/>
</phase>
Puede analizar aún más el archivo hotspot.log producido por el compilador JIT utilizando la herramienta JitWatch https://github.com/AdoptOpenJDK/jitwatch/wiki
Para deshabilitar el compilador JIT y ejecutar la máquina virtual Java en modo todo interpretado, puede usar la opción -Djava.compiler = NONE JVM.
Una pregunta similar es en este post ¿Por qué mi JVM está optimizando el bucle en tiempo de ejecución y haciendo que mi código funcione mal?
Sospecho que cuando no se imprime el resultado, el compilador nota que el valor del value
nunca se usa y, por lo tanto, puede eliminar todo el ciclo como una optimización.
Entonces, sin la println
no está bucle y el programa simplemente sale de una vez, mientras que al imprimir el valor está haciendo todas las 5.000,000,000,000 de iteraciones y esto puede ser un poco largo.
Como sugerencia prueba
public class TestClient {
public static long loop() {
long value =0;
for (int j = 0; j < 50000; j++) {
for (int i = 0; i < 100000000; i++) {
value += 1;
}
}
return value
}
public static void main(String[] args) {
// this might also take rather long
loop();
// as well as this
// System.out.println(loop());
}
}
Aquí el compilador no podrá optimizar el bucle en loop()
ya que puede ser llamado desde varias otras clases, por lo que se ejecutará en todos los casos.
¿Por qué lleva tiempo ejecutar cuando se imprime el valor del resultado? Tenga en cuenta que si no imprimo valor, el mismo ciclo se soluciona inmediatamente.
Para ser sincero, ejecuté su código en eclipse (windows) y sigue ejecutándose incluso si comenta la línea system.out.println. Lo comprobé dos veces en el modo de depuración (si abre la perspectiva de depuración, verá todas las aplicaciones en ejecución (por defecto) en la parte superior izquierda).
Pero si funciona rápidamente para usted, entonces la respuesta más plausible es que se debe a la optimización del compilador Java / JVM. Todos hemos aprendido que Java es rápido, a pesar de ser un lenguaje interpretado (principalmente) porque convierte el código fuente en bytecode, usa el compilador JIT, Hot Spot, etc.
¿Por qué el eclipse no puede matar el programa?
Puedo matar el programa con éxito en Eclipse (Windows). Tal vez algún problema con la versión específica de eclipse o Linux. (No estoy seguro). Una búsqueda rápida en Google ofrece múltiples escenarios cuando Eclipse no puede finalizar un programa.