2008-10-01 12 views
15

एक ऐ आवेदन मैं सी ++ में लिख रहा हूँ में,सी ++ में एआई अनुप्रयोग: वर्चुअल फ़ंक्शन कितने महंगा हैं? संभावित अनुकूलन क्या हैं?

  1. वहाँ बहुत संख्यात्मक गणना नहीं है
  2. जिसके लिए रन-टाइम बहुरूपता
  3. की जरूरत है बहुत बार संरचनाओं के बहुत कई बहुरूपी संरचनाओं बातचीत देखते हैं, गणना के दौरान

ऐसी स्थिति में, क्या कोई अनुकूलन तकनीक है? हालांकि, मुझे अभी एप्लिकेशन को अनुकूलित करने की परवाह नहीं है, प्रोजेक्ट के लिए जावा पर सी ++ चुनने का एक पहलू ऑप्टिमाइज़ करने के लिए अधिक लाभ उठाने और गैर-ऑब्जेक्ट उन्मुख तरीकों (टेम्पलेट्स, प्रक्रियाओं, अधिभार) का उपयोग करने में सक्षम होना था।

विशेष रूप से, आभासी कार्यों से संबंधित अनुकूलन तकनीक क्या हैं? आभासी कार्यों को स्मृति में आभासी तालिकाओं के माध्यम से लागू किया जाता है। क्या इन वर्चुअल टेबल को एल 2 कैश पर प्री-फ़ेच करने का कोई तरीका है (स्मृति/एल 2 कैश से लाने की लागत बढ़ रही है)?

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

अद्यतन: Performance Penalty for Interface, Several Levels of Base Classes

उत्तर

27

वर्चुअल फ़ंक्शन बहुत ही कुशल हैं। 32 बिट संकेत स्मृति लेआउट मान लिया जाये कि लगभग है:

classptr -> [vtable:4][classdata:x] 
vtable -> [first:4][second:4][third:4][fourth:4][...] 
first -> [code:x] 
second -> [code:x] 
... 

classptr अंक स्मृति ढेर पर आम तौर पर यह है कि, कभी-कभी ढेर पर, और उस वर्ग के लिए vtable के लिए एक चार बाइट सूचक के साथ शुरू होता है। लेकिन याद रखने की महत्वपूर्ण बात यह है कि vtable खुद को आवंटित स्मृति नहीं है। यह एक स्थिर संसाधन है और उसी वर्ग प्रकार की सभी वस्तुएं उनके vtable सरणी के लिए बिल्कुल उसी स्मृति स्थान को इंगित करती हैं। अलग-अलग मामलों पर कॉल करने से विभिन्न मेमोरी स्थानों को एल 2 कैश में नहीं खींचा जाएगा।

यह example from msdn वर्चुअल func1, func2, और func3 के साथ कक्षा ए के लिए vtable दिखाता है। 12 बाइट से ज्यादा कुछ नहीं। एक अच्छा मौका है कि अलग-अलग वर्गों के vtables भी संकलित पुस्तकालय में शारीरिक रूप से आसन्न होंगे (आप यह सत्यापित करना चाहते हैं कि यह विशेष रूप से चिंतित है) जो कैश दक्षता को सूक्ष्म रूप से बढ़ा सकता है।

CONST SEGMENT 
[email protected]@[email protected] 
    DD FLAT:[email protected]@@UAEXXZ 
    DD FLAT:[email protected]@@UAEXXZ 
    DD FLAT:[email protected]@@UAEXXZ 
CONST ENDS 

अन्य प्रदर्शन चिंता एक vtable फ़ंक्शन के माध्यम से कॉल करने के निर्देश ओवरहेड होगी। यह भी बहुत ही कुशल है। एक गैर वर्चुअल फ़ंक्शन को कॉल करने के लगभग समान। फिर example from msdn से:

