जब भी मैं C ++ मानक पुस्तकालय iostreams के धीमे प्रदर्शन का उल्लेख करता हूं, तो मुझे अविश्वास की लहर के साथ मिलता है। फिर भी मेरे पास प्रोइलर परिणाम हैं, जो आईओस्ट्रीम लाइब्रेरी कोड (पूर्ण कंपाइलर ऑप्टिमाइज़ेशन) में बड़ी मात्रा में समय बिताते हैं, और आईओट्रीम से ओएस-विशिष्ट I / O एपीआई और कस्टम बफर प्रबंधन पर स्विच करने से परिमाण में सुधार का आदेश मिलता है।
C ++ मानक पुस्तकालय क्या अतिरिक्त काम कर रहा है, क्या यह मानक द्वारा आवश्यक है, और क्या यह व्यवहार में उपयोगी है? या कुछ कंपाइलर iostreams के कार्यान्वयन को प्रदान करते हैं जो मैनुअल बफर प्रबंधन के साथ प्रतिस्पर्धी हैं?
मानक
गतिमान मामलों को प्राप्त करने के लिए, मैंने कुछ छोटे कार्यक्रमों के लिए लिखा है जो आईस्ट्रीम आंतरिक बफरिंग का अभ्यास करते हैं:
- एक http://ideone.com/2PPYw में बाइनरी डेटा डाल रहा है
ostringstream - बाइनरी डेटा को एक
char[]बफर में डालना http://ideone.com/Ni5ct - एक http://ideone.com/Mj2Fi
vector<char>का उपयोग करके बाइनरी डेटा डाल रहा हैback_inserter - नया :
vector<char>सरल पुनरावृत्ति http://ideone.com/9iitv - नई : http://ideone.com/qc9QA में सीधे बाइनरी डेटा डाल रहा है
stringbuf - नई :
vector<char>सरल पुनरावृत्ति प्लस सीमा की जाँच करें http://ideone.com/YyrKy
ध्यान दें कि ostringstreamऔर stringbufसंस्करण कम पुनरावृत्तियों चलाते हैं क्योंकि वे बहुत धीमे हैं।
आइडोन पर, + + ostringstreamकी तुलना में लगभग 3 गुना धीमा है , और कच्चे बफर की तुलना में लगभग 15 गुना धीमा है । यह पहले और बाद की रूपरेखा के अनुरूप है जब मैंने अपने असली एप्लिकेशन को कस्टम बफरिंग में बदल दिया।std:copyback_inserterstd::vectormemcpy
ये सभी इन-मेमोरी बफ़र हैं, इसलिए आईस्ट्रीम की सुस्ती को धीमी डिस्क I / O पर दोष नहीं दिया जा सकता है, बहुत अधिक निस्तब्धता, stdio के साथ सिंक्रनाइज़ेशन, या किसी भी अन्य चीज का उपयोग करने के लिए लोग C ++ मानक पुस्तकालय की मनाया सुस्ती का उपयोग करते हैं iostream।
अन्य प्रणालियों पर बेंचमार्क देखना और उन चीजों पर टिप्पणी करना अच्छा होगा जो सामान्य कार्यान्वयन करते हैं (जैसे कि gcc के libc ++, विजुअल C ++, Intel C ++) और मानक द्वारा कितना ओवरहेड अनिवार्य है।
इस परीक्षण के लिए तर्क
कई लोगों ने सही ढंग से बताया है कि आमतौर पर स्वरूपित आउटपुट के लिए आईस्ट्रीम का अधिक उपयोग किया जाता है। हालाँकि, वे बाइनरी फ़ाइल एक्सेस के लिए C ++ मानक द्वारा प्रदान किया गया एकमात्र आधुनिक API भी हैं। लेकिन आंतरिक बफरिंग पर प्रदर्शन परीक्षण करने का असली कारण I / O के विशिष्ट स्वरूपित पर लागू होता है: यदि iostreams कच्चे डेटा के साथ आपूर्ति की गई डिस्क नियंत्रक को नहीं रख सकते हैं, तो संभवतः जब वे प्रारूपण के लिए जिम्मेदार होते हैं, तो वे कैसे रख सकते हैं?
बेंचमार्क समय
ये सभी बाहरी ( k) लूप के प्रति पुनरावृत्ति हैं ।
Ideone पर (gcc-4.3.4, अज्ञात OS और हार्डवेयर):
ostringstream: 53 मिली सेकेंडstringbuf: 27 मिvector<char>औरback_inserter: 17.6 एमएसvector<char>साधारण पुनरावृत्ति के साथ: 10.6 मिvector<char>पुनरावृत्ति और सीमा जाँच: 11.4 मिchar[]: 3.7 मि
मेरे लैपटॉप पर (विजुअल C ++ 2010 x86,, cl /Ox /EHscविंडोज 7 अल्टीमेट 64-बिट, इंटेल कोर i7, 8 जीबी रैम):
ostringstream: 73.4 मिलीसेकंड, 71.6 मिसेstringbuf: 21.7 एमएस, 21.3 एमएसvector<char>औरback_inserter: 34.6 एमएस, 34.4 एमएसvector<char>साधारण पुनरावृत्ति के साथ: 1.10 एमएस, 1.04 एमएसvector<char>पुनरावृत्ति और सीमा जाँच: 1.11 एमएस, 0.87 एमएस, 1.12 एमएस, 0.89 एमएस, 1.02 एमएस, 1.14 एमएसchar[]: 1.48 एमएस, 1.57 एमएस
Visual C ++ 2010 x86, प्रोफ़ाइल-गाइडेड अनुकूलन के साथ cl /Ox /EHsc /GL /c, link /ltcg:pgi, रन link /ltcg:pgo, उपाय:
ostringstream: 61.2 एमएस, 60.5 एमएसvector<char>साधारण पुनरावृत्ति के साथ: 1.04 एमएस, 1.03 एमएस
एक ही लैपटॉप, एक ही OS, जो साइबर 4.3 gcc का उपयोग कर रहा है g++ -O3:
ostringstream: 62.7 एमएस, 60.5 एमएसstringbuf: 44.4 एमएस, 44.5 एमएसvector<char>औरback_inserter: 13.5 एमएस, 13.6 एमएसvector<char>साधारण पुनरावृत्ति के साथ: 4.1 एमएस, 3.9 एमएसvector<char>पुनरावृत्ति और सीमा की जाँच करें: 4.0 एमएस, 4.0 एमएसchar[]: 3.57 एमएस, 3.75 एमएस
समान लैपटॉप, विजुअल C ++ 2008 SP1 cl /Ox /EHsc:
ostringstream: 88.7 एमएस, 87.6 एमएसstringbuf: 23.3 एमएस, 23.4 एमएसvector<char>औरback_inserter: 26.1 एमएस, 24.5 एमएसvector<char>साधारण पुनरावृत्ति के साथ: 3.13 एमएस, 2.48 एमएसvector<char>पुनरावृति और सीमा जाँच: 2.97 एमएस, 2.53 एमएसchar[]: 1.52 एमएस, 1.25 एमएस
एक ही लैपटॉप, विजुअल C ++ 2010 64-बिट संकलक:
ostringstream: 48.6 एमएस, 45.0 एमएसstringbuf: 16.2 एमएस, 16.0 एमएसvector<char>औरback_inserter: 26.3 एमएस, 26.5 एमएसvector<char>साधारण पुनरावृत्ति के साथ: 0.87 एमएस, 0.89 एमएसvector<char>पुनरावृति और सीमा जाँच: 0.99 एमएस, 0.99 एमएसchar[]: 1.25 एमएस, 1.24 एमएस
संपादित करें: परिणाम देखने के लिए लगातार दो बार दौड़े। सुंदर लगातार IMO।
नोट: मेरे लैपटॉप पर, चूंकि मैं विचारधारा की अनुमति देने की तुलना में अधिक सीपीयू समय को छोड़ सकता हूं, इसलिए सभी तरीकों के लिए पुनरावृत्तियों की संख्या 1000 तक निर्धारित करता हूं। इसका मतलब यह है कि ostringstreamऔर vectorवास्तविककरण, जो केवल पहले पास पर होता है, अंतिम परिणामों पर बहुत कम प्रभाव पड़ता है।
संपादित करें: उफ़, एक बग के vectorसाथ-साधारण-इटरेटर में पाया गया, इटरेटर उन्नत नहीं था और इसलिए बहुत सारे कैश हिट थे। मैं सोच रहा था कि कैसे vector<char>बेहतर प्रदर्शन कर रहा था char[]। हालांकि इससे बहुत फर्क नहीं पड़ा, लेकिन vector<char>यह अभी भी char[]VC ++ 2010 के मुकाबले तेज है ।
निष्कर्ष
आउटपुट स्ट्रीम की बफरिंग के लिए हर बार डेटा संलग्न होने के लिए तीन चरणों की आवश्यकता होती है:
- जांचें कि आने वाला ब्लॉक उपलब्ध बफर स्थान को फिट बैठता है।
- आने वाले ब्लॉक को कॉपी करें।
- डेटा-प्वाइंटर के अंत का अद्यतन करें।
नवीनतम कोड स्निपेट मैंने पोस्ट किया, " vector<char>सिंपल इटरेटर प्लस बाउंड चेक" न केवल ऐसा करता है, यह अतिरिक्त स्थान भी आवंटित करता है और मौजूदा डेटा को स्थानांतरित करता है जब आने वाला ब्लॉक फिट नहीं होता है। जैसा कि क्लिफोर्ड ने बताया, एक फ़ाइल I / O वर्ग में बफरिंग को ऐसा नहीं करना होगा, यह सिर्फ वर्तमान बफर को फ्लश करेगा और इसका पुन: उपयोग करेगा। तो यह बफरिंग आउटपुट की लागत पर एक ऊपरी बाध्य होना चाहिए। और यह वही है जो काम करने वाले मेमोरी बफर को बनाने के लिए आवश्यक है।
तो stringbufideone पर 2.5x धीमा क्यों है , और जब मैं इसका परीक्षण करता हूं तो कम से कम 10 गुना धीमा होता है? इस सरल माइक्रो-बेंचमार्क में इसका उपयोग पॉलीमॉर्फिक रूप से नहीं किया जा रहा है, ताकि इसकी व्याख्या न हो।
std::ostringstreamतेजी से अपने बफर आकार को बढ़ाने के लिए स्मार्ट नहीं std::vectorहै, तो वह (ए) बेवकूफ और (बी) I / O प्रदर्शन के बारे में सोचने वाले कुछ लोगों को सोचना चाहिए। वैसे भी, बफर का पुन: उपयोग हो जाता है, यह हर बार वास्तविक नहीं मिलता है। और std::vectorएक गतिशील रूप से बढ़ते बफर का उपयोग भी कर रहा है। मैं यहां निष्पक्ष रहने की कोशिश कर रहा हूं।
ostringstreamऔर आप जितना संभव हो उतना तेज़ प्रदर्शन चाहते हैं तो आपको सीधे जाने पर विचार करना चाहिए stringbuf। ostreamवर्गों लचीला बफर विकल्प (फ़ाइल, स्ट्रिंग, आदि) के माध्यम से के साथ एक साथ लोकेल बारे में पता स्वरूपण कार्यक्षमता टाई लगता है कर रहे हैं rdbuf()और इसके आभासी समारोह इंटरफ़ेस। यदि आप कोई स्वरूपण नहीं कर रहे हैं, तो उस अप्रत्यक्ष स्तर का अतिरिक्त निश्चित रूप से अन्य दृष्टिकोणों की तुलना में आनुपातिक रूप से महंगा दिखने वाला है।
ofstreamकरने के लिए fprintfजब प्रवेश युगल शामिल जानकारी outputting। WinXPsp3 पर MSVC 2008। iostreams सिर्फ कुत्ता धीमा है।