2014-09-23 7 views
10
auto lam = [](int a, int b, int c) { return a < b && b < c; }; 

struct functor { 
    int a; 
    int b; 
    bool operator()(int n) const { return a < n && n < b; } 
}; 

संस्करण एक में, हमस्पीड functor struct के ऑपरेटर() बनाम (std :: समारोह के माध्यम से)

std::vector<std::function<bool (int)>> lamvals; 
// get parameters and for each 
lamvals.emplace_back(std::bind(lam, a, std::placeholders::_1, b)); 

विकल्प

std::vector<functor> lamvals; 
// get parameters and for each 
lamvals.emplace_back(functor{a, b}); 

दोनों ही मामलों में है हम एक साधारण पुनरावृत्ति

return std::any_of(lamvals.cbegin(), lamvals.cend(), 
      [n](const decltype(lamvals)::value_type & t){return t(n);}); 

मुझे गति दिखाई दे रही है बाध्य लैम्ब्डा धीमी गति से 3: 1 का अंतर। फंक्चर इंटीजर जोड़े को संग्रहित करने और परीक्षणों को हार्डकोड करने के रूप में लगभग तेज़ है। जाहिर है, हार्डकोडिंग उत्पादन कोड के लिए उतनी उपयोगी नहीं है, क्योंकि इसमें केवल कई ही काम नहीं होंगे। हालांकि, मैं या तो कई मजेदार या कई lambdas के साथ जा सकते हैं। उत्तरार्द्ध कोड की कम लाइनें है और क्लीनर दिखता है, लेकिन मुझे नहीं लगता कि मैं उस गति अंतर को बर्दाश्त कर सकता हूं; यह कोड एक महत्वपूर्ण पाश में है।

मैं speedup सुझावों की तलाश में हूं।

+10

'std :: function' का उपयोग निश्चित रूप से धीमा होगा। इसके साथ सबसे अधिक संभवतः टाइप एरर (जो 'आभासी' और संभावित गतिशील आवंटन तक उबाल जाता है) के माध्यम से लागू किया जाता है, तो यह "सादे" मज़ेदार की तुलना में महत्वपूर्ण ओवरहेड का कारण बनता है। –

+0

अपने मज़ेदार को 'std :: function' में स्टोर करें और यह लैम्ब्डा के समान समय लेगा। – Oktalist

+0

यह ध्यान रखना बहुत महत्वपूर्ण है कि लैम्बडा मज़दूरों की तुलना में धीमी नहीं हैं।इसके बजाय, 'std :: function' कॉल नियमित फ़ंक्शन कॉल की तुलना में धीमी हैं। [Gcc.godbolt.org] के लिए –

उत्तर

13

दो मामलों के बीच का अंतर मूल रूप से है कि मज़ेदार के साथ, संकलक जानता है कि संकलन समय पर क्या कहा जाएगा, इसलिए फ़ंक्शन कॉल को रेखांकित किया जा सकता है। Lambdas, दिलचस्प रूप से पर्याप्त, एक अद्वितीय प्रकार भी है। इसका मतलब फिर से होता है, जब आप लैम्ब्डा का उपयोग संकलन प्रकार पर करते हैं (क्योंकि संकलक को सभी प्रकार के बारे में पता होना चाहिए) जिसे फ़ंक्शन कहा जा रहा है, पहले ही ज्ञात है, इसलिए इनलाइनिंग हो सकती है। दूसरी ओर, फ़ंक्शन पॉइंटर केवल अपने हस्ताक्षर पर आधारित प्रकार है। हस्ताक्षर ज्ञात होना चाहिए ताकि इसे उचित रूप से बुलाया जा सके और लौटाया जा सके, लेकिन इसके अलावा एक फ़ंक्शन पॉइंटर रन-टाइम पर कुछ भी इंगित कर सकता है, जहां तक ​​संकलक का संबंध है। Std :: फ़ंक्शन के बारे में भी यही सच है।

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

यह लिंक, http://goo.gl/60QFjH, दिखाता है कि मेरा क्या मतलब है (वैसे, गॉडबॉल्ट पेज बहुत आसान है, मैं सुझाव देता हूं कि इससे परिचित हो)। मैंने यहां आपके जैसे तीन उदाहरण लिखे हैं। पहली बार std :: फ़ंक्शन को लैम्ब्डा को लपेटने का उपयोग करता है, दूसरा एक मजेदार, तीसरा नग्न लैम्ब्डा (अनचाहे), decltype का उपयोग कर। आप दाईं ओर असेंबली देख सकते हैं और देख सकते हैं कि बाद के दोनों को रेखांकित किया गया है, लेकिन पहले नहीं।

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

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

+2

+1 (http://gcc.godbolt.org/) - यह बहुत अच्छा है! –

+0

बहुत उपयोगी। जब आप विभिन्न ऑपरेटर() एस को उस हस्ताक्षर के साथ चाहते हैं, तो आप संरचना को कैसे घोषित करेंगे? वर्चुअल इनलाइनिंग को मार देगा। क्या संकलक std :: bind के आउटपुट की अस्वीकृति स्वीकार करने जा रहा है? –

+1

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

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