प्रोफ़ाइल के हिसाब से ऑप्टिमाइज़ेशन (पीजीओ), कॉम्पाइलर ऑप्टिमाइज़ेशन की एक जानी-मानी तकनीक है. PGO में, कंपाइलर किसी प्रोग्राम के रनटाइम प्रोफ़ाइल का इस्तेमाल करता है, ताकि इनलाइनिंग और कोड लेआउट के बारे में सबसे सही विकल्प चुने जा सकें. इससे परफ़ॉर्मेंस बेहतर होती है और कोड का साइज़ कम होता है.
यहां दिए गए तरीके का इस्तेमाल करके, पीजीओ को आपके ऐप्लिकेशन या लाइब्रेरी में डिप्लॉय किया जा सकता है: 1. किसी ऐसे वर्कलोड की पहचान करें जो आपके काम का हो. 2. प्रोफ़ाइलें इकट्ठा करना. 3. किसी रिलीज़ बिल्ड में प्रोफ़ाइलों का इस्तेमाल करना.
पहला चरण: प्रतिनिधि के तौर पर वर्कलोड की पहचान करना
सबसे पहले, अपने ऐप्लिकेशन के लिए कोई मान्य मानदंड या वर्कलोड तय करें. यह एक अहम चरण है, क्योंकि वर्कलोड से इकट्ठा की गई प्रोफ़ाइलों से, कोड में मौजूद हॉट और कोल्ड क्षेत्रों की पहचान की जाती है. प्रोफ़ाइलों का इस्तेमाल करते समय, कंपाइलर एग्रेसिव ऑप्टिमाइज़ेशन और गर्म इलाकों में इनलाइनिंग करेगा. कंपाइलर, परफ़ॉर्मेंस के दौरान ट्रेडिंग के दौरान कोल्ड रीजन के कोड का साइज़ कम भी कर सकता है.
सामान्य तौर पर परफ़ॉर्मेंस को ट्रैक करने के लिए, अच्छे वर्कलोड की पहचान करना भी फ़ायदेमंद होता है.
दूसरा चरण: प्रोफ़ाइलें इकट्ठा करना
प्रोफ़ाइल इकट्ठा करने के लिए, ये तीन चरण पूरे करने होते हैं: - इंस्ट्रूमेंटेशन की मदद से नेटिव कोड बनाना, - डिवाइस पर इंस्ट्रूमेंट किए गए ऐप्लिकेशन को चलाना और प्रोफ़ाइलें जनरेट करना, और - होस्ट पर प्रोफ़ाइलों को मर्ज करना/पोस्ट-प्रोसेस करना.
इंस्ट्रूमेंट किया गया बिल्ड बनाना
प्रोफ़ाइलें इकट्ठा करने के लिए, ऐप्लिकेशन के इंस्ट्रूमेंट किए गए बिल्ड पर, पहले चरण से वर्कलोड चलाया जाता है. इंस्ट्रूमेंट किया गया बिल्ड जनरेट करने के लिए, कंपाइलर और लिंकर फ़्लैग में -fprofile-generate
जोड़ें. इस फ़्लैग को किसी अलग बिल्ड वैरिएबल से कंट्रोल किया जाना चाहिए, क्योंकि डिफ़ॉल्ट बिल्ड के दौरान इस फ़्लैग की ज़रूरत नहीं होती.
प्रोफ़ाइल जनरेट करें
इसके बाद, डिवाइस पर इंस्ट्रुमेंटेड ऐप्लिकेशन चलाएं और प्रोफ़ाइल बनाएं.
जब इंस्ट्रुमेंटेड बाइनरी चलाया जाता है और बाहर निकलने पर
प्रोफ़ाइलों को मेमोरी में लिखा जाता है. हालांकि, atexit
के साथ रजिस्टर किए गए फ़ंक्शन, Android ऐप्लिकेशन में नहीं चलते — ऐप्लिकेशन बंद हो जाता है.
प्रोफ़ाइल फ़ाइल के लिए पाथ सेट करने के लिए, ऐप्लिकेशन/वर्कलोड को ज़्यादा काम करना पड़ता है. इसके बाद, प्रोफ़ाइल में डेटा लिखने की प्रोसेस को साफ़ तौर पर ट्रिगर करना पड़ता है.
- प्रोफ़ाइल फ़ाइल पाथ सेट करने के लिए,
__llvm_profile_set_filename(PROFILE_DIR "/default-%m.profraw
को कॉल करें.%m
तब काम आता है, जब शेयर की गई एक से ज़्यादा लाइब्रेरी हों. इस लाइब्रेरी के लिए,%m
एक यूनीक मॉड्यूल सिग्नेचर बन जाता है. इससे हर लाइब्रेरी के लिए, अलग प्रोफ़ाइल बन जाती है. पैटर्न की जानकारी देने वाले अन्य उपयोगी सुझावों के लिए यहां देखें.PROFILE_DIR
ऐसी डायरेक्ट्री है जिसे ऐप्लिकेशन से लिखा जा सकता है. रनटाइम के दौरान इस डायरेक्ट्री का पता लगाने के लिए, डेमो देखें. - प्रोफ़ाइल में डेटा लिखने की प्रोसेस को साफ़ तौर पर ट्रिगर करने के लिए,
__llvm_profile_write_file
फ़ंक्शन को कॉल करें.
extern "C" {
extern int __llvm_profile_set_filename(const char*);
extern int __llvm_profile_write_file(void);
}
#define PROFILE_DIR "<location-writable-from-app>"
void workload() {
// ...
// run workload
// ...
// set path and write profiles after workload execution
__llvm_profile_set_filename(PROFILE_DIR "/default-%m.profraw");
__llvm_profile_write_file();
return;
}
अहम जानकारी: अगर वर्कलोड एक स्टैंडअलोन बाइनरी है, तो प्रोफ़ाइल फ़ाइल जनरेट करना आसान होता है — बाइनरी को चलाने से पहले, LLVM_PROFILE_FILE
एनवायरमेंट वैरिएबल को %t/default-%m.profraw
पर सेट करें.
पोस्ट-प्रोसेस प्रोफ़ाइल
प्रोफ़ाइल फ़ाइलें .profraw फ़ॉर्मैट में होती हैं. सबसे पहले, उन्हें adb pull
का इस्तेमाल करके
डिवाइस से फ़ेच करना होगा. फ़ेच करने के बाद, एनडीके में llvm-profdata
सुविधा का इस्तेमाल करके .profraw
से .profdata
में बदलें. इसके बाद, इस टूल को कंपाइलर को भेजा जा सकता है.
$NDK/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-profdata \
merge --output=pgo_profile.profdata \
<list-of-profraw-files>
प्रोफ़ाइल फ़ाइल फ़ॉर्मैट के वर्शन के मेल न खाने से बचने के लिए, एक ही NDK रिलीज़ से llvm-profdata
और clang
का इस्तेमाल करें.
तीसरा चरण: ऐप्लिकेशन बनाने के लिए प्रोफ़ाइलों का इस्तेमाल करना
अपने ऐप्लिकेशन के रिलीज़ बिल्ड के दौरान, पिछले चरण की प्रोफ़ाइल का इस्तेमाल करें. इसके लिए, कंपाइलर और लिंकर को -fprofile-use=<>.profdata
पास करें. कोड में बदलाव होने के बाद भी प्रोफ़ाइलों का इस्तेमाल किया जा सकता है — Clang कंपाइलर, सोर्स और प्रोफ़ाइलों के बीच थोड़े अंतर को बर्दाश्त कर सकता है.
अहम जानकारी: आम तौर पर, ज़्यादातर लाइब्रेरी के लिए, प्रोफ़ाइलें सभी आर्किटेक्चर में एक जैसी होती हैं. उदाहरण के लिए, लाइब्रेरी के Arm64 बिल्ड से जनरेट की गई प्रोफ़ाइल का इस्तेमाल सभी आर्किटेक्चर के लिए किया जा सकता है. चेतावनी यह है कि अगर लाइब्रेरी में आर्किटेक्चर से जुड़े कोड पाथ (आर्म बनाम x86 या 32-बिट बनाम 64-बिट) हैं, तो ऐसे हर कॉन्फ़िगरेशन के लिए अलग-अलग प्रोफ़ाइल का इस्तेमाल किया जाना चाहिए.
यह रही पूरी जानकारी
https://github.com/DanAlbert/ndk-samples/tree/pgo/pgo पर, किसी ऐप्लिकेशन से PGO का इस्तेमाल करने के बारे में पूरी जानकारी दी गई है. इसमें ऐसी अतिरिक्त जानकारी भी दी गई है जो इस दस्तावेज़ में नहीं दी गई है.
- CMake के बने बाइल्ड नियमों से, CMake वैरिएबल को सेटअप करने का तरीका पता चलता है. यह वैरिएबल, इंस्ट्रूमेंटेशन की मदद से नेटिव कोड बनाता है. बिल्ड वैरिएबल सेट न होने पर, नेटिव कोड को पहले से जनरेट की गई पीजीओ प्रोफ़ाइलों का इस्तेमाल करके ऑप्टिमाइज़ किया जाता है.
- इंस्ट्रुमेंटेड बिल्ड में, pgodemo.cpp लिखता है कि प्रोफ़ाइल वर्कलोड एक्ज़ीक्यूशन हैं.
applicationContext.cacheDir.toString()
का इस्तेमाल करके, MainActivity.kt में रनटाइम के दौरान प्रोफ़ाइलों के लिए, जगह की जानकारी हासिल की जाती है.adb root
की ज़रूरत के बिना, डिवाइस से प्रोफ़ाइल बनाने के लिए, यहां दी गईadb
रेसिपी का इस्तेमाल करें.