यह जाँचने में तेज़ क्यों है कि यदि शब्दकोश में कुंजी है, तो अपवाद को पकड़ने के बजाय, यदि ऐसा नहीं है तो?


234

कोड की कल्पना करें:

public class obj
{
    // elided
}

public static Dictionary<string, obj> dict = new Dictionary<string, obj>();

विधि 1

public static obj FromDict1(string name)
{
    if (dict.ContainsKey(name))
    {
        return dict[name];
    }
    return null;
}

विधि 2

public static obj FromDict2(string name)
{
    try
    {
        return dict[name];
    }
    catch (KeyNotFoundException)
    {
        return null;
    }
}

अगर इन 2 कार्यों के प्रदर्शन में अंतर होता है, तो मैं उत्सुक था, क्योंकि पहले एक SHOULD दूसरे की तुलना में SLOWER होता है - यह देखते हुए कि यह दो बार जांचना आवश्यक है कि क्या शब्दकोश में कोई मान है, जबकि दूसरे फ़ंक्शन को केवल शब्दकोश तक पहुंचने की आवश्यकता है एक बार लेकिन वाह, यह वास्तव में विपरीत है:

1 000 000 मानों के लिए लूप (100 000 मौजूदा और 900 000 गैर मौजूदा के साथ):

पहला कार्य: 306 मिली सेकेंड

दूसरा कार्य: 20483 मिलीसेकंड

ऐसा क्यों है?

संपादित करें: जैसा कि आप इस प्रश्न के नीचे टिप्पणियों में देख सकते हैं, दूसरे फ़ंक्शन का प्रदर्शन वास्तव में पहले वाले की तुलना में थोड़ा बेहतर है, जिसमें 0 मौजूदा मौजूदा कुंजी हैं। लेकिन एक बार कम से कम 1 या अधिक गैर मौजूदा कुंजियों के होने के बाद, दूसरे का प्रदर्शन तेजी से घटता है।


39
पहले वाले को धीमा क्यों होना चाहिए? दरअसल, पहली नज़र में, मैं कहूंगा कि यह तेज़ होना चाहिए, ContainsKeyउम्मीद की जा रही है O(1)...
Patryk iewiek


8
@Petr O(1)शब्दकोश में लुकअप की तुलना में अपवाद को शामिल करने में बहुत अधिक निर्देश शामिल हैं ... विशेष रूप से दो O(1)ऑपरेशन करने के बाद भी अभी भी asymptotically है O(1)
पेट्रीक kwiek

9
जैसा कि नीचे दिए गए अच्छे जवाब में बताया गया है, अपवाद फेंकना महंगा है। उनके नाम से यह पता चलता है: वे अपवाद-संबंधी परिस्थितियों के लिए आरक्षित होने के लिए हैं । यदि आप एक लूप चला रहे हैं, जहाँ आप एक शब्दकोश को कुंजी के लिए एक लाख बार क्वेरी करते हैं जो मौजूद नहीं है, तो यह एक असाधारण परिस्थिति बनना बंद कर देता है। यदि आप चाबियों के लिए एक शब्दकोष की क्वेरी कर रहे हैं, और यह एक अपेक्षाकृत सामान्य मामला है कि वे कुंजी मौजूद नहीं होंगे, तो यह पहले जांचने के लिए समझ में आता है।
जेसन आर

6
यह मत भूलो कि आपने केवल एक लाख अनुपस्थित मूल्यों के लिए जाँच की लागत की तुलना की है, बनाम एक लाख अपवादों को फेंक दिया है। लेकिन मौजूदा मूल्य तक पहुँचने की लागत में दो विधियाँ भी भिन्न हैं । यदि अनुपलब्ध कुंजियाँ पर्याप्त रूप से दुर्लभ हैं, तो अपवाद विधि सभी पर अधिक तेज़ होगी, बावजूद इसके कि उच्च अनुपस्थिति जब एक कुंजी अनुपस्थित हो।
एलेक्सिस

जवाबों:


404

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

BTW: ऐसा करने का सही तरीका उपयोग करना है TryGetValue

obj item;
if(!dict.TryGetValue(name, out item))
    return null;
return item;

यह दो बार के बजाय केवल एक बार शब्दकोश तक पहुँचता है।
यदि आप वास्तव में बस वापस करना चाहते हैं nullयदि कुंजी मौजूद नहीं है, तो उपरोक्त कोड को और सरल बनाया जा सकता है:

obj item;
dict.TryGetValue(name, out item);
return item;

यह काम करता है, क्योंकि अगर कोई कुंजी मौजूद नहीं है तो TryGetValueसेट करता है।itemnullname


