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

Android 13 और इससे पहले के वर्शन के लिए Android बिल्ड सिस्टम, नेटिव Android मॉड्यूल पर Clang के प्रोफ़ाइल के हिसाब से ऑप्टिमाइज़ेशन (PGO) का इस्तेमाल करता है. हालांकि, इसके लिए ज़रूरी है कि मॉड्यूल में ब्लूप्रिंट बिल्ड नियम हों. इस पेज पर, Clang PGO के बारे में बताया गया है. साथ ही, PGO के लिए इस्तेमाल की जाने वाली प्रोफ़ाइलों को लगातार जनरेट और अपडेट करने का तरीका बताया गया है. इसके अलावा, इस्तेमाल के उदाहरण के साथ, PGO को बिल्ड सिस्टम के साथ इंटिग्रेट करने का तरीका भी बताया गया है.

अहम जानकारी: इस दस्तावेज़ में, Android प्लैटफ़ॉर्म में PGO के इस्तेमाल के बारे में बताया गया है. Android ऐप्लिकेशन से PGO का इस्तेमाल करने के बारे में जानने के लिए, इस पेज पर जाएं.

Clang PGO के बारे में जानकारी

Clang, दो तरह की प्रोफ़ाइलों का इस्तेमाल करके, प्रोफ़ाइल के हिसाब से ऑप्टिमाइज़ेशन कर सकता है:

  • इंस्ट्रूमेंटेशन पर आधारित प्रोफ़ाइलें, इंस्ट्रूमेंट किए गए टारगेट प्रोग्राम से जनरेट होती हैं. इन प्रोफ़ाइलों में ज़्यादा जानकारी होती है और ये प्रोफ़ाइलें, प्रोग्राम के रनटाइम पर ज़्यादा बोझ डालती हैं.
  • आम तौर पर, सैंपलिंग पर आधारित प्रोफ़ाइलें, सैंपलिंग हार्डवेयर काउंटर से जनरेट होती हैं. इनसे कम रनटाइम ओवरहेड होता है. साथ ही, इन्हें बिना किसी इंस्ट्रूमेंटेशन या बाइनरी में बदलाव किए इकट्ठा किया जा सकता है. इनमें, इंस्ट्रूमेंटेशन पर आधारित प्रोफ़ाइलों के मुकाबले कम जानकारी होती है.

सभी प्रोफ़ाइलें, ऐसे प्रतिनिधि वर्कलोड से जनरेट की जानी चाहिए जो ऐप्लिकेशन के सामान्य व्यवहार को दिखाता हो. Clang, AST-आधारित (-fprofile-instr-generate) और LLVM IR-आधारित (-fprofile-generate)) दोनों के साथ काम करता है. हालांकि, Android, इंस्ट्रूमेंटेशन-आधारित PGO के लिए सिर्फ़ LLVM IR-आधारित प्रोफ़ाइल के साथ काम करता है.

प्रोफ़ाइल कलेक्शन के लिए, इन फ़्लैग की ज़रूरत होती है:

  • -fprofile-generate के लिए, आईआर पर आधारित इंस्ट्रुमेंटेशन. इस विकल्प का इस्तेमाल करके, बैकएंड, इंस्ट्रुमेंटेशन पॉइंट की संख्या कम करने के लिए, कम से कम स्पैनिंग ट्री के तरीके का इस्तेमाल करता है. साथ ही, कम वज़न वाले किनारों के लिए, उनके प्लेसमेंट को ऑप्टिमाइज़ करता है. लिंक करने के चरण के लिए भी इस विकल्प का इस्तेमाल करें. Clang ड्राइवर, प्रोफ़ाइलिंग रनटाइम (libclang_rt.profile-arch-android.a) को लिंकर को अपने-आप पास कर देता है. इस लाइब्रेरी में, प्रोग्राम के बंद होने पर प्रोफ़ाइलों को डिस्क पर लिखने के लिए रूटीन होते हैं.
  • -gline-tables-only, सैंपलिंग के आधार पर प्रोफ़ाइल इकट्ठा करने के लिए, कम से कम डीबग जानकारी जनरेट करने के लिए.

