2012-02-29 13 views
7

तो मैं कुछ कोड के साथ खेल रहा था और देखना चाहता था कि std :: स्ट्रिंग को ऊपरी मामले में बदलने की कौन सी विधि सबसे कुशल थी। मैंने सोचा कि दोनों कुछ समान प्रदर्शन के समान होंगे, लेकिन मैं बहुत गलत था। अब मैं जानना चाहता हूं क्यों।std :: स्ट्रिंग को ऊपरी मामले में परिवर्तित करना: प्रमुख प्रदर्शन अंतर?

स्ट्रिंग को परिवर्तित करने की पहली विधि निम्नानुसार है: स्ट्रिंग में प्रत्येक वर्ण के लिए (लंबाई को बचाने, 0 से लंबाई तक), अगर यह 'ए' और 'z' के बीच है, तो इसे बदलें इसके बजाय 'ए' और 'जेड' के बीच।

दूसरी विधि निम्नानुसार काम करती है: स्ट्रिंग में प्रत्येक वर्ण के लिए (0 से शुरू करें, जब तक हम शून्य टर्मिनेटर नहीं दबाते) तब तक टचपर() फ़ंक्शन में लागू करें।

#include <iostream> 
#include <string> 

inline std::string ToUpper_Reg(std::string str) 
{ 
    for (int pos = 0, sz = str.length(); pos < sz; ++pos) 
    { 
     if (str[pos] >= 'a' && str[pos] <= 'z') { str[pos] += ('A' - 'a'); } 
    } 

    return str; 
} 

inline std::string ToUpper_Alt(std::string str) 
{ 
    for (int pos = 0; str[pos] != '\0'; ++pos) { str[pos] = toupper(str[pos]); } 

    return str; 
} 


int main() 
{ 
    std::string test = " [email protected]#$%^&*()_+=-`'{}[]\\|\";:<>,./?"; 

    for (size_t i = 0; i < 100000000; ++i) { ToUpper_Reg(test); /* ToUpper_Alt(test); */ } 

    return 0; 
} 

पहली विधि ToUpper_Reg के बारे में 169 सेकंड में 100 मिलियन पुनरावृत्तियों प्रति ले लिया:

कोड यह रहा।
दूसरी विधि Toupper_Alt ने लगभग 10012 पुनरावृत्तियों के बारे में 37 9 सेकंड लिया।

क्या देता है?


संपादित करें: मैं दूसरी विधि बदल इतना है कि यह स्ट्रिंग iterates कैसे पहले एक है के बारे में दो बार के रूप (लंबाई अलग पाश निर्धारित करते हैं, जबकि लंबाई से भी कम) और यह थोड़ा तेज है, लेकिन अभी भी धीमी गति से।


संपादित करें 2: अपनी प्रस्तुतियाँ के लिए धन्यवाद हर कोई! जिस डेटा का मैं उपयोग कर रहा हूं वह एएससीआई होने की गारंटी है, इसलिए मुझे लगता है कि मैं उस समय के लिए पहली विधि के साथ चिपके रहूंगा। मुझे यह ध्यान में रखेगा कि toupper लोकल विशिष्ट है जब मुझे इसकी आवश्यकता होती है।

+7

toupper _Reg में आपके द्वारा किए गए कार्यों की तुलना में धीमा है क्योंकि यह रेग में आपके से अधिक करता है? – Almo

+4

