क्या JDK कोड चलाते समय Java JIT धोखा देती है?


405

मैं कुछ कोड बेंच रहा था, और मैं इसे java.math.BigIntegerठीक उसी एल्गोरिथ्म का उपयोग करते हुए भी उतनी तेजी से चलाने के लिए नहीं मिला । इसलिए मैंने java.math.BigIntegerस्रोत को अपने पैकेज में कॉपी किया और यह कोशिश की:

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

जब मैंने इसे (jdk 1.8.0_144-b01 को MacOS पर चलाया) तो यह आउटपुट:

12089nsec/mul
2559044166

जब मैं इसे आयात लाइन के साथ चलाता हूँ

4098nsec/mul
2559044166

BigInteger बनाम मेरे संस्करण के JDK संस्करण का उपयोग करते समय यह लगभग तीन गुना तेज़ है, भले ही यह ठीक उसी कोड का उपयोग कर रहा हो।

मैंने javap के साथ bytecode की जांच की है, और विकल्प के साथ चलने पर कंपाइलर आउटपुट की तुलना की है:

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

और दोनों संस्करण एक ही कोड उत्पन्न करते हैं। तो हॉटस्पॉट कुछ प्रीक्म्प्यूटेड ऑप्टिमाइज़ेशन का उपयोग कर रहा है जो मैं अपने कोड में उपयोग नहीं कर सकता हूं? मुझे हमेशा समझ में आया कि वे नहीं करते। इस अंतर को क्या समझाता है?


29
दिलचस्प। 1. परिणाम सुसंगत है (या सिर्फ भाग्यशाली यादृच्छिक)? 2. क्या आप जेवीएम को गर्म करने के बाद कोशिश कर सकते हैं? 3. क्या आप यादृच्छिक कारक को समाप्त कर सकते हैं और दोनों टेस्ट के लिए इनपुट के रूप में एक ही डेटासेट प्रदान कर सकते हैं?
जिगर जोशी

7
क्या आपने JMH के साथ अपने बेंचमार्क को खोलने की कोशिश की थी jj.net.net/projects/code-tools/jmh ? मैन्युअल रूप से माप करना आसान नहीं है (वार्म अप और वह सब सामान)।
रोमन पुचकोव्स्की

2
हाँ यह बहुत सुसंगत है। अगर मैं इसे 10 मिनट तक चलने दूं तो भी मुझे उतना ही अंतर मिलेगा। निश्चित यादृच्छिक बीज यह सुनिश्चित करता है कि दोनों रन समान डेटासेट प्राप्त करें।
Koen Hendrikx

5
आप शायद अभी भी जेएमएच चाहते हैं, बस मामले में। और आपको अपना संशोधित बिगइंटर कहीं लगाना चाहिए ताकि लोग आपके परीक्षण को पुन: उत्पन्न कर सकें और सत्यापित कर सकें कि आप जो सोच रहे हैं वह चल रहा है।
pvg

जवाबों:


529

हां, हॉटस्पॉट JVM "धोखा" की तरह है, क्योंकि इसमें कुछ BigIntegerविधियों का एक विशेष संस्करण है जो आपको जावा कोड में नहीं मिलेगा। इन विधियों को जेवीएम इंट्रिनिक्स कहा जाता है ।

विशेष रूप से, BigInteger.multiplyToLenहॉटस्पॉट में एक सहज विधि है। जेवीएम स्रोत आधार में एक विशेष हाथ-कोडित विधानसभा कार्यान्वयन है, लेकिन केवल x86-64 वास्तुकला के लिए।

आप -XX:-UseMultiplyToLenIntrinsicJVM को शुद्ध जावा कार्यान्वयन का उपयोग करने के लिए बाध्य करने के विकल्प के साथ इस वृत्ति को निष्क्रिय कर सकते हैं । इस मामले में प्रदर्शन आपके कॉपी किए गए कोड के प्रदर्शन के समान होगा।

PS यहाँ अन्य हॉटस्पॉट आंतरिक तरीकों की एक सूची है।


141

में जावा 8 यह वास्तव में एक आंतरिक विधि है; विधि का थोड़ा संशोधित संस्करण:

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

इसके साथ चल रहा है:

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

यह बहुत सारी लाइनें प्रिंट करेगा और उनमें से एक होगा:

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

में जावा 9 दूसरी ओर विधि अब एक आंतरिक नहीं हो रहा है कि है, लेकिन बदले में यह एक तरीका है कि एक स्वाभाविक है कहता है:

 @HotSpotIntrinsicCandidate
 private static int[] implMultiplyToLen

तो जावा 9 के तहत एक ही कोड चलाने (समान मापदंडों के साथ) से पता चलेगा:

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

नीचे यह विधि के लिए एक ही कोड है - बस थोड़ा अलग नामकरण।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.