C ++ निम्न-स्तरीय अनुकूलन युक्तियाँ [बंद]


79

मान लें कि आपके पास पहले से ही सर्वश्रेष्ठ-पसंद का एल्गोरिदम है, तो आप C ++ कोड से मीठे मीठे फ्रेम दर की अंतिम कुछ बूंदों को निचोड़ने के लिए क्या निम्न-स्तरीय समाधान दे सकते हैं?

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


1
यह एक गेम डेवलपमेंट
क्वेश्चन

@ डैनी - यह शायद एक सामान्य प्रोग्रामिंग प्रश्न हो सकता है। यह निश्चित रूप से गेम प्रोग्रामिंग से संबंधित प्रश्न है। मुझे लगता है कि यह दोनों साइटों पर एक व्यवहार्य प्रश्न है।
स्मैशरी

@ स्मैशरी दोनों के बीच एकमात्र अंतर यह है कि गेम प्रोग्रामिंग के लिए विशिष्ट ग्राफिक्स इंजन स्तर के अनुकूलन या shader कोडर अनुकूलन की आवश्यकता हो सकती है, C ++ भाग समान है।
डैनी व्रॉड

@ डैनी - सच है, कुछ प्रश्न एक साइट या दूसरे पर "अधिक" प्रासंगिक होंगे; लेकिन मैं किसी भी प्रासंगिक सवाल को दूर नहीं करना चाहूंगा क्योंकि उन्हें किसी अन्य साइट पर भी पूछा जा सकता है।
स्मैशरी

जवाबों:


76

अपने डेटा लेआउट का अनुकूलन करें! (यह सिर्फ C ++ से अधिक भाषाओं पर लागू होता है)

आप अपने डेटा, अपने प्रोसेसर, बहु-कोर को अच्छी तरह से संभालना आदि के लिए इसे विशेष रूप से देखते हुए बहुत गहरे जा सकते हैं, लेकिन मूल अवधारणा यह है:

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

इस तरह, जब CPU आपके लूप के पहले पुनरावृत्ति के लिए डेटा प्राप्त करता है, तो डेटा के अगले कई पुनरावृत्तियों इसके साथ कैश में लोड हो जाएंगे।

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


यह लिंक किए गए Cache सुसंगतता पृष्ठ में C उदाहरण को आज़माने लायक है। जब मुझे पहली बार इस बारे में पता चला तो मैं चौंक गया कि इससे कितना फर्क पड़ता है।
नील

9
ऑब्जेक्ट ओरिएंटेड प्रोग्रामिंग प्रेजेंटेशन (सोनी आरएंडडी) ( research.scee.net/files/pretations/gcapaustralia09/… ) की उत्कृष्ट खामियों को भी देखें - और माइक एक्टन ( cellperformance.beyond3d.com/articles ) के क्रैंक लेकिन आकर्षक CellPerformance लेख index.html )। ब्लॉग के भीतर से नोएल लोपिस के खेल भी अक्सर इस विषय पर छूते हैं ( gamesfromwithin.com )। मैं सिफारिश नहीं कर सकते हैं नुकसान पर्याप्त स्लाइड ...
लिएंडर

2
मैं बस इसके बारे में चेतावनी देता हूं कि "प्रत्येक पुनरावृत्ति के लिए डेटा को जितना संभव हो उतना छोटा बना दें, और जितना संभव हो उतना करीब स्मृति में" । गैर-संरेखित डेटा तक पहुंचने से चीजें धीमी हो सकती हैं; किस मामले में पैडिंग बेहतर प्रदर्शन देगा। आदेश डेटा के महत्वपूर्ण भी है, साथ ही आदेश दिया डेटा कम गद्दी को जन्म दे सकता है। स्कॉट मेयर्स मुझे इससे बेहतर समझा सकते हैं :)
जोनाथन कॉनेल

सोनी प्रस्तुति के लिए +1। मैंने पढ़ा है कि पहले एक और यह वास्तव में समझ में आता है कि कैसे प्लेटफ़ॉर्म स्तर पर डेटा को अनुकूलित किया जाए, डेटा को विखंडू में विभाजित करने और इसे ठीक से संरेखित करने पर विचार किया जाए।
क्रिस

