इस लेख का उद्देश्य इंटेल सी++ कंपाइलर्स पर ध्यान केंद्रित करते हुए कंपाइलर अनुकूलन की क्षमता पर प्रकाश डालना है - जो अपनी लोकप्रियता और व्यापक उपयोग के लिए प्रसिद्ध है।
मुख्य विशेषताएं: कंपाइलर अनुकूलन क्या हैं? | -पर | वास्तु लक्षित | अंतरप्रक्रियात्मक अनुकूलन | -fno-एलियासिंग | कंपाइलर अनुकूलन रिपोर्ट
कोई भी कंपाइलर उच्च-स्तरीय स्रोत कोड को निम्न-स्तरीय मशीन कोड में परिवर्तित करने के लिए चरणों की एक श्रृंखला निष्पादित करता है। इनमें शाब्दिक विश्लेषण, वाक्यविन्यास विश्लेषण, सिमेंटिक विश्लेषण, इंटरमीडिएट कोड जनरेशन (या आईआर), अनुकूलन और कोड जनरेशन शामिल हैं।
अनुकूलन चरण के दौरान, कंपाइलर सावधानीपूर्वक एक प्रोग्राम को बदलने के तरीकों की तलाश करता है, जिसका लक्ष्य शब्दार्थ रूप से समकक्ष आउटपुट होता है जो कम संसाधनों का उपयोग करता है या अधिक तेजी से निष्पादित होता है। इस प्रक्रिया में नियोजित तकनीकों में निरंतर फोल्डिंग, लूप ऑप्टिमाइज़ेशन, फ़ंक्शन इनलाइनिंग और डेड कोड उन्मूलन शामिल हैं, लेकिन इन्हीं तक सीमित नहीं हैं।
डेवलपर्स संकलन प्रक्रिया के दौरान कंपाइलर फ़्लैग का एक सेट निर्दिष्ट कर सकते हैं, जो डिबगिंग और प्रोफ़ाइलिंग जानकारी के लिए जीसीसी के साथ " -जी" या "-पीजी" जैसे विकल्पों का उपयोग करने वालों के लिए परिचित अभ्यास है। जैसे-जैसे हम आगे बढ़ेंगे, हम ऐसे ही कंपाइलर फ़्लैग पर चर्चा करेंगे जिनका उपयोग हम Intel C++ कंपाइलर के साथ अपने एप्लिकेशन को संकलित करते समय कर सकते हैं। ये आपके कोड की दक्षता और प्रदर्शन को बेहतर बनाने में आपकी मदद कर सकते हैं।
u(x,y,t) समय t पर बिंदु (x,y) पर तापमान है।
हमारे पास अनिवार्य रूप से एक C++ कोडिंग है जो वैरिएबल आकारों (जिसे हम रिज़ॉल्यूशन कहते हैं) के ग्रिड पर जैकोबी पुनरावृत्तियों का प्रदर्शन करती है। मूल रूप से, 500 के ग्रिड आकार का अर्थ है 500x500 आकार के मैट्रिक्स को हल करना, इत्यादि।
/* * One Jacobi iteration step */ void jacobi(double *u, double *unew, unsigned sizex, unsigned sizey) { int i, j; for (j = 1; j < sizex - 1; j++) { for (i = 1; i < sizey - 1; i++) { unew[i * sizex + j] = 0.25 * (u[i * sizex + (j - 1)] + // left u[i * sizex + (j + 1)] + // right u[(i - 1) * sizex + j] + // top u[(i + 1) * sizex + j]); // bottom } } for (j = 1; j < sizex - 1; j++) { for (i = 1; i < sizey - 1; i++) { u[i * sizex + j] = unew[i * sizex + j]; } } }
एमएफएलओपी/एस का अर्थ है "मिलियन फ्लोटिंग पॉइंट ऑपरेशंस प्रति सेकंड।" यह माप की एक इकाई है जिसका उपयोग फ्लोटिंग-पॉइंट ऑपरेशन के संदर्भ में कंप्यूटर या प्रोसेसर के प्रदर्शन को मापने के लिए किया जाता है। फ़्लोटिंग-पॉइंट ऑपरेशंस में फ़्लोटिंग-पॉइंट प्रारूप में दर्शाए गए दशमलव या वास्तविक संख्याओं के साथ गणितीय गणना शामिल होती है।
नोट 1: एक स्थिर परिणाम प्रदान करने के लिए, मैं प्रत्येक रिज़ॉल्यूशन के लिए निष्पादन योग्य को 5 बार चलाता हूं और एमएफएलओपी/एस मानों का औसत मूल्य लेता हूं।
नोट 2: यह ध्यान रखना महत्वपूर्ण है कि Intel C++ कंपाइलर पर डिफ़ॉल्ट अनुकूलन -O2 है। इसलिए, स्रोत कोड संकलित करते समय -O0 निर्दिष्ट करना महत्वपूर्ण है।
जब कोई कंपाइलर अनुकूलन के साथ शुरुआत करता है तो ये सबसे अधिक उपयोग किए जाने वाले कंपाइलर फ़्लैग में से कुछ हैं। आदर्श स्थिति में, Ofast > O3 > O2 > O1 > O0 का प्रदर्शन। हालाँकि, ऐसा जरूरी नहीं है. इन विकल्पों के महत्वपूर्ण बिंदु इस प्रकार हैं:
-O1:
-O2:
-O3:
-तेजी से:
यह स्पष्ट रूप से स्पष्ट है कि ये सभी अनुकूलन हमारे आधार कोड ("-O0" के साथ) से बहुत तेज़ हैं। निष्पादन रन समय बेस केस की तुलना में 2-3 गुना कम है। एमएफएलओपी/एस के बारे में क्या??
कुल मिलाकर, हालांकि थोड़ा ही सही, "-O3" सबसे अच्छा प्रदर्शन करता है।
"- Ofast " (" -no-prec-div -fp-model fast=2 ") द्वारा उपयोग किए गए अतिरिक्त झंडे कोई अतिरिक्त गति नहीं दे रहे हैं।
इसका उत्तर रणनीतिक संकलक झंडों में निहित है। " -xHost " और, अधिक सटीक रूप से, " -xCORE-AVX512 " जैसे विकल्पों के साथ प्रयोग करने से हमें मशीन की क्षमताओं की पूरी क्षमता का उपयोग करने और इष्टतम प्रदर्शन के लिए अनुकूलन करने की अनुमति मिल सकती है।
-एक्सहोस्ट:
-xCORE-AVX512:
लक्ष्य: कंपाइलर को इंटेल एडवांस्ड वेक्टर एक्सटेंशन्स 512 (एवीएक्स-512) इंस्ट्रक्शन सेट का उपयोग करने वाले कोड को उत्पन्न करने के लिए स्पष्ट रूप से निर्देश दें।
मुख्य विशेषताएं: AVX-512 एक उन्नत SIMD (सिंगल इंस्ट्रक्शन, मल्टीपल डेटा) इंस्ट्रक्शन सेट है जो AVX2 जैसे पिछले संस्करणों की तुलना में व्यापक वेक्टर रजिस्टर और अतिरिक्त संचालन प्रदान करता है। इस फ़्लैग को सक्षम करने से कंपाइलर अनुकूलित प्रदर्शन के लिए इन उन्नत सुविधाओं का लाभ उठा सकता है।
विचार: पोर्टेबिलिटी फिर से यहाँ अपराधी है। AVX-512 निर्देशों के साथ उत्पन्न बायनेरिज़ उन प्रोसेसर पर बेहतर ढंग से नहीं चल सकती हैं जो इस निर्देश सेट का समर्थन नहीं करते हैं। हो सकता है कि वे बिल्कुल भी काम न करें!
डिफ़ॉल्ट रूप से, " -xCORE-AVX512 " मानता है कि प्रोग्राम को zmm रजिस्टरों के उपयोग से कोई लाभ नहीं होगा। कंपाइलर zmm रजिस्टरों का उपयोग करने से बचता है जब तक कि प्रदर्शन लाभ की गारंटी न हो।
यदि कोई बिना किसी प्रतिबंध के zmm रजिस्टरों का उपयोग करने की योजना बना रहा है, तो " " को उच्च पर सेट किया जा सकता है। हम भी यही करेंगे.
वू हू!
उल्लेखनीय बात यह है कि हमने इन परिणामों को बिना किसी महत्वपूर्ण मैन्युअल हस्तक्षेप के प्राप्त किया है - बस एप्लिकेशन संकलन प्रक्रिया के दौरान मुट्ठी भर कंपाइलर फ़्लैग को शामिल करके।
नोट: यदि आपका हार्डवेयर AVX-512 का समर्थन नहीं करता है तो चिंता न करें। Intel C++ कंपाइलर AVX, AVX-2 और यहां तक कि SSE के लिए अनुकूलन का समर्थन करता है। में वह सब कुछ है जो आपको जानना आवश्यक है!
आईपीओ एक बहु-चरणीय प्रक्रिया है जो एक कार्यक्रम के भीतर विभिन्न कार्यों या प्रक्रियाओं के बीच बातचीत पर ध्यान केंद्रित करती है। आईपीओ में कई अलग-अलग प्रकार के अनुकूलन शामिल हो सकते हैं, जिनमें फॉरवर्ड प्रतिस्थापन, अप्रत्यक्ष कॉल रूपांतरण और इनलाइनिंग शामिल हैं।
-आईपीओ:
लक्ष्य: अंतरप्रक्रियात्मक अनुकूलन को सक्षम बनाता है, जिससे कंपाइलर को संकलन के दौरान व्यक्तिगत स्रोत फ़ाइलों से परे, पूरे प्रोग्राम का विश्लेषण और अनुकूलन करने की अनुमति मिलती है।
मुख्य विशेषताएं: - संपूर्ण प्रोग्राम अनुकूलन: " -आईपीओ " पूरे कार्यक्रम में कार्यों और प्रक्रियाओं के बीच बातचीत पर विचार करते हुए, सभी स्रोत फ़ाइलों में विश्लेषण और अनुकूलन करता है। - क्रॉस-फ़ंक्शन और क्रॉस-मॉड्यूल अनुकूलन: ध्वज इनलाइनिंग फ़ंक्शंस, सिंक्रनाइज़ेशन की सुविधा देता है विभिन्न प्रोग्राम भागों में अनुकूलन और डेटा प्रवाह विश्लेषण।
विचार: इसके लिए एक अलग लिंक चरण की आवश्यकता है। " -ipo " के साथ संकलन करने के बाद, अंतिम निष्पादन योग्य उत्पन्न करने के लिए एक विशेष लिंक चरण की आवश्यकता होती है। कंपाइलर लिंकिंग के दौरान संपूर्ण प्रोग्राम दृश्य के आधार पर अतिरिक्त अनुकूलन करता है।
-आईपी:
लक्ष्य: अंतरप्रक्रियात्मक विश्लेषण-प्रसार को सक्षम बनाता है, जिससे कंपाइलर को अलग लिंक चरण की आवश्यकता के बिना कुछ अंतरप्रक्रियात्मक अनुकूलन करने की अनुमति मिलती है।
मुख्य विशेषताएं:- विश्लेषण और प्रसार: " -आईपी " कंपाइलर को संकलन के दौरान विभिन्न कार्यों और मॉड्यूल में अनुसंधान और डेटा प्रसार करने में सक्षम बनाता है। हालाँकि, यह उन सभी अनुकूलन को निष्पादित नहीं करता है जिनके लिए पूर्ण प्रोग्राम दृश्य की आवश्यकता होती है। - तेज़ संकलन: " -ipo " के विपरीत, " -ip " के लिए एक अलग लिंकिंग चरण की आवश्यकता नहीं होती है, जिसके परिणामस्वरूप त्वरित संकलन समय होता है। यह विकास के दौरान फायदेमंद हो सकता है जब त्वरित प्रतिक्रिया आवश्यक हो।
विचार: फ़ंक्शन इनलाइनिंग सहित केवल कुछ सीमित अंतरप्रक्रियात्मक अनुकूलन होते हैं।
-आईपीओ आम तौर पर अधिक व्यापक अंतरप्रक्रियात्मक अनुकूलन क्षमताएं प्रदान करता है क्योंकि इसमें एक अलग लिंक चरण शामिल होता है लेकिन लंबे संकलन समय की कीमत पर आता है। [ ] -आईपी एक तेज़ विकल्प है जो अलग लिंक चरण की आवश्यकता के बिना कुछ अंतरप्रक्रियात्मक अनुकूलन करता है, जो इसे विकास और परीक्षण चरणों के लिए उपयुक्त बनाता है। [ ]
चूँकि हम केवल प्रदर्शन और विभिन्न अनुकूलन, संकलन समय या निष्पादन योग्य के आकार के बारे में बात कर रहे हैं, यह हमारी चिंता का विषय नहीं है, हम " -ipo " पर ध्यान केंद्रित करेंगे।
/* * One Jacobi iteration step */ void jacobi(double *u, double *unew, unsigned sizex, unsigned sizey) { int i, j; for (j = 1; j < sizex - 1; j++) { for (i = 1; i < sizey - 1; i++) { unew[i * sizex + j] = 0.25 * (u[i * sizex + (j - 1)] + // left u[i * sizex + (j + 1)] + // right u[(i - 1) * sizex + j] + // top u[(i + 1) * sizex + j]); // bottom } } for (j = 1; j < sizex - 1; j++) { for (i = 1; i < sizey - 1; i++) { u[i * sizex + j] = unew[i * sizex + j]; } } }
जैकोबी() फ़ंक्शन पैरामीटर के रूप में दोगुना करने के लिए कुछ पॉइंटर्स लेता है और फिर नेस्टेड फॉर लूप्स के अंदर कुछ करता है। जब कोई कंपाइलर इस फ़ंक्शन को सोर्स फ़ाइल में देखता है, तो उसे बहुत सावधान रहना होगा।
यू का उपयोग करके यून्यू की गणना करने की अभिव्यक्ति में 4 पड़ोसी यू मानों का औसत शामिल है। यदि आप और यून्यू दोनों एक ही स्थान की ओर इशारा करें तो क्या होगा? यह उपनाम सूचकों की शास्त्रीय समस्या बन जाएगी [ ]।
आधुनिक कंपाइलर बहुत स्मार्ट हैं और सुरक्षा सुनिश्चित करने के लिए, वे मानते हैं कि अलियासिंग संभव हो सकता है। और इस तरह के परिदृश्यों के लिए, वे ऐसे किसी भी अनुकूलन से बचते हैं जो कोड के शब्दार्थ और आउटपुट को प्रभावित कर सकता है।
हमारे मामले में, हम जानते हैं कि यू और यून्यू अलग-अलग मेमोरी स्थान हैं और अलग-अलग मान संग्रहीत करने के लिए हैं। इसलिए, हम कंपाइलर को आसानी से बता सकते हैं कि यहां कोई उपनाम नहीं होगा।
दो विधियाँ हैं. पहला है C “ ” कीवर्ड । लेकिन इसके लिए कोड बदलने की आवश्यकता है। फिलहाल हम ऐसा नहीं चाहते.
कुछ भी सरल? आइए " -fno-alias " का प्रयास करें।
-fno-उपनाम:
लक्ष्य: कंपाइलर को प्रोग्राम में अलियासिंग न मानने का निर्देश दें।
मुख्य विशेषताएं: यह मानते हुए कि कोई अलियासिंग नहीं है, कंपाइलर अधिक स्वतंत्र रूप से कोड को अनुकूलित कर सकता है, संभावित रूप से प्रदर्शन में सुधार कर सकता है।
विचार: डेवलपर को इस ध्वज का उपयोग करने में सावधानी बरतनी होगी क्योंकि किसी भी अनुचित उपनाम के मामले में, प्रोग्राम अप्रत्याशित आउटपुट दे सकता है।
खैर, अब हमारे पास कुछ है!!!
असेंबली कोड (हालांकि यहां साझा नहीं किया गया है) और जेनरेट की गई कंपाइल ऑप्टिमाइज़ेशन रिपोर्ट ( नीचे देखें) की बारीकी से जांच से कंपाइलर के और के समझदार अनुप्रयोग का पता चलता है। ये परिवर्तन अत्यधिक अनुकूलित प्रदर्शन में योगदान करते हैं, जो कोड दक्षता पर कंपाइलर निर्देशों के महत्वपूर्ण प्रभाव को प्रदर्शित करते हैं।
Intel C++ कंपाइलर एक मूल्यवान सुविधा प्रदान करता है जो उपयोगकर्ताओं को अनुकूलन उद्देश्यों के लिए किए गए सभी समायोजनों का सारांश देते हुए एक अनुकूलन रिपोर्ट तैयार करने की अनुमति देता है [ ]। यह व्यापक रिपोर्ट YAML फ़ाइल स्वरूप में सहेजी गई है, जो कोड के भीतर कंपाइलर द्वारा लागू अनुकूलन की एक विस्तृत सूची प्रस्तुत करती है। विस्तृत विवरण के लिए, " " पर आधिकारिक दस्तावेज़ देखें।
इसी तरह, Intel C++ कंपाइलर (और सभी लोकप्रिय कंपाइलर) भी Pragma निर्देशों का समर्थन करते हैं, जो बहुत अच्छी विशेषताएं हैं। पर कुछ प्राग्मा जैसे आईवीडीईपी, पैरेलल, सिमड, वेक्टर इत्यादि की जांच करना उचित है।