2010-03-21 8 views
10

पिछले कुछ वर्षों से आक्रामक, मैं आम तौर पर स्वीकार कर लिया है किबनाम सी में गैर इनवेसिव रेफरी बार गणना संकेत ++

मैं रेफरी बार गणना स्मार्ट संकेत

आक्रामक स्मार्ट संकेत है उपयोग करने के लिए जा रहा हूँ अगर

    :

    हालांकि, मैं निम्नलिखित की वजह से गैर इनवेसिव स्मार्ट संकेत पसंद करने के लिए शुरू कर रहा हूँ -

    जाने का रास्ता

  1. मैं केवल स्मार्ट पॉइंटर्स का उपयोग करता हूं (इसलिए कोई फू * झूठ बोल रहा है, केवल पीआरआर)
  2. मैं प्रत्येक कक्षा के लिए कस्टम आवंटकों का निर्माण शुरू कर रहा हूं। (तो फू ऑपरेटर को अधिभारित करेगा)।
  3. अब, अगर फू में सभी पीआरटी की सूची है (क्योंकि यह आसानी से गैर-आक्रामक स्मार्ट पॉइंटर्स के साथ हो सकती है)।
  4. फिर, मैं स्मृति विखंडन समस्याओं से बच सकता हूं क्योंकि कक्षा फू ऑब्जेक्ट्स को चारों ओर स्थानांतरित करता है (और केवल इसी पीआरटी को अपडेट करता है)।

केवल कारण है कि इस फू गैर इनवेसिव स्मार्ट संकेत आक्रामक स्मार्ट संकेत की तुलना में आसान होने में चारों ओर की वस्तुओं चलती है:

गैर इनवेसिव स्मार्ट संकेत में, वहाँ केवल एक सूचक है कि प्रत्येक फू ओर इशारा करता है ।

आक्रामक स्मार्ट पॉइंटर्स में, मुझे नहीं पता कि प्रत्येक फू को कितनी वस्तुएं इंगित करती हैं।

अब, गैर-आक्रामक स्मार्ट पॉइंटर्स की एकमात्र लागत ... डबल इंडिकेशन है। [शायद यह कैश को खराब कर देता है]।

क्या किसी के पास महंगी का एक अच्छा अध्ययन है, इस अतिरिक्त परत की संकेत है?

संपादित करें: स्मार्ट पॉइंटर्स द्वारा, मैं दूसरों को "साझा-पॉइंटर्स" कहने का जिक्र कर सकता हूं; पूरा विचार है: ऑब्जेक्ट्स से जुड़ी एक संदर्भ-गणना है, और जब यह 0 हिट हो जाती है, तो ऑब्जेक्ट स्वचालित रूप से हटा दिया जाता है

+1

मुझे लगता है कि स्मार्ट पॉइंटर्स द्वारा आपका मतलब साझा पॉइंटर्स (जो कि स्मार्ट पॉइंटर्स का एक प्रकार है) का मतलब है। मैंने shared_ptr (अक्सर विनाश आदेश के साथ समस्याएं, विशेष रूप से थ्रेडेड सिस्टम में समस्याएं) के साथ बहुत सी समस्याओं में भाग लिया है और ऐसा लगता है कि साझा पॉइंटर्स का उपयोग आम तौर पर निराश होना चाहिए। ऑटो स्टोरेज अभी तक सबसे अच्छा विकल्प है (जब यह उपयुक्त है) और यहां तक ​​कि कहीं और scoped_ptrs share_ptrs से बेहतर विकल्प होते हैं। – Tronic

+0

@ पुरानी: मैंने अपना खुद का आक्रामक स्मार्ट पॉइंटर लिखा था। मैं उन्हें बहु थ्रेडेड समस्याओं में उपयोग कर रहा हूं। मुझे विनाश आदेश की समस्या कभी नहीं मिली है। इन विनाश आदेश की समस्याएं क्या हैं जो केवल बहु थ्रेडेड मामलों में प्रकट होती हैं? – anon

+0

मान लें कि आपके पास दो अलग-अलग स्मार्ट पॉइंटर ऑब्जेक्ट्स हैं जो पॉइंट साझा करते हैं। मान लीजिए, दो थ्रेड हैं जो प्रत्येक स्मार्ट पॉइंटर की प्रतिलिपि बनाना चाहते हैं (इसलिए आपको अंत में 4 मिलते हैं)। यहां समस्या यह है कि रेफरी काउंटर एक्सेस को सिंक्रनाइज़ किया जाना चाहिए, भले ही ऑब्जेक्ट स्वयं को छुआ न जाए। – sellibitze

