2015-04-17 9 views
5

मुझे पता है कि बहुत सारे कंपाइलर के अनुकूलन बल्कि गूढ़ हो सकते हैं, लेकिन मेरा उदाहरण इतना सरल है कि मैं यह देखना चाहता हूं कि मैं समझ सकता हूं कि क्या मुझे समझ में आ रहा है, अगर किसी को पता है कि यह क्या कर सकता है।कंपाइलर कैसे getline() को प्रभावी ढंग से अनुकूलित करता है?

मेरे पास 500 एमबी टेक्स्ट फ़ाइल है। मैं एक fstream घोषित और प्रारंभ:

std::fstream file(path,std::ios::in) 

मुझे अनुक्रमिक रूप से फ़ाइल को पढ़ने की आवश्यकता है। यह टैब सीमित है लेकिन फ़ील्ड की लंबाई ज्ञात नहीं है और रेखा से भिन्न होती है। वास्तविक पार्सिंग को मुझे प्रत्येक पंक्ति में करने की ज़रूरत है जो कुल मिलाकर बहुत कम समय में जोड़ा गया है (जो वास्तव में मुझे आश्चर्यचकित कर रहा था क्योंकि मैं स्ट्रिंग कर रहा था :: गेटलाइन से प्रत्येक पंक्ति पर ढूंढें। मैंने सोचा कि धीमा होगा)।

आम तौर पर मैं एक स्ट्रिंग के लिए प्रत्येक पंक्ति खोजना चाहता हूं, और जब मुझे लगता है तो लूप को रोकना। मैं अपनी खुद की जिज्ञासा के लिए लाइन संख्याओं को बढ़ा रहा हूं और थूक रहा हूं, मैंने पुष्टि की है कि इसमें थोड़ा समय (5 सेकंड या उससे अधिक) जोड़ता है और मुझे यह देखने देता है कि यह पिछली छोटी रेखाओं को कैसे उड़ाता है और लंबी लाइनों पर धीमा हो जाता है।

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

long long int lineNum = 0; 
while (std::getline (file, line)) 
{ 
    pos = line.find(text); 
    lineNum += 1; 
    std::cout << std::to_string(lineNum) << std::endl; 
    if (pos != -1) 
     return file.tellg(): 
} 
    return std::string::npos; 

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

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

लगता है कि गेटलाइन पूरी तरह से चालक है। हालांकि ... अगर मैं/ओ 2 ध्वज (एमएसवीसी ++) के साथ संकलित करता हूं तो मुझे कॉमिक रूप से तेज़ 26 सेकंड मिलते हैं। इसके अलावा, लंबी लाइनों बनाम कम पर कोई स्पष्ट धीमा नहीं है। स्पष्ट रूप से संकलक कुछ अलग कर रहा है। मुझसे कोई शिकायत नहीं, लेकिन किसी भी विचार के रूप में यह कैसे हासिल किया जाता है? एक अभ्यास के रूप में मैं संकलक अनुकूलन से पहले तेजी से निष्पादित करने के लिए कोशिश करना चाहता हूं और अपना कोड प्राप्त करना चाहता हूं।

मुझे यकीन है कि गेटलाइन स्ट्रिंग में हेरफेर करने के तरीके से कुछ करना है। स्ट्रिंग के लिए पूरी फाइलसाइज को आरक्षित करने के लिए यह तेजी से होगा (या थोड़ी देर के लिए परीक्षण नहीं कर सकता), और चरित्र द्वारा वर्ण पढ़ते हैं, जब मैं/n पास करता हूं तो मेरी लाइन संख्या में वृद्धि करता है? साथ ही, क्या कंपाइलर एमएमएपी जैसी चीजें नियोजित करेगा?

अपडेट: मैं इस शाम को घर आने पर कोड पोस्ट करूंगा। ऐसा लगता है कि रनटाइम चेक बंद करने से 400 सेकंड से 50 तक निष्पादन गिरा दिया गया है! मैंने कच्चे सी स्टाइल सरणी का उपयोग करके एक ही कार्यक्षमता करने की कोशिश की। मैं सुपर अनुभवी नहीं हूं, लेकिन डेटा को एक वर्ण सरणी में डंप करने के लिए काफी आसान था, और इसके माध्यम से लूप न्यूलाइन या मेरी लक्ष्य स्ट्रिंग के पहले अक्षर की तलाश में था।

