Android 8.0 में, Android रनटाइम (एआरटी) को काफ़ी बेहतर बनाया गया है. यहां दी गई सूची में, डिवाइस बनाने वाली कंपनियों को ART में मिलने वाले बेहतर अनुभव के बारे में खास जानकारी दी गई है.
कॉन्करेंट कंपैक्टिंग गारबेज कलेक्टर
Google I/O में बताए गए अपडेट के मुताबिक, Android 8.0 में ART में एक नया कॉन्करेंट कंपैक्टिंग गार्बेज कलेक्टर (GC) शामिल किया गया है. यह कलेक्टर, हर बार GC के चलने पर हीप को कंपैक्ट करता है. साथ ही, ऐप्लिकेशन के चलने के दौरान, थ्रेड रूट को प्रोसेस करने के लिए सिर्फ़ एक बार कुछ समय के लिए रुकता है. इसके फ़ायदे यहां दिए गए हैं:
- GC हमेशा हीप को कंपैक्ट करता है: Android 7.0 की तुलना में, हीप का साइज़ औसतन 32% कम होता है.
- कंपैक्शन की मदद से, थ्रेड लोकल बंप पॉइंटर ऑब्जेक्ट को असाइन किया जा सकता है: असाइनमेंट, Android 7.0 की तुलना में 70% तेज़ी से होते हैं.
- Android 7.0 GC की तुलना में, H2 बेंचमार्क के लिए 85% कम समय में पॉज़ करने की सुविधा देता है.
- अब पॉज़ होने का समय, हीप साइज़ के हिसाब से नहीं बढ़ता है. ऐप्लिकेशन को बड़े हीप का इस्तेमाल करने में कोई समस्या नहीं होनी चाहिए.
- जीसी लागू करने से जुड़ी जानकारी - पढ़ने में आने वाली समस्याएं:
- रीड बैरियर, हर ऑब्जेक्ट फ़ील्ड को पढ़ने के लिए किया गया छोटा-सा काम है.
- इन्हें कंपाइलर में ऑप्टिमाइज़ किया जाता है. हालांकि, इससे कुछ मामलों में परफ़ॉर्मेंस धीमी हो सकती है.
लूप ऑप्टिमाइज़ेशन
Android 8.0 में ART, लूप को ऑप्टिमाइज़ करने के लिए कई तरह की तकनीकों का इस्तेमाल करता है:
- बाउंड्री की जांच करने की ज़रूरत नहीं
- स्टैटिक: कंपाइल-टाइम पर यह साबित हो जाता है कि रेंज, सीमाओं के अंदर हैं
- डाइनैमिक: रन-टाइम टेस्ट यह पक्का करते हैं कि लूप तय सीमाओं के अंदर रहें. ऐसा न होने पर, डीऑप्ट किया जाता है
- इंडक्शन वैरिएबल एलिमिनेशन
- डेड इंडक्शन को हटाना
- इंडक्शन को बंद फ़ॉर्म वाले एक्सप्रेशन से बदलें. इंडक्शन का इस्तेमाल सिर्फ़ लूप के बाद किया जाता है
- लूप-बॉडी में मौजूद डेड कोड को हटाना और ऐसे पूरे लूप को हटाना जो डेड हो जाते हैं
- मज़बूती में कमी
- लूप ट्रांसफ़ॉर्मेशन: रिवर्सल, इंटरचेंजिंग, स्प्लिटिंग, अनरोलिंग, यूनिमॉडुलर वगैरह.
- एसआईएमडी (इसे वेक्टराइज़ेशन भी कहा जाता है)
लूप ऑप्टिमाइज़र, ART कंपाइलर में अपने ऑप्टिमाइज़ेशन पास में मौजूद होता है. लूप ऑप्टिमाइज़ेशन से जुड़ी ज़्यादातर सुविधाएं, अन्य ऑप्टिमाइज़ेशन और आसान बनाने वाली सुविधाओं से मिलती-जुलती हैं. कुछ ऑप्टिमाइज़ेशन में समस्याएं आती हैं. ये ऑप्टिमाइज़ेशन, CFG को सामान्य से ज़्यादा विस्तार से फिर से लिखते हैं. ऐसा इसलिए होता है, क्योंकि ज़्यादातर CFG यूटिलिटी (nodes.h देखें) CFG बनाने पर फ़ोकस करती हैं, न कि उसे फिर से लिखने पर.
क्लास के लेआउट का विश्लेषण
Android 8.0 में ART, क्लास हैरारकी विश्लेषण (सीएचए) का इस्तेमाल करता है. यह कंपाइलर ऑप्टिमाइज़ेशन है. यह क्लास हैरारकी का विश्लेषण करके जनरेट की गई जानकारी के आधार पर, वर्चुअल कॉल को डायरेक्ट कॉल में बदलता है. वर्चुअल कॉल महंगे होते हैं, क्योंकि इन्हें वीटेबल लुकअप के आस-पास लागू किया जाता है. साथ ही, इनमें कुछ डिपेंडेंट लोड लगते हैं. वर्चुअल कॉल को भी इनलाइन नहीं किया जा सकता.
यहां इससे जुड़े सुधारों की खास जानकारी दी गई है:
- डाइनैमिक सिंगल-इंप्लीमेंटेशन के तरीके की स्थिति अपडेट करना - क्लास लिंक करने के समय के आखिर में, जब vtable भर जाता है, तो ART, सुपर क्लास के vtable से एंट्री-बाय-एंट्री तुलना करता है.
- कंपाइलर ऑप्टिमाइज़ेशन - कंपाइलर, किसी तरीके को लागू करने की एक ही जानकारी का फ़ायदा उठाएगा. अगर किसी A.foo तरीके में सिंगल-इंप्लीमेंटेशन फ़्लैग सेट है, तो कंपाइलर वर्चुअल कॉल को डायरेक्ट कॉल में बदल देगा. इसके बाद, वह डायरेक्ट कॉल को इनलाइन करने की कोशिश करेगा.
- कंपाइल किए गए कोड को अमान्य करना - क्लास लिंक करने के समय के आखिर में भी ऐसा होता है. जब सिंगल-इंप्लीमेंटेशन की जानकारी अपडेट की जाती है, तब अगर पहले सिंगल-इंप्लीमेंटेशन वाली A.foo मेथड की स्थिति अब अमान्य हो गई है, तो कंपाइल किए गए ऐसे सभी कोड को अमान्य करना होगा जो इस अनुमान पर निर्भर करते हैं कि A.foo मेथड में सिंगल-इंप्लीमेंटेशन है.
- डीऑप्टिमाइज़ेशन - स्टैक पर मौजूद लाइव कंपाइल किए गए कोड के लिए, डीऑप्टिमाइज़ेशन शुरू किया जाएगा. इससे, अमान्य कंपाइल किए गए कोड को इंटरप्रेटर मोड में ले जाया जा सकेगा, ताकि यह पक्का किया जा सके कि कोड सही है. डीऑप्टिमाइज़ेशन के लिए एक नई प्रोसेस का इस्तेमाल किया जाएगा. यह प्रोसेस, सिंक्रोनस और असिंक्रोनस डीऑप्टिमाइज़ेशन का हाइब्रिड है.
.oat फ़ाइलों में इनलाइन कैश मेमोरी
ART अब इनलाइन कैश मेमोरी का इस्तेमाल करता है. साथ ही, उन कॉल साइटों को ऑप्टिमाइज़ करता है जिनके लिए काफ़ी डेटा मौजूद है. इनलाइन कैश मेमोरी की सुविधा, रनटाइम की अतिरिक्त जानकारी को प्रोफ़ाइलों में रिकॉर्ड करती है. साथ ही, इसका इस्तेमाल, कंपाइल करने से पहले डाइनैमिक ऑप्टिमाइज़ेशन जोड़ने के लिए करती है.
Dexlayout
Dexlayout, Android 8.0 में पेश की गई एक लाइब्रेरी है. इसका इस्तेमाल, dex फ़ाइलों का विश्लेषण करने और उन्हें प्रोफ़ाइल के हिसाब से फिर से क्रम में लगाने के लिए किया जाता है. Dexlayout का मकसद, डिवाइस पर आइडल मेंटेनेंस कंपाइलेशन के दौरान, dex फ़ाइल के सेक्शन का क्रम बदलने के लिए, रनटाइम प्रोफ़ाइलिंग की जानकारी का इस्तेमाल करना है. डेक्स फ़ाइल के उन हिस्सों को एक साथ ग्रुप किया जाता है जिन्हें अक्सर एक साथ ऐक्सेस किया जाता है. इससे प्रोग्राम को बेहतर मेमोरी ऐक्सेस पैटर्न मिलते हैं. साथ ही, इससे रैम की बचत होती है और ऐप्लिकेशन को शुरू होने में कम समय लगता है.
फ़िलहाल, प्रोफ़ाइल की जानकारी सिर्फ़ ऐप्लिकेशन चलाने के बाद उपलब्ध होती है. इसलिए, dexlayout को dex2oat के ऑन-डिवाइस कंपाइलेशन में इंटिग्रेट किया जाता है. यह कंपाइलेशन, डिवाइस के इस्तेमाल में न होने पर रखरखाव के दौरान होता है.
Dex कैश मेमोरी हटाना
Android 7.0 तक, DexCache ऑब्जेक्ट के पास चार बड़े ऐरे होते थे. ये ऐरे, DexFile में मौजूद कुछ एलिमेंट की संख्या के हिसाब से होते थे. जैसे:
- स्ट्रिंग (हर DexFile::StringId के लिए एक रेफ़रंस),
- टाइप (हर DexFile::TypeId के लिए एक रेफ़रंस),
- तरीके (हर DexFile::MethodId के लिए एक नेटिव पॉइंटर),
- फ़ील्ड (हर DexFile::FieldId के लिए एक नेटिव पॉइंटर).
इन ऐरे का इस्तेमाल, उन ऑब्जेक्ट को तुरंत वापस पाने के लिए किया जाता था जिन्हें हमने पहले ठीक कर दिया था. Android 8.0 में, methods ऐरे को छोड़कर सभी ऐरे हटा दिए गए हैं.
अनुवादक की परफ़ॉर्मेंस
Android 7.0 रिलीज़ में इंटरप्रेटर की परफ़ॉर्मेंस को बेहतर बनाया गया है. इसके लिए, "mterp" को पेश किया गया है. यह एक इंटरप्रेटर है, जिसमें असेंबली लैंग्वेज में लिखा गया फ़ेच/डिकोड/इंटरप्रेट करने का मुख्य तरीका शामिल है. Mterp को फ़ास्ट डेलविक इंटरप्रेटर के हिसाब से बनाया गया है. यह arm, arm64, x86, x86_64, mips, और mips64 के साथ काम करता है. कम्प्यूटेशनल कोड के लिए, Art का mterp, Dalvik के फ़ास्ट इंटरप्रेटर के बराबर है. हालांकि, कुछ मामलों में यह बहुत ज़्यादा और यहां तक कि काफ़ी हद तक धीमा हो सकता है:
- परफ़ॉर्मेंस को बेहतर बनाएं.
- स्ट्रिंग में बदलाव करने और Dalvik में इंट्रिंसिक के तौर पर पहचाने जाने वाले तरीकों का इस्तेमाल करने वाले अन्य उपयोगकर्ता.
- स्टैक मेमोरी का ज़्यादा इस्तेमाल.
Android 8.0 में इन समस्याओं को ठीक कर दिया गया है.
ज़्यादा इनलाइनिंग
Android 6.0 के बाद से, ART एक ही dex फ़ाइल में मौजूद किसी भी कॉल को इनलाइन कर सकता है. हालांकि, यह अलग-अलग dex फ़ाइलों से सिर्फ़ लीफ़ मेथड को इनलाइन कर सकता है. इस सीमा की दो वजहें थीं:
- किसी दूसरी DEX फ़ाइल से इनलाइन करने के लिए, उस DEX फ़ाइल की DEX कैश मेमोरी का इस्तेमाल करना ज़रूरी होता है. हालांकि, एक ही DEX फ़ाइल से इनलाइन करने के लिए, कॉलर की DEX कैश मेमोरी का दोबारा इस्तेमाल किया जा सकता है. संकलित कोड में, कुछ निर्देशों के लिए डेक्स कैश मेमोरी की ज़रूरत होती है. जैसे, स्टैटिक कॉल, स्ट्रिंग लोड या क्लास लोड.
- स्टैक मैप, सिर्फ़ मौजूदा DEX फ़ाइल में किसी तरीके के इंडेक्स को एन्कोड कर रहे हैं.
इन सीमाओं को ठीक करने के लिए, Android 8.0 में ये बदलाव किए गए हैं:
- यह कंपाइल किए गए कोड से डेक्स कैश ऐक्सेस करने की सुविधा हटाता है. "डेक्स कैश हटाने की सुविधा" सेक्शन भी देखें
- यह स्टैक किए गए मैप के एन्कोडिंग को बढ़ाता है.
सिंक करने की सुविधा में सुधार
एआरटी टीम ने MonitorEnter/MonitorExit कोड पाथ को ट्यून किया है. साथ ही, ARMv8 पर पारंपरिक मेमोरी बैरियर पर हमारी निर्भरता को कम किया है. इसके बजाय, जहां भी संभव हो, उन्हें नए (acquire/release) निर्देशों से बदल दिया है.
ज़्यादा तेज़ नेटिव तरीके
@FastNative और @CriticalNative एनोटेशन का इस्तेमाल करके, Java नेटिव इंटरफ़ेस (जेएनआई) को नेटिव कॉल ज़्यादा तेज़ी से किए जा सकते हैं. ART रनटाइम में पहले से मौजूद ऑप्टिमाइज़ेशन की वजह से, JNI ट्रांज़िशन की प्रोसेस तेज़ हो जाती है. साथ ही, अब इस्तेमाल नहीं किए जा रहे !bang JNI नोटेशन की जगह इनका इस्तेमाल किया जाता है. एनोटेशन का असर, नॉन-नेटिव तरीकों पर नहीं पड़ता. साथ ही, ये सिर्फ़ bootclasspath पर प्लैटफ़ॉर्म Java लैंग्वेज कोड के लिए उपलब्ध होते हैं (Play Store पर कोई अपडेट नहीं).
@FastNative एनोटेशन, नॉन-स्टैटिक तरीकों के साथ काम करता है. अगर कोई तरीका, पैरामीटर या रिटर्न वैल्यू के तौर पर jobject को ऐक्सेस करता है, तो इस एनोटेशन का इस्तेमाल करें.
@CriticalNative एनोटेशन, नेटिव तरीकों को और भी तेज़ी से चलाने का तरीका उपलब्ध कराता है. हालांकि, इस पर ये पाबंदियां लागू होती हैं:
-
तरीके स्टैटिक होने चाहिए. पैरामीटर, रिटर्न वैल्यू या
thisके लिए कोई ऑब्जेक्ट नहीं होना चाहिए. - नेटिव तरीके को सिर्फ़ प्रिमिटिव टाइप पास किए जाते हैं.
-
नेटिव तरीके में, फ़ंक्शन की परिभाषा में
JNIEnvऔरjclassपैरामीटर का इस्तेमाल नहीं किया जाता. -
इस तरीके को डाइनैमिक जेएनआई लिंकिंग पर भरोसा करने के बजाय,
RegisterNativesके साथ रजिस्टर किया जाना चाहिए.
@FastNative से नेटिव तरीके की परफ़ॉर्मेंस तीन गुना तक बेहतर हो सकती है. वहीं, @CriticalNative से पांच गुना तक बेहतर हो सकती है. उदाहरण के लिए, Nexus 6P डिवाइस पर मेज़र किया गया JNI ट्रांज़िशन:
| Java नेटिव इंटरफ़ेस (JNI) इनवोकेशन | एक्ज़ीक्यूशन में लगने वाला समय (नैनोसेकंड में) |
|---|---|
| रेगुलर JNI | 115 |
| !bang JNI | 60 |
@FastNative |
35 |
@CriticalNative |
25 |