2008-11-07 23 views
6

प्रोफाइलिंग के माध्यम से मैंने पाया है कि यहां sprintf एक लंबा समय लगता है। क्या कोई बेहतर प्रदर्शन विकल्प है जो अभी भी वाई/एम/डी एच/एम/एस क्षेत्रों में अग्रणी शून्यों को संभालता है?मैं sprintf को कैसे सुधार/प्रतिस्थापित कर सकता हूं, जिसे मैंने एक प्रदर्शन हॉटस्पॉट के रूप में मापा है?

SYSTEMTIME sysTime; 
GetLocalTime(&sysTime); 
char buf[80]; 
for (int i = 0; i < 100000; i++) 
{ 

    sprintf(buf, "%4d-%02d-%02d %02d:%02d:%02d", 
     sysTime.wYear, sysTime.wMonth, sysTime.wDay, 
     sysTime.wHour, sysTime.wMinute, sysTime.wSecond); 

} 

नोट: ओपी टिप्पणी है कि यह एक छीन नीचे उदाहरण है में बताते हैं। "असली" लूप में अतिरिक्त कोड होता है जो डेटाबेस से अलग-अलग समय मानों का उपयोग करता है। प्रोफाइलिंग ने अपराधी के रूप में sprintf() को ठहराया है।

+0

"लंबे समय तक कितना समय है "? मैं उम्मीद करता हूं कि यह मिलीसेकंड के बजाय माइक्रोसेकंड में होगा (सीपीयू के आधार पर) – Roddy

+0

आप इसके बजाए अक्सर स्प्रिंटफ को कॉल करने के लिए एक रास्ता खोजने से बेहतर होगा। – Brian

उत्तर

18

यदि आप नौकरी करने के लिए अपना स्वयं का कार्य लिख रहे थे, तो 0 के स्ट्रिंग मानों की एक लुकअप तालिका .. 61 वर्ष से अलग सब कुछ के लिए कोई अंकगणित करने से बचेंगी।

संपादित करें: है कि छलांग सेकंड के साथ सामना नोट करने के लिए (और strftime() मैच के लिए) आप और 60 के सेकंड मूल्यों मुद्रित करने के लिए सक्षम होना चाहिए 61.

char LeadingZeroIntegerValues[62][] = { "00", "01", "02", ... "59", "60", "61" }; 

वैकल्पिक रूप से, strftime() के बारे में कैसे? मुझे नहीं पता कि प्रदर्शन की तुलना कैसे की जाती है (यह सिर्फ sprintf() को कॉल कर सकता है), लेकिन यह देखने लायक है (और यह उपरोक्त लुकअप स्वयं कर सकता है)।

+3

+1। जब समय आकार से अधिक महत्वपूर्ण होता है, तो लुकअप टेबल को हरा करना मुश्किल होता है! –

+0

अच्छा। लेकिन सावधान रहें कि आप इसका उपयोग कैसे करते हैं। तारों को जोड़ने के लिए स्ट्रैट() को कॉल करना भी बदसूरत, प्रदर्शन-वार होने की संभावना है। – Roddy

+0

हाँ, क्योंकि आप जानते हैं कि वे सभी 2 अक्षर हैं जिन्हें आप बस याद कर सकते हैं। इसके अलावा, स्ट्रैटटाइम पेज को देखते हुए मेरे साथ अभी कुछ हुआ, तारों की सरणी शायद "61" तक होनी चाहिए, क्योंकि स्ट्रेट समय ऐसा लगता है, संभवतः लीप सेकेंड के लिए खाते हैं। –

6

आप बदले में उत्पादन में प्रत्येक चार भरने की कोशिश कर सकते।

buf[0] = (sysTime.wYear/1000) % 10 + '0' ; 
buf[1] = (sysTime.wYear/100) % 10 + '0'; 
buf[2] = (sysTime.wYear/10) % 10 + '0'; 
buf[3] = sysTime.wYear % 10 + '0'; 
buf[4] = '-'; 

... आदि ...

सुंदर नहीं है, लेकिन आप चित्र प्राप्त। यदि कुछ और नहीं है, तो यह समझाने में मदद कर सकता है कि क्यों स्प्रिंटफ तेजी से नहीं जा रहा है।

ओटीओएच, शायद आप अंतिम परिणाम कैश कर सकते हैं। इस तरह आपको केवल हर सेकेंड में एक उत्पन्न करने की आवश्यकता होगी।

+0