84

एक बहुत, बहुत कम-स्तरीय टिप, लेकिन एक जो काम में आ सकता है:

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

if(__builtin_expect(entity->extremely_unlikely_flag, 0)) {
  // code that is rarely run
}

मैंने इसके उचित उपयोग के साथ 10-20% स्पीडअप देखा है।


1
अगर मैं कर सकता हूं तो मैं दो बार वोट करूंगा।
दसपूल

10
+1, लिनक्स कर्नेल शेड्यूलर कोड में microoptimifications के लिए बड़े पैमाने पर इसका उपयोग करता है , और यह कुछ कोड पथों में एक महत्वपूर्ण अंतर बनाता है।
ग्रेफेड

2
दुर्भाग्य से, विजुअल स्टूडियो में कोई अच्छा समकक्ष नहीं है। stackoverflow.com/questions/1440570/…
मिमी जूल 22'10

1
तो किस आवृत्ति पर अपेक्षित मूल्य आमतौर पर प्रदर्शन हासिल करने के लिए सही होना चाहिए? 49/50 बार? या 999999/1000000 बार?
डगलस

36

पहली चीज जिसे आपको समझने की आवश्यकता है वह वह हार्डवेयर है जिस पर आप चल रहे हैं। यह ब्रांचिंग को कैसे संभालती है? कैशिंग के बारे में क्या? क्या इसमें SIMD निर्देश सेट है? कितने प्रोसेसर का उपयोग कर सकते हैं? क्या इसे प्रोसेसर समय को किसी और चीज़ के साथ साझा करना है?

आप बहुत भिन्न तरीकों से एक ही समस्या को हल कर सकते हैं - यहां तक ​​कि एल्गोरिथ्म की आपकी पसंद हार्डवेयर पर निर्भर होनी चाहिए। कुछ मामलों में O (N) O (NlogN) (कार्यान्वयन के आधार पर) की तुलना में धीमी गति से चल सकता है।

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

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

एक बार जब आपने मेमोरी सबसिस्टम के ओवरहेड को कम कर दिया है, तो कोशिश करें और निर्धारित करें कि क्या निर्देश अब अड़चन हैं (उम्मीद है कि वे हैं), फिर अपने एल्गोरिथ्म के SIMD कार्यान्वयन को देखें - स्ट्रक्चर-ऑफ-अर्रेज़ (SoA) कार्यान्वयन बहुत डेटा हो सकता है और निर्देश कैश कुशल। यदि SIMD आपकी समस्या के लिए एक अच्छा मेल नहीं है, तो आंतरिक और कोडांतरक स्तर कोडिंग की आवश्यकता हो सकती है।

यदि आपको अभी भी अधिक गति की आवश्यकता है तो समानांतर चलें। यदि आपको PS3 पर चलने का लाभ है तो SPU आपके मित्र हैं। उनका उपयोग करो, उनसे प्रेम करो। यदि आप पहले से ही एक SIMD समाधान लिख चुके हैं, तो आपको SPU में जाने का एक बड़ा लाभ मिलेगा।

और फिर, प्रोफ़ाइल कुछ और। खेल परिदृश्यों में परीक्षण - क्या यह कोड अभी भी अड़चन है? क्या आप इस कोड का उपयोग उच्च स्तर पर इसके उपयोग को कम करने के लिए कर सकते हैं (वास्तव में, यह आपका पहला कदम होना चाहिए)? आप कई तख्ते पर गणना स्थगित कर सकते हैं?

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

और फिर इसे फिर से प्रोफाइल करें।


31

पहला कदम: अपने एल्गोरिदम के संबंध में अपने डेटा के बारे में ध्यान से सोचें। O (log n) हमेशा O (n) से तेज नहीं होता है। सरल उदाहरण: केवल कुछ कुंजियों वाली एक हैश तालिका को रैखिक खोज के साथ अक्सर बेहतर किया जाता है।

