2013-02-21 47 views
10

मैं वर्तमान में सी ++ के साथ वैक्टर का उपयोग कर एक आवेदन कर रहा हूं।सी ++ push_back बनाम इन्सर्ट बनाम

मुझे पता है कि पूर्व-अनुकूलन सभी बुराइयों की जड़ कैसे है।

लेकिन मैं वास्तव में उत्सुक होने में मदद नहीं कर सकता।

मैं अन्य वैक्टरों के हिस्सों को एक और वेक्टर में जोड़ रहा हूं।
a.reserve(300);
a.insert(a.end(), b.begin(), b.end());

:
हम कहूँगा वेक्टर एक आकार कि 300

की कभी नहीं परिवर्तन के बाद से मैं हमेशा वेक्टर

के अंत में संलग्न करना होगा यह तेजी से करने के लिए है

या वे वेक्टर के माध्यम से लूप करने के लिए तेज़ी से होंगे जो मैं जोड़ना चाहता हूं और push_back या emplace के साथ प्रत्येक आइटम को अलग-अलग जोड़ना चाहता हूं (अभी भी पहले से ही आरक्षित कर रहा हूं)। (अनिश्चित जो तेज है)

कोई भी इस पर मेरी सहायता कर सकता है?

+5

"प्रभावी एसटीएल" आइटम 5: रेंज सदस्य कार्यों को उनके एकल-तत्व समकक्षों को पसंद करें – Cubbi

+0

क्लीनर कोड के लिए जाएं, एसटीएल आपको क्या प्रदान करता है ... जब तक आपको यह नहीं करना है तब तक पुन: प्रयास न करें। ज्यादातर मामलों में कोड का पुन: उपयोग करने से इस तरह के सरल संचालन के हाथ से तैयार संस्करणों को ट्रम्प कर दिया जाएगा। उन कार्यों को दिमाग में दक्षता के साथ डिजाइन किया गया था। – eazar001

+6

'डालने' तेज हो सकता है, या यह वही हो सकता है, लेकिन (एक गंभीर रूप से खराब लाइब्रेरी कार्यान्वयन से कम) कभी भी लूप से भी बदतर नहीं होगा। –

उत्तर

9

यहाँ एक सामान्य सिद्धांत है: जब एक पुस्तकालय दोनों do_x_once और do_x_in_batch प्रदान करता है, तो बाद के एक सरल पाश में do_x_once बुला के रूप में कम से कम के रूप में तेजी से होना चाहिए। यदि ऐसा नहीं है, तो पुस्तकालय बहुत बुरी तरह लागू किया गया है क्योंकि एक सरल लूप एक तेज संस्करण प्राप्त करने के लिए पर्याप्त है। अक्सर, ऐसे बैच फ़ंक्शन/विधियां अतिरिक्त अनुकूलन निष्पादित कर सकती हैं क्योंकि उन्हें डेटा संरचना आंतरिक का ज्ञान है।

तो, insertकम से कम तेजी से एक पाश में push_back के रूप में होना चाहिए। इस विशेष मामले में, insert का एक स्मार्ट कार्यान्वयन उन सभी तत्वों के लिए एक reserve कर सकता है जिन्हें आप सम्मिलित करना चाहते हैं। push_back हर बार वेक्टर की क्षमता की जांच करनी होगी। लाइब्रेरी को आउटमार्ट करने की कोशिश न करें :)

+0

वास्तव में वास्तव में मेरी मदद करता है। मैं सी ++ के लिए अपेक्षाकृत नया हूं। आपके द्वारा डालने वाले सभी तत्वों के लिए एक ही रिजर्व द्वारा आपका क्या मतलब है? सोचें कि आप मुझे और विवरण दे सकते हैं? – Darkalfx

+1

@ डार्कफैक्स: कुछ प्रकार के इटरेटर के लिए, 'डालने' तत्वों की संख्या की गणना कर सकते हैं जिन्हें 'b.begin() - b.end() 'का उपयोग करके डालने के लिए तत्वों की संख्या की गणना कर सकते हैं। जब यह तत्वों की संख्या जानता है, तो यह एक ऑपरेशन में ठीक उसी संख्या के लिए वेक्टर में स्थान बना सकता है। –

4

