2015-10-29 21 views
7

में सदस्य टेम्पलेट फ़ंक्शन में तत्वों तक पहुंचने पर unordered_map से कम प्रदर्शन मैं एक गेम इंजन प्रोजेक्ट पर घटक आधारित आर्किटेक्चर को कार्यान्वित करने का प्रयास कर रहा हूं। प्रत्येक GameObject में unordered_map है जो Component बेस क्लास के लिए पॉइंटर रखता है। इस बिंदु पर, मेरे पास केवल एक घटक व्युत्पन्न वर्ग है, जो Transform वर्ग है। मैं यूनिटी के सम्मेलन के समान इस घटक आधारित आर्किटेक्चर को कार्यान्वित करना चाहता था: मैं सदस्य टेम्पलेट फ़ंक्शन को GetComponent<Transform>() पर कॉल करके गेम ऑब्जेक्ट का एक घटक प्राप्त करना चाहता हूं।व्युत्पन्न वर्ग

यहाँ हेडर हैं:

Component.h

enum type{ 
    TRANSFORM // more will be added later 
}; 

class Component // base class 
{ 
public: 
    Component() : _owner(NULL) {} 
    virtual ~Component(){} 
    static type Type; 

protected: 
    GameObject* _owner; 
}; 

Transform.h

class Transform : public Component 
{ 
public: 
    Transform(); 
    ~Transform(); 
    static type Type; 

    void Rotate(float deg); 

    // to be encapsulated later on 
    Vector2D _position; 
    float _rotation; 
    Vector2D _scale; 
}; 

GameObject.h

class GameObject 
{ 
public: 
    GameObject(); 
    ~GameObject(); 

    void Update(); 
    //and more member functions 

    template<class T> 
    T* GetComponent(); 
private: 
    // some more private members 
    unordered_map<type, Component*> _componentList; // only 1 component of each type 
}; 

template<class T> 
T* GameObject::GetComponent() 
{  
    return static_cast<T*>(_componentList[T::Type]); 
} 

मेरे प्रारंभिक कार्यान्वयन Component* रखने के लिए इस्तेमाल किया std::vector और आवेदन (मैं भी एक फ्रेम दर नियंत्रक, जो सिर्फ 60 एफपीएस की सीमा है) 60 fps भाग गया। जब मैं उन घटक पॉइंटर्स तक पहुंचने के लिए unordered_map में बदल गया, तो प्रदर्शन डाउनहिल 15 एफपीएस तक चला गया।

enter image description here

मैं केवल दो quads आकर्षित और मैं इस बिंदु पर फ्रेम प्रति GetComponent<Transform>() केवल 6 बार फोन, इसलिए वहाँ ज्यादा दृश्य में चल रही नहीं है।


मैंने क्या प्रयास किया?

मैं unordered_map के लिए महत्वपूर्ण मूल्यों के रूप में const char*, std::string, type_info और अंत में enum type उपयोग करने के लिए कोशिश की, लेकिन कुछ भी नहीं है वास्तव में मदद करता है: सभी कार्यान्वयन मुझे 15-16 एफपीएस मिला है।

इस प्रदर्शन समस्या का कारण क्या है? मैं इस मुद्दे को अलग कैसे कर सकता हूं?

मुझे आशा है कि मैं पर्याप्त विस्तार प्रदान की है, यदि आवश्यक हो तो अधिक कोड के लिए पूछने के लिए स्वतंत्र महसूस

+5

इकाइयों में आमतौर पर केवल कुछ घटक होते हैं, शायद 5 या अधिकतम 30. 30 तत्वों के लिए रैखिक खोज वाले वेक्टर एक हैश_मैप से बेहतर प्रदर्शन करते हैं। मैं सिर्फ वैक्टर के साथ रहना होगा। – nwp

+1

मुझे गेम प्रोग्रामिंग के बारे में कुछ भी पता नहीं है, लेकिन क्या आप हमेशा num_components तत्वों के साथ एक पॉइंटर सरणी को चालू नहीं कर सकते? प्रकार enum मान सरणी में सूचकांक के रूप में डाला जा सकता है, और यदि आपके पास कुछ घटक हैं तो यह उच्च स्मृति ओवरहेड नहीं हो सकता – dgel

+1

