FIFO के लिए मुझे कौन से STL कंटेनर का उपयोग करना चाहिए?


93

कौन सा एसटीएल कंटेनर मेरी ज़रूरतों को पूरा करेगा? मेरे पास मूल रूप से एक 10 तत्व चौड़ा कंटेनर है जिसमें मैं सबसे पुराने तत्व (लगभग एक मिलियन समय) में push_backरहते हुए लगातार नए तत्व रखता हूं pop_front

मैं वर्तमान std::dequeमें कार्य के लिए उपयोग कर रहा हूं , लेकिन सोच रहा था कि std::listक्या यह अधिक कुशल होगा क्योंकि मुझे खुद को पुनः प्राप्त करने की आवश्यकता नहीं होगी (या शायद मैं एक के std::dequeलिए गलत कर रहा हूं std::vector?)। या मेरी जरूरत के लिए और भी अधिक कुशल कंटेनर है?

पुनश्च मैं यादृच्छिक उपयोग की जरूरत नहीं है


5
क्यों नहीं इसे दोनों के साथ आज़माएं और यह देखने के लिए कि आपकी ज़रूरत के लिए कौन सा तेज़ है?
KTC

5
मैं ऐसा करने वाला था, लेकिन मैं एक सैद्धांतिक जवाब भी ढूंढ रहा था।
गैब रोयर

2
std::dequeपुनः आवंटित नहीं होंगे। यह एक हाइब्रिड है std::listऔर std::vectorजहां यह बड़ी मात्रा में आवंटन करता है, std::listलेकिन ए की तरह फिर से नहीं बढ़ेगा std::vector
मैट प्राइस 20

2
नहीं, यहां मानक से प्रासंगिक गारंटी है: "किसी भी तत्व को या तो शुरुआत या अंत में एक हिरण को हमेशा स्थिर समय लगता है और एक एकल कॉल को टी के कॉपी निर्माता का कारण बनता है।"
मैट की कीमत

1
@ जॉन: नहीं, यह फिर से आवंटित करता है। शायद हम सिर्फ शब्दों को मिला रहे हैं। मुझे लगता है कि वास्तविक आवंटन का मतलब पुराने आवंटन को लेना है, इसे नए आवंटन में कॉपी करना है, और पुराने को छोड़ देना है।
GManNickG

जवाबों:


198

चूंकि उत्तर के असंख्य हैं, आप भ्रमित हो सकते हैं, लेकिन संक्षेप में:

एक का उपयोग करें std::queue। इसका कारण सरल है: यह एक फीफो संरचना है। आप FIFO चाहते हैं, आप एक का उपयोग करें std::queue

यह आपके इरादे को स्पष्ट करता है किसी और को, और यहाँ तक कि खुद को भी। A std::listया std::dequeनहीं। एक सूची कहीं भी सम्मिलित और हटा सकती है, जो कि ऐसा नहीं है जिसे एक फीफो संरचना को करने के लिए माना जाता है, और dequeदोनों छोर से एक जोड़ और हटा सकते हैं, जो कि एक ऐसी चीज है जिसे कोई फीफो संरचना नहीं कर सकती है।

यही कारण है कि आपको एक का उपयोग करना चाहिए queue

अब, आपने प्रदर्शन के बारे में पूछा। सबसे पहले, हमेशा अंगूठे के इस महत्वपूर्ण नियम को याद रखें: अच्छा कोड पहले, प्रदर्शन अंतिम।

इसका कारण सरल है: जो लोग स्वच्छता और लालित्य से पहले प्रदर्शन के लिए प्रयास करते हैं, वे हमेशा अंतिम रूप से समाप्त होते हैं। उनका कोड एक प्रकार का मूस बन जाता है, क्योंकि उन्होंने वह सब त्याग दिया है जो वास्तव में अच्छा है ताकि इससे कुछ भी न निकले।

पहले अच्छा, पठनीय कोड लिखकर, आपमें से अधिकांश प्रदर्शन समस्याएं स्वयं हल कर लेंगे। और अगर बाद में आपको पता चलता है कि आपके प्रदर्शन में कमी है, तो अब अपने अच्छे, साफ कोड में एक प्रोफाइलर को जोड़ना आसान है, और यह पता लगाएं कि समस्या कहाँ है।

