2013-04-08 6 views
8

हाल ही में एक दृश्य स्टूडियो सी ++ संकलक का उपयोग कर Windows पर सी की ++ विशेष रूप से सोच रही थी, मैं ढेर कार्यान्वयन के बारे में सोच रहा हूँ:क्या हेप मेमोरी आवंटन (जैसे ढेर में मार्कर) से जुड़ी मेमोरी ओवरहेड है?

यह मानते हुए कि मैं रिलीज संकलक उपयोग कर रहा हूँ, और मैं स्मृति विखंडन/पैकिंग के साथ संबंध नहीं कर रहा हूँ मुद्दों, ढेर पर स्मृति आवंटित करने के साथ जुड़े एक स्मृति ओवरहेड है? यदि हां, तो लगभग आवंटन कितने बाइट्स हो सकता है? क्या यह 32-बिट से 64-बिट कोड में बड़ा होगा?

मुझे वास्तव में आधुनिक ढेर कार्यान्वयन के बारे में बहुत कुछ पता नहीं है, लेकिन मुझे आश्चर्य है कि प्रत्येक आवंटन के साथ ढेर में लिखे गए मार्कर हैं, या फिर किसी प्रकार की तालिका को बनाए रखा जाता है (फ़ाइल आवंटन तालिका की तरह)।

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

+2

आमतौर पर जब आप सरणी आवंटित करते हैं, तो कुछ बाइट भविष्य में हटाए गए आकार के लिए आवंटित आकार के साथ संग्रहीत होते हैं। बड़े आवंटन के लिए, ओवरहेड किसी के नजदीक नहीं है। यदि आप एक समय में 4 बाइट आवंटित करते हैं, तो यह स्मृति को बहुत अच्छी तरह से दोगुना कर सकता है। तकनीकी रूप से –

उत्तर

7

हां, बिल्कुल।

आवंटित स्मृति के प्रत्येक ब्लॉक में "हेडर" का निरंतर ओवरहेड होगा, साथ ही एक छोटा चर हिस्सा (आमतौर पर अंत में) होगा। वास्तव में यह कितना सटीक सी रनटाइम लाइब्रेरी पर निर्भर करता है। अतीत में, मैंने प्रयोगात्मक रूप से इसे 32-64 बाइट प्रति आवंटन के रूप में पाया है। परिवर्तनीय भाग संरेखण से निपटने के लिए है - स्मृति के प्रत्येक ब्लॉक को कुछ अच्छे 2^एन बेस-एड्रेस के साथ गठबंधन किया जाएगा - आमतौर पर 8 या 16 बाइट्स।

मैं std::map या इसी तरह के कार्यों के आंतरिक डिजाइन से परिचित नहीं हूं, लेकिन मुझे बहुत संदेह है कि उनके पास विशेष अनुकूलन हैं।

आप काफी आसानी से भूमि के ऊपर का परीक्षण कर सकते हैं:

char *a, *b; 

a = new char; 
b = new char; 

ptrdiff_t diff = a - b; 

cout << "a=" << a << " b=" << b << " diff=" << diff; 

[pedants के लिए नोट है, जो शायद नियमित यहां के अधिकांश, अब अभिव्यक्ति ऊपर अपरिभाषित व्यवहार का आह्वान, एक टुकड़ा का पता घटाकर के बाद से आवंटित और दूसरे का पता, अपरिभाषित व्यवहार है। यह उन मशीनों से निपटने के लिए है जिनके पास रैखिक स्मृति पते नहीं हैं, उदा। विभाजित स्मृति या "विभिन्न प्रकार के डेटा उनके प्रकार के आधार पर स्थानों में संग्रहीत किया जाता है"। उपर्युक्त निश्चित रूप से किसी भी x86- आधारित ओएस पर काम करना चाहिए जो ढेर के लिए एकाधिक डेटा सेगमेंट के साथ खंडित मेमोरी मॉडल का उपयोग नहीं करता है - जिसका अर्थ है कि यह विंडोज और लिनक्स के लिए 32- और 64-बिट मोड में निश्चित रूप से काम करता है]।

आप अलग-अलग प्रकार के साथ इसे चलाने के लिए चाहते हो सकता है -। बस ध्यान में रखना है कि diff में है "प्रकार की संख्या है, इसलिए यदि आप इसे में होगा बनाने के" चार बाइट्स इकाइयों "आप एक reinterpret_cast<char*>(a) - reinterpret_cast<char *>(b);

कर सकता है

[diff नकारात्मक हो सकता है, और यदि आप (a और b हटाए बिना) एक पाश में इस चलाने के लिए, आप अचानक छलांग जहां स्मृति में से एक बड़ा वर्ग समाप्त हो रहा है न मिलें, इसलिए क्रम पुस्तकालय एक और बड़ी ब्लॉक आवंटित]