किसी प्रोफ़ाइल का इस्तेमाल, -fprofile-use=pathname या -fprofile-sample-use=pathname का इस्तेमाल करके, पीजीओ के लिए किया जा सकता है. इनका इस्तेमाल, इंस्ट्रूमेंटेशन पर आधारित और सैंपलिंग पर आधारित प्रोफ़ाइलों के लिए किया जाता है.

ध्यान दें: कोड में बदलाव किए जाने पर, अगर Clang अब प्रोफ़ाइल डेटा का इस्तेमाल नहीं कर सकता, तो वह -Wprofile-instr-out-of-date चेतावनी जनरेट करता है.

PGO का इस्तेमाल करना

PGO का इस्तेमाल करने के लिए, यह तरीका अपनाएं:

  1. कंपाइलर और लिंकर को -fprofile-generate भेजकर, लाइब्रेरी/एक्सीक्यूटेबल को इंस्ट्रुमेंटेशन के साथ बनाएं.
  2. इंस्ट्रूमेंट किए गए बाइनरी पर, काम के वर्कलोड को चलाकर प्रोफ़ाइलें इकट्ठा करें.
  3. llvm-profdata यूटिलिटी का इस्तेमाल करके, प्रोफ़ाइलों को पोस्ट-प्रोसेस करें (ज़्यादा जानकारी के लिए, LLVM प्रोफ़ाइल फ़ाइलों को मैनेज करना देखें).
  4. PGO लागू करने के लिए प्रोफ़ाइलों का इस्तेमाल करें. इसके लिए, कंपाइलर और लिंकर को -fprofile-use=<>.profdata पास करें.

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

प्रोफ़ाइलें इकट्ठा करना

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

  1. किसी मानदंड और उस मानदंड के साथ काम करने वाली लाइब्रेरी के सेट की पहचान करना.
  2. बेंचमार्क और लाइब्रेरी में pgo प्रॉपर्टी जोड़ें (जानकारी यहां दी गई है).
  3. इन लाइब्रेरी की इंस्ट्रुमेंट की गई कॉपी के साथ Android बिल्ड बनाएं. इसके लिए, इनका इस्तेमाल करें:
    make ANDROID_PGO_INSTRUMENT=benchmark

benchmark एक प्लेसहोल्डर है, जो बिल्ड के दौरान इंस्ट्रूमेंट की गई लाइब्रेरी के कलेक्शन की पहचान करता है. असल प्रतिनिधि इनपुट (और शायद कोई ऐसा दूसरा एक्सीक्यूटेबल जो किसी ऐसी लाइब्रेरी से लिंक करता है जिसे जांचा जा रहा है) खास तौर पर PGO के लिए नहीं होते और ये इस दस्तावेज़ के दायरे से बाहर होते हैं.

  1. किसी डिवाइस पर, इंस्ट्रूमेंट किए गए बिल्ड को फ़्लैश या सिंक करें.
  2. प्रोफ़ाइलें इकट्ठा करने के लिए, बेंचमार्क चलाएं.
  3. प्रोफ़ाइलों को पोस्ट-प्रोसेस करने और उन्हें सोर्स ट्री में डालने के लिए, llvm-profdata टूल (इसके बारे में नीचे बताया गया है) का इस्तेमाल करें.

बिल्ड के दौरान प्रोफ़ाइलों का इस्तेमाल करना

Android ट्री में toolchain/pgo-profiles में प्रोफ़ाइलें देखें. यह नाम, लाइब्रेरी के लिए pgo प्रॉपर्टी की profile_file सब-प्रॉपर्टी में बताए गए नाम से मेल खाना चाहिए. लाइब्रेरी बनाते समय, बिल्ड सिस्टम प्रोफ़ाइल फ़ाइल को Clang के पास अपने-आप भेज देता है. ANDROID_PGO_DISABLE_PROFILE_USE एनवायरमेंट वैरिएबल को true पर सेट किया जा सकता है, ताकि PGO को कुछ समय के लिए बंद किया जा सके और उसकी परफ़ॉर्मेंस से मिलने वाले फ़ायदे का आकलन किया जा सके.