आप और क्या बदल गए? मुझे विश्वास नहीं है * केवल * आपके घटकों के भंडारण कंटेनर को बदल दिया है। एक हैश मान की गणना तुच्छ है। जीपीयू को ड्रा निर्देश भेजने की लागत से कम। –

उत्तर

0

अस्वीकरण: जबकि नीचे दी गई जानकारी पर ध्यान दिए बिना धारण करना चाहिए, पहले बुनियादी मानसिक स्वास्थ्य की जांच के साथ प्रदर्शन में इस तरह के एक कठोर अंतर को देखते हुए इतना आसान मामला यह सुनिश्चित करना है कि निर्माण अनुकूलन चालू हैं। इसके साथ ...

unordered_map अंततः काफी बड़े पैमाने पर कंटेनर के रूप में डिज़ाइन किया गया है, जो संभावित रूप से बड़ी संख्या में बाल्टी को रोकता है।

यहाँ देखें: std::unordered_map very high memory usage

और यहाँ: How does C++ STL unordered_map resolve collisions?

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

इकाई-घटक प्रणालियों के लिए, आम तौर पर आपके पास एक इकाई से जुड़े कई घटक नहीं होते हैं - शायद कुछ दर्जनों शीर्ष, और अक्सर कुछ ही। नतीजतन, std::vector वास्तव में एक बहुत अधिक उपयुक्त संरचना है और मुख्य रूप से संदर्भ के इलाके के संदर्भ में (छोटे सरणी जिन्हें हर बार जब आप किसी इकाई से घटक इंटरफ़ेस प्राप्त करते हैं तो अक्सर बार-बार उपयोग किया जा सकता है)। एक कम बिंदु, std::vector::operator[] एक ऐसा कार्य भी है जो त्रि-रूप से-रेखांकित है।

आप std::vector यहाँ से भी बेहतर करना चाहते हैं तो (लेकिन मैं केवल इस रूपरेखा और आपको इसकी आवश्यकता का निर्धारण करने के बाद सलाह देते हैं), यह है कि आप आम तौर पर उपलब्ध घटकों की संख्या के लिए कुछ सामान्य मामला मान सकते हैं ऊपरी बाध्य, N, बशर्ते components को ptr की स्थापना द्वारा बंद

struct ComponentList 
{ 
    Component* components[N]; 
    Component** ptr; 
    int num; 
}; 

प्रारंभ और उन्हें ptr के माध्यम से बाद में का उपयोग: एक इकाई, कुछ इस तरह और भी बेहतर काम कर सकते हैं। एक नया घटक डालने से num बढ़ता है। जब num >= 4 (दुर्लभ मामला), ptr को एक बड़े आकार के साथ गतिशील रूप से आवंटित ब्लॉक को इंगित करने के लिए बदलें। ComponentList को नष्ट करते समय, ptr != components पर गतिशील रूप से आवंटित स्मृति को मुक्त करें। यदि आप N तत्वों से कम भंडारण कर रहे हैं (हालांकि std::vector भी आमतौर पर प्रारंभिक क्षमता और जिस तरह से बढ़ता है) के साथ यह संग्रहीत करता है, लेकिन यह num > N तक आपकी इकाई और घटक सूची को पूरी तरह से संयोजित संरचना में प्रदान करता है। नतीजतन, आपको संदर्भ के सर्वोत्तम इलाके और संभावित रूप से बेहतर परिणाम मिलते हैं जो आपने शुरू किया था (मैं फ्रेम दर में महत्वपूर्ण गिरावट से मान रहा हूं कि आप इकाइयों से घटकों को अक्सर लूप से बाहर ले जा रहे हैं जो असामान्य नहीं है ईसीएस में)।

यह देखते हुए कि एक इकाई से कितनी बार घटक इंटरफेस का उपयोग किया जा सकता है, और अक्सर बहुत तंग लूप में, यह प्रयास के लायक हो सकता है।

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

मैं स्थिरांक चार *, std :: स्ट्रिंग, type_info उपयोग करने के लिए और अंत में unordered_map के लिए महत्वपूर्ण मूल्यों के रूप में प्रकार Enum कोशिश की, लेकिन कुछ भी नहीं है वास्तव में मदद करता है: सभी कार्यान्वयन मुझे 15-16 एफपीएस मिला है।

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

+1

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

+0

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

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