2011-01-28 15 views
13

new ऑपरेटर (या के लिए फलियों, malloc/calloc) जब स्मृति के बड़े हिस्से का आवंटन में नाकाम रहने के एक सरल और प्रभावी रूप समर्थन करते हैं।क्या मानक लाइब्रेरी (एसटीएल) कंटेनर नोट्रो आवंटन के एक रूप का समर्थन करते हैं?

कहते हैं कि हम इस राशि:

const size_t sz = GetPotentiallyLargeBufferSize(); // 1M - 1000M 
T* p = new (nothrow) T[sz]; 
if(!p) { 
    return sorry_not_enough_mem_would_you_like_to_try_again; 
} 
... 

वहाँ std :: कंटेनरों के लिए इस तरह के किसी भी निर्माण है, या मैं हमेशा std::vector और दोस्तों के साथ एक (उम्मीद !!) अपवाद को संभालने के लिए होगा?


वहाँ शायद एक तरीका है कि स्मृति preallocates एक कस्टम संभाजक बारे में और उसके बाद से आप में डाल दिया तो जब तक कि के रूप में वेक्टर अधिक स्मृति के लिए पूछना नहीं है, वेक्टर को यह कस्टम संभाजक पारित करने के लिए होगा आवंटक पहले से, यह असफल नहीं होगा?


बाद का विचार: क्या वास्तव में जरूरत होगी सामान्य आरक्षित समारोह के अलावा एक सदस्य समारोह bool std::vector::reserve(std::nothrow) {...} थे। लेकिन चूंकि इससे केवल तभी अर्थ होगा जब आवंटन को आवंटित करने की अनुमति देने के लिए भी विस्तारित किया गया था, यह अभी नहीं होगा। लगता है (nothrow) नया कुछ के लिए अच्छा है सब के बाद :-)


संपादित करें: क्यों मैं भी इस पूछ रहा हूँ के रूप में:

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

तो new (nothrow) यह वैध उपयोग करता है है, एक वेक्टर nothrow-आरक्षित भी होगा।

+3

'कोशिश करें ... पकड़' निर्माण लिखना इतना बड़ा सौदा नहीं है। यदि आपके पास बहुत सारे हैं, तो आप अंदरूनी हैंडलिंग के साथ एक एकल रैपर फ़ंक्शन 'टेम्पलेट टी * माईएलोक (size_t)' लिख सकते हैं। – TonyK

+1

यह कभी-कभी इतना बड़ा सौदा होता है। –

+0

जब भी आप एक गीगाबाइट आवंटित कर रहे हैं और ओओएम पहुंच रहे हैं? मुझे शक है। –

उत्तर

14

डिफ़ॉल्ट रूप से, मानक एसटीएल कंटेनर कक्षाएं std::allocator कक्षा का उपयोग उनके आवंटन के लिए हुड के तहत करती हैं, यही कारण है कि यदि कोई स्मृति उपलब्ध नहीं है तो वे std::bad_alloc फेंक सकते हैं। दिलचस्प बात यह है allocators पर सी ++ आईएसओ विनिर्देश कहा गया है कि किसी भी संभाजक प्रकार के रिटर्न मान तत्वों में से कुछ संख्या है, जो स्वचालित रूप से है कि संभावित इस्तेमाल कर सकते हैं एक कस्टम संभाजक के निर्माण से आप precludes पकड़े करने में सक्षम स्मृति का एक ब्लॉक करने के लिए एक सूचक होना चाहिए nothrownew का संस्करण इस तरह के मौन आवंटन विफलताओं के लिए है। हालांकि, यदि आप कोई स्मृति उपलब्ध नहीं हैं, तो प्रोग्राम को समाप्त करने वाले कस्टम आवंटक का निर्माण कर सकते हैं, तब से यह सचमुच सच है कि लौटाई गई स्मृति मान्य है जब कोई स्मृति नहीं छोड़ी जाती है। :-)

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

+2

