java - एक अपवाद फेंकने का कौन सा हिस्सा महंगा है?




performance exception (4)

अधिकांश Throwable कंस्ट्रक्टरों में पहला ऑपरेशन स्टैक ट्रेस को भरना है, जो कि अधिकांश खर्च होता है।

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

यदि आप सामान्य तरीकों से किसी भी प्रकार का एकल अपवाद बनाते हैं, तो आप स्टैक ट्रेस में भरने के ओवरहेड के बिना इसे कई बार फिर से फेंक सकते हैं। हालांकि, इसका स्टैक ट्रेस यह दर्शाता है कि इसका निर्माण कहां किया गया था, न कि जहां इसे एक विशेष उदाहरण में फेंका गया था।

जावा के वर्तमान संस्करण स्टैक ट्रेस निर्माण को अनुकूलित करने के लिए कुछ प्रयास करते हैं। स्टैक ट्रेस में भरने के लिए मूल कोड को आमंत्रित किया जाता है, जो एक हल्के वजन, मूल संरचना में ट्रेस को रिकॉर्ड करता है। जावा StackTraceElement अनुरूप ऑब्जेक्ट्स को इस रिकॉर्ड से केवल तभी बनाया जाता है जब getStackTrace() , printStackTrace() , या अन्य तरीकों से जिन्हें ट्रेस की आवश्यकता होती है, कहा जाता है।

यदि आप स्टैक ट्रेस जनरेशन को समाप्त करते हैं, तो अन्य मुख्य लागत थ्रो और कैच के बीच स्टैक को खोलना है। अपवाद को पकड़ने से पहले कम अंतराल वाले फ़्रेम का सामना करना पड़ता है, जितना तेज़ होगा।

अपने कार्यक्रम को डिज़ाइन करें ताकि अपवाद केवल वास्तव में असाधारण मामलों में ही फेंक दिए जाएं, और इन जैसे अनुकूलन को औचित्य देना मुश्किल है।

जावा में, तर्क के एक भाग के रूप में थ्रो / कैच का उपयोग करना जब वास्तव में कोई त्रुटि नहीं होती है तो आमतौर पर एक बुरा विचार (भाग में) होता है क्योंकि अपवाद को फेंकना और पकड़ना महंगा होता है, और इसे लूप में कई बार करना आमतौर पर अन्य की तुलना में बहुत धीमा होता है नियंत्रण संरचनाएं जिसमें अपवाद फेंकना शामिल नहीं है।

मेरा सवाल यह है कि क्या थ्रो / कैच खुद में खर्च होता है, या एक्सेप्शन ऑब्जेक्ट बनाते समय (क्योंकि इसमें निष्पादन स्टैक सहित बहुत से रनटाइम की जानकारी मिलती है)?

दूसरे शब्दों में, अगर मैं करता हूं

Exception e = new Exception();

लेकिन इसे फेंकना नहीं है, कि फेंकने की लागत का सबसे अधिक है, या क्या फेंक + पकड़ संभाल महंगा है?

मैं यह नहीं पूछ रहा हूं कि क्या कोड को एक कोशिश / कैच ब्लॉक में डालने से उस कोड को निष्पादित करने की लागत बढ़ जाती है, मैं पूछ रहा हूं कि क्या एक्सेप्शन को पकड़ना महंगा हिस्सा है, या बनाने (निर्माणकर्ता को कॉल करना) अपवाद महंगा हिस्सा है ।

यह पूछने का एक और तरीका है, अगर मैंने अपवाद का एक उदाहरण बनाया और फेंक दिया और इसे बार-बार पकड़ा, तो क्या मैं हर बार फेंकने से एक नया अपवाद बनाने की तुलना में काफी तेज हो जाएगा?


एक अपवाद ऑब्जेक्ट बनाना अन्य नियमित ऑब्जेक्ट बनाने से अधिक महंगा नहीं है। मुख्य लागत मूल fillInStackTrace पद्धति में छिपी हुई है जो कॉल स्टैक के माध्यम से चलती है और स्टैक ट्रेस बनाने के लिए सभी आवश्यक जानकारी एकत्र करती है: कक्षाएं, विधि नाम, लाइन नंबर आदि।