लार्समैन कहते हैं, जितना अधिक आप एक लाइब्रेरी कॉल में करते हैं, अधिक संभावना है कि यह अधिक कुशल हो। insert के मामले में वेक्टर में, लाइब्रेरी आमतौर पर लगभग पुन: आवंटन पर करेगी, और प्रत्येक स्थानांतरित तत्व को सबसे अधिक बार कॉपी करने के लिए। यदि आप लूप और push_back का उपयोग करते हैं, तो यह कई बार पुनः आवंटित कर सकता है, जो काफी धीमा हो सकता है ( परिमाण के क्रम की तरह)।

प्रकार के आधार पर, तथापि, यह भी तेजी से कुछ करने के लिए हो सकता है की तरह:

a.resize(300); 
std::copy(b.begin(), b.end(), a.end() - 300); 

मैंने पाया इस सरल अदिश प्रकार के लिए तेजी से होने के लिए (जैसे int) एक पर जी ++ का उपयोग करते हुए इंटेल मशीन

+0

एक साइड नोट के रूप में: 'resize' को लूप के अंदर कभी भी नहीं कहा जाना चाहिए (यहां तक ​​कि एक लूप के अंदर एक फ़ंक्शन के अंदर भी) 'push_back' (और मुझे लगता है' डालने ') को स्थिर समय (घातीय वृद्धि चरण) को आवंटित करने की आवश्यकता होती है। और एक रूढ़िवादी 'आकार बदलें' इसे तोड़ देता है। – BCS

+1

@BCS 'आकार बदलें' आमतौर पर लूप में नहीं, लूप में नहीं कहा जाएगा। यहां समस्या 'आकार बदलें' '' [] 'बनाम' आरक्षित '+ 'push_back' है। –

4

मुझे लगता है कि यह वास्तव में संकलक (लाइब्रेरी कार्यान्वयन), संकलन विकल्पों और वास्तुकला पर निर्भर करता है। इंटेल जिऑन पर अनुकूलन (/ ओवर ड्राफ्ट) के बिना VS2005 में एक त्वरित बेंचमार्क कर:

std::vector<int> a; 
std::vector<int> b; 

// fill 'a' with random values for giggles 

timer.start() 
// copy values from 'a' to 'b' 
timer.stop() 

मैं "कॉपी मूल्यों के इन विभिन्न तरीकों का उपयोग कर 10 000 000 मदों के लिए ये परिणाम प्राप्त ...":

    'बी' के लिए
  1. रिजर्व स्पेस दें, फिर के लिए लूप का उपयोग कर b.push_back(a[i]);: 0.808 सेकंड
  2. आकार 'बी', तो के लिए लूप सूचकांक काम b[i] = a[i]; का उपयोग कर: 0.264 सेकंड
  3. कोई फिर से आकार देने 'बी', बस b.insert(b.end(), a.begin(), a.end());: 0.021 सेकंड (आरक्षित पहले के साथ कोई महत्वपूर्ण अंतर)
  4. std::copy(a.begin(), a.end(), std::back_inserter(b));: 0.944 सेकंड (0.871 आरक्षित पहले के साथ)
  5. आकार 'बी', तो आधार संकेत memcpy(&(b[0]), &(a[0]), 10000000*sizeof(int)); पर memcopy: 0.061 सेकंड
  6. 012,

ऑप्टिमाइज़ेशन चालू होने के साथ (/ ऑक्स) हालांकि, यह एक अलग कहानी है।

  1. push_back पाश: मैं और अधिक भेदभाव प्राप्त करने के लिए 100 000 000 करने के लिए आकार बढ़ाने के लिए किया था .659 सेकंड
  2. सूचकांक पाश: 0.482 सेकंड
  3. डालने: 0.210 सेकंड (आरक्षित पहले के साथ कोई महत्वपूर्ण अंतर)
  4. std :: प्रतिलिपि: पहले आरक्षित के साथ 0.422 सेकंड। इसके बिना एक bad_alloc मिला।
  5. memcpy: 0.329 सेकंड

क्या नोट करने के लिए दिलचस्प है कि अनुकूलन के साथ या बिना, सम्मिलित विधि रैखिक बढ़ाया है। ऑप्टिमाइज़ेशन के बिना अन्य विधियां स्पष्ट रूप से अक्षम थीं लेकिन फिर भी उनके साथ उतनी तेज नहीं हो सका। जेम्स कन्ज़ ने नोट किया, यह जी ++ पर अलग है। सत्यापित करने के लिए अपने मंच के साथ एक परीक्षण चलाएं।