2009-02-19 22 views
20

क्या आप जानते हैं कि जावा में कितना महंगा अपवाद फेंकना और हैंडलिंग करना है?कितने महंगे हैं अपवाद

हमारी टीम में अपवादों की वास्तविक लागत के बारे में हमने कई चर्चाएं की थीं। कुछ लोग जितनी बार संभव हो उससे बचते हैं, कुछ कहते हैं कि अपवादों का उपयोग करके प्रदर्शन की हानि अतिरंजित है।

आज मैं हमारे सॉफ्टवेयर में कोड का निम्न भाग पाया:

private void doSomething() 
{ 
    try 
    { 
     doSomethingElse(); 
    } 
    catch(DidNotWorkException e) 
    { 
     log("A Message"); 
    } 
    goOn(); 
} 
private void doSomethingElse() 
{ 
    if(isSoAndSo()) 
    { 
     throw new DidNotWorkException(); 
    } 
    goOnAgain(); 
} 

कैसे

private void doSomething() 
{ 
    doSomethingElse(); 
    goOn(); 
} 
private void doSomethingElse() 
{ 
    if(isSoAndSo()) 
    { 
     log("A Message"); 
     return; 
    } 
    goOnAgain(); 
} 

मैं कोड सौंदर्य या कुछ भी चर्चा नहीं करना चाहते हैं की तुलना में इस बात का प्रदर्शन किया जाता है, यह सिर्फ रनटाइम व्यवहार के बारे में है! क्या आपके पास वास्तविक अनुभव/माप हैं?

+1

असाधारण रूप से महंगा। –

उत्तर

6

मैं अपवाद पर पढ़ने के लिए परेशान नहीं किया है, लेकिन तुम्हारा के कुछ संशोधित कोड के साथ एक बहुत त्वरित परीक्षण कर रहा निष्कर्ष पर आते हैं कि बुलियन मामले की तुलना में अपवाद परिस्थिति काफी धीमी है।

Exception:20891ms 
Boolean:62ms 

इस कोड से::

मैं निम्नलिखित परिणाम मिला

public class Test { 
    public static void main(String args[]) { 
      Test t = new Test(); 
      t.testException(); 
      t.testBoolean(); 
    } 
    public void testException() { 
      long start = System.currentTimeMillis(); 
      for(long i = 0; i <= 10000000L; ++i) 
        doSomethingException(); 
      System.out.println("Exception:" + (System.currentTimeMillis()-start) + "ms"); 
    } 
    public void testBoolean() { 
      long start = System.currentTimeMillis(); 
      for(long i = 0; i <= 10000000L; ++i) 
        doSomething(); 
      System.out.println("Boolean:" + (System.currentTimeMillis()-start) + "ms"); 
    } 

    private void doSomethingException() { 
     try { 
      doSomethingElseException(); 
     } catch(DidNotWorkException e) { 
      //Msg 
     } 
    } 
    private void doSomethingElseException() throws DidNotWorkException { 
     if(!isSoAndSo()) { 
      throw new DidNotWorkException(); 
     } 
    } 
    private void doSomething() { 
     if(!doSomethingElse()) 
      ;//Msg 
    } 
    private boolean doSomethingElse() { 
     if(!isSoAndSo()) 
      return false; 
     return true; 
    } 
    private boolean isSoAndSo() { return false; } 
    public class DidNotWorkException extends Exception {} 
} 

मैं मूर्खता मेरी कोड काफी अच्छी तरह से पढ़ नहीं किया था और पहले से उस में एक बग था (कैसे शर्मनाक), अगर कोई इस कोड को तीन बार जांच सकता है तो मैं इसे बहुत सराहना करता हूं, बस अगर मैं सिनिल जा रहा हूं।

मेरे विनिर्देश है:

  • संकलित और पर 1.5.0_16
  • सूर्य JVM चलाने
  • WinXP SP3
  • इंटेल Centrino जोड़ी T7200 (2.00Ghz, 977Mhz)
  • 2.00 जीबी राम

मेरी राय में आपको ध्यान देना चाहिए कि अपवाद hods ऐसा करने में लॉग त्रुटि नहीं देते हैं कुछ भी है लेकिन इसके बजाय एक बुलियन लौटता है ताकि कॉलिंग कोड विफलता से निपट सके। यदि ऐसे कई क्षेत्र हैं जिनमें यह असफल हो सकता है तो अंदर एक त्रुटि लॉगिंग या अपवाद फेंकने की आवश्यकता हो सकती है।

