2011-06-29 9 views
20

वैक्टर से निपटने के दौरान आरक्षित का उपयोग करने का क्या फायदा है। मुझे उनका उपयोग कब करना चाहिए? इस पर स्पष्ट कट जवाब नहीं मिला लेकिन मुझे लगता है कि जब आप उनका उपयोग करने से पहले अग्रिम आरक्षित करते हैं तो यह तेज़ होता है।वेक्टर में आरक्षित() का उपयोग करने के लाभ - सी ++

क्या आप मुझसे ज्यादा चालाक कहते हैं?

+0

उत्तर के लिए सभी को धन्यवाद। इसके अतिरिक्त, आरक्षित (20) का उपयोग करने का मतलब है कि अब 20 तत्व हैं या सिर्फ 20 तत्वों की स्मृति आवंटित की गई है? –

+0

बस स्मृति आवंटित की गई है। स्मृति शुरू नहीं हुई है। – Jason

+0

'आरक्षित()' क्षमता जोड़ता है, लेकिन तत्वों को जोड़ता नहीं है। 'आकार()' द्वारा वापस जो किया जाता है वह अपरिवर्तित होगा (हालांकि, 'क्षमता()' आपके उदाहरण में कम से कम 20 लौटाएगा)। –

उत्तर

22

यह उपयोगी है अगर आपको पता है कि वेक्टर कितने तत्वों को अंततः पकड़ेंगे - यह वेक्टर को बार-बार स्मृति आवंटित करने से बचने में मदद कर सकता है (और डेटा को नई मेमोरी में ले जाने के लिए)।

आम तौर पर यह संभवतः एक संभावित अनुकूलन है जिसके बारे में आपको चिंता करने की आवश्यकता नहीं है, लेकिन यह हानिकारक नहीं है (यदि आप अनुमान लगाते हैं तो सबसे खराब आप स्मृति को बर्बाद कर देते हैं)।

एक क्षेत्र जहां यह अनुकूलन से अधिक हो सकता है, जब आप यह सुनिश्चित करना चाहते हैं कि मौजूदा तत्वों को नए तत्व जोड़कर अमान्य नहीं किया जाए।

उदाहरण के लिए, push_back() कॉल वेक्टर को मौजूदा इटरेटर्स को अमान्य कर सकता है (यदि कोई पुनर्वितरण होता है)। हालांकि अगर आपने पर्याप्त तत्व आरक्षित किए हैं तो आप यह सुनिश्चित कर सकते हैं कि पुनर्वितरण नहीं होगा। यह एक ऐसी तकनीक है जिसे अक्सर इस्तेमाल करने की आवश्यकता नहीं होती है।

+7

... और वर्तमान में उपलब्ध स्थान समाप्त होने पर बार-बार आवंटित/प्रतिलिपि/अनुक्रमों को आवंटित करना, ढेर विखंडन का कारण बन सकता है। –

+3

".. यह सुनिश्चित करें कि मौजूदा तत्वों को नए तत्व जोड़कर अवैध नहीं किया जा सके।" यह * विशिष्ट * प्रकार के जोड़ों के लिए सच है (push_back उदाहरण एक ऐसा प्रकार है)। लेकिन एक 'डालने() 'सम्मिलन बिंदु से सभी इटरेटर को अमान्य क्षमता के साथ भी अमान्य कर देगा (और निश्चित रूप से, अगर क्षमता को आकार बदलने की आवश्यकता होती है तो सबकुछ तालिका से गिर जाता है)। चरम संस्करण, ज़ाहिर है, एक 'डालने (v.begin(), ...)' जो * सभी * iterators * हमेशा * को अमान्य करता है। शुक्र है, वेक्टर की तुलना में बेहतर कंटेनर हैं जो इसे संभालने के लिए हैं यदि यह किसी के कोड द्वारा लगातार संचालन होता है। – WhozCraig

7

यह हो सकता है ... विशेष रूप से यदि आप समय के साथ वेक्टर में बहुत से तत्व जोड़ रहे हैं, और आप स्वचालित स्मृति विस्तार से बचना चाहते हैं कि कंटेनर उपलब्ध स्लॉट से बाहर होने पर बना देगा।

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