उत्तर

7

वहाँ आक्रामक या गैर इनवेसिव संकेत के बीच कई महत्वपूर्ण अंतर हैं:

दूसरा (गैर इनवेसिव) का सबसे बड़ा लाभ यह:

  • यह दूसरा एक को कमजोर संदर्भ (लागू करने के लिए बहुत सरल है अर्थातshared_ptr/weak_ptr)।

पहले का लाभ जब आप (कम से कम boost::shared_ptr के मामले में std::tr1::shared_ptr) इस पर स्मार्ट सूचक प्राप्त करने के लिए

  • आप निर्माता और नाशक में इस से shared_ptr उपयोग नहीं कर सकते की जरूरत है।
  • कक्षाओं के पदानुक्रम में इसे साझा करने के लिए काफी गैर-तुच्छ है।
+0

+1 अच्छा अंक। – sellibitze

4

मुझे आक्रामक पर गैर-आक्रामक होने के कारण अतिरिक्त लागत पर एक अध्ययन के बारे में पता नहीं है। लेकिन मुझे लगता है कि सी ++ विशेषज्ञों द्वारा गैर-आक्रामक सार्वभौमिक अनुशंसा की जाती है। बेशक, इसका मतलब कुछ भी नहीं हो सकता! लेकिन तर्क बहुत सुंदर है: यदि आपको स्मार्ट पॉइंटर्स की आवश्यकता है, तो ऐसा इसलिए है क्योंकि आप इसे हाथ से लिखने के बजाय ऑब्जेक्ट आजीवन प्रबंधन को कार्यान्वित करने का एक आसान तरीका चाहते हैं, इसलिए आप प्रदर्शन पर शुद्धता और सादगी पर जोर दे रहे हैं, जो हमेशा तक एक अच्छा विचार है अपने पूरे डिजाइन का एक यथार्थवादी मॉडल प्रोफाइल किया है।

यह अच्छी तरह से हो सकता है कि एक सरलीकृत परीक्षण में, गैर-आक्रामक आक्रमणकारी जितना धीमा होता है, और फिर भी वास्तविक कार्य में वास्तविक कार्य होता है, यह गति अंतर शोर में खो जाता है और यह इतना महत्वहीन हो जाता है कि आप ' यह भी मापने के लिए। यह काफी आम घटना है; जो चीजें आप कल्पना करते हैं वे प्रदर्शन के लिए महत्वपूर्ण हैं अक्सर नहीं हैं।

यदि आपको प्रदर्शन बाधा मिलती है, तो यह संभव है (संभव है?) कि संदर्भ को बनाए रखने का कार्य स्वयं (दोनों दृष्टिकोणों में) गैर-आक्रामक दृष्टिकोण में अतिरिक्त संकेत के रूप में प्रदर्शन पर अधिक प्रभाव डालेगा।

p1 = p2; 

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

if (p1 != p2) 
{ 
    if ((p1 != 0) && (--(p1->count) == 0)) 
     delete p1; 

    p1 = p2; 

    if (p1 != 0) 
     p1->count++; 
} 

यह हर स्मार्ट सूचक तर्क हर कार्य के लिए पारित के साथ होता है: लेकिन अगर वे संदर्भ स्मार्ट संकेत गिनती, कर रहे हैं आक्रामक साथ भी यह की तरह है। इसलिए प्रत्येक बार गिनती बढ़ाने और कम करने के लिए स्मृति के संभावित दूर-दूर के क्षेत्रों में बहुत से अतिरिक्त पहुंच हैं। और थ्रेड-सुरक्षित होने के लिए, वृद्धि और कमी-जांच संचालन को अंतःस्थापित/परमाणु होना चाहिए, जो कई कोरों पर गंभीर रूप से नकारात्मक प्रभाव डाल सकता है।

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

एक और मामूली बिंदु: दुर्भाग्यवश "गैर-आक्रामक स्मार्ट पॉइंटर्स में, केवल एक पॉइंटर होता है जो प्रत्येक फू को इंगित करता है" असत्य है। Foo के अंदर this पॉइंटर है जो Foo * है और इसलिए नग्न पॉइंटर्स अभी भी काफी कठिन तरीके से लीक हो सकते हैं।

+0

मुझे लगता है कि फू * बाहर लीक किया जा सकता है। हालांकि, यह एक निजी परियोजना पर है; जब तक मैं अनुशासित हूं, यह एक समस्या नहीं होगी। – anon

+0