+0

क्या आप सुनिश्चित हैं, आपने निजी बूलियन isSoAndSo() {वापसी ** झूठी ** के साथ परीक्षण किया है; } उस स्थिति में मैं आपके परिणामों के बारे में आश्चर्यचकित हूं। –

+0

हे भगवान, आप जानते हैं, मुझे उन खूनी दिनों में से एक है :(मेरे द्वारा दिए गए परिणाम सत्य थे, अगर अपवाद नहीं फेंक दिया गया है तो 2x अंतर है। –

+0

कोई प्रश्न नहीं और इस प्रश्न पर आपके सभी कार्यों के लिए धन्यवाद –

11

अपवाद स्वतंत्र नहीं हैं ... इसलिए वे कर रहे हैं महंगा :-)

पुस्तक Effective Java अच्छा विस्तार से इस को शामिल किया गया।

  • आइटम 39 असाधारण स्थितियों के लिए अपवादों का उपयोग करें।
  • वसूली की स्थिति

लेखक के लिए आइटम 40 उपयोग अपवादों में पाया गया कि अपवाद कोड ट्यूनिंग, उसकी विशेष वी एम और ओएस कॉम्बो के साथ 70 बार उसकी मशीन पर अपने परीक्षण मामले के लिए धीमी में हुई।

+0

तार्किक झुकाव - वे सिर्फ सस्ते हो सकते हैं! – alex

+0

नहीं ... वे नहीं हैं :-) (लेकिन मुझे उद्धरण मिलना था ...) – TofuBeer

+0

अपवाद महंगे हैं, वास्तव में। मैंने एक बेंचमार्क बनाया है जहां अपवाद केवल 1% कॉल में होता है, जो कम या ज्यादा यथार्थवादी मामला है। इन परिस्थितियों में भी, प्रदर्शन दंड बहुत अधिक है, 70 गुना नहीं, लेकिन अभी भी बहुत अधिक है। स्रोत कोड और बेंचमार्क परिणाम देखें [यहां] (http://www.jquantlib.org/index.php/Cost_of_Exceptions_in_Java)। –

3

मेरे पास कोई वास्तविक माप नहीं है, लेकिन अपवाद फेंकना अधिक महंगा है।

ठीक है, यह नेट ढांचे के बारे में एक कड़ी है, लेकिन मुझे लगता है कि एक ही रूप में अच्छी तरह जावा के लिए लागू होता है:

exceptions & performance

कहा कि, आप जब उन्हें इस्तेमाल करने में संकोच नहीं करना चाहिए उचित । यही है: फ्लो-कंट्रोल के लिए उनका इस्तेमाल न करें, लेकिन असाधारण खुशी के दौरान उनका उपयोग करें; ऐसा कुछ जिसे आप होने की उम्मीद नहीं करते थे।

+0

पूरी तरह से सही - यदि आप असाधारण मामलों के लिए अपवाद का उपयोग करते हैं, तो आप ठीक हैं। यदि आप प्रति सेकंड कई फेंक देते हैं, तो यह आपको धीमा कर देगा, क्योंकि वे इस तरह प्रवाह नियंत्रण के लिए डिज़ाइन नहीं किए गए हैं। – ojrac

+0

हम्म, डाउनग्रेड क्यों? अगर कोई जवाब डाउनग्रेड किया गया है, तो आपको प्रेरित करना चाहिए क्यों। JVM विशिष्ट के लिए –

3

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

4

यह मूल रूप से जेवीएम विशिष्ट है, इसलिए आपको जो भी सलाह दी जाती है, उसे अंधाधुंध पर भरोसा नहीं करना चाहिए, बल्कि वास्तव में आपकी स्थिति में उपाय करना चाहिए। किसी न किसी विचार को प्राप्त करने के लिए "एक लाख अपवादों को फेंकना और System.currentTimeMillis के अंतर को मुद्रित करना" मुश्किल नहीं होना चाहिए।

आपके द्वारा सूचीबद्ध कोड स्निपेट के लिए, मुझे व्यक्तिगत रूप से मूल लेखक को पूरी तरह से दस्तावेज करने की आवश्यकता होती है कि उन्होंने यहां अपवाद का उपयोग क्यों किया क्योंकि यह "कम से कम आश्चर्य का मार्ग" नहीं है जो इसे बाद में बनाए रखने के लिए महत्वपूर्ण है।

(जब भी आप एक ठोस तरीके से कुछ करते हैं तो आप पाठक द्वारा अनचाहे काम करने का कारण बनते हैं ताकि यह समझ सके कि आपने इसे सामान्य तरीके से क्यों किया है - यह काम मेरी राय में उचित होना चाहिए लेखक ध्यान से समझाते हैं कि ऐसा क्यों किया गया क्योंकि एक कारण होना चाहिए)।

अपवाद एक बहुत, बहुत उपयोगी उपकरण हैं, लेकिन केवल इस्तेमाल किया जाना चाहिए जब आवश्यक :)

