it-swarm.com.de

Betrügt Java JIT, wenn JDK-Code ausgeführt wird?

Ich habe einen Code einem Benchmarking unterzogen und konnte ihn nicht so schnell ausführen wie mit Java.math.BigInteger , selbst wenn genau derselbe Algorithmus verwendet wurde. Also habe ich Java.math.BigInteger source in mein eigenes Paket kopiert und folgendes ausprobiert:

_//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); 
    }
}
_

Wenn ich dies starte (jdk 1.8.0_144-b01 unter MacOS), gibt es Folgendes aus:

_12089nsec/mul
2559044166
_

Wenn ich es mit der unkommentierten Importzeile ausführe:

_4098nsec/mul
2559044166
_

Es ist fast dreimal so schnell, wenn die JDK-Version von BigInteger im Vergleich zu meiner Version verwendet wird, selbst wenn der exakt gleiche Code verwendet wird .

Ich habe den Bytecode mit javap untersucht und die Compiler-Ausgabe beim Ausführen mit Optionen verglichen:

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

und beide Versionen scheinen den gleichen Code zu generieren. Verwendet Hotspot also einige vorberechnete Optimierungen, die ich in meinem Code nicht verwenden kann? Ich habe immer verstanden, dass sie es nicht tun. Was erklärt diesen Unterschied?

394
Koen Hendrikx

Ja, HotSpot JVM ist eine Art "Betrug", da es eine spezielle Version einiger BigInteger Methoden gibt, die Sie im Java Code nicht finden. Diese Methoden heißen JVM intrinsics .

Insbesondere ist BigInteger.multiplyToLen eine instrumentelle Methode in HotSpot. Es gibt eine spezielle handcodierte Assembly-Implementierung in der JVM-Quellbasis, jedoch nur für die x86-64-Architektur.

Sie können dieses Instrument mit der Option -XX:-UseMultiplyToLenIntrinsic deaktivieren, um JVM zu zwingen, die reine Implementierung Java zu verwenden. In diesem Fall entspricht die Leistung der Leistung Ihres kopierten Codes.

P.S Hier ist eine Liste anderer HotSpot-Methoden.

518
apangin

In Java 8 ist dies in der Tat eine intrinsische Methode; eine leicht modifizierte Version der Methode:

 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;
}

Laufen diese mit:

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

Dies wird viele Zeilen drucken und eine davon wird sein:

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

In Java 9 hingegen scheint diese Methode keine intrinsische Methode mehr zu sein, sondern ruft wiederum eine intrinsische Methode auf:

 @HotSpotIntrinsicCandidate
 private static int[] implMultiplyToLen

Wenn Sie also denselben Code unter Java 9 (mit denselben Parametern) ausführen, wird Folgendes angezeigt:

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

Darunter befindet sich derselbe Code für die Methode - nur eine etwas andere Benennung.

136
Eugene