प्रॉडक्ट के हिसाब से बनाई गई अन्य प्रोफ़ाइल डायरेक्ट्री की जानकारी देने के लिए, उन्हें PGO_ADDITIONAL_PROFILE_DIRECTORIES make वैरिएबल में जोड़ें BoardConfig.mk. अगर अन्य पाथ तय किए जाते हैं, तो इन पाथ में मौजूद प्रोफ़ाइलें toolchain/pgo-profiles में मौजूद प्रोफ़ाइलों की जगह ले लेती हैं.

dist से make टारगेट का इस्तेमाल करके रिलीज़ इमेज जनरेट करते समय, बिल्ड सिस्टम उन प्रोफ़ाइल फ़ाइलों के नाम $DIST_DIR/pgo_profile_file_missing.txt में लिखता है जो मौजूद नहीं हैं. इस फ़ाइल की मदद से, यह देखा जा सकता है कि गलती से कौनसी प्रोफ़ाइल फ़ाइलें हटाई गई हैं. इन फ़ाइलों की वजह से, PGO की सुविधा अपने-आप बंद हो जाती है.

Android.bp फ़ाइलों में PGO को चालू करना

नेटिव मॉड्यूल के लिए Android.bp फ़ाइलों में PGO को चालू करने के लिए, pgo प्रॉपर्टी की जानकारी दें. इस प्रॉपर्टी में ये सब-प्रॉपर्टी हैं:

प्रॉपर्टी जानकारी
instrumentation इंस्ट्रूमेंटेशन का इस्तेमाल करके PGO के लिए, इसकी वैल्यू को true पर सेट करें. डिफ़ॉल्ट वैल्यू false है.
sampling सैंपलिंग का इस्तेमाल करके PGO के लिए, true पर सेट करें. डिफ़ॉल्ट वैल्यू false है.
benchmarks स्ट्रिंग की सूची. अगर सूची में मौजूद किसी भी मानदंड को ANDROID_PGO_INSTRUMENT बिल्ड विकल्प में बताया गया है, तो यह मॉड्यूल प्रोफ़ाइलिंग के लिए बनाया गया है.
profile_file PGO के साथ इस्तेमाल करने के लिए, toolchain/pgo-profile से जुड़ी प्रोफ़ाइल फ़ाइल. अगर enable_profile_use प्रॉपर्टी को false पर सेट नहीं किया गया है या ANDROID_PGO_NO_PROFILE_USE बिल्ड वैरिएबल को true पर सेट नहीं किया गया है, तो बिल्ड इस फ़ाइल को $DIST_DIR/pgo_profile_file_missing.txt में जोड़कर चेतावनी देता है कि यह फ़ाइल मौजूद नहीं है.
enable_profile_use अगर आपको बिल्ड के दौरान प्रोफ़ाइलों का इस्तेमाल नहीं करना है, तो इसे false पर सेट करें. इसका इस्तेमाल, प्रोफ़ाइल इकट्ठा करने की सुविधा चालू करने या PGO को कुछ समय के लिए बंद करने के लिए, बूटस्ट्रैप के दौरान किया जा सकता है. डिफ़ॉल्ट वैल्यू true है.
cflags इंस्ट्रूमेंट किए गए बिल्ड के दौरान इस्तेमाल करने के लिए अतिरिक्त फ़्लैग की सूची.

PGO वाले मॉड्यूल का उदाहरण:

cc_library {
    name: "libexample",
    srcs: [
        "src1.cpp",
        "src2.cpp",
    ],
    static: [
        "libstatic1",
        "libstatic2",
    ],
    shared: [
        "libshared1",
    ]
    pgo: {
        instrumentation: true,
        benchmarks: [
            "benchmark1",
            "benchmark2",
        ],
        profile_file: "example.profdata",
    }
}