दूसरा चरण: उत्पन्न विधानसभा को देखें। C ++ तालिका में कई अंतर्निहित कोड पीढ़ी लाता है। कभी-कभी, बिना जाने-समझे ही यह आपके ऊपर झपटा।

लेकिन यह मानते हुए कि यह वास्तव में पेडल-टू-द-मेटल समय है: प्रोफ़ाइल। गंभीरता से। बेतरतीब ढंग से "प्रदर्शन के गुर" लागू करने के बारे में चोट के रूप में यह मदद करने के लिए है।

फिर, सब कुछ इस बात पर निर्भर करता है कि आपकी अड़चनें क्या हैं।

डेटा कैश याद आती है => अपने डेटा लेआउट का अनुकूलन करें। यहाँ एक अच्छा प्रारंभिक बिंदु है: http://gamesfromwithin.com/data-oriented-design

कोड कैश याद आती है => आभासी फ़ंक्शन कॉल, अत्यधिक कॉलस्टैक गहराई आदि को देखें। खराब प्रदर्शन का एक सामान्य कारण यह गलत धारणा है कि आधार कक्षाएं आभासी होनी चाहिए।

अन्य सामान्य C ++ प्रदर्शन डूब:

  • अत्यधिक आवंटन / निपटान। यदि यह प्रदर्शन महत्वपूर्ण है, तो रनटाइम में कॉल न करें। कभी।
  • निर्माण की प्रतिलिपि बनाएँ। जहां भी आप कर सकते हैं से बचें। यदि यह एक संदर्भ हो सकता है, तो इसे एक बनाएं।

जब आप विधानसभा को देखते हैं तो उपरोक्त सभी तुरंत स्पष्ट होते हैं, इसलिए ऊपर देखें;)


19

अनावश्यक शाखाओं को हटा दें

कुछ प्लेटफार्मों पर और कुछ कंपाइलरों के साथ, शाखाएं आपकी पूरी पाइपलाइन को फेंक सकती हैं, इसलिए भी अगर () ब्लॉक महंगा हो सकते हैं।

PowerPC वास्तुकला (PS3 / x360), फ्लोटिंग प्वाइंट का चयन अनुदेश प्रदान करता है fsel। इसका उपयोग शाखा के स्थान पर किया जा सकता है यदि ब्लॉक सरल कार्य हैं:

float result = 0;
if (foo > bar) { result = 2.0f; }
else { result = 1.0f; }

हो जाता है:

float result = fsel(foo-bar, 2.0f, 1.0f);

जब पहला पैरामीटर 0 से अधिक या उसके बराबर होता है, तो दूसरा पैरामीटर वापस आ जाता है, तीसरा।

शाखा को खोने की कीमत यह है कि अगर {} और बाकी {} ब्लॉक दोनों को निष्पादित किया जाएगा, तो यदि कोई एक महंगा ऑपरेशन है या एक पूर्ण सूचक को डीरेल करता है तो यह अनुकूलन उपयुक्त नहीं है।

कभी-कभी आपके कंपाइलर ने यह काम किया है, इसलिए पहले अपनी असेंबली की जाँच करें

यहाँ शाखा और fsel पर अधिक जानकारी है:

http://assemblyrequired.crashworks.org/tag/intrinsics/


फ्लोट परिणाम = (फू> बार)? 2.f: 1.f
knight666

3
@ knight666: यह अभी भी कहीं भी एक शाखा का उत्पादन करेगा कि एक longhand "अगर" होगा। मैं इसे इस तरह से कहता हूं क्योंकि एआरएम पर, कम से कम, उस तरह के छोटे अनुक्रमों को सशर्त निर्देशों के साथ लागू किया जा सकता है जो डॉन टी की आवश्यकता नहीं है।
क्रिसबट्टू

1
@ knight666 यदि आप भाग्यशाली हैं कि संकलक उसे एक फेल में बदल सकता है, लेकिन यह निश्चित नहीं है। एफडब्ल्यूआईडब्ल्यू, मैं आम तौर पर एक तृतीयक ऑपरेटर के साथ उस स्निपेट को लिखता हूं, और फिर बाद में फेलियर के अनुकूलन पर यदि प्रोफाइलर सहमत हो जाता है।
तेनपुन

