2008-11-12 20 views
9

मैं एक लॉगिंग मॉड्यूल का उपयोग कर रहा हूं जो रनटाइम पर सक्षम/अक्षम रिपोर्टिंग कर सकता है। कॉल आम तौर पर की तरह कुछ जाना:उत्तीर्ण तर्कों के सी ++ संकलक अनुकूलन

WARN(
    "Danger Will Robinson! There are " 
    + boost::lexical_cast<string>(minutes) 
    + " minutes of oxygen left!" 
); 

मैं चेतावनी के लिए एक इनलाइन समारोह का उपयोग कर रहा है, लेकिन मैं कितना अनुकूलन पर्दे के पीछे चल रहा है के रूप में उत्सुक हूँ - पूरे कार्यक्रम के दौरान तर्क का मूल्यांकन होगा महंगा हो।

bool WARNINGS_ENABLED = false; 
inline void WARN(const string &message) { 
    if (!WARNINGS_ENABLED) { 
     return; 
    } 
    // ... 
} 

यह देखते हुए कि स्ट्रिंग तर्क के निर्माण में कोई साइड इफेक्ट, संकलक इसे बाहर अनुकूलित करेंगे: WARN समारोह कुछ इस तरह चला जाता है? कुछ x के लिए g++ में अनुकूलन का एक निश्चित स्तर आवश्यक है (-Ox)?

उत्तर

12

यदि आपको रन-टाइम पर चेतावनियों को चुनिंदा रूप से सक्षम और अक्षम करने में सक्षम होना आवश्यक है, तो संकलक कॉल को अनुकूलित करने में सक्षम नहीं होगा।

#define WARN(s) do {if (WARNINGS_ENABLED) WARN2(s);} while (false) 

यह जब तक आप चेतावनी सक्षम होनी रन-टाइम में रों के मूल्यांकन कर पाएगा:

आप क्या जरूरत हैWARN2 करने के लिए अपने समारोह का नाम बदलने और इस तरह एक मैक्रो कुछ जोड़ने के लिए है।

डू-वेटिंग सामान एक चाल है जो इसे कोड में कहीं भी इस्तेमाल करने की अनुमति देता है (नग्न कथन, एक ब्रेस्ड आई-ब्लॉक के भीतर कथन, एक अनब्रेस्ड आई-ब्लॉक, ब्रेस और अनब्रेस्ड के बयान के दौरान कथन और इतने पर)।

+0

यह काम करना चाहिए, अच्छी तरह से किया जाना चाहिए। –

+0

हाँ - एक स्पष्ट स्थिति जहां मैक्रोज़ इनलाइन फ़ंक्शंस पर उपयोग करने की आवश्यकता है। – cdleary

+0

+1 लेकिन यह वास्तव में होना चाहिए: # परिभाषित करें {अगर) {if (WARNINGS_ENABLED) WARN2 (ओं) करें; } जबकि (झूठा) –

1

मुझे लगता है कि अगर इसे साबित कर दिया जा सकता है कि इसका कोई साइड इफेक्ट नहीं है (जो कि कंपाइलर को महंगे फ़ंक्शन कॉल के लिए करना मुश्किल हो सकता है)।

मैं बूस्ट विशेषज्ञ नहीं हूं, लेकिन मुझे लगता है कि लैम्ब्डा बनाने का एक तरीका है जिसका मूल्यांकन केवल WARNINGS_ENABLED सत्य होने पर स्ट्रिंग उत्पन्न करने के लिए किया जाएगा। कुछ की तरह ...

inline void warnFunc(some_boost_lambda &message_generator) { 
    if (WARNINGS_ENABLED) { 
    cerr << message_generator() << endl; 
    } 
} 

#define WARN(msg) warnFunc(...insert boost magic here to turn msg into a lambda...) 
+0

दिलचस्प विचार! – cdleary

+0

हाँ आपका विचार बहुत अच्छा काम करता है। बस इसे बढ़ावा देने के साथ परीक्षण :: लैम्ब्डा (इसमें कोई विशेषज्ञ नहीं है, लेकिन यह सीधे आगे था) –

+0

हाँ आपका विचार बहुत अच्छा काम करता है। बस इसे बढ़ावा दिया :: लैम्ब्डा (इसमें कोई विशेषज्ञ नहीं है, लेकिन यह सीधे आगे था)। आउटपुट के साथ नमूना यहां दिया गया है: http://codepad.org/PmUh7AHj –

6

आप देख सकते हैं क्या जीसीसी/जी ++ एस विकल्प का उपयोग करके करते हैं। यह वास्तव में इकट्ठा होने से पहले कोड आउटपुट करेगा - gcc(1) देखें।