वास्तविक जीवन में, समय मूल्य लूप के प्रत्येक पुनरावृत्ति (डीबी से) अलग होता है। मैंने उदाहरण के लिए कोड को सरल बना दिया है, –

6

प्रिंटफ को कई अलग-अलग प्रारूपों से निपटने की आवश्यकता है। आप निश्चित रूप से source for printf को पकड़ सकते हैं और इसे अपने स्वयं के संस्करण को रोल करने के आधार के रूप में उपयोग कर सकते हैं जो विशेष रूप से sysTime संरचना से संबंधित है। इस तरह आप एक तर्क में गुजरते हैं, और यह केवल वही कार्य करता है जिसे करने की आवश्यकता होती है और कुछ भी नहीं।

1

आपको संभवतः हाथ से रोलिंग बढ़ाना होगा जो रिटर्न बफ में अंकों को बताता है, क्योंकि आप बार-बार एक प्रारूप स्ट्रिंग को पार्स करने से बच सकते हैं और अधिक जटिल मामलों से निपटने की आवश्यकता नहीं होती है sprintf संभालती है। मैं वास्तव में ऐसा करने की सिफारिश करने के लिए नाराज हूँ।

मैं यह पता लगाने की अगर आप किसी भी तरह आदि राशि है जो आप इन तार उत्पन्न करने के लिए की जरूरत को कम कर सकते हैं, वे somegtimes वैकल्पिक, वे कैश्ड किया जा सकता है, प्रयास करने की अनुशंसा करेंगे

0

यह कल्पना करना है कि आप जा रहे हैं मुश्किल है स्वरूपण पूर्णांक पर sprintf को हरा करने के लिए। क्या आपको यकीन है कि स्प्रिंटफ आपकी समस्या है?

+0

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

2

आप एक "लंबे समय" समय से क्या मतलब है - के बाद से sprintf() अपने पाश में केवल बयान और लूप (वेतन वृद्धि, तुलना) नगण्य है की "पाइपलाइन" है, sprintf()उपभोग करने के लिए है सबसे अधिक समय।

उस व्यक्ति के बारे में पुराना मजाक याद रखें जिसने अपनी शादी की अंगूठी को एक रात में एक रात में खो दिया था, लेकिन 5 वें स्थान पर देखा क्योंकि प्रकाश वहां उज्ज्वल था? आपने एक उदाहरण बनाया है जो आपकी धारणा को साबित करने के लिए डिज़ाइन किया गया है कि sprintf() असुरक्षित है।

यदि आप "वास्तविक" कोड को देखते हैं तो आपके परिणाम अधिक सटीक होंगे यदि sprintf() आपके द्वारा उपयोग किए जाने वाले सभी अन्य कार्यों और एल्गोरिदम के अतिरिक्त है। वैकल्पिक रूप से, अपने स्वयं के संस्करण को लिखने का प्रयास करें जो आपको आवश्यक शून्य-गद्देदार संख्यात्मक रूपांतरण को संबोधित करता है।

आप परिणामों पर आश्चर्यचकित हो सकते हैं।

+0

कोड स्निपेट एक सरलीकृत उदाहरण है। असली उदाहरण में, लूप में कई चीजें हैं। (char *) _bstr_t, itoa .... यह यहां स्प्रिंट है जो खराब है। –

+0

क्या यह आइंस्टीन था जिसने कहा कि सबकुछ जितना संभव हो उतना आसान बनाया जाना चाहिए, लेकिन कोई आसान नहीं? :-) इसे प्रतिबिंबित करने के लिए आपके प्रश्न को संपादित किया। –

1

परिणाम कैशिंग करने के बारे में कैसे? क्या यह संभावना नहीं है? यह ध्यान में रखते हुए कि यह विशेष sprintf() कॉल आपके कोड में अक्सर किया जाता है, मुझे लगता है कि इनमें से अधिकतर कॉलों के बीच, वर्ष, महीना और दिन नहीं बदलता है।

इस प्रकार, हम निम्नलिखित की तरह कुछ कार्यान्वित कर सकते हैं। एक पुरानी और एक मौजूदा SYSTEMTIME संरचना घोषित:

SYSTEMTIME sysTime, oldSysTime; 

इसके अलावा, अलग अलग हिस्सों की घोषणा की तारीख और समय पकड़ करने के लिए:

char datePart[80]; 
char timePart[80]; 

के लिए, पहली बार, आप दोनों को भरने के लिए होगा sysTime, oldSysTime के साथ ही डेटपार्ट और टाइमपार्ट।