+1

+1। जावा 1.4 में ट्राइक-कैच ब्लॉक के साथ बड़ी प्रदर्शन समस्याएं थीं उदाहरण के लिए (ज्यादातर बाद में रिलीज़ में सुधार)। J12 विशिष्ट के लिए – cletus

+0

+1 और वास्तव में इसका परीक्षण। जावा गति में एक लंबा रास्ता आ गया है। – Kiv

0

मुझे लगता है कि आप थोड़ा गलत कोण से यह पूछ रहे हैं। अपवादों को असाधारण मामलों को संकेत देने के लिए डिज़ाइन किया गया है, और उन मामलों के लिए प्रोग्राम प्रवाह तंत्र के रूप में। तो सवाल जो आपको पूछना चाहिए, अपवादों के लिए कोड कॉल का "तर्क" है।

अपवाद आमतौर पर उस उपयोग में पर्याप्त प्रदर्शन करने के लिए डिज़ाइन किए जाते हैं जिसके लिए उनका इरादा है। यदि उनका उपयोग इस तरह से किया जाता है कि वे एक बाधा हैं, तो सबसे ऊपर, यह शायद एक संकेत है कि उनका उपयोग सिर्फ "गलत चीज़" पूर्ण स्टॉप के लिए किया जा रहा है - यानि आपके पास अंतर्निहित रूप से एक प्रोग्राम है प्रदर्शन समस्या के बजाय समस्या।

इसके विपरीत, यदि अपवाद "सही चीज़ के लिए उपयोग किया जा रहा" प्रतीत होता है, तो शायद इसका मतलब है कि यह भी ठीक प्रदर्शन करेगा।

11

अपवाद फेंकने का सबसे धीमा हिस्सा filling in the stack trace है।

यदि आप अपना अपवाद पूर्व-निर्मित करते हैं और इसका पुनः उपयोग करते हैं, तो JIT इसे "a machine level goto" पर अनुकूलित कर सकता है।

यह सब कहा गया है, जब तक कि आपके प्रश्न का कोड वास्तव में तंग पाश में न हो, अंतर नगण्य होगा।

+0

हो सकता है कि आप इसे अपरिपक्व किए बिना नए अपवाद बना सकें यदि आपका अपवाद कन्स्ट्रक्टर सुपर कॉल करता है (msg, कारण,/* enableSuppression */false,/* writableStackTrace */false); – giampaolo

3

सभी प्रतिक्रियाओं के लिए धन्यवाद।

अंततः मैंने थोरबोजर्न के सुझाव का पालन किया और थोड़ा परीक्षण कार्यक्रम लिखा, जो प्रदर्शन को मापता था। परिणाम यह है: कोई दो प्रकारों (प्रदर्शन के मामलों में) के बीच अंतर।

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

यह पता चला कि लॉगिंग ("एक संदेश") (अन्य सभी चीजों की तुलना में) है बहुत महंगा, इसलिए मुझे लगता है, मैं इससे छुटकारा पाउंगा।

संपादित:

परीक्षण कोड बिल्कुल मूल पोस्ट में से एक है, एक पाश में एक विधि testPerfomance() द्वारा कहा जाता है जो System.currentTimeMillis() -calls से घिरा हुआ है निष्पादन समय प्राप्त करने के लिए ... लेकिन की तरह है:

मैंने अब परीक्षण कोड की समीक्षा की है, सबकुछ (लॉग स्टेटमेंट) से बदलकर और 100 गुना अधिक लूपिंग, इससे पहले और यह पता चला है कि आप ए के बजाय बी का उपयोग करते समय दस लाख सेकेंड के लिए 4.7 सेकेंड बचाते हैं मूल पोस्ट जैसा कि रॉन ने कहा fillStackTrace सबसे महंगा हिस्सा है (इसके लिए +1) और यदि आप इसे ओवरराइट करते हैं तो आप लगभग उसी (4.5 सेकंड) को बचा सकते हैं (यदि आपको इसकी आवश्यकता नहीं है, तो मुझे पसंद है)। सब कुछ अभी भी मेरे मामले में लगभग शून्य अंतर है, क्योंकि कोड को 1000 बार एक घंटे कहा जाता है और माप दिखाता है कि मैं उस समय 4.5 मिलियन बचा सकता हूं ...

