मैं C ++ में सिस्टेमैटिक एरर हैंडलिंग देख रहा था - आंद्रेई अलेक्जेंड्रेस्कु उनका दावा है कि C ++ में एक्सेप्शन बहुत धीमे हैं।
क्या यह अभी भी C ++ 98 के लिए सही है?
मैं C ++ में सिस्टेमैटिक एरर हैंडलिंग देख रहा था - आंद्रेई अलेक्जेंड्रेस्कु उनका दावा है कि C ++ में एक्सेप्शन बहुत धीमे हैं।
क्या यह अभी भी C ++ 98 के लिए सही है?
जवाबों:
अपवादों के लिए आज इस्तेमाल किया जाने वाला मुख्य मॉडल (इटेनियम एबीआई, वीसी ++ 64 बिट्स) जीरो-कॉस्ट मॉडल अपवाद है।
विचार यह है कि एक गार्ड की स्थापना करके समय खोने के बजाय और स्पष्ट रूप से हर जगह अपवादों की उपस्थिति के लिए जाँच की जाती है, कंपाइलर एक साइड टेबल बनाता है जो किसी भी बिंदु को मैप करता है जो अपवाद (प्रोग्राम काउंटर) को हैंडलर की सूची में फेंक सकता है। जब एक अपवाद को फेंक दिया जाता है, तो इस सूची को सही हैंडलर (यदि कोई हो) लेने के लिए सलाह दी जाती है और स्टैक निराधार होता है।
ठेठ if (error)रणनीति की तुलना में :
ifएक अपवाद होने पर इसकी लागत लगभग 10x / 20x होती हैहालांकि, लागत को मापने के लिए तुच्छ नहीं है:
dynamic_castप्रत्येक हैंडलर के लिए एक परीक्षण)तो, ज्यादातर कैश की कमी होती है, और इस तरह शुद्ध सीपीयू कोड की तुलना में तुच्छ नहीं।
नोट: अधिक जानकारी के लिए, TR18015 रिपोर्ट पढ़ें , अध्याय 5.4 अपवाद हैंडलिंग (पीडीएफ)
तो, हाँ, अपवाद असाधारण पथ पर धीमा हैं , लेकिन वे ifसामान्य रूप से स्पष्ट जांच ( रणनीति) की तुलना में अधिक तेज हैं ।
नोट: आंद्रेई अलेक्जेंड्रेस्कु इस "क्विकर" पर सवाल उठाता है। मैंने व्यक्तिगत रूप से चीजों को दोनों तरह से देखा है, कुछ कार्यक्रम अपवादों के साथ तेजी से हो रहे हैं और अन्य शाखाओं के साथ तेजी से हो रहे हैं, इसलिए वास्तव में कुछ स्थितियों में अनुकूलन क्षमता का नुकसान होता है।
फर्क पड़ता है क्या ?
मैं यह दावा करता हूं कि ऐसा नहीं है। एक कार्यक्रम को ध्यान में पठनीयता के साथ लिखा जाना चाहिए , प्रदर्शन नहीं (कम से कम, पहली कसौटी के रूप में नहीं)। अपवादों का उपयोग तब किया जाता है जब कोई यह उम्मीद करता है कि कॉलर मौके पर विफलता को संभालने की इच्छा नहीं कर सकता है या नहीं, और इसे स्टैक को पारित कर देगा। बोनस: C ++ 11 अपवादों में मानक पुस्तकालय का उपयोग करते हुए थ्रेड्स के बीच marshalled किया जा सकता है।
यह सूक्ष्म हालांकि है, मुझे लगता है कि दावा map::findफेंक नहीं करना चाहिए, लेकिन मैं के साथ ठीक हूँ map::findलौटने एक checked_ptrजो अगर एक प्रयास भिन्नता के लिए यह विफल रहता है, क्योंकि यह के अशक्त फेंकता है: उत्तरार्द्ध मामले में, वर्ग के मामले कि Alexandrescu शुरू की, के रूप में फोन करने वाले चुनता स्पष्ट जाँच और अपवादों पर निर्भर होने के बीच। उसे अधिक जिम्मेदारी दिए बिना कॉल करने वाले को सशक्त बनाना आमतौर पर अच्छे डिजाइन का संकेत है।
abortआपको द्विआधारी आकार के पदचिह्न को मापने और यह जांचने की अनुमति देगा कि लोड-टाइम / आई-कैश समान व्यवहार करता है। बेशक, बेहतर नहीं किसी भी हिट abort...
जब प्रश्न पोस्ट किया गया तो मैं टैक्सी के इंतजार में डॉक्टर के पास गया था, इसलिए मेरे पास केवल समय था, फिर एक छोटी टिप्पणी के लिए। लेकिन अब टिप्पणी की है और upvoted और नीचा दिखाया मैं बेहतर अपने जवाब जोड़ देंगे। भले ही मैथ्यू का जवाब पहले से ही काफी अच्छा हो।
दावा फिर से करें
"मैं C ++ में सिस्टेमैटिक एरर हैंडलिंग देख रहा था - आंद्रेई अलेक्जेंड्रेस्कु का दावा है कि C ++ में एक्सेप्शन बहुत धीमा है।"
अगर यह सचमुच आंद्रेई का दावा है, तो एक बार के लिए वह बहुत ही भ्रामक है, अगर यह गलत नहीं है। प्रोग्रामिंग की भाषा की परवाह किए बिना भाषा में अन्य बुनियादी संचालन की तुलना में एक उठाया / फेंका अपवाद हमेशा धीमा होता है । केवल सी ++ में या अन्य भाषाओं की तुलना में सी ++ में अधिक नहीं, जैसा कि कथित दावा इंगित करता है।
सामान्य तौर पर, ज्यादातर भाषा की परवाह किए बिना, दो बुनियादी भाषा सुविधाएँ जो कि बाकी की तुलना में परिमाण धीमी करने के आदेश हैं, क्योंकि वे रूटीन के कॉल का अनुवाद करते हैं जो जटिल डेटा संरचनाओं को संभालते हैं,
अपवाद फेंकने, और
गतिशील स्मृति आवंटन।
सी ++ में खुशी से एक बार-महत्वपूर्ण कोड में दोनों से बच सकते हैं।
दुर्भाग्य से वहाँ एक नि: शुल्क दोपहर के भोजन के रूप में ऐसी कोई बात नहीं है , भले ही सी ++ की डिफ़ॉल्ट दक्षता बहुत करीब आती है। :-) अपवाद फेंकने से बचने के लिए प्राप्त दक्षता और गतिशील मेमोरी आवंटन आमतौर पर अमूर्त के निचले स्तर पर कोडिंग द्वारा प्राप्त किया जाता है, सी ++ का उपयोग केवल "बेहतर सी" के रूप में किया जाता है। और कम अमूर्तता का अर्थ है "जटिलता"।
ग्रेटर जटिलता का अर्थ है, रखरखाव पर अधिक समय और कोड के पुन: उपयोग से कोई लाभ नहीं, जो वास्तविक मौद्रिक लागत हैं, भले ही अनुमान लगाना या मापना मुश्किल हो। यानी, C ++ एक के साथ, यदि वांछित हो, तो निष्पादन दक्षता के लिए कुछ प्रोग्रामर दक्षता का व्यापार करें। क्या ऐसा करना काफी हद तक एक इंजीनियरिंग और आंत-संबंधी निर्णय है, क्योंकि व्यवहार में केवल लाभ, लागत नहीं, आसानी से अनुमान लगाया जा सकता है और मापा जा सकता है।
हां, अंतर्राष्ट्रीय C ++ मानकीकरण समिति ने C ++ प्रदर्शन, TR18015 पर एक तकनीकी रिपोर्ट प्रकाशित की है ।
मुख्य रूप से इसका मतलब है कि हैंडलर की खोज के कारण, throwउदाहरण intके लिए, असाइनमेंट की तुलना में बहुत लंबा समय ले सकता है ।
TR18015 के रूप में इसके खंड 5.4 "अपवाद" में चर्चा की गई है कि कार्यान्वयन रणनीतियों को संभालने वाले दो प्रमुख अपवाद हैं,
एप्रोच जहां प्रत्येक- tryडिस्क डायनामिक रूप से अपवाद पकड़ने को सेट करता है, ताकि हैंडलर की डायनामिक चेन की खोज एक अपवाद को फेंकने पर की जाए और
एप्रोच जहां संकलक स्थिर लुक-अप टेबल उत्पन्न करता है जो कि हैंडलर को फेंकने के लिए निर्धारित करने के लिए उपयोग किया जाता है।
32-बिट विंडोज में पहला बहुत लचीला और सामान्य दृष्टिकोण लगभग मजबूर है, जबकि 64-बिट भूमि और * निक्स-लैंड में दूसरा अधिक कुशल दृष्टिकोण आमतौर पर उपयोग किया जाता है।
जैसा कि उस रिपोर्ट में चर्चा की गई है, प्रत्येक दृष्टिकोण के लिए तीन मुख्य क्षेत्र हैं जहां दक्षता पर प्रभाव से निपटने के अपवाद हैं:
try-blocks,
नियमित कार्य (अनुकूलन अवसर), और
throw-expressions।
मुख्य रूप से, गतिशील हैंडलर दृष्टिकोण (32-बिट विंडोज) अपवाद हैंडलिंग पर tryब्लॉक का प्रभाव पड़ता है , ज्यादातर भाषा की परवाह किए बिना (क्योंकि यह विंडोज ' संरचित अपवाद हैंडलिंग योजना द्वारा मजबूर है ), जबकि स्थिर तालिका दृष्टिकोण के लिए लगभग शून्य लागत है try- ब्लॉक। इस पर चर्चा करना एक बहुत अधिक स्थान और शोध की तुलना में एक एसओ उत्तर के लिए व्यावहारिक होगा। इसलिए, विवरण के लिए रिपोर्ट देखें।
दुर्भाग्य से, रिपोर्ट, 2006 से, पहले से ही 2012 के उत्तरार्ध के रूप में पहले से ही थोड़ा सा दिनांकित है, और जहां तक मुझे पता है कि कुछ भी तुलनीय नहीं है जो नया है।
एक अन्य महत्वपूर्ण परिप्रेक्ष्य यह है कि प्रदर्शन पर अपवादों के उपयोग का प्रभाव सहायक भाषा सुविधाओं की अलग-थलग दक्षता से बहुत अलग है, क्योंकि रिपोर्ट नोट्स के अनुसार,
"अपवाद से निपटने पर विचार करते समय, इसे त्रुटियों से निपटने के वैकल्पिक तरीकों के विपरीत होना चाहिए।"
उदाहरण के लिए:
विभिन्न प्रोग्रामिंग शैलियों (शुद्धता) के कारण रखरखाव की लागत
निरर्थक कॉल साइट ifविफलता जाँच बनाम केंद्रीकृतtry
कैशिंग समस्याएं (उदाहरण के लिए छोटा कोड कैश में फिट हो सकता है)
रिपोर्ट में विचार करने के लिए पहलुओं की एक अलग सूची है, लेकिन वैसे भी निष्पादन दक्षता के बारे में कठिन तथ्यों को प्राप्त करने का एकमात्र व्यावहारिक तरीका संभवतः एक ही कार्यक्रम को अपवाद का उपयोग करके लागू करना है और अपवादों का उपयोग नहीं करना है, विकास के समय पर एक तय टोपी के भीतर, और डेवलपर्स के साथ। हर तरह से परिचित है, और फिर MEASURE ।
सुधार लगभग हमेशा दक्षता ट्रम्प करता है।
अपवादों के बिना, निम्नलिखित आसानी से हो सकता है:
कुछ कोड P एक संसाधन प्राप्त करने या कुछ जानकारी की गणना करने के लिए है।
कॉलिंग कोड C को सफलता / विफलता के लिए जाँचना चाहिए था, लेकिन नहीं।
एक गैर-मौजूद संसाधन या अमान्य जानकारी का उपयोग C के बाद के कोड में किया जाता है, जिससे सामान्य तबाही होती है।
मुख्य समस्या बिंदु (2) है, जहां सामान्य रिटर्न कोड योजना के साथ कॉलिंग सी चेक करने के लिए मजबूर नहीं किया जाता है।
दो मुख्य दृष्टिकोण हैं जो इस तरह की जाँच को बल देते हैं:
जहां P फेल होने पर सीधे एक अपवाद को फेंक देता है।
जहां P एक वस्तु देता है जिसे C को अपने मुख्य मूल्य (अन्यथा एक अपवाद या समाप्ति) का उपयोग करने से पहले निरीक्षण करना पड़ता है ।
दूसरा दृष्टिकोण था, AFAIK, जिसे सबसे पहले बार्टन और नेकमैन ने अपनी पुस्तक * साइंटिफिक एंड इंजीनियरिंग C ++: एन इंट्रोडक्शन विद एडवांस्ड टेक्नीक्स एंड एग्ज़ाम्पल, में वर्णित किया है , जहाँ उन्होंने Fallow"संभावित" फंक्शन रिजल्ट के लिए क्लास बुलाया । एक समान वर्ग जिसे optionalअब बूस्ट लाइब्रेरी द्वारा पेश किया जाता है। और आप आसानी से एक Optionalवर्ग को लागू कर सकते हैं , std::vectorगैर-पीओडी परिणाम के मामले के लिए एक मूल्य वाहक के रूप में।
पहले दृष्टिकोण के साथ कॉलिंग सी के पास अपवाद संचालन तकनीकों का उपयोग करने के अलावा कोई विकल्प नहीं है। दूसरे दृष्टिकोण के साथ, हालांकि, कॉलिंग कोड सी ही यह तय कर सकता है कि ifआधारित जाँच करना है, या सामान्य अपवाद हैंडलिंग। इस प्रकार, दूसरा दृष्टिकोण प्रोग्रामर बनाम निष्पादन समय दक्षता व्यापार बंद करने का समर्थन करता है।
"मैं जानना चाहता हूं कि क्या यह अभी भी C ++ 98 के लिए सही है"
C ++ 98 पहला C ++ मानक था। अपवादों के लिए इसने अपवाद वर्गों के एक मानक पदानुक्रम (दुर्भाग्य से अपूर्ण) को पेश किया। प्रदर्शन पर मुख्य प्रभाव अपवाद विनिर्देशों की संभावना थी (सी ++ 11 में हटा दिया गया), जो हालांकि मुख्य विंडोज सी ++ कंपाइलर विज़ुअल सी ++ द्वारा पूरी तरह से कभी लागू नहीं किया गया था: विजुअल सी ++ सी ++ 98 अपवाद विनिर्देश वाक्यविन्यास को स्वीकार करता है, लेकिन सिर्फ उपेक्षा करता है अपवाद विनिर्देशों।
C ++ 03, C ++ 98 का सिर्फ एक तकनीकी गलियारा था। C ++ 03 में केवल वास्तव में नया था मूल्य आरंभीकरण । जिसका अपवादों से कोई लेना-देना नहीं है।
C ++ 11 मानक सामान्य अपवाद विनिर्देशों को हटा दिया गया, और noexceptकीवर्ड के साथ बदल दिया गया ।
C ++ 11 मानक ने भी अपवादों के भंडारण और पुनर्संरक्षण के लिए समर्थन जोड़ा, जो C भाषा के कॉलबैक में C ++ अपवादों के प्रचार के लिए महान है। यह समर्थन प्रभावी रूप से यह बताता है कि वर्तमान अपवाद को कैसे संग्रहीत किया जा सकता है। हालाँकि, जहाँ तक मुझे पता है कि प्रदर्शन पर कोई असर नहीं पड़ता है, सिवाय इस बात के कि नए कोड में अपवाद आसानी से C भाषा के कॉलबैक के दोनों ओर आसानी से उपयोग किए जा सकते हैं।
longjmpहैंडलर के पास हैं।
try..finallyनिर्माण स्टैक के बिना लागू किया जा सकता। F #, C # और जावा सभी try..finallyस्टैक अनइंडिंग का उपयोग किए बिना लागू करते हैं। आप बस longjmpहैंडलर को (जैसा कि मैंने पहले ही समझाया)।
यह कंपाइलर पर निर्भर करता है।
उदाहरण के लिए, जीसीसी अपवादों को संभालते हुए बहुत खराब प्रदर्शन के लिए जाना जाता था, लेकिन पिछले कुछ वर्षों में यह काफी बेहतर हुआ।
लेकिन ध्यान दें कि अपवादों को संभालना चाहिए - जैसा कि नाम कहता है - आपके सॉफ़्टवेयर डिज़ाइन में नियम के बजाय अपवाद हो। जब आपके पास एक ऐसा एप्लिकेशन होता है जो प्रति सेकंड इतने अपवादों को फेंकता है कि यह प्रदर्शन को प्रभावित करता है और यह अभी भी सामान्य ऑपरेशन माना जाता है, तो आपको चीजों को अलग तरीके से करने के बारे में सोचना चाहिए।
अपवाद कोड को उस तरीके से बाहर निकालने के लिए कोड को अधिक पठनीय बनाने का एक शानदार तरीका है, लेकिन जैसे ही वे सामान्य कार्यक्रम प्रवाह का हिस्सा बनते हैं, वे वास्तव में अनुसरण करना कठिन हो जाते हैं। याद रखें कि भेस में throwएक बहुत सुंदर है goto catch।
throw new Exceptionएक जावा-इस्म है। एक नियम के रूप में एक संकेत कभी नहीं फेंकना चाहिए ।
जब तक आप कोड को असेंबली या बेंचमार्क में परिवर्तित नहीं करते, आप प्रदर्शन के बारे में कभी दावा नहीं कर सकते।
यहाँ आप क्या देख रहे हैं: (त्वरित बेंच)
त्रुटि कोड घटना के प्रतिशत के प्रति संवेदनशील नहीं है। अपवादों में थोड़ा बहुत उपरि होता है जब तक कि वे कभी भी फेंक नहीं दिए जाते हैं। एक बार जब आप उन्हें फेंक देते हैं, तो दुख शुरू हो जाता है। इस उदाहरण में, इसे 0%, 1%, 10%, 50% और 90% मामलों में फेंक दिया जाता है। जब अपवादों को 90% समय के लिए फेंक दिया जाता है, तो कोड उस मामले की तुलना में 8 गुना धीमा होता है जहां अपवादों को समय का 10% फेंक दिया जाता है। जैसा कि आप देखते हैं, अपवाद वास्तव में धीमा हैं। अगर उन्हें बार-बार फेंका जाए तो उनका इस्तेमाल न करें। यदि आपके आवेदन की कोई वास्तविक समय की आवश्यकता नहीं है, तो उन्हें फेंकने के लिए स्वतंत्र महसूस करें यदि वे बहुत कम होते हैं।
आप उनके बारे में कई विरोधाभासी राय देखते हैं। लेकिन आखिरकार, क्या अपवाद धीमे हैं? मैं न्याय नहीं करता। बस बेंचमार्क देखो।
हां, लेकिन इससे कोई फर्क नहीं पड़ता। क्यों?
इसे पढ़ें:
https://blogs.msdn.com/b/ericlippert/archive/2008/09/10/vexing-exc.nx
मूल रूप से यह कहता है कि अलेक्जेंड्रेस्कु जैसे अपवादों का उपयोग करना (50x मंदी क्योंकि वे उपयोग catchकरते हैं else) सिर्फ गलत है। पीपीएल के लिए कहा जा रहा है जो इसे पसंद करता है जो मैं चाहता हूं कि सी ++ 22 :) कुछ इस तरह से जोड़ देगा:
(ध्यान दें कि यह मूल भाषा होनी चाहिए क्योंकि यह मूल रूप से एक से संकलक कोड बनाने वाला है)
result = attempt<lexical_cast<int>>("12345"); //lexical_cast is boost function, 'attempt'
//... is the language construct that pretty much generates function from lexical_cast, generated function is the same as the original one except that fact that throws are replaced by return(and exception type that was in place of the return is placed in a result, but NO exception is thrown)...
//... By default std::exception is replaced, ofc precise configuration is possible
if (result)
{
int x = result.get(); // or result.result;
}
else
{
// even possible to see what is the exception that would have happened in original function
switch (result.exception_type())
//...
}
PS यह भी ध्यान दें कि भले ही अपवाद धीमे हों ... निष्पादन के दौरान कोड के उस हिस्से में बहुत समय व्यतीत न करना एक समस्या नहीं है ... उदाहरण के लिए यदि फ्लोट डिवीजन धीमा है और आप इसे 4x बनाते हैं तेजी से कि अगर आप अपने समय का 0.3% एफपी डिवीजन कर खर्च नहीं करता है ...
जैसे सिलिको में इसका कार्यान्वयन निर्भर है, लेकिन सामान्य तौर पर अपवादों को किसी भी कार्यान्वयन के लिए धीमा माना जाता है और इसका उपयोग प्रदर्शन गहन कोड में नहीं किया जाना चाहिए।
संपादित करें: मैं यह नहीं कह रहा हूँ कि उनका उपयोग बिल्कुल न करें, लेकिन प्रदर्शन गहन कोड के लिए उनसे बचना सबसे अच्छा है।