sprintf (timePart, "%02d:%02d:%02d", sysTime.wHour, sysTime.wMinute, sysTime.wSecond); 
if (oldSysTime.wYear == sysTime.wYear && 
    oldSysTime.wMonth == sysTime.wMonth && 
    oldSysTime.wDay == sysTime.wDay) 
    { 
    // we can reuse the date part 
    strcpy (buff, datePart); 
    strcat (buff, timePart); 
    } 
else { 
    // we need to regenerate the date part as well 
    sprintf (datePart, "%4d-%02d-%02d", sysTime.wYear, sysTime.wMonth, sysTime.wDay); 
    strcpy (buff, datePart); 
    strcat (buff, timePart); 
} 

memcpy (&oldSysTime, &sysTime, sizeof (SYSTEMTIME)); 

कोड से ऊपर कोड को समझने के लिए आसान बनाने के लिए कुछ अतिरेक है: नीचे दिए गए के रूप में लेकिन बाद में sprintf() के काफी तेजी से किया जा सकता है। आप आसानी से कारक कर सकते हैं। यदि आप जानते हैं कि नियमित रूप से आपके कॉल से भी घंटे और मिनट तेज़ी से नहीं बदलेंगे तो आप आगे बढ़ सकते हैं।

+0

संख्या। मेरा कोड स्निपेट वास्तविक चीज़ से सरलीकृत है। पुराने एम्बेडेड इंजीनियर से –

1

मैं कुछ चीजें करना होगा ...

  • कैश वर्तमान समय ताकि आप टाइमस्टैम्प हर बार
  • पुनर्जीवित करने के लिए समय रूपांतरण मैन्युअल रूप से करना नहीं है। printf का सबसे धीमा हिस्सा -फैमिली फ़ंक्शंस प्रारूप-स्ट्रिंग पार्सिंग है, और यह प्रत्येक लूप निष्पादन पर उस पार्सिंग को चक्रों को समर्पित करने के लिए मूर्खतापूर्ण है।
  • सभी रूपांतरणों के लिए 2-बाइट लुकअप टेबल का उपयोग करने का प्रयास करें ({ "00", "01", "02", ..., "99" })। ऐसा इसलिए है क्योंकि आप मॉड्यूलर अंकगणित से बचना चाहते हैं, और 2-बाइट टेबल का मतलब है कि आपको वर्ष के लिए केवल एक मॉड्यूल का उपयोग करना होगा।
2

ऐसा लगता है जैसे जयवाल्कर एक बहुत ही समान विधि का सुझाव दे रहा है (मुझे एक घंटे से भी कम समय तक हराया जाता है)।

पहले से सुझाए गए लुकअप टेबल विधि (एन 2 एस [] सरणी के अलावा), अपने प्रारूप बफर को उत्पन्न करने के बारे में कैसे करें ताकि सामान्य स्प्रिंटफ कम गहन हो? नीचे दिए गए कोड को केवल लूप के माध्यम से मिनट और दूसरे बार भरना होगा जब तक कि वर्ष/महीना/दिन/घंटा नहीं बदला जाता है। जाहिर है, यदि उनमें से कोई भी बदल गया है तो आप एक और स्प्रिंटफ हिट लेते हैं लेकिन कुल मिलाकर यह उस चीज़ से अधिक नहीं हो सकता है जो आप वर्तमान में देख रहे हैं (जब सरणी लुकअप के साथ संयुक्त हो)।


static char fbuf[80]; 
static SYSTEMTIME lastSysTime = {0, ..., 0}; // initialize to all zeros. 

for (int i = 0; i < 100000; i++) 
{ 
    if ((lastSysTime.wHour != sysTime.wHour) 
    || (lastSysTime.wDay != sysTime.wDay) 
    || (lastSysTime.wMonth != sysTime.wMonth) 
    || (lastSysTime.wYear != sysTime.wYear)) 
    { 
     sprintf(fbuf, "%4d-%02s-%02s %02s:%%02s:%%02s", 
       sysTime.wYear, n2s[sysTime.wMonth], 
       n2s[sysTime.wDay], n2s[sysTime.wHour]); 

     lastSysTime.wHour = sysTime.wHour; 
     lastSysTime.wDay = sysTime.wDay; 
     lastSysTime.wMonth = sysTime.wMonth; 
     lastSysTime.wYear = sysTime.wYear; 
    } 

    sprintf(buf, fbuf, n2s[sysTime.wMinute], n2s[sysTime.wSecond]); 

} 
+0