'boost :: shared_ptr' कार्यान्वयन के मामले में कोई अतिरिक्त संकेत नहीं है: इसमें दो पॉइंटर्स हैं, एक गिनती संरचना में से एक और ऑब्जेक्ट में ही एक है। –

+0

@Maththieu एम - यह शून्य ओवरहेड के समान नहीं है। इसका मतलब है कि दोगुनी ज्यादा भंडारण की आवश्यकता होती है, आवश्यक वस्तुओं की अधिक संभावना कैश में फिट नहीं होती है, आदि। हालांकि मेरा मुद्दा आम तौर पर यह है कि यह शायद अभ्यास में उतना ही महत्वपूर्ण नहीं होगा जितना आसान बनाए रखने के लिए, सही कार्यक्रम। –

3

गैर-आक्रामक रेफ-गिनती w.r.t. की एकमात्र वास्तविक लागत। प्रदर्शन यह है कि आपको कभी-कभी रेफ-काउंटर के लिए एक अतिरिक्त आवंटन की आवश्यकता होती है। जहां तक ​​मुझे पता है, tr1 :: shared_ptr कार्यान्वयन "डबल इंडिकेशन" नहीं करते हैं। मुझे लगता है कि साझा_प्टर को पॉइंटर को सीधे स्टोर करने के बिना रूपांतरणों का समर्थन करना मुश्किल होगा। एक समझदार shared_ptr कार्यान्वयन दो पॉइंटर्स स्टोर करेगा: ऑब्जेक्ट के लिए एक पॉइंटर (कोई डबल इंडिकेशन नहीं) और कुछ नियंत्रण संरचना के लिए एक पॉइंटर।

यहां तक ​​कि सभी परिस्थितियों में आवंटन ओवरहेड भी आवश्यक नहीं है। make_shared देखें। सी ++ 0 एक्स make_shared फ़ंक्शन भी प्रदान करेगा जो ऑब्जेक्ट और रेफ-काउंटर दोनों को आवंटित करता है जो घुसपैठ करने वाले रिफ-गिनती विकल्प के संबंध में समान होता है।

[...] सुविधा और शैली के अलावा, ऐसा फ़ंक्शन भी अपवाद सुरक्षित और काफी तेज़ है क्योंकि यह ऑब्जेक्ट और उसके संबंधित नियंत्रण ब्लॉक दोनों के लिए एक आवंटन का उपयोग कर सकता है, जो साझा_प्टर के निर्माण के एक महत्वपूर्ण हिस्से को समाप्त करता है भूमि के ऊपर। यह shared_ptr के बारे में प्रमुख दक्षता शिकायतों में से एक को समाप्त करता है। [...]

shared_ptr और make_shared के प्रकाश में, मैं एक कठिन समय समस्याओं जहां एक घुसपैठ स्मार्ट सूचक shared_ptr काफी हरा होगा साथ आ सकते है। साझा पॉइंटर्स की प्रतिलिपि बनाना और नष्ट करना हालांकि, धीमा हो सकता है। ऐसा कहकर, मुझे जोड़ने दो कि मैं शायद ही कभी इन प्रकार के स्मार्ट पॉइंटर्स का उपयोग करता हूं। अधिकांश समय अनूठी औपचारिकता मुझे चाहिए।

+0

कोई डबल दिशा कैसे है, मान लीजिए कि मेरे पास फू को एक संक्षिप्त पॉइंटर है। कच्चा फू * है; एक संरचना है Ptr_Foo {Foo * ptr; int गिनती; } ... और मेरे पास Ptr_Foo के लिए एक पॉइंटर है - क्या यह कैश पर विनाश को बर्बाद नहीं करता है, या क्या मुझे कुछ याद आ रही है? – anon

+0

@anon - विचार यह है कि संरचना में फू के साथ-साथ गिनती का वास्तविक उदाहरण होगा, फू को पॉइंटर नहीं, –

+1

जैसा कि मैंने कहा था, कोई भी आपको अपने स्मार्ट पॉइंटर के अंदर दो पॉइंटर्स स्टोर करने से रोकता है, एक आपके फू में वस्तु और नियंत्रण संरचना में से एक। ऑब्जेक्ट तक पहुंचने के लिए आपको नियंत्रण नियंत्रक को छूने की आवश्यकता नहीं है। – sellibitze

6

अच्छा, सबसे पहले और सबसे महत्वपूर्ण, मैं आपको याद दिलाना चाहूंगा कि साझा स्वामित्व आम तौर पर एक कठिन जानवर है और बग को जड़ने में काफी मुश्किल हो सकती है।

