प्रोफ़ाइल-गाइडेड ऑप्टिमाइज़ेशन (पीजीओ) कैसे काम करता है

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

इस डायग्राम में, PGO के काम करने के तरीके के बारे में खास जानकारी दी गई है

पहली इमेज. पीजीओ के काम करने के तरीके के बारे में खास जानकारी.

PGO का इस्तेमाल करने के लिए, सबसे पहले अपनी बिल्ड को इंस्ट्रूमेंट करें, ताकि प्रोफ़ाइल डेटा जनरेट किया जा सके. इस डेटा का इस्तेमाल कंपाइलर कर सकता है. इसके बाद, उस बिल्ड को चलाकर अपने कोड का इस्तेमाल करें और एक या एक से ज़्यादा प्रोफ़ाइल डेटा फ़ाइलें जनरेट करें. आखिर में, उन फ़ाइलों को डिवाइस से वापस कॉपी करें और उन्हें कंपाइलर के साथ इस्तेमाल करें. इससे, आपने जो प्रोफ़ाइल की जानकारी कैप्चर की है उसका इस्तेमाल करके, अपनी एक्ज़ीक्यूटेबल फ़ाइल को ऑप्टिमाइज़ किया जा सकता है.

PGO के बिना ऑप्टिमाइज़ की गई बिल्ड कैसे काम करती हैं

प्रोफ़ाइल डेटा का इस्तेमाल किए बिना ऑप्टिमाइज़ किया गया बिल्ड, ऑप्टिमाइज़ किया गया कोड जनरेट करने का तरीका तय करते समय कई अनुमानित तरीकों का इस्तेमाल करता है.

कुछ को डेवलपर साफ़ तौर पर सिग्नल देता है. उदाहरण के लिए, C++ 20 या उसके बाद के वर्शन में, [[likely]] और [[unlikely]] जैसे ब्रांच-डायरेक्शन हिंट का इस्तेमाल करके. एक और उदाहरण inline कीवर्ड का इस्तेमाल करना है. इसके अलावा, __forceinline का इस्तेमाल भी किया जा सकता है. हालांकि, आम तौर पर पहले वाले कीवर्ड का इस्तेमाल करना ज़्यादा बेहतर और आसान होता है. डिफ़ॉल्ट रूप से, कुछ कंपाइलर यह मान लेते हैं कि ब्रांच का पहला लेग (यानी कि if स्टेटमेंट, न कि else हिस्सा) सबसे ज़्यादा संभावित है. ऑप्टिमाइज़र, कोड के स्टैटिक विश्लेषण से यह भी अनुमान लगा सकता है कि कोड कैसे काम करेगा. हालांकि, यह अनुमान आम तौर पर सीमित होता है.

इन ह्यूरिस्टिक की समस्या यह है कि ये कंपाइलर की हर स्थिति में सही तरीके से मदद नहीं कर सकते. भले ही, मैन्युअल मार्कअप की पूरी जानकारी दी गई हो. इसलिए, जनरेट किया गया कोड आम तौर पर अच्छी तरह से ऑप्टिमाइज़ किया जाता है. हालांकि, अगर कंपाइलर के पास रनटाइम में इसके व्यवहार के बारे में ज़्यादा जानकारी होती, तो यह और भी बेहतर हो सकता था.

प्रोफ़ाइल जनरेट की जा रही है

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

कुछ अन्य तरह की ट्रैकिंग भी की जाती है. उदाहरण के लिए, किसी ब्लॉक में कॉपी करने की सामान्य कार्रवाइयों का साइज़. इससे बाद में, कार्रवाई के तेज़ और इनलाइन किए गए वर्शन जनरेट किए जा सकते हैं.

