अंतरिम कारणों से शेष ऑपरेटर java.util.Objects.requireNonNull?


12

मैं कुछ आंतरिक विधि से अधिक से अधिक प्रदर्शन प्राप्त करने की कोशिश कर रहा हूं।

जावा कोड है:

List<DirectoryTaxonomyWriter> writers = Lists.newArrayList();
private final int taxos = 4;

[...]

@Override
public int getParent(final int globalOrdinal) throws IOException {
    final int bin = globalOrdinal % this.taxos;
    final int ordinalInBin = globalOrdinal / this.taxos;
    return this.writers.get(bin).getParent(ordinalInBin) * this.taxos + bin; //global parent
}

मेरे प्रोफाइलर में मैंने देखा कि 1% CPU खर्च होता है java.util.Objects.requireNonNull, लेकिन मैं उसे कॉल भी नहीं करता। जब बाइटकोड का निरीक्षण किया, तो मैंने यह देखा:

 public getParent(I)I throws java/io/IOException 
   L0
    LINENUMBER 70 L0
    ILOAD 1
    ALOAD 0
    INVOKESTATIC java/util/Objects.requireNonNull (Ljava/lang/Object;)Ljava/lang/Object;
    POP
    BIPUSH 8
    IREM
    ISTORE 2

तो संकलक यह (बेकार?) जाँच उत्पन्न करता है। मैं आदिम पर काम करता हूं, जो nullवैसे भी नहीं हो सकता है , इसलिए कंपाइलर इस लाइन को क्यों उत्पन्न करता है? यह एक बग है? या 'सामान्य' व्यवहार?

(मैं एक बिटमास्क के साथ काम कर सकता हूं, लेकिन मैं बस उत्सुक हूं)

[अपडेट करें]

  1. ऑपरेटर को इससे कोई लेना-देना नहीं है (नीचे उत्तर देखें)

  2. ग्रहण संकलक (संस्करण 4.10) का उपयोग करके मुझे यह अधिक उचित परिणाम मिलता है:

    public getParent (I) I java / io / IOException फेंकता है 
       L0
        LINENUMBER 77 एल 0
        ILOAD 1
        ICONST_4
        IREM
        ISTORE 2
       एल 1
        LINENUMBER 78 एल

तो यह अधिक तार्किक है।


@ लिनो यकीन है, लेकिन यह वास्तव में लाइन 70 के लिए प्रासंगिक नहीं हैINVOKESTATIC
रोबू

आप किस कंपाइलर का उपयोग करते हैं? सामान्य javacयह उत्पन्न नहीं करता है।
अपंगिन

आप किस कंपाइलर का उपयोग करते हैं? जावा संस्करण, Openjdk / Oracle / etc? संपादित करें: व्हाट्स, @apangin तेज़ था, क्षमा करें
lugiorgi

1
इसे Intellij 2019.3 से संकलित किया गया है, जावा 11 के साथ, openjdk version "11.0.6" 2020-01-14ubuntu 64 बिट पर।
15

जवाबों:


3

क्यों नहीं?

यह मानते हुए

class C {
    private final int taxos = 4;

    public int test() {
        final int a = 7;
        final int b = this.taxos;
        return a % b;
    }
}

जैसा कॉल c.test()जहां cके रूप में घोषित किया जाता है C चाहिए फेंक जब cहै null। आपकी विधि के बराबर है

    public int test() {
        return 3; // `7 % 4`
    }

जैसा कि आप केवल स्थिरांक के साथ काम करते हैं। साथ testगैर स्थिर किया जा रहा है, की जांच किया जाना चाहिए। आम तौर पर, यह अंतर्निहित रूप से तब किया जाता है जब कोई फ़ील्ड एक्सेस हो जाती है या एक गैर-स्थिर विधि कहलाती है, लेकिन आप ऐसा नहीं करते हैं। तो एक स्पष्ट जाँच की आवश्यकता है। एक संभावना कॉल करने के लिए है Objects.requireNonNull

बाइटकोड

यह न भूलें कि प्रदर्शन के लिए मूल रूप से बाईटेकोड अप्रासंगिक है। का काम कुछ बायोटेक javacका उत्पादन करना है जिसका निष्पादन आपके स्रोत कोड के साथ मेल खाता है। यह किसी भी अनुकूलन करने के लिए नहीं है, क्योंकि अनुकूलित कोड का विश्लेषण करने के लिए आमतौर पर लंबा और कठिन होता है, जबकि बाईटेकोड वास्तव में जेआईटी संकलक के अनुकूलन के लिए स्रोत कोड है । इसलिए javacउम्मीद की जाती है कि इसे सरल रखा जाए ...।

प्रदर्शन

मेरे प्रोफाइलर में मैंने देखा कि 1% CPU खर्च होता है java.util.Objects.requireNonNull

मैं पहले प्रोफाइलर को दोषी ठहराता हूं। प्रोफाइलिंग जावा काफी कठिन है और आप कभी भी सही परिणाम की उम्मीद नहीं कर सकते हैं।

आपको शायद विधि को स्थिर बनाने की कोशिश करनी चाहिए। आपको निश्चित रूप से अशक्त जांच के बारे में यह लेख पढ़ना चाहिए ।


1
आपके उत्साहजनक उत्तर के लिए @maaartinus धन्यवाद। मैं आपके जुड़े हुए लेख को जरूर पढ़ूंगा।
रोबआऊ