1

तेज़ और बचाता स्मृति

यदि आप किसी अन्य तत्व push_back है, तो एक पूर्ण वेक्टर आम तौर पर डबल स्मृति को आबंटित यह वर्तमान में उपयोग कर रहा है जाएगा - के बाद से + प्रतिलिपि आवंटित

1

लोगों के बारे में अधिक चालाक पता नहीं है महंगा है आप, लेकिन मैं कहूंगा कि आपकोअग्रिम में कॉल करना चाहिए यदि आप सम्मिलन संचालन में बहुत कुछ करने जा रहे हैं और आप कम से कम परिमाण के क्रम में तत्वों की कुल संख्या का अनुमान लगा सकते हैं या अनुमान लगा सकते हैं। यह आपको अच्छी परिस्थितियों में बहुत से पुनर्स्थापन बचा सकता है।

3

यदि आप वेक्टर के अंतिम आकार को जानते हैं तो आरक्षित का उपयोग करने लायक है।

अन्यथा जब भी वेक्टर आंतरिक कक्ष से बाहर निकलता है तो यह बफर को फिर से आकार देगा। इसमें आमतौर पर दोगुनी होती है (या 1।5 * वर्तमान आकार) आंतरिक बफर का आकार (यदि आप इसे बहुत करते हैं तो महंगा हो सकता है)।

वास्तविक महंगा बिट प्रत्येक तत्व पर प्रतिलिपि बनाने वाले को नए बफर से प्रतिलिपि बनाने के लिए प्रतिलिपि बनाने वाला है, इसके बाद पुराने बफर में प्रत्येक तत्व पर विनाशक को बुलाया जाता है।

यदि कॉपी कन्स्ट्रक्टर महंगा है तो यह एक समस्या हो सकती है।

+0

यदि कॉपी कन्स्ट्रक्टर महंगा है, तो 'std :: deque' का उपयोग करना बेहतर विकल्प हो सकता है। http://www.gotw.ca/gotw/054.htm –

5

This excellent articledeque और vector कंटेनर के बीच अंतर को गहराई से बताता है। खंड "प्रयोग 2" vector::reserve() के लाभ दिखाता है।

0

हालांकि यह एक पुराना सवाल है, अंतरों के लिए मेरा कार्यान्वयन यहां है।

#include <iostream> 
#include <chrono> 
#include <vector> 

using namespace std; 

int main(){ 
    vector<int> v1; 
    chrono::steady_clock::time_point t1 = chrono::steady_clock::now(); 
    for(int i = 0; i < 1000000; ++i){ 
     v1.push_back(1); 
    } 
    chrono::steady_clock::time_point t2 = chrono::steady_clock::now(); 
    chrono::duration<double> time_first = chrono::duration_cast<chrono::duration<double>>(t2-t1); 
    cout << "Time for 1000000 insertion without reserve: " << time_first.count() * 1000 << " miliseconds." << endl; 

    vector<int> v2; 
    v2.reserve(1000000); 
    chrono::steady_clock::time_point t3 = chrono::steady_clock::now(); 
    for(int i = 0; i < 1000000; ++i){ 
     v2.push_back(1); 
    } 
    chrono::steady_clock::time_point t4 = chrono::steady_clock::now(); 
    chrono::duration<double> time_second = chrono::duration_cast<chrono::duration<double>>(t4-t3); 
    cout << "Time for 1000000 insertion with reserve: " << time_second.count() * 1000 << " miliseconds." << endl; 
    return 0; 
} 

जब आप संकलन और इस कार्यक्रम चलाने के लिए, यह आउटपुट:

Time for 1000000 insertion without reserve: 24.5573 miliseconds. 

Time for 1000000 insertion with reserve: 17.1771 miliseconds. 

आरक्षित के साथ कुछ सुधार हो सकता है लगता है, लेकिन नहीं है कि बहुत ज्यादा सुधार। मुझे लगता है कि यह जटिल वस्तुओं के लिए और अधिक सुधार होगा, मुझे यकीन नहीं है। कोई भी सुझाव, परिवर्तन और टिप्पणियां आपका स्वागत है।

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