+1

, 'ए-बी' यूबी है। –

+0

मैंने उस प्रभाव पर एक टिप्पणी जोड़ा है। यदि आपके पास दो स्वतंत्र आवंटन के बीच की दूरी निर्धारित करने के बेहतर तरीके के लिए एक सुझाव है, तो बेझिझक मुक्त महसूस करें। –

+0

नहीं, मैं इसका मतलब नहीं लगा रहा था, बस कुछ बाहर इंगित करता हूं। इस तरह की चीजों को साबित करने के लिए, कभी-कभी आप यूबी का सहारा लेते हैं, कोई समस्या नहीं है :) –

4

विज़ुअल सी ++ आवंटित बफर की सीमाओं के पास नियंत्रण जानकारी (लिंक/आकार और संभवतः कुछ चेकसम) एम्बेड करता है। इससे सी में भी मदद मिलती है। स्मृति आवंटन और deallocation के दौरान कुछ बफर ओवरफ्लो खाओ।

उस के शीर्ष पर आप याद रखना चाहिए कि malloc() जरूरतों संकेत उपयुक्त रूप से सभी मौलिक प्रकार (char, int, long long, double, void*, void(*)()) और कहा कि संरेखण के लिए गठबंधन वापस जाने के लिए, आम तौर पर सबसे बड़ा प्रकार के आकार की है, इसलिए यह 8 या 16 बाइट भी हो सकता है। यदि आप एक बाइट आवंटित करते हैं, तो 7 से 15 बाइट केवल संरेखण के लिए खो जा सकते हैं। मुझे यकीन नहीं है कि operator new का एक ही व्यवहार है, लेकिन यह मामला बहुत अच्छा हो सकता है।

यह आपको एक विचार देना चाहिए। सटीक स्मृति अपशिष्ट केवल दस्तावेज़ीकरण (यदि कोई हो) या परीक्षण से निर्धारित किया जा सकता है। भाषा मानक किसी भी शर्तों में इसे परिभाषित नहीं करता है।

2

हां। सभी व्यावहारिक गतिशील स्मृति आवंटकों में न्यूनतम ग्रैन्युलरिटी है। उदाहरण के लिए, यदि ग्रैन्युलरिटी 16 बाइट्स है और आप केवल 1 बाइट का अनुरोध करते हैं, तो पूरे 16 बाइट आवंटित किए जाते हैं। यदि आप 17 बाइट्स के लिए पूछते हैं, तो एक ब्लॉक जिसका आकार 32 बाइट आवंटित किया गया है ...

संरेखण का एक (संबंधित) मुद्दा भी है।

काफी कुछ allocators एक आकार नक्शा और सूचियों का एक संयोजन होने लगते हैं - वे संभावित आवंटन आकार विभाजित करने के लिए "बकेट" और उनमें से प्रत्येक के लिए एक अलग मुक्त सूची रखने के लिए। Doug Lea's malloc पर एक नज़र डालें। वहाँ विभिन्न समझौतों से साथ कई अन्य आवंटन करने की तकनीक है, लेकिन यह गुंजाइश यहाँ परे चला जाता है ...


आमतौर पर 8 या 16 बाइट्स। यदि आवंटक एक मुफ्त सूची का उपयोग करता है तो उसे प्रत्येक फ्री स्लॉट के अंदर दो पॉइंटर्स को एन्कोड करना होगा, इसलिए एक फ्री स्लॉट 8 बाइट्स (32-बिट पर) या 16 बाइट (16-बिट पर) से छोटा नहीं हो सकता है। उदाहरण के लिए, यदि आवंटक ने 4-बाइट अनुरोध को पूरा करने के लिए 8-बाइट स्लॉट को विभाजित करने का प्रयास किया है, तो शेष 4 बाइट्स में मुफ्त सूची पॉइंटर्स को एन्कोड करने के लिए पर्याप्त जगह नहीं होगी।

2 उदाहरण के लिए, यदि आपके मंच पर long long 8 बाइट्स, तो संभाजक के आंतरिक डाटा संरचनाओं कि तुलना में छोटे ब्लॉकों संभाल कर सकते हैं, भले ही, वास्तव में छोटे ब्लॉक आवंटन अगले 8-बाइट आवंटन धक्का सकता है एक unaligned स्मृति पते के लिए।

+0

जानकारी और मॉलोक लिंक के लिए धन्यवाद , @ ब्रैंको, जो ढेर प्रबंधन डिजाइन के लिए एक बहुत अच्छा बुनियादी परिचय प्रतीत होता है। –

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