IA32 पर आपको इसके बजाय CMOVcc मिला है।
स्किज़

यह भी देखें blueraja.com/blog/285/… (ध्यान दें कि इस मामले में, यदि कंपाइलर कोई अच्छा है, तो इसे स्वयं को अनुकूलित करने में सक्षम होना चाहिए, इसलिए यह ऐसी चीज नहीं है जिसके बारे में आपको आमतौर पर चिंता करने की ज़रूरत है)
ब्लूराजा - डैनी एफ़्लूघोफ्ट

16

हर कीमत पर मेमोरी एक्सेस और विशेष रूप से यादृच्छिक लोगों से बचें।

यह आधुनिक सीपीयू पर अनुकूलन के लिए सबसे महत्वपूर्ण चीज है। जब आप रैम से डेटा की प्रतीक्षा करते हैं तो आप अंकगणित और यहां तक ​​कि कई गलत पूर्वानुमानित शाखाओं का एक शेड लोड कर सकते हैं।

आप इस नियम को दूसरे तरीके से भी पढ़ सकते हैं: मेमोरी एक्सेस के बीच अधिक से अधिक गणना करें।


13

कंपाइलर इंट्रिंसिक्स का उपयोग करें।

सुनिश्चित करें कि इंट्रिंसिक्स का उपयोग करके संकलक कुछ संचालन के लिए सबसे कुशल विधानसभा उत्पन्न कर रहा है - ऐसे निर्माण जो फ़ंक्शन कॉल की तरह दिखते हैं जो कंपाइलर अनुकूलित विधानसभा में बदल जाता है:

यहां विजुअल स्टूडियो के लिए एक संदर्भ है , और यहां जीसीसी के लिए एक है


11

अनावश्यक वर्चुअल फ़ंक्शन कॉल निकालें

वर्चुअल फ़ंक्शन का प्रेषण बहुत धीमा हो सकता है। यह लेख क्यों की एक अच्छी व्याख्या देता है। यदि संभव हो, तो फ़ंक्शंस के लिए जिन्हें प्रति फ्रेम कई बार कई बार कहा जाता है, उनसे बचें।

आप इसे कुछ तरीकों से कर सकते हैं। कभी-कभी आप केवल विरासत की आवश्यकता नहीं करने के लिए कक्षाओं को फिर से लिख सकते हैं - शायद यह पता चला है कि मशीनगुन हथियार का एकमात्र उपवर्ग है, और आप उन्हें समाहित कर सकते हैं।

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


9

मेरा मूल सिद्धांत है: ऐसा कुछ भी न करें जो आवश्यक नहीं है

यदि आपने पाया है कि एक विशेष फ़ंक्शन एक अड़चन है, तो आप फ़ंक्शन को अनुकूलित कर सकते हैं - या आप इसे पहली जगह में रखने से रोकने की कोशिश कर सकते हैं।

यह जरूरी नहीं कि आप एक खराब एल्गोरिथ्म का उपयोग कर रहे हैं। इसका मतलब यह हो सकता है कि आप प्रत्येक फ्रेम की गणना कर रहे हैं जो उदाहरण के लिए थोड़ी देर (या पूरी तरह से पूर्वनिर्मित) के लिए कैश किया जा सकता है।

मैं वास्तव में निम्न-स्तर के अनुकूलन पर किसी भी प्रयास से पहले इस दृष्टिकोण की कोशिश करता हूं।


2
यह प्रश्न मानता है कि आपने पहले से ही सभी संरचनात्मक सामान कर लिए हैं।
दस

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

2
... लेकिन कभी-कभी यह वास्तव में गणना करने के लिए तेज हो सकता है, भले ही आप परिणाम को शाखा के बजाय फेंकने जा रहे हों।
दस

9

यदि आप पहले से ही ऐसा नहीं करते हैं, तो SIMD (SSE द्वारा) का उपयोग करें। इस पर गामासूत्र का अच्छा लेख है । आप लेख के अंत में प्रस्तुत पुस्तकालय से स्रोत कोड डाउनलोड कर सकते हैं।