यह सब कहा, std::queueकेवल एक एडाप्टर है। यह सुरक्षित इंटरफ़ेस प्रदान करता है, लेकिन अंदर एक अलग कंटेनर का उपयोग करता है। आप इस अंतर्निहित कंटेनर को चुन सकते हैं, और यह लचीलेपन का एक अच्छा सौदा करने की अनुमति देता है।

तो, आपको किस अंतर्निहित कंटेनर का उपयोग करना चाहिए? हम यह जानते हैं std::listऔर std::dequeदोनों आवश्यक कार्यों प्रदान करते हैं ( push_back(), pop_front(), और front()), तो हम कैसे तय करते हैं?

सबसे पहले, यह समझें कि मेमोरी (और डीलिंग) को आवंटित करना आम तौर पर करने के लिए एक त्वरित बात नहीं है, क्योंकि इसमें ओएस से बाहर जाना और कुछ करने के लिए पूछना शामिल है। A listको हर बार जब कुछ जोड़ा जाता है, तो मेमोरी आवंटित करना पड़ता है और जब वह चला जाता है तो उसे डीलिट कर देता है।

dequeदूसरी ओर ए , चंक्स में आवंटित करता है। यह एक से कम बार आवंटित करेगा list। इसे एक सूची के रूप में सोचें, लेकिन प्रत्येक मेमोरी चंक में कई नोड हो सकते हैं। (बेशक, मेरा सुझाव है कि आप वास्तव में सीखें कि यह कैसे काम करता है ।)

इसलिए, इसके साथ ही dequeबेहतर प्रदर्शन करना चाहिए, क्योंकि यह अक्सर मेमोरी से निपटता नहीं है। इस तथ्य के साथ मिश्रित कि आप निरंतर आकार के डेटा को संभाल रहे हैं, इसे संभवतः डेटा के माध्यम से पहले पास के बाद आवंटित नहीं करना होगा, जबकि एक सूची लगातार आवंटित और डीलिंग होगी।

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

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

इसलिए, इस पर विचार करते हुए, dequeएक बेहतर विकल्प होना चाहिए। यही कारण है कि यह एक का उपयोग करते समय डिफ़ॉल्ट कंटेनर है queue। उस सभी ने कहा, यह अभी भी केवल एक (बहुत) शिक्षित अनुमान है: आपको इस कोड को प्रोफाइल करना होगा, dequeएक परीक्षण listमें और दूसरे में वास्तव में कुछ जानने के लिए।

लेकिन याद रखें: एक साफ इंटरफ़ेस के साथ काम करने वाला कोड प्राप्त करें, फिर प्रदर्शन के बारे में चिंता करें।

जॉन चिंता जताता है कि लपेटने listया dequeप्रदर्शन में कमी आएगी। एक बार फिर, वह और न ही मैं खुद को रूपरेखा के बिना कुछ के लिए कह सकता हूं, लेकिन संभावना है कि कंपाइलर उन कॉल को इनलाइन करेगा जो queueबनाता है। यही है, जब आप कहते हैं queue.push(), यह वास्तव में सिर्फ कहेगा queue.container.push_back(), फ़ंक्शन कॉल को पूरी तरह से छोड़ देगा।

एक बार फिर, यह केवल एक शिक्षित अनुमान है, लेकिन queueजब अंतर्निहित कंटेनर कच्चे का उपयोग करने की तुलना में एक विल का उपयोग प्रदर्शन को नीचा नहीं करेगा। जैसे मैंने पहले कहा है, का उपयोग करें queue, क्योंकि यह साफ है, उपयोग करने में आसान है, और सुरक्षित है, और अगर यह वास्तव में एक समस्या प्रोफ़ाइल और परीक्षण बन जाता है।