जीसीसी और जी ++ इस मामले में कम या ज्यादा व्यवहार करते हैं।

char WARNINGS_ENABLED = 0; 

inline void WARN(const char* message) { 
    if (!WARNINGS_ENABLED) { 
     return; 
    } 
    puts(message); 
} 

int main() { 
    WARN("foo"); 
    return 0; 
} 

रन जीसीसी -O3 एस file.c और आउटपुट फ़ाइल 'file.s'
आप होगा पर गौर: तो मैं पहले कोड सी में कुछ और परीक्षण करने के लिए अनुवाद देखें कि जीसीसी ने को कुछ भी नहीं हटाया!

है कि आप के लिए क्या कहा, लेकिन आदेश संकलक कि कोड बाहर अनुकूलन करने के लिए अवसर देने के लिए, आप WARNINGS_ENABLED निरंतर बनाने के लिए होता नहीं है। एक विकल्प यह है कि इसे स्थिर और उस फ़ाइल के भीतर मान को नहीं बदला जाए। लेकिन: इसे स्थिर का दुष्प्रभाव है कि प्रतीक निर्यात नहीं किया जाता है।

static const char WARNINGS_ENABLED = 0; 

inline void WARN(const char* message) { 
    if (!WARNINGS_ENABLED) { 
     return; 
    } 
    puts(message); 
} 

int main() { 
    WARN("foo"); 
    return 0; 
} 

जीसीसी फिर पूरी तरह से कोड को साफ़ करता है।

+0

बहुत अच्छा जवाब! दुर्भाग्यवश, लॉगिंग व्यवहार को रनटाइम पर छेड़छाड़ करने में सक्षम होना चाहिए। – cdleary

0

क्या आप प्रीप्रोसेसर का उपयोग करके पूरी चीज को परिभाषित नहीं कर सकते?

void inline void LogWarning(const string &message) 
{ 
    //Warning 
} 

#ifdef WARNINGS_ENABLED 
#define WARN(a) LogWarning(a) 
#else 
#define WARN(a) 
#endif 

यह केवल एएसएसईआरटी() मैक्रो काम करता है। WARN में ब्रैकेट के अंदर मौजूद सभी कोड इसे प्रीप्रोसेसर के माध्यम से कंपाइलर तक भी नहीं बनाते हैं। इसका मतलब है कि आप की तरह

#ifdef WARNINGS_ENABLED 
// Extra setup for warning 
#endif 
//.... 
WARN(uses setup variables) 

अन्य सामान कर सकते हैं और यह दोनो तरीकों संकलित कर देगा।

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

+0

इसे रनटाइम टॉगलिंग करने में सक्षम होना चाहिए, लेकिन यह निश्चित रूप से एक व्यवहार्य संकलन-समय समाधान होगा। – cdleary

+0

तो आपका प्रश्न है: एक फ़ंक्शन एक्स (पैराम्स) {if (b) {quit}; ...} को देखते हुए कंपाइलर देरी की गणना तब तक होती है जब तक कि? यह प्रभावशाली होगा। चूंकि शाखा गतिशील है, इसलिए संकलक उस ऑप्टिमाइज़ करने में सक्षम होने की संभावना नहीं है। –

+0

हाँ, यही वह है जो मैं पूछ रहा हूं! सिवाय इसके कि संकलक कॉल से छुटकारा पा सकता है और सामान को आपके '...' में रेखांकित कर सकता है। कुछ अन्य लोगों की तरह यह इंगित किया गया है कि कंपाइलर को यह साबित करने में सक्षम होना चाहिए कि तर्क पारित करने में कोई बाहरी दुष्प्रभाव नहीं हैं, जो कि काफी मुश्किल लगता है। – cdleary

1

नहीं, संकलक किसी भी मामले में कोड को ऑप्टिमाइज़ नहीं करना चाहिए जब तक कि वैश्विक WARNING_ENABLED को घोषित नहीं किया जाता है।

बीटीडब्लू, अगर डब्ल्यूएएनएन इनलाइन फ़ंक्शन है, तो आप अभी भी संदेश निर्माण की कीमत का भुगतान करेंगे (जो आपके उदाहरण में लेक्सिकल_कास्ट और ऑपरेटर + स्ट्रिंग्स पर बहुत अक्षम है), भले ही यह अक्षम हो।

रनटाइम अक्षम होने पर कुछ कुशल (न्यूनतम (सीपीयू भविष्यवाणी करने वाली शाखा के साथ शून्य के करीब शून्य) के ऊपर हैं) logging macros जो फ़ंक्शन और स्ट्रीम शैली दोनों लॉगिंग का समर्थन करता है।

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