6

CPU pipleline का बेहतर उपयोग करने के लिए निर्भरता श्रृंखलाओं को कम से कम करें।

सरल मामलों में कंपाइलर आपके लिए ऐसा कर सकता है यदि आप लूप को अनियंत्रित करने में सक्षम करते हैं। हालांकि यह अक्सर ऐसा नहीं करेगा, खासकर जब वहाँ तैरता है अभिव्यक्ति अभिव्यक्त करने के रूप में परिणाम बदल जाता है।

उदाहरण:

float *data = ...;
int length = ...;

// Slow version
float total = 0.0f;
int i;
for (i=0; i < length; i++)
{
  total += data[i]
}

// Fast version
float total1, total2, total3, total4;
for (i=0; i < length-3; i += 4)
{
  total1 += data[i];
  total2 += data[i+1];
  total3 += data[i+2];
  total4 += data[i+3];
}
for (; i < length; i++)
{
  total += data[i]
}
total += (total1 + total2) + (total3 + total4);

4

अपने कंपाइलर को नजरअंदाज न करें - यदि आप इंटेल पर जीसीसी का उपयोग कर रहे हैं, तो आप उदाहरण के लिए इंटेल सी / सी ++ कंपाइलर पर स्विच करके आसानी से प्रदर्शन लाभ प्राप्त कर सकते हैं। यदि आप एक एआरएम प्लेटफॉर्म को लक्षित कर रहे हैं, तो एआरएम के वाणिज्यिक संकलक देखें। यदि आप iPhone पर हैं, तो Apple ने Clang को iOS 4.0 SDK के साथ शुरू करने की अनुमति दी थी।

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

मेरी सबसे अच्छी सलाह यह होगी कि निम्न-स्तरीय अनुकूलन को अनदेखा करें और उच्च स्तर पर ध्यान केंद्रित करें। कंपाइलर और CPU आपके एल्गोरिथ्म को O (n ^ 2) से O (1) एल्गोरिथ्म में नहीं बदल सकते, चाहे उन्हें कितना भी अच्छा क्यों न मिले। आपको यह देखने की आवश्यकता है कि आप वास्तव में क्या करने की कोशिश कर रहे हैं और इसे करने का एक बेहतर तरीका ढूंढ सकते हैं। कंपाइलर और सीपीयू को निम्न स्तर की चिंता करने दें और आप मध्य से उच्च स्तर पर ध्यान केंद्रित करें।


मैं देख रहा हूँ कि आप क्या कह रहे हैं, लेकिन एक बिंदु आता है जब आप O (logN) पर पहुँच गए हैं और आप किसी भी तरह के संरचनात्मक परिवर्तन से बाहर नहीं जा रहे हैं, जहाँ निम्न-स्तर के अनुकूलन खेलने में आ सकते हैं और आपको लाभान्वित कर सकते हैं कि अतिरिक्त आधा मिलीसेकंड।
दस

1
मेरा उत्तर देखें: O (log n)। इसके अलावा, यदि आप आधा मिलीसेकंड की तलाश करते हैं, तो आपको उच्च स्तर पर देखने की आवश्यकता हो सकती है। यह आपके फ्रेम समय का 3% है!
राचेल ब्लम

4

प्रतिबंधित कीवर्ड विशेष रूप से ऐसे मामलों में जहां आप संकेत के साथ वस्तुओं में हेरफेर करने की जरूरत है संभावित उपयोगी होता है,। यह संकलक को इंगित करने वाली वस्तु को किसी अन्य तरीके से संशोधित नहीं करने देता है जो बदले में इसे अधिक आक्रामक अनुकूलन करने की अनुमति देता है जैसे कि रजिस्टर में ऑब्जेक्ट के कुछ हिस्सों को रखना या रीडिंग को फिर से लिखना और अधिक प्रभावी ढंग से लिखना।

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

तकनीकी रूप से 'प्रतिबंधित' मानक C ++ में मौजूद नहीं है, लेकिन अधिकांश C ++ कंपाइलरों के लिए प्लेटफ़ॉर्म-विशिष्ट समकक्ष उपलब्ध हैं, इसलिए यह विचार करने योग्य है।