यहां तक ​​कि पूर्ण डीबग मोड में भी यह अंत तक पहुंच जाता है और 54 सेकंड में स्ट्रिंग को सही तरीके से पाता है। चेक के साथ 26 सेकंड, और 20 सेकंड अनुकूलित। तो मेरे अनौपचारिक, विज्ञापन-प्रसार प्रयोगों से ऐसा लगता है कि स्ट्रिंग और स्ट्रीम फ़ंक्शंस रनटाइम चेक द्वारा पीड़ित हैं? फिर, जब मैं घर जाता हूं तो मैं दोबारा जांच करूंगा।

+0

सी ++ तेज़ है, यो। – Barry

+0

आप केवल 'std :: cout << lineNum << std :: endl;'। जो आपको कुछ समय बचा सकता है। – Lingxi

+1

यह पहली बार होना चाहिए जब मैं किसी को iostreams "तेज़" कहता हूं। संभावनाओं में डीबग चेक छोड़ना शामिल है (एमएसवीसी की मानक पुस्तकालय में उनमें से बहुत सारे हैं), बेहतर इनलाइनिंग, devirtualization। –

उत्तर

1

इस नाटकीय गति के कारण का कारण यह है कि iostream क्लास पदानुक्रम टेम्पलेट्स पर आधारित है (std::ostream वास्तव में std::basic_ostream नामक टेम्पलेट का एक टाइपिफ़ है), और इसका बहुत से कोड हेडर में है। सी ++ iostreams स्ट्रीम में हर बाइट को संसाधित करने के लिए कई फ़ंक्शन कॉल लेते हैं। हालांकि, उनमें से अधिकतर कार्य काफी तुच्छ हैं। अनुकूलन को चालू करके, इनमें से अधिकतर कॉलों को रेखांकित किया गया है, संकलक को उजागर करते हुए तथ्य यह है कि std::getline अनिवार्य रूप से एक बफर से दूसरे तक वर्णों की प्रतिलिपि बनाते हैं जब तक कि यह एक नई लाइन न पाएं - आमतौर पर यह फ़ंक्शन कॉल की कई परतों के तहत "छिपी हुई" होती है। यह आगे अनुकूलित किया जा सकता है, परिमाण के आदेश द्वारा प्रति बाइट ओवरहेड को कम करता है।

बफरिंग व्यवहार वास्तव में अनुकूलित और गैर-अनुकूलित संस्करण के बीच नहीं बदलता है, अन्यथा गति भी अधिक होगी।

+0

एमएसवीसी जैसे एलटीओ कंपाइलर पूरे कार्यक्रम को भी देख सकते हैं और वर्चुअल फ़ंक्शन कॉल को सीधे कॉल में बदल सकते हैं। इससे iostreams भी बहुत मदद करता है। –

+0

"इस नाटकीय गति के कारण" - क्या नाटकीय गति? क्यू ऑप्टिमाइज़ेशन से "कॉमिकल" के रूप में गति का वर्णन करता है। Iostreams के प्रदर्शन के लिए अनुकूलन वास्तव में महत्वपूर्ण है * यदि I/O कोड को तनाव देने के लिए पर्याप्त तेज़ प्रदर्शन किया जा सकता है, लेकिन ऐसा इसलिए है क्योंकि वास्तविक डिस्क पढ़ने के समान आकार के buffered भागों में किया जाता है कि देखा गया प्रदर्शन अपेक्षाकृत सुसंगत है ओपी –

+0

यदि आप ओपी की संख्या पढ़ते हैं, तो/O2 का उपयोग करके 20 के कारक द्वारा चलने का समय कम हो जाता है। यह बाकी पोस्ट से काफी स्पष्ट है कि ओपी ने इतनी बड़ी गति की अपेक्षा नहीं की थी, और "हास्यपूर्ण रूप से तेज़" वास्तव में उसका अजीब तरीका है "नाटकीय रूप से तेज़" कह रहा है। –

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