2010-03-07 14 views
6

यदि मैं कक्षा Foo वर्ग की लाखों वस्तुओं को आवंटित करना चाहता हूं, और मैं स्मृति और समय-कुशल होना चाहता हूं, तो मुझे Foo कक्षा को कैसे डिजाइन करना चाहिए?लाखों आवंटन के लिए उपयुक्त वर्ग कैसे डिज़ाइन किया जाए?

जाहिर है, Foo में अधिक सदस्य डेटा नहीं होना चाहिए।

इसके अलावा, मुझे लगता है, यह वर्चुअल फ़ंक्शंस का उपयोग नहीं करना चाहिए?

और बेस क्लास से प्राप्त करने के लिए Foo के लिए यह कितना महंगा है? और कई बेस वर्गों से?

क्या Foo वस्तुओं को लाखों बनाने के लिए कोई अन्य सुझाव हैं?

+0

वर्चुअल फ़ंक्शंस होने से ऑब्जेक्ट में केवल 4 और बाइट जोड़े जाते हैं (64-बिट के लिए 8)। बेस क्लास से व्युत्पन्न कुछ भी नहीं है। (ये एकल विरासत मानते हैं।) – kennytm

+1

आपको कस्टम आवंटक का उपयोग करके आवंटन को तेजी से बनाना चाहिए। – yesraaj

+3

आपको क्या करना चाहिए इस पर निर्भर करता है कि आप क्या कर रहे हैं। तुम क्या कर रहे हो? – GManNickG

उत्तर

4

Flyweight pattern पर एक नज़र डालें। पैटर्न को समझाते हुए GoF book विकिपीडिया की तुलना में एक बेहतर काम करता है।

8

मुझे नहीं लगता कि लाखों आवंटन के लिए आपकी कक्षा को डिजाइन करने के बारे में कुछ कहना है। हां, स्पष्ट स्मृति सीमा है, इसलिए यदि आपके पास स्मृति की एक निश्चित मात्रा है तो यह आपके लिए एक वास्तविक चिंता हो सकती है, अन्यथा आप हमेशा स्मृति से बाहर होने का जोखिम उठाएंगे। वर्चुअल टेबल के लिए एक पॉइंटर बस, एक सूचक (32 या 64 बिट आर्किटेक्चर पर 4 या 8 बाइट्स), यह सुनिश्चित नहीं है कि यह एकाधिक विरासत में है। वर्चुअल फ़ंक्शंस को कॉल करना वर्चुअल लुकअप (और अतिरिक्त कैश मिस, यदि आपने हाल ही में इसका उपयोग नहीं किया है) का ओवरहेड है, लेकिन केवल वर्चुअल फ़ंक्शंस के लिए, और इन्हें कभी भी रेखांकित नहीं किया जा सकता है।

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

ये मेरा असली सुझाव के लिए तो अब सब बिल्कुल स्पष्ट सामान कर रहे हैं,:

अगर आप विखंडन, जहां अचानक स्मृति का एक समूह हो सकता है मिल क्या वास्तव में अपने स्मृति प्रबंधन को मारने के लिए जा रहा है है, लेकिन अभी भी अपनी वस्तुओं को रखने के लिए कहीं नहीं (पर्याप्त संगत जगह नहीं)। यदि आपके पास बहुत से अंतःस्थापित आवंटन हैं, तो यह वास्तविक समस्या हो सकती है ताकि आप वस्तुओं के बड़े ब्लॉक आवंटित करना चाहें, उन्हें पूल में रखें और पुन: उपयोग करें। या एक कस्टम आवंटक (नया ऑपरेटर) का उपयोग करें, जहां आप स्मृति के एक ब्लॉक को पूर्वस्थापित करते हैं जो आपके ऑब्जेक्ट आकार का एक बहु है, और अपनी ऑब्जेक्ट्स के लिए इसका उपयोग करें।

+3

मैं सहमत हूं। फ्लाईवेट + ऑब्जेक्ट-पूल और ऑब्जेक्ट री-यूज। –

+0

सहमत हुए। इसके अलावा, अगर उन्हें अपरिवर्तनीय बनाने का कोई भी संभावित तरीका है, तो ऐसा करने से पुन: उपयोग करने की अनुमति मिल जाएगी। लाखों स्थानों में संदर्भित हजारों वस्तुओं से दूर होना संभव हो सकता है। समस्या डोमेन के बारे में अधिक जानकारी के बिना जानना मुश्किल है। – kyoryu

3