इसे भी देखें: http://cellperformance.beyond3d.com/articles/2006/05/demystifying-the-restrict-keyword.html


2

सब कुछ कास्ट करें!

अधिक जानकारी आप संकलक को डेटा के बारे में देते हैं जो बेहतर अनुकूलन हैं (कम से कम मेरे अनुभव में)।

void foo(Bar * x) {...;}

बन जाता है;

void foo(const Bar * const x) {...;}

कंपाइलर अब यह जानता है कि प्वाइंटर x नहीं बदलने वाला है और वह जिस डेटा को इंगित कर रहा है वह भी नहीं बदलेगा।

अन्य जोड़ा लाभ यह है कि आप आकस्मिक बगों की संख्या को कम कर सकते हैं, अपने आप को (या अन्य) चीजों को संशोधित कर सकते हैं जो उन्हें नहीं करना चाहिए।


और आपका कोड दोस्त आपको प्यार करेगा!
दस

4
constसंकलक अनुकूलन में सुधार नहीं करता है। सही है कि संकलक बेहतर कोड उत्पन्न कर सकता है अगर यह जानता है कि एक चर नहीं बदलेगा, लेकिन constएक मजबूत पर्याप्त गारंटी प्रदान नहीं करता है।
deft_code

3
नहीं। 'कास्ट ’की तुलना में useful प्रतिबंधित’ कहीं अधिक उपयोगी है। देखें gamedev.stackexchange.com/questions/853/...
Justicle

+1 ppl कह रहा है कि कैंट की मदद गलत है ... infoq.com/pretations/kixeye-scalability
NoSenseEtAl

2

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

मान लिया गया है कि…।

यदि यह वास्तव में महत्वपूर्ण कोड है, तो मेमोरी रीड से बचने की कोशिश करें, सामान की गणना करने से बचने की कोशिश करें, जो कि पूर्व निर्धारित किया जा सकता है (हालांकि कोई लुकअप टेबल नहीं है क्योंकि वे नियम नंबर 1 का उल्लंघन करते हैं)। जानें कि आपका एल्गोरिथ्म क्या करता है और इसे इस तरह से लिखें कि कंपाइलर को भी पता चले। यह सुनिश्चित करने के लिए विधानसभा की जाँच करें।

कैश मिस से बचें। जितना हो सके बैच प्रक्रिया। वर्चुअल फ़ंक्शंस और अन्य इनडायरेक्ट से बचें।

अंततः, सब कुछ मापें। नियम हर समय बदलते रहते हैं। 3 साल पहले कोड को गति देने के लिए जो इस्तेमाल किया गया था, वह अब इसे धीमा कर देता है। एक अच्छा उदाहरण 'फ्लोट संस्करणों के बजाय दोहरे गणित कार्यों का उपयोग करना' है। मुझे एहसास नहीं होता कि अगर मैं इसे नहीं पढ़ता।

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

यह समझने के लिए संपादित किया गया है कि कोई डिफ़ॉल्ट आरंभीकरण क्यों नहीं है: बहुत सारे कोड कहते हैं: वेक्टर 3 ब्ला; bla = DoSomething ();

कंस्ट्रक्टर में गहनता से समय बर्बाद होता है। इसके अलावा, इस मामले में व्यर्थ समय छोटा है (शायद वेक्टर को साफ करना), हालांकि अगर आपके प्रोग्रामर आदतन ऐसा करते हैं तो यह बढ़ जाता है। साथ ही, बहुत सारे फंक्शन एक अस्थायी (ओवरलोड ऑपरेटर्स के बारे में सोचते हैं) बनाते हैं, जो शून्य पर आरंभीकृत हो जाते हैं और सीधे चले जाने के बाद असाइन किए जाते हैं। छिपी हुई खोई हुई साइकिल जो आपके प्रोफाइलर में स्पाइक देखने के लिए बहुत छोटी है, लेकिन आपके कोड आधार पर सभी चक्रों से खून बह रहा है। इसके अलावा, कुछ लोग कंस्ट्रक्टर में बहुत अधिक करते हैं (जो स्पष्ट रूप से एक नहीं है)। मैंने एक अप्रयुक्त चर से बहु-मिलीसेकंड लाभ देखा है जहां कंस्ट्रक्टर भारी तरफ थोड़ा सा हुआ। जैसे ही कंस्ट्रक्टर साइड इफेक्ट्स का कारण बनता है, कंपाइलर इसे बाहर निकालने में सक्षम नहीं होगा, इसलिए जब तक आप ऊपर दिए गए कोड का उपयोग नहीं करते, मैं या तो एक गैर-इनिशियलाइज़िंग कंस्ट्रक्टर पसंद करता हूं, या, जैसा कि मैंने कहा,