आवंटक को कॉल करने वाले अधिकांश तरीकों में किसी भी तरह से अपवाद के माध्यम से त्रुटि की रिपोर्ट करने का कोई अन्य तरीका नहीं है। –

5

अक्सर हम सुनते "मैं अपवाद उपयोग करने के लिए है क्योंकि वे अक्षम हैं नहीं करना चाहती।"

जब तक आप एक "एम्बेडेड" वातावरण का जिक्र नहीं कर रहे हैं, जहां आप सभी रनटाइम प्रकार की जानकारी बंद करना चाहते हैं, तो आपको अपवादों की अक्षमता के बारे में ज्यादा चिंता नहीं करनी चाहिए यदि उन्हें उपयुक्त तरीके से फेंक दिया जा रहा है।स्मृति से बाहर चलना इन उचित तरीकों में से एक है।

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

यदि आपको तब आवंटन का उपयोग करना है जो पहले उपलब्ध होने पर असफल-आवंटन कॉलबैक का प्रयास करेगा, और केवल तब ही यदि आप अभी भी फेंकने के लिए आवंटित नहीं कर सकते हैं, लेकिन फिर भी आपको अपवाद के साथ समाप्त करना होगा।

क्या मैं आपको एक संकेत दे सकता हूं हालांकि: यदि आप वास्तव में इतनी बड़ी मात्रा में डेटा आवंटित कर रहे हैं तो वेक्टर शायद गलत वर्ग का उपयोग करने के लिए है और आपको इसके बजाय std :: deque का उपयोग करना चाहिए। क्यूं कर? क्योंकि डेक को स्मृति के एक संगत ब्लॉक की आवश्यकता नहीं होती है लेकिन अभी भी निरंतर समय लुकअप है।

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

जब मैं पूर्व में हमने पाया कि हम वास्तव में Deque का उपयोग कर हम ऊपर कारण 1 की वजह से वेक्टर का उपयोग कर सकता है के रूप में 4 बार के रूप में ज्यादा डेटा पर संग्रहीत कर सकता है में एक ऐसी प्रणाली पर काम किया है, और यह तेजी से क्योंकि था कारण 2।

हमने कुछ और किया जो 2 एमबी अतिरिक्त बफर आवंटित किया गया था और जब हमने एक बुरा_लोक पकड़ा तो हमने बफर को मुक्त कर दिया और फिर दिखाया कि हम क्षमता तक पहुंच चुके हैं। लेकिन अब 2 एमबी स्पेयर के साथ हम कम से कम जानते थे कि डेटा को मेमोरी से अस्थायी डिस्क स्टोरेज में स्थानांतरित करने के लिए छोटे ऑपरेशन करने की स्मृति थी।

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

+0

'डेक' के बारे में टिप के बारे में धन्यवाद - दिलचस्प अवलोकन, एक कोशिश के लायक हो सकता है। (हालांकि, जैसा कि मेरा मूल कोड sugessts है, अगर किसी को> 500 एमबी बफर की आवश्यकता है, तो यह अधिकतर समय में असफल होने की संभावना है चाहे आप इसे कैसे विभाजित करते हैं।) –

+0

@ मार्टिन: 500 एमजी 'डेक्यू क्यों विफल होने की संभावना है? 'डेक' का बिंदु यह है कि यह एक ही बफर नहीं है, यह कई आवंटन है। यदि आप एक अस्पष्ट आधुनिक पीसी पर भी चल रहे हैं, उदाहरण के लिए, तो छोटे टुकड़ों में 500 एमबी आराम से आवंटित किया जाना चाहिए। लगभग 3 ऐसे आवंटन एक साथ 32 बिट ओएस/प्रक्रिया पर मुश्किल हो सकता है। –

+0

