jre jdk for developers descargar java jvm jit jvm-hotspot

java - jdk - netbeans



¿Java JIT engaña cuando ejecuta código JDK? (2)

En Java 8, este es de hecho un método intrínseco; Una versión ligeramente modificada del método:

private static BigInteger test() { Random r = new Random(1); BigInteger c = null; for (int i = 0; i < 400000; i++) { int s1 = 400, s2 = 400; BigInteger a = new BigInteger(s1 * 8, r), b = new BigInteger(s2 * 8, r); c = a.multiply(b); } return c; }

Ejecutando esto con:

java -XX:+UnlockDiagnosticVMOptions -XX:+PrintInlining -XX:+PrintIntrinsics -XX:CICompilerCount=2 -XX:+PrintCompilation <YourClassName>

Esto imprimirá muchas líneas y una de ellas será:

java.math.BigInteger::multiplyToLen (216 bytes) (intrinsic)

En Java 9, por otro lado, ese método ya no parece ser intrínseco, pero a su vez llama a un método que es intrínseco:

@HotSpotIntrinsicCandidate private static int[] implMultiplyToLen

Por lo tanto, ejecutar el mismo código en Java 9 (con los mismos parámetros) revelará:

java.math.BigInteger::implMultiplyToLen (216 bytes) (intrinsic)

Debajo está el mismo código para el método, solo un nombre ligeramente diferente.

Estaba comparando un código, y no pude lograr que se ejecutara tan rápido como con java.math.BigInteger , incluso cuando usaba exactamente el mismo algoritmo. Así que copié la fuente java.math.BigInteger en mi propio paquete y probé esto:

//import java.math.BigInteger; public class MultiplyTest { public static void main(String[] args) { Random r = new Random(1); long tm = 0, count = 0,result=0; for (int i = 0; i < 400000; i++) { int s1 = 400, s2 = 400; BigInteger a = new BigInteger(s1 * 8, r), b = new BigInteger(s2 * 8, r); long tm1 = System.nanoTime(); BigInteger c = a.multiply(b); if (i > 100000) { tm += System.nanoTime() - tm1; count++; } result+=c.bitLength(); } System.out.println((tm / count) + "nsec/mul"); System.out.println(result); } }

Cuando ejecuto esto (jdk 1.8.0_144-b01 en MacOS) sale:

12089nsec/mul 2559044166

Cuando lo ejecuto con la línea de importación sin comentarios:

4098nsec/mul 2559044166

Es casi tres veces más rápido cuando uso la versión JDK de BigInteger en comparación con mi versión, incluso si usa el mismo código exacto .

He examinado el bytecode con javap y comparado la salida del compilador cuando se ejecuta con opciones:

-Xbatch -XX:-TieredCompilation -XX:+PrintCompilation -XX:+UnlockDiagnosticVMOptions -XX:+PrintInlining -XX:CICompilerCount=1

y ambas versiones parecen generar el mismo código. Entonces, ¿el punto de acceso utiliza algunas optimizaciones precalculadas que no puedo usar en mi código? Siempre entendí que no. ¿Qué explica esta diferencia?


Sí, HotSpot JVM es una especie de "trampa", porque tiene una versión especial de algunos métodos BigInteger que no encontrará en el código Java. Estos métodos se llaman intrínsecos JVM .

En particular, BigInteger.multiplyToLen es un método intrínseco en HotSpot. Existe una implementación especial de ensamblaje codificado a mano en la base de origen JVM, pero solo para la arquitectura x86-64.

Puede deshabilitar este instrínseco con la opción -XX:-UseMultiplyToLenIntrinsic para forzar a JVM a usar la implementación pura de Java. En este caso, el rendimiento será similar al rendimiento de su código copiado.

PD Aquí hay una list de otros métodos intrínsecos de HotSpot.