उच्च अपवाद लागतों के बारे में मिथक इस तथ्य से आता है कि ज्यादातर Throwable कंस्ट्रक्टरों को Throwable पर Throwable कहते हैं। हालांकि, स्टैक ट्रेस के बिना Throwable बनाने के लिए एक Throwable । यह आपको तुरंत फेंकने के लिए बहुत तेजी से फेंकने की अनुमति देता है। हल्के अपवाद बनाने का एक और तरीका है fillInStackTrace को ओवरराइड fillInStackTrace

अब एक अपवाद को फेंकने के बारे में क्या?
वास्तव में, यह इस बात पर निर्भर करता है कि फेंका गया अपवाद कहां पकड़ा गया है

यदि इसे उसी विधि में पकड़ा जाता है (या, अधिक सटीक रूप से, उसी संदर्भ में, क्योंकि संदर्भ में इनलाइनिंग के कारण कई विधियां शामिल हो सकती हैं), तो throw goto तरह तेज और सरल है (बेशक, जेआईटी संकलन के बाद)।

हालांकि अगर स्टैक में कहीं कोई catch ब्लॉक गहरा है, तो जेवीएम को स्टैक फ्रेम को खोलना होगा, और इसमें काफी समय लग सकता है। इसमें और भी अधिक समय लगता है, अगर इसमें synchronized किए synchronized ब्लॉक या तरीके शामिल हैं, क्योंकि अनइंडिंग का अर्थ है हटाए गए स्टैक फ्रेम के स्वामित्व वाले मॉनिटर को जारी करना।

मैं उचित बेंचमार्क द्वारा उपरोक्त कथनों की पुष्टि कर सकता हूं, लेकिन सौभाग्य से मुझे ऐसा करने की आवश्यकता नहीं है, क्योंकि सभी पहलुओं को पहले से ही हॉटस्पॉट के प्रदर्शन इंजीनियर एलेक्सी शिपिलेव: द एक्सेप्शनल परफॉरमेंस ऑफ लेल ’अपवाद के रूप में कवर किया गया है।


यहाँ अपवाद पर एक अच्छा लेखन है।

http://shipilev.net/blog/2014/exceptional-performance/

निष्कर्ष यह है कि स्टैक ट्रेस निर्माण और स्टैक अनइंडिंग महंगे हिस्से हैं। नीचे दिया गया कोड 1.7 में एक सुविधा का लाभ उठाता है जहां हम स्टैक के निशान को चालू और बंद कर सकते हैं। फिर हम इसका उपयोग यह देखने के लिए कर सकते हैं कि विभिन्न परिदृश्यों की लागत किस प्रकार की है

अकेले ऑब्जेक्ट निर्माण के लिए निम्नलिखित समय हैं। मैंने String यहाँ जोड़ा है ताकि आप देख सकें कि बिना स्टैक के लिखा जा सकता है कि JavaException Object और String बनाने में लगभग कोई अंतर नहीं है। स्टैक राइटिंग ऑन डिफरेंस नाटकीय है यानी कम से कम एक क्रम परिमाण धीमा है।

Time to create million String objects: 41.41 (ms)
Time to create million JavaException objects with    stack: 608.89 (ms)
Time to create million JavaException objects without stack: 43.50 (ms)

निम्नलिखित से पता चलता है कि एक विशेष गहराई पर एक थ्रो से एक लाख बार लौटने में कितना समय लगा।

|Depth| WriteStack(ms)| !WriteStack(ms)| Diff(%)|
|   16|           1428|             243| 588 (%)|
|   15|           1763|             393| 449 (%)|
|   14|           1746|             390| 448 (%)|
|   13|           1703|             384| 443 (%)|
|   12|           1697|             391| 434 (%)|
|   11|           1707|             410| 416 (%)|
|   10|           1226|             197| 622 (%)|
|    9|           1242|             206| 603 (%)|
|    8|           1251|             207| 604 (%)|
|    7|           1213|             208| 583 (%)|
|    6|           1164|             206| 565 (%)|
|    5|           1134|             205| 553 (%)|
|    4|           1106|             203| 545 (%)|
|    3|           1043|             192| 543 (%)| 

निम्नलिखित लगभग निश्चित रूप से सरलीकरण पर एक सकल है ...

यदि हम 16 की गहराई लेते हैं, तो स्टैक राइटिंग के साथ ऑब्जेक्ट निर्माण लगभग ~ 40% समय ले रहा है, वास्तविक स्टैक ट्रेस इसमें से अधिकांश के लिए खाता है। ~ 93% JavaException ऑब्जेक्ट को इंस्टेंट करने के कारण स्टैक ट्रेस लिया जा रहा है। इसका मतलब यह है कि इस मामले में स्टैक को खोलना अन्य 50% समय ले रहा है।

