टी एल: डॉ:
- कंपाइलर इंटर्नल शायद इस अनुकूलन को आसानी से देखने के लिए सेट नहीं किए गए हैं, और यह संभवतः केवल छोटे कार्यों के आसपास ही उपयोगी है, कॉल के बीच बड़े कार्यों के अंदर नहीं।
- अधिकांश कार्यों को बनाने के लिए इनलाइनिंग ज्यादातर समय एक बेहतर समाधान है
foo
RBX को बचाने / पुनर्स्थापित करने के लिए नहीं होने पर एक विलंबता बनाम थ्रूपुट ट्रेडऑफ़ हो सकता है।
कंपाइलर मशीनरी के जटिल टुकड़े हैं। वे मानव की तरह "स्मार्ट" नहीं हैं, और हर संभव अनुकूलन को खोजने के लिए महंगे एल्गोरिदम अक्सर अतिरिक्त संकलन समय में लागत के लायक नहीं होते हैं।
मैंने इसे GCC बग 69986 के रूप में सूचित किया - 2016 में वापस फैल / पुनः लोड करने के लिए पुश / पॉप का उपयोग करके -Obs के साथ छोटे कोड संभव ; GCC devs की कोई गतिविधि या उत्तर नहीं है। : /
थोड़ा संबंधित: जीसीसी बग 70408 - एक ही कॉल-संरक्षित रजिस्टर का पुन: उपयोग करने से कुछ मामलों में छोटे कोड मिलेंगे - संकलक देवों ने मुझे बताया कि जीसीसी के लिए यह अनुकूलन करने में सक्षम होने के लिए बड़ी मात्रा में काम लगेगा क्योंकि इसे मूल्यांकन के आदेश की आवश्यकता होती है दो foo(int)
कॉल के आधार पर जो लक्ष्य को आसान बना देगा।
यदि foo
अपने आप को सहेजता / पुनर्स्थापित नहीं करता rbx
है, तो थ्रूपुट (इंस्ट्रक्शन काउंट) बनाम अतिरिक्त स्टोर / रीलोड विलंबता के बीच x
-> रिटेल डिपेंडेंसी चेन पर ट्रेडऑफ होता है।
आमतौर पर कंपाइलर्स थ्रूपुट के ऊपर विलंबता का पक्ष लेते हैं, उदाहरण के लिए imul reg, reg, 10
(3-चक्र विलंबता, 1 / घड़ी थ्रूपुट) के बजाय 2x एलईए का उपयोग करते हुए , क्योंकि अधिकांश कोड का औसत स्काइलेक की तरह 4-चौड़ी पाइपलाइनों पर 4 यूओपी / घड़ी से काफी कम है। (अधिक निर्देश / यूओपी आरओबी में अधिक स्थान लेते हैं, यह कम करते हुए कि एक ही आउट-ऑफ-ऑर्डर विंडो कितनी दूर तक देख सकती है, हालांकि, और निष्पादन वास्तव में स्टालों के साथ फट गया है, जो शायद कम-से-4 यूओपी में से कुछ के लिए लेखांकन है / घड़ी की औसत।)
यदि foo
आरबीएक्स को धक्का / पॉप करता है, तो विलंबता के लिए बहुत कुछ नहीं है। पुनर्स्थापना होने से पहले बस के ret
बजाय बस के बाद होता है शायद प्रासंगिक नहीं है, जब तक कि कोई ret
गलतफहमी या आई-कैश मिस नहीं करता है जो रिटर्न पते पर कोड लाने में देरी करता है।
अधिकांश गैर-तुच्छ कार्य आरबीएक्स को बचाएंगे / पुनर्स्थापित करेंगे, इसलिए अक्सर यह अच्छी धारणा नहीं है कि आरबीएक्स में एक चर छोड़ने का वास्तव में मतलब होगा कि यह कॉल के दौरान एक रजिस्टर में वास्तव में रुका था। (हालांकि कॉल-संरक्षित रजिस्टरों के कार्यों को यादृच्छिक करना कभी-कभी इसे कम करने के लिए एक अच्छा विचार हो सकता है।)
तो हाँ push rdi
/ इस मामले pop rax
में अधिक कुशल होगा , और यह संभवतः छोटे गैर-पत्ती कार्यों के लिए एक चूक अनुकूलन है, जो कॉल करने वाले को बचाने / पुनर्स्थापित करने के लिए अधिक निर्देशों के लिए अतिरिक्त स्टोर / पुनः लोड विलंबता के बीच क्या करता है और शेष के बीच संतुलन पर निर्भर करता है ।foo
x
rbx
स्टैक-लेड मेटाडेटा के लिए यह संभव है कि वह RSP में परिवर्तनों का प्रतिनिधित्व करे, जैसे कि यदि उसने स्टैक स्लॉट में sub rsp, 8
फैल / पुनः लोड करने के लिए उपयोग किया था x
। (लेकिन संकलक इस अनुकूलन को नहीं जानते हैं, या तो push
स्थान आरक्षित करने के लिए और एक चर को इनिशियलाइज़ करने के लिए। किसी सी / सी ++ कंपाइलर का उपयोग लोकल वैरिएबल बनाने के लिए पुश पॉप निर्देशों का उपयोग कर सकता है, बजाय केवल एक बार जासूसी करने के? और इससे भी अधिक के लिए। एक स्थानीय संस्करण बड़े .eh_frame
स्टैक को मेटाडेटा के लिए ले जाएगा क्योंकि आप स्टैक पॉइंटर को प्रत्येक पुश के साथ अलग-अलग स्थानांतरित कर रहे हैं। यह कॉलर्स को संरक्षित-संरक्षित रेज को बचाने / बहाल करने के लिए पुश / पॉप का उपयोग करने से कंपाइलर्स को नहीं रोकता है, हालांकि।)
IDK यदि यह अनुकूलन देखने के लिए शिक्षण संकलक के लायक होगा
हो सकता है कि यह एक फंक्शन के आसपास हो, एक फंक्शन के अंदर एक कॉल के दौरान नहीं। और जैसा कि मैंने कहा, यह निराशावादी धारणा पर आधारित है जो foo
वैसे भी RBX को बचाएगा / बहाल करेगा। (या थ्रूपुट के लिए अनुकूलन यदि आप जानते हैं कि x से रिटर्न मान के लिए विलंबता महत्वपूर्ण नहीं है। लेकिन संकलक यह नहीं जानते हैं और आमतौर पर विलंबता के लिए अनुकूलन करते हैं)।
यदि आप बहुत सारे कोड में निराशावादी धारणा बनाना शुरू कर देते हैं (जैसे कि फ़ंक्शन के अंदर एकल फ़ंक्शन कॉल के आसपास), तो आपको अधिक ऐसे मामले मिलने शुरू हो जाएंगे, जहां RBX को बचाया / पुनर्स्थापित नहीं किया गया है और आप लाभ उठा सकते हैं।
आप लूप में इस अतिरिक्त बचत / पुनर्स्थापना पुश / पॉप को भी नहीं चाहते हैं, बस लूप के बाहर RBX को सहेजें / पुनर्स्थापित करें और फ़ंक्शन कॉल करने वाले लूप में कॉल-संरक्षित रजिस्टरों का उपयोग करें। यहां तक कि छोरों के बिना, सामान्य मामले में अधिकांश फ़ंक्शन कई फ़ंक्शन कॉल करते हैं। यह अनुकूलन विचार लागू हो सकता है यदि आप वास्तव में x
किसी भी कॉल के बीच प्रयोग नहीं करते हैं, सिर्फ पहले और आखिरी के बाद, अन्यथा आपको प्रत्येक के लिए 16-बाइट स्टैक संरेखण बनाए रखने की समस्या है call
यदि आप एक के बाद एक पॉप कर रहे हैं दूसरे कॉल से पहले कॉल करें।
संकलक सामान्य रूप से छोटे कार्यों में महान नहीं हैं। लेकिन यह सीपीयू के लिए बहुत अच्छा नहीं है। गैर-इनलाइन फ़ंक्शन कॉल का अनुकूलन पर सबसे अच्छा समय पर प्रभाव पड़ता है, जब तक कि कंपाइलर्स कैली के इंटर्नल को नहीं देख सकते हैं और सामान्य से अधिक अनुमान लगा सकते हैं। एक गैर-इनलाइन फ़ंक्शन कॉल एक अंतर्निहित स्मृति अवरोधक है: एक कॉलर को यह मान लेना होगा कि कोई फ़ंक्शन किसी भी वैश्विक-सुलभ डेटा को पढ़ या लिख सकता है, इसलिए ऐसे सभी संस्करणों को सी सार मशीन के साथ सिंक करना होगा। (एस्केप एनालिसिस कॉल के दौरान रजिस्टरों में स्थानीय लोगों को रखने की अनुमति देता है, यदि उनका पता फ़ंक्शन से बच नहीं गया है।) इसके अलावा, कंपाइलर को यह मान लेना होगा कि कॉल-क्लोब रजिस्टर्ड सभी क्लोएबर्ड हैं। यह x86-64 सिस्टम V में फ्लोटिंग पॉइंट के लिए बेकार है, जिसमें कोई कॉल-संरक्षित एक्सएमएम रजिस्टर नहीं है।
जैसे छोटे कार्य bar()
उनके कॉलर में इनलाइनिंग से बेहतर हैं। साथ संकलित करें -flto
तो यह ज्यादातर मामलों में फ़ाइल सीमाओं के पार भी हो सकता है। (फ़ंक्शन पॉइंटर्स और साझा-लाइब्रेरी सीमाएँ इसे हरा सकती हैं।)
मुझे लगता है कि इन ऑप्टिमाइज़ेशन को करने के लिए एक कारण संकलक ने परेशान नहीं किया है, यह है कि कंपाइलर इंटर्नल में अलग-अलग कोड की एक पूरी गुच्छा की आवश्यकता होगी , सामान्य स्टैक बनाम रजिस्टर-आवंटन कोड से अलग है जो कॉल-संरक्षित को बचाने का तरीका जानता है। रजिस्टर और उनका उपयोग करें।
यानी इसे लागू करने के लिए बहुत काम करना होगा, और इसे बनाए रखने के लिए बहुत सारे कोड, और अगर यह ऐसा करने के बारे में अति-उत्साही हो जाता है तो यह बदतर कोड बना सकता है ।
और यह भी कि यह (उम्मीद है) महत्वपूर्ण नहीं है; यदि यह मायने रखता है, तो आपको bar
इसके कॉलर में inlining होना चाहिए , या इनलाइन foo
करना चाहिए bar
। यह ठीक है जब तक कि विभिन्न प्रकार के बहुत सारे bar
कार्य नहीं होते हैं और foo
बड़े होते हैं, और किसी कारण से वे अपने कॉलर्स में इनलाइन नहीं कर सकते हैं।