गेम के कुछ काम करने के बाद, एक्ज़ीक्यूटेबल को एक फ़ंक्शन – __llvm_profile_write_file() – कॉल करना होगा, ताकि प्रोफ़ाइल डेटा को डिवाइस पर पसंद के मुताबिक जगह पर लिखा जा सके. जब आपके बिल्ड कॉन्फ़िगरेशन में PGO इंस्ट्रूमेंटेशन चालू होता है, तब यह फ़ंक्शन आपके गेम से अपने-आप लिंक हो जाता है.

इसके बाद, प्रोफ़ाइल के डेटा की लिखी गई फ़ाइल को वापस होस्ट कंप्यूटर पर कॉपी किया जाना चाहिए. साथ ही, इसे उसी बिल्ड की अन्य प्रोफ़ाइलों के साथ किसी जगह पर रखा जाना चाहिए, ताकि इनका एक साथ इस्तेमाल किया जा सके.

उदाहरण के लिए, गेम के मौजूदा सीन के खत्म होने पर __llvm_profile_write_file() को कॉल करने के लिए, गेम के कोड में बदलाव किया जा सकता है. इसके बाद, आपको एक प्रोफ़ाइल बनानी होगी. इस प्रोफ़ाइल का इस्तेमाल करके, आपको इंस्ट्रुमेंटेशन की सुविधा चालू करके गेम बनाना होगा. इसके बाद, इसे अपने Android डिवाइस पर डिप्लॉय करना होगा. जब यह सुविधा चालू होती है, तब प्रोफ़ाइल का डेटा अपने-आप कैप्चर हो जाता है. आपका क्यूए इंजीनियर, गेम को अलग-अलग स्थितियों में चलाता है या सिर्फ़ सामान्य टेस्ट पास करता है.

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

इसके बाद, स्क्रिप्ट का इस्तेमाल करके टेस्ट डिवाइस से प्रोफ़ाइल डेटा कॉपी किया जा सकता है. साथ ही, इसे एक सेंट्रल रिपॉज़िटरी में अपलोड किया जा सकता है, ताकि बाद में इसका इस्तेमाल किया जा सके.

प्रोफ़ाइल का डेटा मर्ज करना

किसी डिवाइस से प्रोफ़ाइल मिलने के बाद, उसे इंस्ट्रुमेंटेड बिल्ड से जनरेट की गई प्रोफ़ाइल डेटा फ़ाइल से ऐसे फ़ॉर्म में बदलना होता है जिसे कंपाइलर इस्तेमाल कर सके. AGDE, आपके प्रोजेक्ट में जोड़ी गई किसी भी प्रोफ़ाइल डेटा फ़ाइल के लिए, यह काम अपने-आप करता है.

PGO को, इंस्ट्रुमेंट किए गए कई प्रोफ़ाइल रन के नतीजों को एक साथ जोड़ने के लिए डिज़ाइन किया गया है. अगर आपके पास एक प्रोजेक्ट में कई फ़ाइलें हैं, तो AGDE भी आपके लिए यह काम अपने-आप करता है.

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

इससे भी बेहतर यह है कि जब लंबी अवधि तक चलने वाले टेस्ट किए जाते हैं, तो इंटरनल रिलीज़ से इंटरनल रिलीज़ तक प्रोफ़ाइल के डेटा की कॉपी सेव रखी जाती हैं. ऐसे में, प्रोफ़ाइल को फिर से बनाने से, पुराने प्रोफ़ाइल डेटा को अमान्य नहीं किया जाता. ज़्यादातर मामलों में, कोड एक वर्शन से दूसरे वर्शन तक काफ़ी हद तक स्थिर रहता है. इसलिए, पुरानी बिल्ड का प्रोफ़ाइल डेटा अब भी काम का हो सकता है और यह तुरंत पुराना नहीं होता.

प्रोफ़ाइल-गाइडेड ऑप्टिमाइज़ेशन की मदद से बिल्ड जनरेट किए जा रहे हैं

अपने प्रोजेक्ट में प्रोफ़ाइल डेटा जोड़ने के बाद, इसका इस्तेमाल किया जा सकता है. इसके लिए, आपको अपने बिल्ड कॉन्फ़िगरेशन में ऑप्टिमाइज़ेशन मोड में PGO को चालू करना होगा.