जब हम स्टैक ट्रेस ऑब्जेक्ट निर्माण खातों को बहुत छोटे अंश के लिए बंद कर देते हैं अर्थात 20% और स्टैक अनइंडिंग अब 80% समय के लिए खाते हैं।

दोनों मामलों में स्टैक अनइंडिंग समग्र समय का एक बड़ा हिस्सा लेता है।

public class JavaException extends Exception {
  JavaException(String reason, int mode) {
    super(reason, null, false, false);
  }
  JavaException(String reason) {
    super(reason);
  }

  public static void main(String[] args) {
    int iterations = 1000000;
    long create_time_with    = 0;
    long create_time_without = 0;
    long create_string = 0;
    for (int i = 0; i < iterations; i++) {
      long start = System.nanoTime();
      JavaException jex = new JavaException("testing");
      long stop  =  System.nanoTime();
      create_time_with += stop - start;

      start = System.nanoTime();
      JavaException jex2 = new JavaException("testing", 1);
      stop = System.nanoTime();
      create_time_without += stop - start;

      start = System.nanoTime();
      String str = new String("testing");
      stop = System.nanoTime();
      create_string += stop - start;

    }
    double interval_with    = ((double)create_time_with)/1000000;
    double interval_without = ((double)create_time_without)/1000000;
    double interval_string  = ((double)create_string)/1000000;

    System.out.printf("Time to create %d String objects: %.2f (ms)\n", iterations, interval_string);
    System.out.printf("Time to create %d JavaException objects with    stack: %.2f (ms)\n", iterations, interval_with);
    System.out.printf("Time to create %d JavaException objects without stack: %.2f (ms)\n", iterations, interval_without);

    JavaException jex = new JavaException("testing");
    int depth = 14;
    int i = depth;
    double[] with_stack    = new double[20];
    double[] without_stack = new double[20];

    for(; i > 0 ; --i) {
      without_stack[i] = jex.timerLoop(i, iterations, 0)/1000000;
      with_stack[i]    = jex.timerLoop(i, iterations, 1)/1000000;
    }
    i = depth;
    System.out.printf("|Depth| WriteStack(ms)| !WriteStack(ms)| Diff(%%)|\n");
    for(; i > 0 ; --i) {
      double ratio = (with_stack[i] / (double) without_stack[i]) * 100;
      System.out.printf("|%5d| %14.0f| %15.0f| %2.0f (%%)| \n", i + 2, with_stack[i] , without_stack[i], ratio);
      //System.out.printf("%d\t%.2f (ms)\n", i, ratio);
    }
  }
 private int thrower(int i, int mode) throws JavaException {
    ExArg.time_start[i] = System.nanoTime();
    if(mode == 0) { throw new JavaException("without stack", 1); }
    throw new JavaException("with stack");
  }
  private int catcher1(int i, int mode) throws JavaException{
    return this.stack_of_calls(i, mode);
  }
  private long timerLoop(int depth, int iterations, int mode) {
    for (int i = 0; i < iterations; i++) {
      try {
        this.catcher1(depth, mode);
      } catch (JavaException e) {
        ExArg.time_accum[depth] += (System.nanoTime() - ExArg.time_start[depth]);
      }
    }
    //long stop = System.nanoTime();
    return ExArg.time_accum[depth];
  }