वेक्टर 3 bla (noInit); bla = doSomething ();


/ निर्माण में अपने सदस्यों को प्रारंभ / न करें? वह कैसे मदद करता है?
दस

संपादित पोस्ट देखें। टिप्पणी बॉक्स में फिट नहीं था।
काज

const Vector3 = doSomething()? फिर रिटर्न-वैल्यू ऑप्टिमाइज़ेशन किक कर सकता है और संभवतः एक असाइनमेंट या दो को खत्म कर सकता है।
दस

1

बूलियन अभिव्यक्ति का मूल्यांकन कम करें

यह वास्तव में एक हताश है, क्योंकि यह आपके कोड में एक बहुत ही सूक्ष्म लेकिन खतरनाक बदलाव है। हालाँकि यदि आपके पास एक ऐसी स्थिति है जिसका मूल्यांकन कई बार किया जाता है, तो आप इसके बजाय बिटवाइज़ ऑपरेटरों का उपयोग करके बूलियन मूल्यांकन के ओवरहेड को कम कर सकते हैं। इसलिए:

if ((foo && bar) || blah) { ... } 

हो जाता है:

if ((foo & bar) | blah) { ... }

इसके बजाय पूर्णांक अंकगणितीय का उपयोग करना। यदि आपके फ़ोसोस और बार स्थिर हैं या यदि () से पहले मूल्यांकन किया गया है, तो यह सामान्य बूलियन संस्करण की तुलना में तेज़ हो सकता है।

एक बोनस के रूप में अंकगणित संस्करण की नियमित बूलियन संस्करण की तुलना में कम शाखाएं हैं। जो अनुकूलन करने का एक और तरीका है ।

बड़ी नकारात्मक बात यह है कि आप आलसी मूल्यांकन खो देते हैं - पूरे ब्लॉक का मूल्यांकन किया जाता है, इसलिए आप ऐसा नहीं कर सकते foo != NULL & foo->dereference()। इस वजह से, यह तर्कपूर्ण है कि यह बनाए रखना कठिन है, और इसलिए व्यापार बंद बहुत अच्छा हो सकता है।


1
प्रदर्शन की खातिर यह एक बहुत अच्छा व्यापार है, मुख्यतः क्योंकि यह तुरंत स्पष्ट नहीं है कि यह इरादा था।
बॉब सोमरस

मैं आपसे लगभग पूरी तरह सहमत हूं। मैंने कहा कि यह हताश था!
तेनपुन

3
क्या यह शॉर्ट-सर्किटिंग को भी नहीं तोड़ पाएगा और शाखा की भविष्यवाणी को अधिक अविश्वसनीय बना देगा?
एगॉन

1
यदि foo 2 है और बार 1 है, तो कोड बिल्कुल समान व्यवहार नहीं करता है। यह, और प्रारंभिक मूल्यांकन नहीं, मुझे लगता है कि सबसे बड़ा नकारात्मक पहलू है।

1
खासतौर पर, C ++ में बूलियन्स 0 या 1 के लिए ग्वारेंटेड होते हैं , इसलिए जब तक आप केवल बूल्स के साथ ऐसा कर रहे हैं आप सुरक्षित हैं। अधिक: altdevblogaday.org/2011/04/18/understanding-your-bool-type
tenpn

1

अपने स्टैक के उपयोग पर नज़र रखें

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

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.