आप मानक सी ++ इन-प्लेस रूपांतरण क्यों नहीं जोड़ते हैं, 'std :: transform (s.begin(), s.end(), s.begin(), (int (*) (int)) std :: toupper); '? (आपको ',' 'और' 'शामिल करना होगा। –

+0

वाह, यह एक मुट्ठी भर है। जिज्ञासा से, '(int (*) (int)) 'भाग के साथ क्या है? –

उत्तर

13

std::toupper केस रूपांतरण करने के लिए वर्तमान लोकेल का उपयोग करता है, जिसमें फ़ंक्शन कॉल और अन्य अवशोषण शामिल होते हैं। तो स्वाभाविक रूप से, यह धीमा हो जाएगा। लेकिन यह गैर-ASCII पाठ पर भी काम करेगा।

3

toupper() लोकेल को खाते में ले जाता है ताकि यह (कुछ) अंतरराष्ट्रीय पात्रों को संभाल सके और चरित्र श्रेणी 'ए' - 'z' को संभालने से कहीं अधिक जटिल हो।

5

toupper() केवल श्रेणी [ए-जेड] में वर्णों को स्थानांतरित करने से कहीं अधिक है। एक बात के लिए यह लोकेल निर्भर है और केवल ASCII से अधिक संभाल सकता है।

0

दूसरे पर फ़ंक्शन कॉल शामिल है। फ़ंक्शन कॉल एक आंतरिक लूप में एक महंगी ऑपरेशन है। टॉपर भी स्थानीय लोगों का उपयोग यह निर्धारित करने के लिए करता है कि चरित्र कैसे बदला जाना चाहिए।

boost::algorithm::to_upper 

यह है:

कॉल की प्रगति है कि यह मानक है और मेजबान मशीन

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

http://www.boost.org/doc/libs/1_40_0/doc/html/boost/algorithm/to_upper.html

0

मुझे लगता है कि क्योंकि दूसरा एक एक सी मानक पुस्तकालय फ़ंक्शन को कॉल यह है, कि एक तरफ inlined नहीं है, तो आप एक समारोह कॉल की भूमि के ऊपर हो गया। लेकिन इससे भी महत्वपूर्ण बात यह है कि यह फ़ंक्शन शायद दो तुलनाओं, दो कूद और दो पूर्णांक जोड़ों से कहीं अधिक है। यह चरित्र पर अतिरिक्त चेक करता है और वर्तमान लोकेल को ध्यान में रखता है और वह सब कुछ।

3

अच्छा, ToUpper_Reg() काम नहीं करता है। उदाहरण के लिए, यह मेरा नाम सभी अपरकेस वर्णों में नहीं बदलता है। उस ने कहा, ToUpper_Alt() भी काम नहीं करता है क्योंकि यह toupper() कुछ प्लेटफ़ॉर्म पर नकारात्मक मान पारित हो जाता है, यानी यह मेरे नाम के साथ उपयोग करते समय अपरिभाषित व्यवहार (आमतौर पर एक क्रैश) बनाता है। यह आसानी से तय हो गई है, हालांकि, सही ढंग से यह कुछ इस तरह फोन करके:

toupper(static_cast<unsigned char>(str[pos])) 

जिसके अनुसार, कोड के दो संस्करणों समान नहीं होते हैं: संस्करण toupper() का उपयोग कर वर्ण लिख नहीं है हर समय, जबकि onot बाद वाला संस्करण है: एक बार सब कुछ अपरकेस में परिवर्तित हो जाने पर यह हमेशा परीक्षण के बाद एक ही शाखा लेता है और फिर कुछ भी नहीं करता है। आप ToUpper_Alt() बदलने के लिए इस और retest तरह देखने के लिए चाहते हो सकता है:

inline std::string ToUpper_Alt(std::string str) 
{ 
    for (int pos = 0; str[pos] != '\0'; ++pos) { 
     if (islower(static_cast<unsigned char>(str[pos])) { 
      str[pos] = toupper(static_cast<unsigned char>(str[pos])); 
     } 
    } 

    return str; 
} 

मैं अंतर लगता है कि होगा लेखन है: toupper() एक सरणी लुक-अप के लिए तुलना कारोबार करती है। लोकेल को तुरंत एक्सेस किया जाता है और सभी toupper() को वर्तमान पॉइंटर मिलता है और किसी ऑफ़सेट पर स्थान तक पहुंच जाता है। कैश में डेटा के साथ यह शायद शाखा के रूप में तेज़ है।

+0

लिखने की संख्या में अंतर पर अच्छा पकड़। –

+0

क्या प्रत्येक कॉल के लिए 'std :: toupper' के लिए एक नया लोकेल बनाना महंगा है? क्या उपयोगकर्ताओं को आम तौर पर पास करने के लिए लोकेल ऑब्जेक्ट को कैश करना चाहिए? – caps

+0

@ कैप्स: यह एक नया 'std :: locale' ऑब्जेक्ट बनाने के लिए काफी महंगा है। एक प्रतिलिपि को सिंक्रनाइज़ संदर्भ संदर्भ वृद्धि बढ़ाने की आवश्यकता होती है।एक नया 'std :: locale' बनाना और एक पहलू को बदलने के लिए एक प्रति सिंक्रनाइज़ संदर्भ गणना प्रति पहलू की आवश्यकता होती है। 'Std :: locale' को डिफॉल्ट बनाने के लिए वैश्विक 'std :: locale' और प्रतिलिपि की लागत को सिंक्रनाइज़ करने की आवश्यकता होती है। तो, हाँ, आपको 'std :: locale' ऑब्जेक्ट्स को चारों ओर रखना चाहिए। ध्यान दें कि 'std :: toupper()' का संस्करण 'std :: locale' पैरामीटर का उपयोग नहीं कर रहा है, यह एक नहीं बनाता है: यह इसके बजाय सी की लोकेल इकाई तक पहुंचता है! –

0

std :: toupper, वर्तमान स्थान और कारण है कि इस धीमी सी समारोह है कि वर्तमान स्थान साझा किया और अलग धागे से परिवर्तनशील है की तुलना में है का उपयोग करता है तो यह स्थान वस्तु लॉक करने के लिए जब यह करने के लिए पहुँचा है आवश्यक है सुनिश्चित करें कि यह कॉल के दौरान स्विच नहीं है। यह प्रति कॉल एक बार टॉपर करने के लिए होता है और काफी बड़े ओवरहेड पेश करता है (लॉक प्राप्त करने के लिए कार्यान्वयन के आधार पर एक सिस्कल की आवश्यकता हो सकती है)। यदि आप प्रदर्शन प्राप्त करना चाहते हैं और लोकेल का सम्मान करना चाहते हैं तो एक कामकाज लोकेल ऑब्जेक्ट को पहले (स्थानीय प्रतिलिपि बनाना) प्राप्त करना है और फिर अपनी प्रतिलिपि पर टॉपर फ़ेसेट को कॉल करना है, इस प्रकार प्रत्येक टॉपर कॉल के लिए लॉक करने की आवश्यकता से बचें। उदाहरण के लिए नीचे दिए गए लिंक को देखें।

http://www.cplusplus.com/reference/std/locale/ctype/toupper/

0

सवाल पहले से ही उत्तर दिया गया है, लेकिन जैसा कि एक अलग रूप में, के साथ पहली विधि में अपने पाश की हिम्मत की जगह:

std::string::value_type &c = str[pos]; 
if ('a' <= c && c <= 'z') { c += ('A' - 'a'); } 

यह भी तेजी से बनाता है। शायद मेरा कंपाइलर बस बेकार है।

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

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