; A* pa; 
; pa->func3(); 
mov eax, DWORD PTR _pa$[ebp] 
mov edx, DWORD PTR [eax] 
mov ecx, DWORD PTR _pa$[ebp] 
call DWORD PTR [edx+8] 

इस उदाहरण ईबीपी में, स्टैक फ्रेम आधार सूचक, चर A* pa शून्य पर ऑफसेट है। रजिस्टर ईएक्स स्थान [ebp] पर मान के साथ लोड किया गया है, इसलिए इसमें ए * है, और edx स्थान [eax] पर मान के साथ लोड किया गया है, इसलिए इसमें कक्षा ए vtable है। फिर ecx [ebp] से भरा हुआ है, क्योंकि ecx "यह" का प्रतिनिधित्व करता है, अब यह ए * रखता है, और आखिरकार कॉल को स्थान [edx + 8] पर मूल्य पर बनाया जाता है जो vtable में तीसरा फ़ंक्शन पता है।

यदि यह फ़ंक्शन कॉल वर्चुअल नहीं था तो mov eax और mov edx की आवश्यकता नहीं होगी, लेकिन प्रदर्शन में अंतर अनावश्यक रूप से छोटा होगा।

+1

अच्छा उदाहरण पर पाया जा सकता है। क्या किसी और को कॉल से ठीक पहले अनावश्यक स्मृति हिट से खुजली मिलती है जब एक mov ecx, eax यह करेगा? – plinth

+0

एक आधुनिक सीपीयू पर mov पूरी तरह से पाइपलाइन हो सकता है। –

+8

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

1

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

कैश केवल आम तौर पर एक मुद्दा है जब बड़े डेटा संरचनाओं है कि या तो साथ काम:

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

Vtables जैसी चीजें आम तौर पर प्रदर्शन/कैश/मेमोरी समस्या नहीं होने वाली हैं; आमतौर पर प्रति ऑब्जेक्ट प्रकार के केवल एक Vtable है, और ऑब्जेक्ट में Vtable के बजाय Vtable में पॉइंटर होता है। तो जब तक आपके पास कुछ हज़ार प्रकार की वस्तुएं न हों, मुझे नहीं लगता कि Vtables आपके कैश को फेंकने जा रहे हैं।

1), वैसे, यही कारण है कि memcpy जैसे कार्यों को अत्यधिक बड़े (बहु-मेगाबाइट) डेटा इनपुट के लिए movnt (dq | q) जैसे स्ट्रीमिंग निर्देशों को कैश-बायपास करना।

0

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

वहाँ आभासी कार्यों के लिए उपलब्ध अनुकूलन के एक जोड़े,

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

क्या आपने वास्तव में प्रोफाइल किया है और पाया है, और अनुकूलन की क्या आवश्यकता है?

वास्तव में वर्चुअल फ़ंक्शन कॉल को अनुकूलित करने पर कार्य करें जब आपको पता चला कि वे वास्तव में बाधा हैं।

2

आप आभासी कार्यों का उपयोग करके रनटाइम में polymorfism लागू कर सकते हैं और टेम्पलेट का उपयोग करके संकलन समय में। आप टेम्पलेट्स के साथ आभासी कार्यों को प्रतिस्थापित कर सकते हैं। अधिक जानकारी के लिए इस लेख पर एक नजर डालें - http://www.codeproject.com/KB/cpp/SimulationofVirtualFunc.aspx

2

गतिशील बहुरूपता के लिए एक समाधान स्थिर polymmorphism, प्रयोग करने योग्य है, तो अपने प्रकार के संकलन प्रकार में जाना जाता है हो सकता है: CRTP (मजे की बात है आवर्ती टेम्पलेट पैटर्न)।

http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern

विकिपीडिया पर स्पष्टीकरण पर्याप्त स्पष्ट है, और शायद यह सकता है मदद आप यदि आप वास्तव में निर्धारित आभासी विधि कॉल प्रदर्शन बाधाओं के स्रोत थे।

2

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