इस हद तक कि किसी ऑब्जेक्ट को कुशल होने के लिए कहा जा सकता है, बहुत कम होने पर इसे छोटा होना चाहिए, और यदि बहुत सारे कॉल किए जाते हैं तो उस पर सामान्य संचालन अनिवार्य होना चाहिए। स्मृति के संदर्भ में, आभासी कार्यों को आम तौर पर पहले ऑब्जेक्ट के लिए प्रति ऑब्जेक्ट 4 या 8 बाइट्स (पॉइंटर का आकार) प्रति ऑब्जेक्ट होता है। जैसा कि अन्य ने कहा है, फ्लाईवेट वस्तुओं को छोटे बनाने का एक तरीका है, यदि उनमें डुप्लिकेट डेटा होता है जिसे साझा किया जा सकता है। यदि आपके लाखों ऑब्जेक्ट्स में डुप्लिकेट डेटा नहीं है, तो इसके बारे में भूल जाएं।

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

बेस क्लास एक ही प्रकार के सदस्य ऑब्जेक्ट के समान ही खर्च करते हैं। एकाधिक विरासत के साथ, static_cast एक नो-ऑप बन सकता है क्योंकि यह आमतौर पर एकल विरासत के लिए होता है, लेकिन शायद यह चिंता करने योग्य नहीं है। वर्चुअल विरासत और dynamic_cast दोनों रनटाइम पर किए गए काम की मात्रा के मामले में दिलचस्प हो सकते हैं, लेकिन केवल आभासी सदस्य कार्यों के समान पैमाने पर।

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

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

4

मुख्य बात यह है कि new और delete का उपयोग पूल में प्रयुक्त वस्तुओं को रखकर और उनका पुन: उपयोग करके कम करना है। आभासी कार्यों के बारे में चिंता मत करो। उन्हें कॉल करने का उपर आमतौर पर आपके कार्यक्रम में जो भी हो रहा है उससे संबंधित मामूली सापेक्ष है।

+0

और आभासी कार्यों की स्मृति लागत न्यूनतम है - प्रति वस्तु एक सूचक (vftable करने के लिए)। आप वर्चुअल फ़ंक्शन नहीं होने से इसे खत्म करने में सक्षम होना चाहिए। – kyoryu

+0

@kyoryu: ठीक है। और मैं यह नहीं बता सका कि सक्रिय वस्तुओं की स्थिर-स्थिति संख्या क्या है, इसलिए मैं यह नहीं बता सका कि आकार भी एक मुद्दा है या नहीं। –

3

कस्टम आवंटकों पर एक बिंदु स्पष्ट करने के लिए।

डिफ़ॉल्ट new के साथ, आपको sizeof(Foo) के शीर्ष पर आवंटित अतिरिक्त मेमोरी होने की संभावना है। आप वास्तव में क्या करना चाहते हैं यह Foo एस पर इस ओवरहेड को फैलाना है।

विचार new करने के लिए एक कॉल करने के लिए पर्याप्त बड़ा बाइट्स के एक विशाल खंड आवंटित करने के लिए सन्निहित Foo के धारण करने के लिए 100 के या 1000 के (या अधिक?), और फिर उस से बाहर एकल Foo रों आवंटित है।

यदि आप प्री-आवंटित Foo एस का पूल रखते हैं, तो भी आप प्रत्येक इंस्टेंस पर मेमोरी ओवरहेड पीड़ित हो सकते हैं, भले ही यह तेज़ हो।

सी ++ pl2e में, 'मेरे मशीन पर' बाइट के बारे में बात करती है Stroustrup है, तो आप अपने आप को प्रयोगों करना होगा: कितना स्मृति वास्तव में new के साथ एक एकल Foo आवंटित करने के लिए लिया जाता है?

पूल आवंटकों को देखो (उदाहरण के लिए Boost), या छोटे ऑब्जेक्ट आवंटकों (उदाहरण के लिए Loki)।

+0

अच्छा बिंदु, लेकिन आपको मानक से परे देखने की आवश्यकता नहीं है। 'std :: deque ' पहले ही टी ऑब्जेक्ट्स के कई संगत ब्लॉक आवंटित करेगा। टी के प्रति ब्लॉक की संख्या आपके कंपाइलर विक्रेता द्वारा अनुकूलित की जाती है, जो संभावित रूप से आपके प्लेटफ़ॉर्म को समझती है। – MSalters

1

अपनी कक्षा वस्तुओं के आवंटन के संबंध में, Pool लाइब्रेरी को बढ़ावा दें, जो सिस्टम ऑलोकेटर के माध्यम से नियमित रूप से नए/हटाए जाने से छोटी वस्तुओं के कई आवंटन के लिए अधिक कुशल हो सकता है। यह उन्हें स्मृति में एक साथ रख सकता है। fast_pool_allocator एक सी ++ आवंटन कार्यान्वयन के रूप में बहुत आसान है जिसे आप आसानी से लाभ के आकलन के लिए अपने ऐप में छोड़ सकते हैं। मैं वैसे भी आवंटकों का उपयोग करने का सुझाव दूंगा, क्योंकि इससे विभिन्न आवंटन योजनाओं की तुलना करना आसान हो जाएगा।

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