4
मैंने उत्तर के अनुसार अपने परीक्षण को अपडेट किया, और किसी कारण से, सुझाए गए फ़ंक्शन के तेजी से होने के बावजूद, यह वास्तव में बहुत महत्वपूर्ण नहीं है: 264 एमएस मूल, 258ms ने सुझाव दिया
पेट्र

52
@Petr: हाँ, यह महत्वपूर्ण नहीं है, क्योंकि शब्दकोश तक पहुँच बहुत तेज़ है, यह वास्तव में मायने नहीं रखता है अगर आप इसे एक या दो बार करते हैं। उन 250 एमएस में से अधिकांश की संभावना टेस्ट लूप में ही खर्च होती है।
डैनियल हिल्गारथ

4
यह जानना अच्छा है, क्योंकि कभी-कभी किसी को यह धारणा मिलती है कि अपवाद-फेंकना एक बेहतर या साफ-सुथरा तरीका है जैसे कि कोई भी फाइल या अशक्त सूचक जैसी स्थिति को संभालने के लिए, चाहे वे स्थितियां सामान्य हों और प्रदर्शन लागत पर विचार किए बिना।
लार्स

4
@ लार्स यह भी निर्भर करता है कि आप क्या कर रहे हैं। जबकि इस तरह के सरल माइक्रोबैनचर्च वास्तव में अपवादों के लिए बड़े दंड हैं, एक बार जब आपके लूप फ़ाइल या डेटाबेस गतिविधियों सहित शुरू होते हैं, तो प्रत्येक पुनरावृत्ति मामलों पर अपवाद को फेंकने वाले प्रदर्शन के लिए बहुत कम होते हैं। पहली और दूसरी तालिका की तुलना करें: codeproject.com/Articles/11265/…
Dan Is Fiddling By Firelight

8
@ लार्स यह भी ध्यान दें कि किसी फ़ाइल (या किसी अन्य बाहरी संसाधन) तक पहुँचने की कोशिश करते समय, यह चेक और वास्तविक एक्सेस प्रयास के बीच स्थिति को बदल सकता है। इन मामलों में, अपवादों का उपयोग करना सही तरीका है। अतिरिक्त जानकारी के लिए स्टीफन सी के इस प्रश्न का उत्तर देखें ।
yoniLavi

6

शब्दकोश विशेष रूप से सुपर फास्ट कुंजी लुकअप करने के लिए डिज़ाइन किए गए हैं। उन्हें हैशटेबल्स के रूप में लागू किया जाता है और जितनी तेजी से वे अन्य तरीकों के सापेक्ष अधिक प्रविष्टियां करते हैं। अपवाद इंजन का उपयोग केवल तभी किया जाना चाहिए जब आपकी विधि वह करने में विफल हो जो आपने इसे करने के लिए डिज़ाइन किया है क्योंकि यह ऑब्जेक्ट का एक बड़ा सेट है जो आपको त्रुटियों को संभालने के लिए बहुत अधिक कार्यक्षमता प्रदान करता है। मैंने एक बार एक पूरी लाइब्रेरी क्लास का निर्माण किया, जिसमें एक बार कोशिश करने वाले ब्लॉक से घिरे हुए थे और डिबग आउटपुट को देखने के लिए तालियां बजीं, जिसमें 600 से अधिक अपवादों में से हर एक के लिए एक अलग लाइन थी!


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

"उन्हें हैशटैब के रूप में लागू किया जाता है और जितनी अधिक प्रविष्टियाँ होती हैं उतनी ही तेजी से वे अन्य विधियों के सापेक्ष होती हैं।" अगर बाल्टी भर जाती है तो निश्चित रूप से यह सच नहीं है?!?!?
एंथनीलैम्बर्ट

1
@AnthonyLambert वह जो कहने की कोशिश कर रहा है वह यह है कि हैशटेबल की खोज में O (1) समय जटिलता है, जबकि बाइनरी सर्च ट्री सर्च में O (लॉग (n)) होगा; जब तत्व हैशटेबल नहीं होता है तो पेड़ की संख्या कम हो जाती है। इसलिए, हैशटेबल की गति का लाभ तत्वों की संख्या के साथ बढ़ता है, हालांकि यह इतनी धीमी गति से करता है।
डोभाल

@AnthonyLambert सामान्य उपयोग के तहत, डिक्शनरी के हैशटेबल में बहुत कम टकराव होते हैं। यदि आप हैशटेबल का उपयोग कर रहे हैं और आपकी बाल्टियाँ भर जाती हैं, तो आपके पास बहुत अधिक प्रविष्टियाँ (या बहुत कम बाल्टी) हैं। उस स्थिति में, कस्टम हैशटेबल का उपयोग करने का समय आ गया है।
एंड्रयूज
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.