इससे कंपाइलर के ऑप्टिमाइज़र को यह निर्देश मिलता है कि ऑप्टिमाइज़ेशन से जुड़े फ़ैसले लेते समय, पहले कैप्चर किए गए प्रोफ़ाइल डेटा का इस्तेमाल किया जाए.

प्रोफ़ाइल के हिसाब से ऑप्टिमाइज़ेशन का इस्तेमाल कब करना चाहिए

PGO को डेवलपमेंट की शुरुआत में या कोड पर रोज़ाना के इटरेशन के दौरान चालू नहीं किया जाता है. डेवलपमेंट के दौरान, आपको एल्गोरिदम और डेटा-लेआउट पर आधारित ऑप्टिमाइज़ेशन पर ध्यान देना चाहिए. इससे आपको ज़्यादा फ़ायदे मिलेंगे.

PGO, डेवलपमेंट प्रोसेस में बाद में आता है. ऐसा तब होता है, जब रिलीज़ के लिए ऐप्लिकेशन को बेहतर बनाया जा रहा हो. प्रोफ़ाइल-गाइडेड ऑप्टिमाइज़ेशन को सबसे आखिर में किया जाने वाला ऑप्टिमाइज़ेशन समझें. इससे आपको अपने कोड की परफ़ॉर्मेंस को बेहतर बनाने में मदद मिलती है. हालांकि, ऐसा तब होता है, जब आपने पहले ही अपने कोड को ऑप्टिमाइज़ कर लिया हो.

PGO की मदद से परफ़ॉर्मेंस में होने वाले सुधार का अनुमान

यह कई बातों पर निर्भर करता है. जैसे, आपकी प्रोफ़ाइलें कितनी पुरानी और कितनी अप-टू-डेट हैं. साथ ही, ऑप्टिमाइज़ किए गए पारंपरिक बिल्ड के साथ आपका कोड कितना ऑप्टिमल होगा.

आम तौर पर, बहुत कम अनुमान यह है कि मुख्य थ्रेड में सीपीयू की लागत ~5% कम हो जाएगी. आपको अलग-अलग नतीजे दिख सकते हैं.

इंस्ट्रुमेंटेशन ओवरहेड

PGO का इंस्ट्रुमेंटेशन बहुत ज़्यादा है. यह अपने-आप जनरेट होता है, लेकिन इसके लिए शुल्क देना पड़ता है. PGO इंस्ट्रूमेंटेशन का ओवरहेड, आपके कोडबेस के हिसाब से अलग-अलग हो सकता है.

प्रोफ़ाइल-गाइडेड इंस्ट्रुमेंटेशन की परफ़ॉर्मेंस लागत

इंस्ट्रुमेंट की गई बिल्ड में, फ़्रेम रेट में गिरावट दिख सकती है. कुछ मामलों में – सामान्य तौर पर सीपीयू (CPU) का इस्तेमाल 100% के कितना करीब है, इस पर निर्भर करता है – यह गिरावट इतनी ज़्यादा हो सकती है कि गेम को सामान्य तरीके से खेलना मुश्किल हो जाए.

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

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

इस तरह के रीप्ले सिस्टम से Android को बहुत फ़ायदा मिल सकता है. Android के इकोसिस्टम में, डिवाइस के कई वर्शन उपलब्ध हैं. इसके फ़ायदे यहीं खत्म नहीं होते: यह आपके कंटीन्यूअस इंटिग्रेशन बिल्ड सिस्टम का मुख्य हिस्सा भी बन सकता है. इससे आपको परफ़ॉर्मेंस रिग्रेशन और स्मोक टेस्ट को नियमित तौर पर रात भर में पूरा करने में मदद मिलती है.

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