इससे नया ... क्या मेरा पूरा जवाब किसी के लिए दिखाया गया है? मेरे लिए ऐसा लगता है कि केवल 4 लाइनें दिखाई दे रही हैं। – shank

+0

आपको का उपयोग करने के लिए – quinmars

+0

एह के लिए उपयोग करना होगा। धन्यवाद। – shank

1

मैं इस समय एक ही समस्या पर काम कर रहा हूं।

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

किसी ने कहा था "आप मापने/निरीक्षण करने के बिना कुछ माप या निरीक्षण नहीं कर सकते।"

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

इंटरफ़ेस मुझे प्रदान करने की आवश्यकता है- void log(int channel, char *filename, int lineno, format, ...)। मुझे चैनल नाम जोड़ने की ज़रूरत है (जो वर्तमान में रैखिक खोज सूची में है! प्रत्येक एकल डीबग कथन के लिए!) और मिलीसेकंड काउंटर समेत टाइमस्टैम्प। यहां कुछ चीजें हैं जो मैं इसे तेजी से करने के लिए कर रही हूं-

  • चैनल नाम स्ट्रिंग करें ताकि मैं सूची खोजने के बजाय strcpy कर सकूं। मैक्रो LOG(channel, ...etc) को log(#channel, ...etc) के रूप में परिभाषित करें। यदि आप LOG(channel, ...)log("...."#channel - sizeof("...."#channel) + *11*) तय करने के लिए बाइट चैनल को परिभाषित करते हुए स्ट्रिंग की लंबाई को ठीक आप memcpy उपयोग कर सकते हैं लंबाई
  • एक दूसरे एक दो बार उत्पन्न टाइमस्टैम्प स्ट्रिंग। आप एक्टटाइम या कुछ का उपयोग कर सकते हैं। फिर प्रत्येक डीबग कथन के लिए निश्चित लंबाई स्ट्रिंग memcpy।
  • यदि आप वास्तविक समय में टाइमस्टैम्प स्ट्रिंग जेनरेट करना चाहते हैं तो असाइनमेंट के साथ एक लुकअप टेबल (memcpy नहीं!) सही है। लेकिन यह केवल 2 अंकों के लिए काम करता है और शायद साल के लिए।
  • तीन अंकों (मिलीसेकंड) और पांच अंक (लिनेनो) के बारे में क्या? मुझे itoa पसंद नहीं है और मुझे कस्टम itoa (digit = ((value /= value) % 10)) पसंद नहीं है क्योंकि या तो divs और mods धीमी हैं। मैंने नीचे दिए गए कार्यों को लिखा और बाद में पता चला कि एएमडी ऑप्टिमाइज़ेशन मैनुअल (असेंबली में) में कुछ ऐसा ही है जो मुझे विश्वास दिलाता है कि ये सबसे तेज सी कार्यान्वयन के बारे में हैं।

    void itoa03(char *string, unsigned int value) 
    { 
        *string++ = '0' + ((value = value * 2684355) >> 28); 
        *string++ = '0' + ((value = ((value & 0x0FFFFFFF)) * 10) >> 28); 
        *string++ = '0' + ((value = ((value & 0x0FFFFFFF)) * 10) >> 28); 
        *string++ = ' ';/* null terminate here if thats what you need */ 
    } 
    

    इसी तरह, लाइन नंबर के लिए,

    void itoa05(char *string, unsigned int value) 
    { 
        *string++ = ' '; 
        *string++ = '0' + ((value = value * 26844 + 12) >> 28); 
        *string++ = '0' + ((value = ((value & 0x0FFFFFFF)) * 10) >> 28); 
        *string++ = '0' + ((value = ((value & 0x0FFFFFFF)) * 10) >> 28); 
        *string++ = '0' + ((value = ((value & 0x0FFFFFFF)) * 10) >> 28); 
        *string++ = '0' + ((value = ((value & 0x0FFFFFFF)) * 10) >> 28); 
        *string++ = ' ';/* null terminate here if thats what you need */ 
    } 
    

कुल मिलाकर, मेरी कोड बहुत तेजी से अब है। vsnprintf() मुझे उपयोग करने की आवश्यकता है लगभग 9 0% समय और मेरा शेष कोड केवल 9% लेता है (जबकि शेष कोड यानी vsprintf() को छोड़कर 54% पहले इस्तेमाल किया जाता था)

+0

यह बहुत अच्छा है - बहुत धन्यवाद –

+0

क्या आप उन जादू संख्याओं को समझा सकते हैं? – someonewithpc

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

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