@ स्टेव - हाँ, 32 बिट प्रक्रिया। यह भौतिक RAM नहीं चल रहा है, लेकिन प्रक्रिया का पता स्थान "पूर्ण" हो रहा है। एक समस्या जिसे मैं 'डेक' के साथ देखता हूं वह यह है कि आप वास्तव में यह सुनिश्चित नहीं कर सकते कि यह आपके बफर को कैसे विभाजित करने जा रहा है। तो आपके पास ऐसी स्थितियां हो सकती हैं जहां यह महान और अन्य (विखंडन) परिदृश्यों पर काम कर रही हो जहां यह बिल्कुल काम नहीं कर रहा है। अपने आप में एक दिलचस्प सवाल हो सकता है। –

2

मानक कंटेनर इसके लिए अपवाद का उपयोग करते हैं, आप आवंटन के प्रयास के अलावा अन्य चारों ओर नहीं जा सकते हैं केवल एक बार जब आप जानते हैं कि यह सफल होगा। आप उस पोर्टेबल रूप से ऐसा नहीं कर सकते हैं, क्योंकि कार्यान्वयन आम तौर पर अनिर्दिष्ट राशि से अधिक आवंटित होगा। यदि आपको कंपाइलर में अपवाद अक्षम करना है तो आप कंटेनरों के साथ क्या कर सकते हैं उसमें सीमित हैं।

के बारे में "सरल और प्रभावी", मुझे लगता है कि std कंटेनर यथोचित सरल और यथोचित कुशल हैं:

T* p = new (nothrow) T[sz]; 
if(!p) { 
    return sorry_not_enough_mem_would_you_like_to_try_again; 
} 
... more code that doesn't throw ... 
delete[] p; 

try { 
    std::vector<T> p(sz); 
    ... more code that doesn't throw ... 
} catch (std::bad_alloc) { 
    return sorry_not_enough_mem_would_you_like_to_try_again; 
} 

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

लेकिन फिर भी बेहतर है, कैसे भी अपवाद उपयोग करने के लिए अपने API लिखने के बारे में:

std::vector<T> p(sz); 
... more code that doesn't throw ... 

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

"(अपेक्षित !!)" के बारे में - कभी-कभी आप जानते हैं कि त्रुटि स्थिति को कैसे संभालना है। उस मामले में करने की बात अपवाद को पकड़ना है। यह काम करने के लिए अपवादों को माना जाता है। यदि अपवाद फेंकने वाला कोड किसी भी तरह से जानता था कि किसी को भी इसे पकड़ने का कोई मतलब नहीं था, तो यह इसके बजाय प्रोग्राम को समाप्त कर सकता है।

+0

एपीआई को अपवाद का प्रचार करने के आपके प्रस्ताव के बारे में: मुझे लगता है कि जब आप पहले से ही जानते हैं कि महत्वपूर्ण आकार का आवंटन विफल हो सकता है तो बुरा_alloc प्रचार करना एक बहुत बुरा विचार है। जो भी कोड पकड़ने जा रहा है, वह कोई भी बुद्धिमान नहीं होगा जहां आवंटन विफल हो, और यदि आप पहले से ही जानते हैं कि आप महत्वपूर्ण आकार आवंटन कर रहे हैं तो मुझे लगता है कि बुरा_alloc पकड़ना और अधिक जानकारी/कुछ के साथ कुछ पुनर्स्थापित करना वाकई बेहतर है पकड़ा + एक और विशिष्ट तरीके से संभाला। –

+0

@ मार्टिन: ठीक है, आप अपने विशिष्ट उपयोग मामले को जानते हैं और मैं नहीं करता हूं। अगर कोड को स्मृति की आवश्यकता है, तो मुझे नहीं लगता कि 1 जीबी मेमोरी की इसकी आवश्यकता कोड से काफी अलग है जिसके लिए 1 बाइट (गतिशील रूप से आवंटित) मेमोरी की आवश्यकता होती है। आम तौर पर "पुनः प्रयास करें" स्मृति के बाहर एक अच्छी प्रतिक्रिया नहीं है, लेकिन यदि आपके विशिष्ट मामले में कुछ आवंटन हैं जिनके लिए फिर से प्रयास करना संभव हो सकता है, और अन्य जिनके लिए यह नहीं होगा, तो सभी माध्यमों के बीच अंतर दो। –

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