1
"परीक्षण के गैर-स्थिर होने के साथ, चेक को किया जाना चाहिए" वास्तव में, यह परीक्षण करने का कोई कारण नहीं thisहै कि क्या गैर- है null। जैसा कि आपने स्वयं कहा, c.test()जब कॉल करना होता cहै तो फेल होना चाहिए nullऔर इसे विधि में प्रवेश करने के बजाय तुरंत विफल होना चाहिए । तो भीतर test(), thisकभी नहीं हो सकता null(अन्यथा एक जेवीएम बग होगा)। इसलिए जांच की जरूरत नहीं है। वास्तविक फिक्स को फ़ील्ड taxosको बदलना चाहिए static, क्योंकि संकलन-समय स्थिरांक के लिए हर उदाहरण में मेमोरी को संग्रहीत करने का कोई मतलब नहीं है। फिर, कि क्या test()है staticअप्रासंगिक है।
होल्गर

2

वैसे ऐसा लगता है कि मेरा सवाल 'गलत' था क्योंकि इसका ऑपरेटर से कोई लेना-देना नहीं है, बल्कि यह क्षेत्र ही है। अभी भी पता नहीं क्यों ..

   public int test() {
        final int a = 7;
        final int b = this.taxos;
        return a % b;
    }

जो में बदल जाता है:

  public test()I
   L0
    LINENUMBER 51 L0
    BIPUSH 7
    ISTORE 1
   L1
    LINENUMBER 52 L1
    ALOAD 0
    INVOKESTATIC java/util/Objects.requireNonNull (Ljava/lang/Object;)Ljava/lang/Object;
    POP
    ICONST_4
    ISTORE 2
   L2
    LINENUMBER 53 L2
    BIPUSH 7
    ILOAD 2
    IREM
    IRETURN

1
क्या संकलक वास्तव में डर सकता है कि thisसंदर्भ null? क्या यह संभव होगा?
atalantus

1
नहीं, इसका कोई मतलब नहीं है, जब तक कि संकलक Integerकिसी भी तरह से क्षेत्र को संकलित नहीं करता है , और यह ऑटोबॉक्सिंग का परिणाम है?
रोबू

1
ALOAD 0संदर्भ नहीं है this? तो यह समझ में आता है (वास्तव में नहीं) कि संकलक
लिनो

1
तो संकलक वास्तव में के लिए एक अशक्त जाँच जोड़ रहा है this? महान: /
रोबाऊ

1
मैं javacकल को सत्यापित करने के लिए कमांड-लाइन के साथ कोड का एक न्यूनतम टुकड़ा बनाने की कोशिश करूंगा; और अगर वह भी इस व्यवहार को दिखाता है, तो मुझे लगता है कि यह एक ज्वाला-बग हो सकता है?
15

2

सबसे पहले, यहाँ इस व्यवहार का एक न्यूनतम प्रतिलिपि प्रस्तुत करने योग्य उदाहरण है:

/**
 * OS:              Windows 10 64x
 * javac version:   13.0.1
 */
public class Test {
    private final int bar = 5;

    /**
     * public int foo();
     *   Code:
     *     0: iconst_5
     *     1: ireturn
     */
    public int foo() {
        return bar;
    }

    /**
     * public int foo2();
     *   Code:
     *     0: aload_0
     *     1: invokestatic  #13     // Method java/util/Objects.requireNonNull:(Ljava/lang/Object;)Ljava/lang/Object;
     *     4: pop
     *     5: iconst_5
     *     6: ireturn
     */
    public int foo2() {
        return this.bar;
    }
}

व्यवहार यह है कि जावा संकलक संकलन-समय स्थिरांक का अनुकूलन कैसे करता है

ध्यान दें कि foo()किसी वस्तु संदर्भ के बाइट कोड में मान प्राप्त करने के लिए प्रवेश किया जाता है bar। ऐसा इसलिए है क्योंकि यह एक संकलन-समय स्थिर है और इस तरह JVM iconst_5इस मान को वापस करने के लिए ऑपरेशन को निष्पादित कर सकता है ।

जब barएक गैर-संकलन समय में बदलते हैं (या तो finalकीवर्ड को हटाकर या घोषणा के भीतर प्रारंभ न करके, लेकिन निर्माणकर्ता के अंदर):

/**
 * OS:              Windows 10 64x
 * javac version:   13.0.1
 */
public class Test2 {
    private int bar = 5;

    /**
     * public int foo();
     *   Code:
     *     0: aload_0
     *     1: getfield      #7
     *     4: ireturn
     */
    public int foo() {
        return bar;
    }

    /**
     * public int foo2();
     *   Code:
     *     0: aload_0
     *     1: getfield      #7
     *     4: ireturn
     */
    public int foo2() {
        return this.bar;
    }
}

जहां तब इस ऑब्जेक्ट के क्षेत्र को प्राप्त करने के लिए ऑपरेंड स्टैक पर संदर्भ को aload_0धक्का देता है ।thisbar

यहाँ संकलक यह देखने के लिए पर्याप्त चतुर है कि aload_0( thisसदस्य कार्यों के संदर्भ में) तार्किक रूप से नहीं हो सकता है null

अब आपका मामला वास्तव में एक लापता संकलक अनुकूलन है?

@Maaartinus उत्तर देखें।

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