  private int bad_method14(int i, int mode) throws JavaException  {
    if(i > 0) { this.thrower(i, mode); }
    return i;
  }
  private int bad_method13(int i, int mode) throws JavaException  {
    if(i == 13) { this.thrower(i, mode); }
    return bad_method14(i,mode);
  }
  private int bad_method12(int i, int mode) throws JavaException{
    if(i == 12) { this.thrower(i, mode); }
    return bad_method13(i,mode);
  }
  private int bad_method11(int i, int mode) throws JavaException{
    if(i == 11) { this.thrower(i, mode); }
    return bad_method12(i,mode);
  }
  private int bad_method10(int i, int mode) throws JavaException{
    if(i == 10) { this.thrower(i, mode); }
    return bad_method11(i,mode);
  }
  private int bad_method9(int i, int mode) throws JavaException{
    if(i == 9) { this.thrower(i, mode); }
    return bad_method10(i,mode);
  }
  private int bad_method8(int i, int mode) throws JavaException{
    if(i == 8) { this.thrower(i, mode); }
    return bad_method9(i,mode);
  }
  private int bad_method7(int i, int mode) throws JavaException{
    if(i == 7) { this.thrower(i, mode); }
    return bad_method8(i,mode);
  }
  private int bad_method6(int i, int mode) throws JavaException{
    if(i == 6) { this.thrower(i, mode); }
    return bad_method7(i,mode);
  }
  private int bad_method5(int i, int mode) throws JavaException{
    if(i == 5) { this.thrower(i, mode); }
    return bad_method6(i,mode);
  }
  private int bad_method4(int i, int mode) throws JavaException{
    if(i == 4) { this.thrower(i, mode); }
    return bad_method5(i,mode);
  }
  protected int bad_method3(int i, int mode) throws JavaException{
    if(i == 3) { this.thrower(i, mode); }
    return bad_method4(i,mode);
  }
  private int bad_method2(int i, int mode) throws JavaException{
    if(i == 2) { this.thrower(i, mode); }
    return bad_method3(i,mode);
  }
  private int bad_method1(int i, int mode) throws JavaException{
    if(i == 1) { this.thrower(i, mode); }
    return bad_method2(i,mode);
  }
  private int stack_of_calls(int i, int mode) throws JavaException{
    if(i == 0) { this.thrower(i, mode); }
    return bad_method1(i,mode);
  }
}

class ExArg {
  public static long[] time_start;
  public static long[] time_accum;
  static {
     time_start = new long[20];
     time_accum = new long[20];
  };
}

इस उदाहरण में स्टैक फ्रेम आपके द्वारा सामान्य रूप से मिलने वाली तुलना में छोटे हैं।

आप javap का उपयोग करके bytecode में झाँक सकते हैं

javap -c -v -constants JavaException.class

अर्थात यह विधि 4 के लिए है ...

   protected int bad_method3(int, int) throws JavaException;
flags: ACC_PROTECTED
Code:
  stack=3, locals=3, args_size=3
     0: iload_1       
     1: iconst_3      
     2: if_icmpne     12
     5: aload_0       
     6: iload_1       
     7: iload_2       
     8: invokespecial #6                  // Method thrower:(II)I
    11: pop           
    12: aload_0       
    13: iload_1       
    14: iload_2       
    15: invokespecial #17                 // Method bad_method4:(II)I
    18: ireturn       
  LineNumberTable:
    line 63: 0
    line 64: 12
  StackMapTable: number_of_entries = 1
       frame_type = 12 /* same */

Exceptions:
  throws JavaException

सवाल का यह हिस्सा ...

यह पूछने का एक और तरीका है, अगर मैंने अपवाद का एक उदाहरण बनाया और फेंक दिया और इसे बार-बार पकड़ा, तो क्या मैं हर बार फेंकने से एक नया अपवाद बनाने की तुलना में काफी तेज हो जाएगा?

लगता है कि अगर एक अपवाद बनाने और कैशिंग यह कहीं प्रदर्शन में सुधार करता है। हाँ यह करता है। यह ऑब्जेक्ट निर्माण पर लिखे जा रहे स्टैक को बंद करने के समान है क्योंकि यह पहले से ही किया गया है।

ये मुझे मिली टाइमिंग है, कृपया इसके बाद कैविटी पढ़ें ...

|Depth| WriteStack(ms)| !WriteStack(ms)| Diff(%)|
|   16|            193|             251| 77 (%)| 
|   15|            390|             406| 96 (%)| 
|   14|            394|             401| 98 (%)| 
|   13|            381|             385| 99 (%)| 
|   12|            387|             370| 105 (%)| 
|   11|            368|             376| 98 (%)| 
|   10|            188|             192| 98 (%)| 
|    9|            193|             195| 99 (%)| 
|    8|            200|             188| 106 (%)| 
|    7|            187|             184| 102 (%)| 
|    6|            196|             200| 98 (%)| 
|    5|            197|             193| 102 (%)| 
|    4|            198|             190| 104 (%)| 
|    3|            193|             183| 105 (%)| 

बेशक इसके साथ समस्या यह है कि आपका स्टैक ट्रेस अब इंगित करता है कि आपने उस ऑब्जेक्ट को तत्काल कहाँ नहीं फेंक दिया है।






exception