अगर मानदंड benchmark1 और benchmark2, लाइब्रेरी libstatic1, libstatic2 या libshared1 के लिए सही तरीके से काम करते हैं, तो इन लाइब्रेरी की pgo प्रॉपर्टी में भी मानदंड शामिल किए जा सकते हैं. Android.bp में मौजूद defaults मॉड्यूल में, लाइब्रेरी के किसी सेट के लिए एक सामान्य pgo स्पेसिफ़िकेशन शामिल किया जा सकता है. इससे कई मॉड्यूल के लिए, एक ही बिल्ड नियम दोहराने से बचा जा सकता है.

अलग-अलग प्रोफ़ाइल फ़ाइलें चुनने या किसी आर्किटेक्चर के लिए PGO को चुनिंदा तौर पर बंद करने के लिए, हर आर्किटेक्चर के लिए profile_file, enable_profile_use, और cflags प्रॉपर्टी की जानकारी दें. उदाहरण (बोल्ड में आर्किटेक्चर टारगेट के साथ):

cc_library {
    name: "libexample",
    srcs: [
          "src1.cpp",
          "src2.cpp",
    ],
    static: [
          "libstatic1",
          "libstatic2",
    ],
    shared: [
          "libshared1",
    ],
    pgo: {
         instrumentation: true,
         benchmarks: [
              "benchmark1",
              "benchmark2",
         ],
    }

    target: {
         android_arm: {
              pgo: {
                   profile_file: "example_arm.profdata",
              }
         },
         android_arm64: {
              pgo: {
                   profile_file: "example_arm64.profdata",
              }
         }
    }
}

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

LLVM प्रोफ़ाइल फ़ाइलों को मैनेज करना

इंस्ट्रूमेंट की गई लाइब्रेरी या प्रोग्राम को चलाने पर, /data/local/tmp में default_unique_id_0.profraw नाम की प्रोफ़ाइल फ़ाइल बनती है. यहां unique_id एक अंकों वाला हैश है, जो इस लाइब्रेरी के लिए यूनीक होता है. अगर यह फ़ाइल पहले से मौजूद है, तो प्रोफ़ाइल लिखते समय प्रोफ़ाइलिंग रनटाइम, नई प्रोफ़ाइल को पुरानी प्रोफ़ाइल के साथ मर्ज कर देता है. ध्यान दें कि /data/local/tmp को ऐप्लिकेशन डेवलपर ऐक्सेस नहीं कर सकते. इसके बजाय, उन्हें /storage/emulated/0/Android/data/packagename/files जैसी किसी जगह का इस्तेमाल करना चाहिए. प्रोफ़ाइल फ़ाइल की जगह बदलने के लिए, रनटाइम पर LLVM_PROFILE_FILE एनवायरमेंट वैरिएबल सेट करें.

इसके बाद, llvm-profdata यूटिलिटी का इस्तेमाल करके, .profraw फ़ाइल को .profdata फ़ाइल में बदला जाता है. साथ ही, एक से ज़्यादा .profraw फ़ाइलों को मर्ज भी किया जा सकता है:

  llvm-profdata merge -output=profile.profdata <.profraw and/or .profdata files>

इसके बाद, profile.profdata को सोर्स ट्री में चेक इन किया जा सकता है, ताकि उसे बिल्ड के दौरान इस्तेमाल किया जा सके.

अगर किसी बेंचमार्क के दौरान, कई इंस्ट्रूमेंट की गई बाइनरी/लाइब्रेरी लोड की जाती हैं, तो हर लाइब्रेरी, एक अलग यूनीक आईडी के साथ एक अलग .profraw फ़ाइल जनरेट करती है. आम तौर पर, इन सभी फ़ाइलों को एक .profdata फ़ाइल में मर्ज किया जा सकता है और PGO बिल्ड के लिए इस्तेमाल किया जा सकता है. जिन मामलों में किसी लाइब्रेरी को किसी दूसरे मानदंड के हिसाब से जांचा जाता है उनमें उस लाइब्रेरी को दोनों मानदंडों की प्रोफ़ाइलों का इस्तेमाल करके ऑप्टिमाइज़ किया जाना चाहिए. इस स्थिति में, llvm-profdata का show विकल्प काम का है:

  llvm-profdata merge -output=default_unique_id.profdata default_unique_id_0.profraw