मैं एक टेस्ट ऐप को रिगिंग करने का सुझाव दूंगा जो आपको आवंटित आवंटन की संख्या अनुकरण करेगा और विभिन्न रणनीतियों को बेंचमार्क करेगा। आपको एक से अधिक गठबंधन करने की आवश्यकता हो सकती है।

1

अन्य ने फ्लाईवेट पैटर्न का उल्लेख किया है, लेकिन चूंकि आपने इसे सी ++ के लिए टैग किया है, इसलिए मैं Boost.Flyweight पर देखता हूं। इसका डिज़ाइन आपकी ज़रूरत के अनुरूप है और यदि आपको पहिया को फिर से शुरू करने की आवश्यकता है, तो आप हमेशा विवरण के लिए अपना स्रोत देख सकते हैं।

1

इसके लिए क्या योग्य है ... आप एक और मुहावरे का उपयोग करने से भी लाभ प्राप्त कर सकते हैं।

मुझे पता है कि Flyweight पैटर्न काफी क्रोध है, लेकिन यहां आप उन लाखों वस्तुओं को आवंटित करने से भी लाभ उठा सकते हैं।

यदि यह अजीब लगता है, तो String ऑब्जेक्ट Python पर विचार करें। जैसा कि हाल की भाषाओं में, StringPython में अपरिवर्तनीय हैं। बेशक, जिस वस्तु का आप उपयोग करते हैं वह बदल सकता है, लेकिन वास्तविक String नहीं है: आपका हैंडल बस स्थानांतरित हो गया है।

बेशक, Python में स्वचालित कचरा संग्रह है जो इसे अधिक आसान बनाता है, फिर भी यह आपके लिए भी काम कर सकता है।

class FooImpl; 

class Foo 
{ 
public: 
    explicit Foo(int i): mImpl(FooImpl::Build(i)) {} 

    int foo() const { return mImpl->foo(); } 

    void foo(int i) { mImpl = mImpl->foo(i); } 

private: 
    const FooImpl* mImpl; 
}; // class Foo 

class FooImpl 
{ 
public: 
    static const FooImpl* Build(int i) 
    { 
    typedef std::unordered_set<FooImpl> foos_type; 
    FooImpl tmp(i); 
    foos_type::iterator it = gFooImplCollection.insert(tmp); 
    return &(*it); 
    } 

    int foo() const { return mFoo; } 

    const FooImpl* foo(int i) const 
    { 
    return Build(i); 
    } 

    // Useful thingy 
    bool operator==(const FooImpl& rhs) const { return mFoo == rhs.mFoo; } 
    size_t hash() const { return mFoo; } 

private: 
    explicit FooImpl(int i): mFoo(i) {} 

    int mFoo; 
}; 

std::unordered_set<FooImpl> gFooImplCollection; 
बेशक

, यह बहुत किसी न किसी केवल आपके जानकारी देने के लिए है, यहाँ एक स्केच है। यदि विभिन्न वस्तुओं की संभावित संख्या महत्वपूर्ण है, तो आपको कचरा संग्रह की आवश्यकता है।

कचरा संग्रह एक और विषय है, मैं आपको Immutable कोर क्लास (केवल const विधियों का खुलासा करता है) और एक परिवर्तनीय हैंडल (जो कि कोर क्लास को बदलने के लिए कहा जाने पर इंगित करता है) के विचार के साथ आपको छोड़ना पसंद करता है।

और अब है कि आप, समय पढ़ने के लिए लिया है बूस्ट यह है

: Boost.Flyweight :)

नोट:

यह पर सटीक करने के लिए महत्वपूर्ण लगता है, क्योंकि Foo आवंटित किया जाता है (ढेर) लाखों बार, इसका आकार एक सूचक के लिए जितना संभव हो उतना करीब रहना चाहिए। यह Intrusive संदर्भ couting का उपयोग करके पूरा किया जाता है (मुझे उम्मीद है कि बूस्ट क्या करता है)। इसके अलावा, virtualFoo में virtualFooImpl में 10 और Build वास्तव में दृश्यों के पीछे AbstractFactory पर कॉल करने के लिए अनावश्यक है।

इस प्रकार, Foo के बाद से:

  • किसी भी आधार वर्ग नहीं है
  • किसी भी आभासी तरीकों
  • केवल एक विशेषता (एक सूचक)

इसका प्रभावी आकार में नहीं है पॉइंटर का आकार होगा ... जो कि आप उम्मीद कर सकते हैं कि आप प्रत्येक आईडी पर एक आईडी को लुकअप लागत नहीं लेना चाहते हैं :)

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

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