साझा स्वामित्व साझा करने के कई तरीके नहीं हैं। Factory दृष्टिकोण (Boost Pointer Container का उपयोग कर स्वयं लागू) व्यक्तिगत रूप से मेरे पसंदीदा में से एक है।

अब

, के लिए के रूप में संदर्भ गिनती चला जाता है ....

1. घुसपैठ प्वाइंटर

काउंटर वस्तु ही है जिसका अर्थ है में अंतर्निहित है:

  • आप की जरूरत है काउंटर को जोड़ने/घटाने के तरीकों को प्रदान करें, और थ्रेड-सुरक्षित बनाने का आपका कर्तव्य है।
  • गिनती वस्तु जीवित रहने नहीं है, इसलिए कोई weak_ptr है, तो आप एक Observer पैटर्न ... काफी जटिल

2. व्यवधान न डालने प्वाइंटर

उपयोग किए बिना अपने डिजाइन में संदर्भ के चक्र नहीं हो सकता

मैं केवल boost::shared_ptr और boost::weak_ptr के बारे में बात करूंगा। मैंने हाल ही में मैकेनिक्स को देखने के लिए हाल ही में स्रोत में खोला है और वास्तव में ऊपर और अधिक जटिल हैं!

// extract of <boost/shared_ptr.hpp> 

template <class T> 
class shared_ptr 
{ 
    T * px;      // contained pointer 
    boost::detail::shared_count pn; // reference counter 
}; 
  • गिनती का रख-रखाव पहले से ही आप के लिए बाहर लिखा गया है और धागे-सुरक्षित है।
  • चक्रीय संदर्भों के मामले में आप weak_ptr का उपयोग कर सकते हैं।

    // foofwd.h 
    #include <boost/shared_ptr.hpp> 
    
    class Foo; 
    
    typedef boost::shared_ptr<Foo> foo_ptr; 
    
    foo_ptr make_foo(); 
    
    // foo.h 
    #include "foofwd.h" 
    
    class Foo { /** **/ }; 
    
    // foo.cpp 
    #include "foo.h" 
    
    foo_ptr make_foo() { return foo_ptr(new Foo()); } 
    
    // main.cpp 
    #include "foofwd.h" 
    
    int main(int argc, char* argv[]) 
    { 
        foo_ptr p = make_foo(); 
    } // p.get() is properly released 
    

    वहाँ एक है:

  • केवल एक shared_ptr वस्तु के निर्माण वस्तु नाशक के बारे में पता करने के लिए (उदाहरण देखें)

यह एक छोटा सा उदाहरण है, यह आगे घोषणा जादू उदाहरण देकर स्पष्ट करने की जरूरत है इसे अधिकृत करने के लिए टेम्पलेट जादू का थोड़ा सा। असल में काउंटर ऑब्जेक्ट disposer* (अभी तक एक तीसरा आवंटन) एम्बेड करता है जो कुछ प्रकार के मिटाए जाने की अनुमति देता है। हालांकि वास्तव में उपयोगी है, क्योंकि यह वास्तव में आगे की घोषणा के लिए अनुमति देता है!

3।निष्कर्ष

जबकि मैं मानता हूं कि घुसपैठ प्वाइंटर्स शायद कम आवंटन में तेजी से चल रहे हैं (shared_ptr के लिए आवंटित स्मृति के 3 अलग-अलग ब्लॉक हैं), कम व्यावहारिक भी हैं।

तो मैं Boost Intrusive Pointer पुस्तकालय के लिए आप का कहना है करना चाहते हैं, और अधिक विशेष रूप से इसकी शुरूआत करने के लिए:

एक सामान्य नियम के रूप में, अगर यह होता है नहीं स्पष्ट intrusive_ptr बेहतर है कि क्या shared_ptr से अपनी आवश्यकताओं फिट बैठता है , shared_ptr-आधारित डिज़ाइन पहले आज़माएं।

+0

क्यों तीन आवंटन? क्या आप काउंटर ऑब्जेक्ट में डिलीटर एम्बेड नहीं कर सकते? Make_shared के साथ इसे एक आवंटन में कम करने के लिए भी संभव होना चाहिए: एक ऑब्जेक्ट जो पॉइंट, रेफ काउंटर और डिलीटर को एम्बेड करता है। – sellibitze

+0

मैं कर सकता था, ऐसा नहीं है कि यह कैसे डिजाइन किया गया है, शायद चिंता विचार को अलग करने के कारण। मैं सहमत हूं कि यह पूरी तरह से एक हिस्से में आवंटित करने के लिए तेज़ होगा। –

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