तो, ऊपर मेरा पहला उत्तर भाग थोड़ा भ्रामक था, लेकिन एक रिफैक्टरिंग के लागत-लाभ को संतुलित करने के बारे में मैंने जो कहा, वह सच है।

+0

आपके परीक्षण में, अपवाद को कैसे हटाया गया है? हर बार या बहुत कम बार? – TofuBeer

+0

क्या आप अपने परीक्षणों के लिए कोड पोस्ट कर सकते हैं? साथ ही साथ JVM और OS आप पर हैं? (मुझे यह विश्वास करना मुश्किल लगता है कि समय शून्य है या बहुत छोटा है क्योंकि अधिकांश जेवीएम विक्रेता अपवाद मामले को अनुकूलित करने से परेशान नहीं होंगे)। – TofuBeer

+0

ओह ... और यदि यह उत्पादन कोड में है कि आप गति में कोई बदलाव नहीं देख रहे हैं ... 1) हर बार एक विधि कहने पर आप अपवाद क्यों फेंक रहे हैं? 2) इसे कितनी बार बुलाया जाता है (यदि यह अक्सर नहीं होता है तो समय बड़ा नहीं होगा) – TofuBeer

8

अपवादों के बारे में धीमा हिस्सा स्टैक ट्रेस (java.lang.Throwable के निर्माता में) का निर्माण कर रहा है, जो ढेर गहराई पर निर्भर करता है। अपने आप में फेंकना धीमा नहीं है।

विफलताओं को सिग्नल करने के लिए अपवादों का उपयोग करें। प्रदर्शन प्रभाव तब नगण्य है और स्टैक ट्रेस विफलता के कारण को पिन-पॉइंट करने में मदद करता है।

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

public class DidNotWorkException extends Exception { 
    public Throwable fillInStackTrace() { 
     return this; 
    } 
} 

-server मोड (संस्करण 1.6.0_24 में JVM का उपयोग कर इसे चल रहा है:

निम्नलिखित सूक्ष्म बेंचमार्क (albeit flawed) accepted answer में करने के लिए एक सरल विधि जोड़कर स्टैक ट्रेस बिना अपवाद को दर्शाता है) में परिणाम:

Exception:99ms 
Boolean:12ms 

Exception:92ms 
Boolean:11ms 

अंतर अभ्यास में अनजान होने के लिए काफी छोटा है।

+0

@ डेव जार्विस - यदि वे दो अलग-अलग रन और प्रतिनिधि परिणाम हैं, तो मैं 900% निष्पादन समय को "शायद ही कोई अंतर" कहूंगा ... अगर हम सभी हैलो वर्ल्ड कोडिंग कर रहे हैं, तो हाँ अपवादों के साथ पागल हो जाओ ... –

+1

यह शायद एक अंतर के बजाय 10 के कारक की तरह दिखता है। यदि आप इसे कई स्थानों पर उपयोग करते हैं, तो इससे बहुत दर्द होगा। जावा में –

0

मान लें कि कथन 1 और 2 निष्पादित करने का प्रयास करते समय अपवाद नहीं होगा। क्या उन दो नमूना-कोडों के बीच कोई प्रदर्शन हिट है?

यदि नहीं, तो DoSomething() विधि को huuuge कार्य की मात्रा (अन्य तरीकों से कॉल का भार इत्यादि) करना है तो क्या होगा?

1:

try 
{ 
    DoSomething(); 
} 
catch (...) 
{ 
    ... 
} 

2:

DoSomething(); 
+0

आज़माएं मुफ्त है - फेंकने की लागत (और कोशिश वास्तव में मुफ़्त नहीं है ... लेकिन चलो भूलें मैंने कहा :-) तो अगर अपवाद फेंक दिया गया तो कोई गति हिट नहीं होती है। तर्क का एक हिस्सा यह है कि फेंक प्रवाह प्रवाह के लिए उपयोग नहीं किया जाना चाहिए, इसलिए इसे अनुकूलित नहीं किया गया है - यह असामान्य मामला सामान्य नहीं है। – TofuBeer

संबंधित मुद्दे