10
+1 - और अगर यह पता चलता है कि बढ़ावा :: परिपत्र_बफ़र <> का सबसे अच्छा प्रदर्शन है, तो बस उपयोग करें कि अंतर्निहित कंटेनर (यह आवश्यक पुश_बैक (), पॉप_फ्रंट (), सामने (), और वापस () भी प्रदान करता है। )।
माइकल बूर

2
इसे विवरण में समझाने के लिए स्वीकार किया गया (जो मुझे चाहिए था, समय लेने के लिए धन्यवाद)। पहले अच्छे प्रदर्शन के लिए अंतिम रूप से, मुझे स्वीकार करना होगा कि यह मेरी सबसे बड़ी डिफ़ॉल्ट में से एक है, मैं हमेशा पहली बार में पूरी तरह से बात करने की कोशिश करता हूं ... मैंने एक कठिन पहली कड़ी का उपयोग करते हुए कोड लिखा था, लेकिन जब से बात नहीं हुई थी ' टी के रूप में अच्छी तरह से प्रदर्शन के रूप में मैंने सोचा (यह लगभग वास्तविक समय माना जाता है), मैंने अनुमान लगाया कि मुझे इसे थोड़ा सुधारना चाहिए। जैसा कि नील ने यह भी कहा, मुझे वास्तव में एक प्रोफाइलर का इस्तेमाल करना चाहिए था ... हालाँकि मुझे खुशी है कि अब मैंने ये गलतियाँ की हैं जबकि यह वास्तव में मायने नहीं रखता। आप सभी का बहुत बहुत धन्यवाद।
गेब रॉयर

4
-1 समस्या को हल करने के लिए और बेकार जवाब फूला हुआ। यहां सही उत्तर छोटा है और यह बढ़ावा देने वाला है :: परिपत्र_बफ़र <>।
दिमित्री चिचकोव

1
"अच्छा कोड पहले, प्रदर्शन अंतिम", यह एक भयानक उद्धरण है। अगर केवल सभी को यह समझ में आया :)
thegreendroid

मैं प्रोफाइलिंग पर तनाव की सराहना करता हूं। एक सामान्य नियम के प्रदान करना एक बात है और उसके बाद की रूपरेखा के साथ यह साबित है एक बेहतर बात यह है
talekeDskobeDa

28

जांच करें std::queue। यह एक अंतर्निहित कंटेनर प्रकार को लपेटता है, और डिफ़ॉल्ट कंटेनर है std::deque


3
हर अतिरिक्त परत को संकलक द्वारा समाप्त किया जाएगा । आपके तर्क से, हम सभी को बस विधानसभा में कार्यक्रम करना चाहिए, क्योंकि भाषा सिर्फ एक खोल है जो रास्ते में मिलती है। बिंदु काम के लिए सही प्रकार का उपयोग करना है। और queueवह प्रकार है। अच्छा कोड पहले, बाद में प्रदर्शन। नरक, अधिकांश प्रदर्शन पहले स्थान पर अच्छे कोड का उपयोग करने से निकलते हैं।
GManNickG

2
अस्पष्ट होने के लिए खेद है - मेरा कहना था कि एक कतार ठीक वैसी ही है जैसा सवाल पूछ रहा था, और सी ++ डिजाइनरों ने सोचा कि इस उपयोग के मामले के लिए एक अच्छा अंतर्निहित कंटेनर था।
मार्क रैनसम

2
यह इंगित करने के लिए कि उसे प्रदर्शन में कमी मिली, इस प्रश्न में कुछ भी नहीं है। कई शुरुआती लगातार किसी भी समस्या के लिए सबसे प्रभावी समाधान के बारे में पूछ रहे हैं, भले ही उनका वर्तमान समाधान स्वीकार्य रूप से निष्पादित हो या नहीं।
जालफ

1
@ जॉन, अगर उसे प्रदर्शन में कमी महसूस हुई, तो सुरक्षा के खोल को अलग करने से queueप्रदर्शन में वृद्धि नहीं होगी, जैसे मैं कह रहा हूं। आपने एक सुझाव दिया था list, जो संभवतः खराब प्रदर्शन करेगा।
GManNickG