कुछ स्थितियों में उस सुविधा की बर्बादी को रोकने के लिए आप कुछ कर सकते हैं, फ़ंक्शन इनलाइन वर्चुअल घोषित करना है।

Class A { 
    inline virtual int foo() {...} 
}; 

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

class B : public A { 
    inline virtual int foo() 
    { 
     //...do something different 
    } 

    void bar() 
    { 
     //logic... 
     B::foo(); 
     // more logic 
    } 
}; 

इस उदाहरण में, foo() करने के लिए कॉल गैर बहुरूपी और foo() की B कार्यान्वयन करने के लिए बाध्य किया जाएगा। लेकिन केवल तभी ऐसा करें जब आप निश्चित रूप से जानते हैं कि इंस्टेंस प्रकार क्या है, क्योंकि स्वत: बहुरूपता सुविधा समाप्त हो जाएगी, और यह बाद के कोड पाठकों के लिए बहुत स्पष्ट नहीं है।

3

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

मुझे इस तकनीक का उपयोग करने वाले किसी भी C++ कंपाइलर/रनटाइम से अवगत नहीं है।

+1

यह जावा के लिए * बहुत * महत्वपूर्ण है जहां प्रत्येक विधि डिफ़ॉल्ट रूप से आभासी है। सी ++ प्रोग्रामर का अपना प्रोफाइलिंग और सीधा कॉल फिक्स करता है। –

11

draft Technical Report on C++ Performance की धारा 5.3.3 पूरी तरह से आभासी कार्यों के ऊपरी भाग के लिए समर्पित है।

+0

और, स्वीकृत उत्तर की तरह, आवश्यक बिंदु को याद करता है: vtable के माध्यम से अप्रत्यक्ष होने का ओवरहेड प्रदर्शन के लिए एकमात्र संभावित नुकसान से बहुत दूर है। –

1

लागत हाल ही में सीपीयूएस के लिए आजकल सामान्य कार्यों की तुलना में कम या कम है, लेकिन इन्हें रेखांकित नहीं किया जा सकता है। यदि आप लाखों बार फ़ंक्शन को कॉल करते हैं, तो प्रभाव महत्वपूर्ण हो सकता है (उदाहरण के लिए, एक बार एक ही फ़ंक्शन को कॉल करने का प्रयास करें, उदाहरण के लिए, एक बार बिना किसी इनलाइन के, और आप देखेंगे कि फ़ंक्शन स्वयं कुछ आसान करता है, तो यह दो बार धीमा हो सकता है; एक सैद्धांतिक मामला नहीं है: यह बहुत संख्यात्मक गणना के लिए काफी आम है)।

2

मैं सभी उत्तर प्रभाव में कहना है कि मजबूत कर रहा हूँ:

  • आप वास्तव में नहीं जानते, तो यह एक समस्या है, यह तय करने के बारे में कोई चिंता शायद गलत है।

क्या आप जानना चाहते हैं है:

  • निष्पादन समय का कितना भाग (जब यह वास्तव में चल रहा) तरीके लागू करने की प्रक्रिया में खर्च किया जाता है, और विशेष रूप से, जो तरीकों सबसे महंगी हैं (इस उपाय से)।

कुछ प्रोफाइलर्स आपको यह जानकारी अप्रत्यक्ष रूप से दे सकते हैं। उन्हें बयान के स्तर पर सारांशित करने की आवश्यकता है, लेकिन विधि में ही बिताए गए समय के अलावा।

मेरी पसंदीदा तकनीक सिर्फ डीबगर के तहत इसे कई बार रोकना है।

यदि वर्चुअल फ़ंक्शन इनवोकेशन की प्रक्रिया में बिताए गए समय महत्वपूर्ण हैं, तो 20% कहें, फिर 5 नमूनों में से 1 औसत कॉल स्टैक के नीचे, डिस्सेप्लिब्स विंडो में, निर्देश दिखाए जाएंगे वर्चुअल फ़ंक्शन पॉइंटर का पालन करने के लिए।