llvm-profdata show -all-functions default_unique_id.profdata

unique_id को अलग-अलग लाइब्रेरी के साथ मैप करने के लिए, हर unique_id के लिए show आउटपुट में, लाइब्रेरी के हिसाब से यूनीक फ़ंक्शन का नाम खोजें.

केस स्टडी: ART के लिए PGO

इस केस स्टडी में, ART को एक उदाहरण के तौर पर दिखाया गया है. हालांकि, इसमें ART के लिए प्रोफ़ाइल की गई लाइब्रेरी के असल सेट या उनकी इंटरडिपेंडेंसी के बारे में सटीक जानकारी नहीं दी गई है.

ART में dex2oat ऐडवांस-ऑफ़-टाइम कंपाइलर, libart-compiler.so पर निर्भर करता है. यह libart.so पर भी निर्भर करता है. ART रनटाइम मुख्य रूप से libart.so में लागू किया जाता है. कंपाइलर और रनटाइम के लिए, मानदंड अलग-अलग होंगे:

Benchmark प्रोफ़ाइल की गई लाइब्रेरी
dex2oat dex2oat (एक्सीक्यूटेबल), libart-compiler.so, libart.so
art_runtime libart.so
  1. dex2oat, libart-compiler.so में यह pgo प्रॉपर्टी जोड़ें:
        pgo: {
            instrumentation: true,
            benchmarks: ["dex2oat",],
            profile_file: "dex2oat.profdata",
        }
  2. libart.so में यह pgo प्रॉपर्टी जोड़ें:
        pgo: {
            instrumentation: true,
            benchmarks: ["art_runtime", "dex2oat",],
            profile_file: "libart.profdata",
        }
  3. dex2oat और art_runtime मानदंडों के लिए, इंस्ट्रुमेंट किए गए बिल्ड बनाएं. इसके लिए, इनका इस्तेमाल करें:
        make ANDROID_PGO_INSTRUMENT=dex2oat
        make ANDROID_PGO_INSTRUMENT=art_runtime
  4. इसके अलावा, सभी लाइब्रेरी के साथ एक इंस्ट्रुमेंट किया गया बिल्ड बनाएं, जिनमें इनका इस्तेमाल किया गया है:

        make ANDROID_PGO_INSTRUMENT=dex2oat,art_runtime
        (or)
        make ANDROID_PGO_INSTRUMENT=ALL

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

  5. dex2oat और art_runtime का इस्तेमाल करके, बेंचमार्क चलाएं. इससे आपको ये चीज़ें मिलती हैं:
  6. dex2oat और libart-compiler.so के लिए एक सामान्य profdata फ़ाइल बनाएं. इसके लिए, इनका इस्तेमाल करें:
    llvm-profdata merge -output=dex2oat.profdata \
        dex2oat_exe.profdata dex2oat_libart-compiler.profdata
  7. दो मानदंडों की प्रोफ़ाइलों को मर्ज करके, libart.so की प्रोफ़ाइल पाएं:
    llvm-profdata merge -output=libart.profdata \
        dex2oat_libart.profdata art_runtime_libart.profdata

    दोनों प्रोफ़ाइलों में libart.so की रॉ गिनती अलग-अलग हो सकती है, क्योंकि जांच के मामलों की संख्या और जांच के लिए लगने वाले समय में, दोनों मानदंडों में अंतर होता है. इस मामले में, वेटेड मर्ज का इस्तेमाल किया जा सकता है:

    llvm-profdata merge -output=libart.profdata \
        -weighted-input=2,dex2oat_libart.profdata \
        -weighted-input=1,art_runtime_libart.profdata

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

  8. प्रोफ़ाइल फ़ाइलों dex2oat.profdata और libart.profdata को toolchain/pgo-profiles में बदलें, ताकि इन्हें बिल्ड के दौरान इस्तेमाल किया जा सके.