प्लेबैक मोड में, आपके गेम में ऑनलाइन साइन-इन करने का विकल्प नहीं होना चाहिए. साथ ही, इसमें विज्ञापन नहीं दिखने चाहिए. इसके अलावा, इसे तय किए गए टाइमस्टेप (आपके टारगेट फ़्रेम-रेट पर) पर काम करना चाहिए. आपको वीसिंक बंद करने पर विचार करना चाहिए.

यह ज़रूरी नहीं है कि आपके गेम में हर चीज़ (उदाहरण के लिए, पार्टिकल सिस्टम) को पूरी तरह से दोहराया जा सके. हालांकि, एक जैसी कार्रवाइयों से गेम में एक जैसे नतीजे मिलने चाहिए. इसका मतलब है कि गेमप्ले एक जैसा होना चाहिए.

प्रोफ़ाइल-गाइडेड इंस्ट्रूमेंटेशन की मेमोरी लागत

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

प्रोफ़ाइल के डेटा को कब अपडेट या खारिज करना चाहिए

जब भी कोड या गेम के कॉन्टेंट में कोई बड़ा बदलाव किया जाए, तब आपको अपनी प्रोफ़ाइलें अपडेट करनी चाहिए.

इसका सटीक मतलब आपके बिल्ड एनवायरमेंट और डेवलपमेंट के चरण पर निर्भर करता है.

जैसा कि पहले बताया गया है, आपको बिल्ड एनवायरमेंट में बड़े बदलावों के दौरान प्रोफ़ाइल डेटा को ट्रांसफ़र नहीं करना चाहिए. इससे आपको बिल्ड बनाने या बिल्ड को तोड़ने से नहीं रोका जाएगा. हालांकि, इससे PGO का इस्तेमाल करने से मिलने वाले परफ़ॉर्मेंस फ़ायदों में कमी आएगी, क्योंकि बहुत कम प्रोफ़ाइल डेटा नए बिल्ड एनवायरमेंट पर लागू होगा. हालांकि, ऐसा सिर्फ़ तब नहीं होता, जब आपकी प्रोफ़ाइल का डेटा पुराना हो जाता है.

मान लें कि आपको PGO का इस्तेमाल तब तक नहीं करना है, जब तक डेवलपमेंट का काम पूरा होने वाला न हो. ऐसा तब होता है, जब रिलीज़ की तैयारी की जा रही हो. हालांकि, ऐसा हो सकता है कि आपको हर हफ़्ते का डेटा इकट्ठा करना हो, ताकि परफ़ॉर्मेंस पर फ़ोकस करने वाले इंजीनियर यह पुष्टि कर सकें कि रिलीज़ के समय कोई अनचाही समस्या नहीं आएगी.

रिलीज़ विंडो के करीब आने पर, यह संख्या बदल जाती है. ऐसा तब होता है, जब आपकी QA टीम हर दिन टेस्टिंग करती है और गेम की पूरी तरह से जांच करती है. इस फ़ेज़ के दौरान, हर दिन उस डेटा से प्रोफ़ाइलें जनरेट की जा सकती हैं. साथ ही, उनका इस्तेमाल परफ़ॉर्मेंस टेस्टिंग और अपने परफ़ॉर्मेंस बजट को अडजस्ट करने के लिए, आने वाले समय में होने वाले बिल्ड के बारे में जानकारी देने के लिए किया जा सकता है.

रिलीज़ की तैयारी करते समय, आपको उस बिल्ड वर्शन को लॉक करना चाहिए जिसे आपको रिलीज़ करना है. इसके बाद, QA को इसे रन करने के लिए कहें, ताकि वह आपकी नई प्रोफ़ाइल का डेटा जनरेट कर सके. इसके बाद, इस डेटा का इस्तेमाल करके, अपने एक्ज़ीक्यूटेबल का फ़ाइनल वर्शन बनाया जाता है.

इसके बाद, QA टीम इस ऑप्टिमाइज़ किए गए शिपिंग बिल्ड की जांच कर सकती है, ताकि यह पक्का किया जा सके कि इसे रिलीज़ किया जा सकता है.