यदि आप वास्तव में यह नहीं देखते हैं, तो यह कोई समस्या नहीं है।

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

+0

@ डाउनवॉटर: क्या मैंने कुछ गलत कहा था, या जो कुछ आपको सिखाया गया था उसके विपरीत कुछ? –

3

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

ऑप्टिमाइज़ेशन आमतौर पर कॉलस्टैक में भिन्नता को व्यक्त करने के आसपास घूमते हैं ताकि आपको हॉटस्पॉट के भीतर कई बार वर्चुअल फ़ंक्शन का आह्वान करने की आवश्यकता न हो।

2

जैसा कि पहले से ही अन्य उत्तरों द्वारा बताया गया है, वर्चुअल फ़ंक्शन कॉल का वास्तविक ओवरहेड काफी छोटा है। यह एक तंग पाश में एक अंतर डाल सकता है जहां इसे प्रति सेकंड लाखों बार कहा जाता है, लेकिन यह शायद ही कभी एक बड़ा सौदा है।

हालांकि, यह अभी भी एक बड़ा प्रभाव हो सकता है कि संकलक को अनुकूलित करने के लिए यह कठिन है। यह फ़ंक्शन कॉल को इनलाइन नहीं कर सकता है, क्योंकि यह संकलन-समय पर नहीं जानता है कि कौन सा फ़ंक्शन कॉल किया जाएगा। इससे कुछ वैश्विक अनुकूलन भी कठिन हो जाते हैं। और यह आपको कितना प्रदर्शन करता है? निर्भर करता है। आमतौर पर चिंता करने के लिए कुछ भी नहीं है, लेकिन ऐसे मामले हैं जहां इसका एक महत्वपूर्ण प्रदर्शन हिट हो सकता है।

और निश्चित रूप से यह सीपीयू आर्किटेक्चर पर भी निर्भर करता है। कुछ पर, यह काफी महंगा हो सकता है।

लेकिन यह ध्यान में रखना उचित है कि किसी भी तरह का रनटाइम पॉलीमोर्फिज्म एक ही ओवरहेड को कम या कम करता है। कई संभावित कार्यों के बीच चयन करने के लिए स्विच स्टेटमेंट्स या इसी तरह के माध्यम से समान कार्यक्षमता को कार्यान्वित करना सस्ता नहीं हो सकता है।

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

लेकिन सबसे पहले, सुनिश्चित करें कि आपको कोई समस्या है। कोड वास्तव में स्वीकार्य होने के लिए बहुत धीमा है? दूसरा, यह पता लगाएं कि प्रोफाइलर के माध्यम से यह धीमा हो जाता है। और तीसरा, इसे ठीक करें।

+0

मैं केवल उस अनुकूलन को जोड़ना चाहता हूं जिस तरह से अधिक मूल्यांकन किया गया है। यह केवल कोड में महत्वपूर्ण है जहां 1) पीसी महत्वपूर्ण समय बिताता है (जो शायद इसमें कॉल नहीं होगा), और 2) आप वास्तव में संकलित (यानी लाइब्रेरी दिनचर्या नहीं)। –

1

आधुनिक, आगे दिखने वाले, एकाधिक प्रेषण वाले CPUs के साथ वर्चुअल फ़ंक्शन के लिए ओवरहेड शून्य हो सकता है। नाडा। पिन।

+0

यदि आप एक विस्तृत उदाहरण दे सकते हैं तो यह बहुत अच्छा होगा। –

2

स्टेटिक पॉलिमॉर्फिज्म, जैसा कि कुछ उपयोगकर्ताओं ने यहां उत्तर दिया था। उदाहरण के लिए, डब्ल्यूटीएल इस विधि का उपयोग करता है। डब्ल्यूटीएल कार्यान्वयन का एक स्पष्ट स्पष्टीकरण http://www.codeproject.com/KB/wtl/wtl4mfc1.aspx#atltemplates

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