3
Std :: कतार <> के बारे में बात यह है कि अगर deque <> वह नहीं है जो आप चाहते हैं (पूर्णता या जो भी कारण के लिए), यह एक एक-लाइनर है जो इसे std का उपयोग करने के लिए बदल सकता है :: समर्थन स्टोर के रूप में सूची - के रूप में जीएमन ने कहा कि वापस आ जाओ। और अगर आप वास्तव में एक सूची के बजाय रिंग बफर का उपयोग करना चाहते हैं, तो बढ़ावा दें :: circ_buffer <> सही में छोड़ देगा ... std :: कतार <> लगभग निश्चित रूप से 'इंटरफ़ेस' है जिसका उपयोग किया जाना चाहिए। इसके लिए बैकिंग स्टोर को वसीयत में बहुत बदला जा सकता है।
माइकल बूर


7

मैं लगातार push_backनए तत्वों को pop_frontसम्मिलित करता हूं जबकि सबसे पुराने तत्व (लगभग एक मिलियन समय) को शामिल करता हूं ।

कंप्यूटिंग में एक मिलियन वास्तव में एक बड़ी संख्या नहीं है। जैसा कि दूसरों ने सुझाव दिया है, std::queueअपने पहले समाधान के रूप में उपयोग करें। इसकी धीमी गति से होने की अप्रत्याशित घटना में, एक प्रोफाइलर का उपयोग करके टोंटी की पहचान करें (अनुमान न करें!) और एक ही इंटरफ़ेस के साथ एक अलग कंटेनर का उपयोग करके फिर से लागू करें।


1
खैर बात यह है कि यह एक बड़ी संख्या है क्योंकि मैं जो करना चाहता हूं वह वास्तविक समय माना जाता है। हालांकि आप सही हैं कि मुझे इस कारण की पहचान करने के लिए एक प्रोफाइलर का इस्तेमाल करना चाहिए ...
गाब रॉयर

बात यह है, मैं वास्तव में एक प्रोफाइलर का उपयोग करने के लिए उपयोग नहीं कर रहा हूं (हमने अपनी कक्षाओं में एक बिट का उपयोग किया है, लेकिन हम वास्तव में गहराई में नहीं गए ...)। यदि आप मुझे कुछ सूत्रों की ओर संकेत कर सकते हैं, तो मैं बहुत सराहना करूंगा! पुनश्च। मैं VS2008
गैब रॉयर

@ गैब: आपके पास कौन सा VS2008 है (एक्सप्रेस, प्रो ...)? कुछ एक प्रोफाइलर के साथ आते हैं।
sbi

@Gab क्षमा करें, मैं वी.एस. किसी भी अधिक तो वास्तव में सलाह नहीं दे सकता का उपयोग नहीं करते

@ एसबीआई, जो मैं देख रहा हूं, वह केवल टीम सिस्टम संस्करण में है (जिसकी मुझे एक्सेस है)। मैं इस पर गौर करूंगा।
गेब रॉयर

5

क्यों नहीं std::queue? सभी यह है push_backऔर pop_front


3

एक कतार शायद एक छल से अधिक सरल इंटरफ़ेस है लेकिन इतनी छोटी सूची के लिए, प्रदर्शन में अंतर शायद नगण्य है।

उसी सूची के लिए चला जाता है । यह आप क्या चाहते हैं एपीआई के एक विकल्प के लिए नीचे है।


लेकिन मैं सोच रहा था कि अगर लगातार push_back कतार या Deque खुद को पुनः आवंटित कर रहे थे
बकवाद रोयेर

std :: कतार एक अन्य कंटेनर के चारों ओर एक आवरण है, इसलिए एक deque को लपेटने वाली कतार कच्चे deque की तुलना में कम कुशल होगी।
जॉन मिलिकिन 20

1
10 वस्तुओं के लिए, प्रदर्शन सबसे अधिक संभावना है कि यह एक छोटा मुद्दा है, कोड-टाइम की तुलना में प्रोग्रामर-टाइम में "दक्षता" को बेहतर ढंग से मापा जा सकता है। और किसी भी सभ्य संकलक अनुकूलन द्वारा कतार से deque करने के लिए कॉल कुछ भी नहीं होगा।
20

