زمان اجرای اندروید (ART) در نسخه اندروید ۸.۰ به طور قابل توجهی بهبود یافته است. لیست زیر خلاصهای از پیشرفتهایی است که تولیدکنندگان دستگاه میتوانند در ART انتظار داشته باشند.
جمع کننده زباله فشرده سازی همزمان
همانطور که در کنفرانس Google I/O اعلام شد، ART در اندروید ۸.۰ از یک جمعآوریکنندهی زبالهی فشردهسازی همزمان (GC) جدید بهره میبرد. این جمعآوریکننده هر بار که GC اجرا میشود و در حین اجرای برنامه، هیپ را فشرده میکند و تنها یک مکث کوتاه برای پردازش ریشههای نخها دارد. مزایای آن به شرح زیر است:
- GC همیشه هیپ را فشرده میکند: به طور متوسط ۳۲٪ اندازه هیپ در مقایسه با اندروید ۷.۰ کوچکتر شده است.
- فشردهسازی، تخصیص شیء اشارهگر برآمدگی محلی نخ را امکانپذیر میکند: تخصیصها 70٪ سریعتر از اندروید 7.0 هستند.
- در مقایسه با اندروید ۷.۰ GC، زمان مکث برای بنچمارک H2، ۸۵٪ کمتر است.
- زمانهای مکث دیگر با اندازه هیپ مقیاسبندی نمیشوند؛ برنامهها باید بتوانند از هیپهای بزرگ بدون نگرانی در مورد کندی استفاده کنند.
- جزئیات پیادهسازی GC - موانع خواندن:
- موانع خواندن، مقدار کمی کار هستند که برای هر بار خواندن فیلد شیء انجام میشوند.
- اینها در کامپایلر بهینه شدهاند، اما ممکن است در برخی موارد استفاده، سرعت را کاهش دهند.
بهینهسازی حلقه
طیف گستردهای از بهینهسازیهای حلقه توسط ART در نسخه اندروید ۸.۰ به کار گرفته شده است:
- حذفهای بررسی محدودهها
- استاتیک: ثابت شده است که محدودهها در زمان کامپایل در محدوده مشخصی قرار دارند.
- پویا: تستهای زمان اجرا تضمین میکنند که حلقهها در محدوده خود باقی میمانند (در غیر این صورت انتخاب کنید)
- حذف متغیرهای القایی
- القای مرده را حذف کنید
- جایگزینی القایی که فقط بعد از حلقه استفاده میشود با عبارات بسته
- حذف کدهای مرده درون بدنه حلقه، حذف کل حلقههایی که مرده میشوند
- کاهش قدرت
- تبدیلات حلقه: معکوس کردن، تعویض حلقه، تقسیم حلقه، باز کردن حلقه، تک ماژولی و غیره
- SIMDization (که به آن برداریسازی نیز گفته میشود)
بهینهساز حلقه در مسیر بهینهسازی خود در کامپایلر ART قرار دارد. اکثر بهینهسازیهای حلقه مشابه بهینهسازیها و سادهسازیها در جاهای دیگر هستند. چالشهایی در مورد برخی بهینهسازیها که CFG را به روشی پیچیدهتر از حد معمول بازنویسی میکنند، ایجاد میشود، زیرا اکثر ابزارهای CFG (به nodes.h مراجعه کنید) بر ساخت CFG تمرکز دارند، نه بازنویسی آن.
تحلیل سلسله مراتب کلاس
ART در اندروید ۸.۰ از تحلیل سلسله مراتب کلاس (CHA) استفاده میکند، یک بهینهسازی کامپایلر که فراخوانیهای مجازی را بر اساس اطلاعات تولید شده توسط تحلیل سلسله مراتب کلاسها، به فراخوانیهای مستقیم تبدیل میکند. فراخوانیهای مجازی پرهزینه هستند زیرا حول یک جستجوی جدول پیادهسازی میشوند و چندین بار وابسته را به خود اختصاص میدهند. همچنین فراخوانیهای مجازی را نمیتوان درونخطی کرد.
در اینجا خلاصهای از پیشرفتهای مرتبط آمده است:
- بهروزرسانی وضعیت متد تکاجرایی پویا - در پایان زمان اتصال کلاس، هنگامی که vtable پر شده است، ART مقایسهای ورودی به ورودی با vtable کلاس اصلی انجام میدهد.
- بهینهسازی کامپایلر - کامپایلر از اطلاعات تک-پیادهسازی یک متد بهره میبرد. اگر متدی مانند A.foo دارای پرچم تک-پیادهسازی باشد، کامپایلر فراخوانی مجازی را به یک فراخوانی مستقیم تبدیل میکند و در نتیجه سعی میکند فراخوانی مستقیم را درونخطی کند.
- نامعتبرسازی کد کامپایل شده - همچنین در پایان زمان اتصال کلاس، هنگامی که اطلاعات پیادهسازی تکی بهروزرسانی میشود، اگر متد A.foo که قبلاً پیادهسازی تکی داشته اما آن وضعیت اکنون نامعتبر شده است، تمام کدهای کامپایل شدهای که به این فرض وابسته هستند که متد A.foo دارای پیادهسازی تکی است، باید کد کامپایل شده خود را نامعتبر کنند.
- بهینهسازیزدایی - برای کد کامپایلشدهی زندهای که روی پشته قرار دارد، بهینهسازیزدایی آغاز میشود تا کد کامپایلشدهی نامعتبر را مجبور به ورود به حالت مفسر کند تا صحت آن تضمین شود. از یک مکانیسم جدید بهینهسازیزدایی که ترکیبی از بهینهسازیزدایی همزمان و غیرهمزمان است، استفاده خواهد شد.
حافظههای نهان درونخطی در فایلهای .oat
ART اکنون از حافظههای نهان درونخطی استفاده میکند و سایتهای فراخوانی را که دادههای کافی برای آنها وجود دارد، بهینه میکند. ویژگی حافظههای نهان درونخطی، اطلاعات زمان اجرا اضافی را در پروفایلها ثبت میکند و از آن برای افزودن بهینهسازیهای پویا به کامپایل قبل از زمان استفاده میکند.
دکسلایوت
Dexlayout کتابخانهای است که در اندروید ۸.۰ معرفی شده است تا فایلهای dex را تجزیه و تحلیل کرده و آنها را بر اساس یک پروفایل مرتب کند. Dexlayout قصد دارد از اطلاعات پروفایلبندی زمان اجرا برای مرتبسازی مجدد بخشهای فایل dex در طول کامپایل تعمیر و نگهداری بیکار در دستگاه استفاده کند. با گروهبندی بخشهایی از فایل dex که اغلب با هم مورد دسترسی قرار میگیرند، برنامهها میتوانند الگوهای دسترسی به حافظه بهتری از محل بهبود یافته داشته باشند، در رم صرفهجویی کنند و زمان راهاندازی را کوتاهتر کنند.
از آنجایی که اطلاعات پروفایل در حال حاضر فقط پس از اجرای برنامهها در دسترس است، dexlayout در کامپایل روی دستگاه dex2oat در طول نگهداری غیرفعال ادغام میشود.
حذف حافظه پنهان Dex
تا اندروید ۷.۰، شیء DexCache چهار آرایه بزرگ داشت که متناسب با تعداد عناصر خاص در DexFile بودند، یعنی:
- رشتهها (یک ارجاع به ازای هر DexFile::StringId)،
- انواع (یک ارجاع به ازای هر DexFile::TypeId)،
- متدها (یک اشارهگر بومی به ازای هر DexFile::MethodId)،
- فیلدها (یک اشارهگر بومی به ازای هر DexFile::FieldId).
این آرایهها برای بازیابی سریع اشیاء که قبلاً حل کرده بودیم، استفاده میشدند. در اندروید ۸.۰، همه آرایهها به جز آرایه methods حذف شدهاند.
عملکرد مترجم
عملکرد مفسر در نسخه اندروید ۷.۰ با معرفی "mterp" به طور قابل توجهی بهبود یافت - مفسری که دارای مکانیزم اصلی واکشی/رمزگشایی/تفسیر نوشته شده به زبان اسمبلی است. Mterp از مفسر سریع Dalvik مدلسازی شده است و از arm، arm64، x86، x86_64، mips و mips64 پشتیبانی میکند. برای کد محاسباتی، mterp در Art تقریباً با مفسر سریع Dalvik قابل مقایسه است. با این حال، در برخی شرایط میتواند به طور قابل توجهی - و حتی به طرز چشمگیری - کندتر باشد:
- عملکرد را فراخوانی کنید.
- دستکاری رشتهها، و سایر کاربران پرمصرف متدهایی که به عنوان ذاتی در Dalvik شناخته میشوند.
- استفاده بالاتر از حافظه پشته.
اندروید ۸.۰ این مشکلات را برطرف میکند.
خطوط داخلی بیشتر
از اندروید ۶.۰ به بعد، ART میتواند هر فراخوانی را در فایلهای dex یکسان inline کند، اما فقط میتواند متدهای leaf را از فایلهای dex مختلف inline کند. دو دلیل برای این محدودیت وجود دارد:
- برخلاف inline کردن همان فایل dex که میتواند از کش dex فراخوانیکننده دوباره استفاده کند، inline کردن از یک فایل dex دیگر نیاز به استفاده از کش dex آن فایل dex دیگر دارد. کش dex در کد کامپایل شده برای چند دستورالعمل مانند فراخوانیهای استاتیک، بارگذاری رشته یا بارگذاری کلاس مورد نیاز است.
- نقشههای پشته فقط یک اندیس متد را در فایل dex فعلی کدگذاری میکنند.
برای رفع این محدودیتها، اندروید ۸.۰:
- دسترسی به حافظه پنهان dex را از کد کامپایل شده حذف میکند (همچنین به بخش «حذف حافظه پنهان Dex» مراجعه کنید)
- کدگذاری نقشه پشته را گسترش میدهد.
بهبودهای همگامسازی
تیم ART مسیرهای کد MonitorEnter/MonitorExit را تنظیم کرد و وابستگی ما به موانع حافظه سنتی در ARMv8 را کاهش داد و در صورت امکان آنها را با دستورالعملهای جدیدتر (acquire/release) جایگزین کرد.
روشهای بومی سریعتر
فراخوانیهای بومی سریعتر به رابط بومی جاوا (JNI) با استفاده از حاشیهنویسیهای @FastNative و @CriticalNative امکانپذیر است. این بهینهسازیهای داخلی زمان اجرا در ART، انتقالهای JNI را سرعت میبخشند و جایگزین نمادگذاری JNI !bang که اکنون منسوخ شده است، میشوند. این حاشیهنویسیها هیچ تاثیری بر متدهای غیربومی ندارند و فقط برای کد زبان جاوا پلتفرم در bootclasspath در دسترس هستند (بهروزرسانیهای Play Store وجود ندارد).
حاشیهنویسی @FastNative از متدهای غیراستاتیک پشتیبانی میکند. اگر متدی به عنوان پارامتر یا مقدار بازگشتی به یک jobject دسترسی پیدا میکند، از این استفاده کنید.
حاشیهنویسی @CriticalNative روشی سریعتر برای اجرای متدهای native ارائه میدهد، البته با محدودیتهای زیر:
- متدها باید ایستا باشند - هیچ شیء برای پارامترها، مقادیر برگشتی یا یک
thisضمنی وجود ندارد. - فقط انواع اولیه به متد native ارسال میشوند.
- متد native از پارامترهای
JNIEnvوjclassدر تعریف تابع خود استفاده نمیکند. - این متد باید به جای تکیه بر لینکدهی پویای JNI، با
RegisterNativesثبت شود.
@FastNative میتواند عملکرد متدهای native را تا ۳ برابر و @CriticalNative تا ۵ برابر بهبود بخشد. برای مثال، یک گذار JNI که روی دستگاه Nexus 6P اندازهگیری شده است:
| فراخوانی رابط بومی جاوا (JNI) | زمان اجرا (به نانوثانیه) |
|---|---|
| JNI معمولی | ۱۱۵ |
| !بنگ JNI | ۶۰ |
@FastNative | ۳۵ |
@CriticalNative | ۲۵ |