2
@ जॉन: मैं चाहूंगा कि आप मुझे इस तरह के प्रदर्शन अंतर का प्रदर्शन करने वाले बेंचमार्क का एक सेट दिखाएं। यह एक कच्चे छत्ते से कम कुशल नहीं है। C ++ कंपाइलर बहुत आक्रामक तरीके से इनलाइन करता है।
जालफ

3
मैंने इसे आजमाया है। : DA त्वरित और गंदा 10 तत्व कंटेनर 100,000,000 pop_front () और push_back () रैंड () के साथ int संख्या VC9 पर गति के लिए रिलीज़ बिल्ड पर: सूची (27), कतार (6), deque (6), सरणी (8) ।
KTC

0

एक का उपयोग करें std::queue, लेकिन दो मानक Containerवर्गों के प्रदर्शन ट्रेडऑफ़्स के संज्ञान में रहें ।

डिफ़ॉल्ट रूप से, std::queueशीर्ष पर एक एडेप्टर है std::deque। आमतौर पर, यह अच्छा प्रदर्शन देगा जहां आपके पास बड़ी संख्या में प्रविष्टियों वाली छोटी संख्या में कतारें हैं, जो यकीनन आम मामला है।

हालाँकि, std :: deque के क्रियान्वयन के लिए अंधे न हों । विशेष रूप से:

"... आम तौर पर देवताओं की न्यूनतम स्मृति लागत होती है; केवल एक तत्व को रखने वाले एक छल को अपने पूर्ण आंतरिक सरणी को आवंटित करना होता है (उदाहरण के लिए वस्तु का आकार 64-बिट libstdc ++ पर 8 गुना; वस्तु का आकार 16 गुना या 4096 बाइट्स, जो भी बड़ा हो; , 64-बिट libc ++ पर)। "

शुद्ध करने के लिए, यह मानते हुए कि एक कतार प्रविष्टि एक ऐसी चीज है जिसे आप कतार में रखना चाहते हैं, अर्थात, आकार में काफी छोटा, तो यदि आपके पास 4 कतारें हैं, जिनमें से प्रत्येक में 30,000 प्रविष्टियाँ हैं, तो std::dequeकार्यान्वयन पसंद का विकल्प होगा। इसके विपरीत, यदि आपके पास 30,000 कतारें हैं, जिनमें से प्रत्येक में 4 प्रविष्टियां हैं, तो संभावना से अधिक std::listकार्यान्वयन इष्टतम होगा, क्योंकि आप std::dequeउस परिदृश्य में ओवरहेड को कभी भी संशोधित नहीं करेंगे ।

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


-1

यह मामला इतना सरल है कि आप सिर्फ अपना लिख ​​सकते हैं। यहां कुछ ऐसी चीजें हैं जो माइक्रो-कॉनरोलर स्थितियों के लिए अच्छी तरह से काम करती हैं जहां एसटीएल का उपयोग बहुत अधिक जगह लेता है। यह डेटा और सिग्नल को बाधित करने वाले हैंडलर से आपके मुख्य लूप में जाने का अच्छा तरीका है।

// FIFO with circular buffer
#define fifo_size 4

class Fifo {
  uint8_t buff[fifo_size];
  int writePtr = 0;
  int readPtr = 0;
  
public:  
  void put(uint8_t val) {
    buff[writePtr%fifo_size] = val;
    writePtr++;
  }
  uint8_t get() {
    uint8_t val = NULL;
    if(readPtr < writePtr) {
      val = buff[readPtr%fifo_size];
      readPtr++;
      
      // reset pointers to avoid overflow
      if(readPtr > fifo_size) {
        writePtr = writePtr%fifo_size;
        readPtr = readPtr%fifo_size;
      }
    }
    return val;
  }
  int count() { return (writePtr - readPtr);}
};

लेकिन यह कैसे / कब होगा?
user10658782

ओह, मैंने सोचा कि यह किसी कारण से हो सकता है। कोई बात नहीं!
Ry-
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.