مطالعات موردی

Grab با R8 و «بهینه‌سازی با راهنمایی نمایه»، خطاهای ANR را ۳۲٪ کاهش می‌دهد و زمان راه‌اندازی را ۳۵٪ بهبود می‌بخشد

‫۵ دقیقه خواندن
۳ نویسنده
Ajesh Pai, Nguyen Van Minh, Tracy Agyemang

‫Grab سوپربرنامه پیشرو در آسیای جنوب شرقی است که خدمات تحویل، جابه‌جایی، و مالی را ازطریق یک برنامه واحد به میلیون‌ها کاربر در هشت کشور ارائه می‌دهد. با یک پایگاه کد ۱۰ ساله حاوی بیش‌از ۱۰ میلیون خط کد و صدها کتابخانه طرف سوم که بااستفاده از راه‌اندازی ترکیبی Bazel و Gradle ساخته شده است، تیم Grab با سه چالش روبه‌رشد مواجه بود: نرخ بالای «برنامه پاسخ نمی‌دهد» (ANR)، اندازه برنامه‌ای که با افزودن سرویس‌های جدید به سوپربرنامه همچنان درحال رشد بود، و زمان راه‌اندازی که سال‌به‌سال به‌آرامی درحال افزایش بود.  با فعال کردن بهینه‌سازی R8، اصلاح قوانین حفظ، و پیاده‌سازی Baseline Profiles و Startup Profiles،‏ Grab توانست نرخ خطای ANR مشهود برای کاربر را ۳۲٪ کاهش دهد، حجم بارگیری برنامه را حدود ۳۰٪ کم کند، و زمان راه‌اندازی را ۳۵٪ بهبود بخشد.

حذف جهش‌های خطای ANR پس‌از انتشار برای تجربه کاربری روان‌تر

پیش‌از بهینه‌سازی فرایند ساخت، Grab با تأخیر در راه‌اندازی سرد و افزایش ناگهانی «برنامه پاسخ نمی‌دهد» (ANR) بلافاصله پس‌از به‌روزرسانی‌های جدید مواجه می‌شد که عمدتاً ناشی از سربار خواندن فایل‌های DEX متعدد درطول مقداردهی اولیه برنامه بود. 

درحالی‌که نمایه‌های ابری Google Play بهینه‌سازی را برای جریان‌های استاندارد در کل پایگاه کاربر ارائه می‌داد، همه مسیرهای مقداردهی اولیه موردنیاز برای مسیرهای قطعی کاربر Grab را بهینه‌سازی نمی‌کردند—مانند مقداردهی اولیه نمودارهای وابستگی Dagger درطول راه‌اندازی برنامه. برای مسیرهای کدی که در توزیع اولیه پوشش داده نشده بودند، دستگاه‌ها به ترجمه درجا و بهینه‌سازی پس‌زمینه محلی در روزهای بعدی متکی بودند. پیاده‌سازی نمایه پایه سفارشی این مشکل را با ارائه این مسیرهای قطعی کاربر مستقیماً در «نمایه‌های ابری» Google Play حل کرد و پوشش بهینه‌سازی آن‌ها را گسترش داد تا این مسیرهای کلیدی بلافاصله پس‌از نصب به کد ماشینی کامپایل شوند 

ANDDM_Grab_Inline_Quote_ALT.png

نگوین وان مین، مهندس نرم‌افزار ارشد در تیم اصلی برنامه مسافر Grab، توضیح می‌دهد: «ما به‌طور مداوم با هر انتشار هفتگی برنامه، شاهد افزایش ناگهانی نرخ ANR بودیم.» 

Grab برای حذف خطاهای ANR و بهبود زمان راه‌اندازی، بهینه‌سازی‌های R8،‏ Baseline Profiles، و Startup Profiles را پیاده‌سازی کرد. بهینه‌سازی هدایت‌شده با نمایه در کنار بهینه‌ساز R8 باعث بهبود فوری ۱۲ درصدی در راه‌اندازی سرد از R8 و ۱۵ درصد دیگر از Baseline Profiles شد—که منجر به دستیابی به زمان راه‌اندازی ۲۷ درصد سریع‌تر و حذف مؤثر جهش‌های ANR پس‌از انتشار شد.

‫Grab این بهینه‌سازی برنامه را در سه مرحله استراتژیک اجرا کرد: فعال کردن بهینه‌سازی R8 در حالت سازگاری، انتقال به «حالت کامل R8»، و سپس اصلاح قوانین نگهداری با Configuration Analyzer و افزودن حاکمیت CI/CD.

فاز ۱: فعال کردن بهینه‌سازی R8  (عرضه‌شده در ۲۰۲۵)

در این مرحله اول، Grab از فایل Proguard پیش‌فرض قدیمی فاصله گرفت تا proguard-android-optimize.txt را اتخاذ کند که بهینه‌سازی‌های R8 را فعال می‌کند.  ازآنجایی‌که پایگاه کد بیش‌از یک دهه قدمت داشت و حاوی بیش‌از ۱۰ میلیون خط کد بود، فعال کردن بهینه‌ساز مستلزم رسیدگی به پیکربندی‌های قدیمی و استفاده از بازتاب بود.

برای به‌حداکثر رساندن تأثیر این مرحله، Grab به‌روزرسانی برنامه را با حالت R8 Compat فعال‌شده با Baseline Profiles و Startup Profiles منتشر کرد. این تلاش ترکیبی منجر به کاهش فوری ۲۵ درصدی در نرخ خطای ANR مشهود برای کاربر، کاهش ۱۶ درصدی در اندازه بارگیری برنامه، و بهبود کلی ۲۷ درصدی در زمان‌های شروع سرد شد.

فاز ۲: حالت کامل R8 و انتقال (عرضه در ۲۰۲۶)

این مرحله که نه ماه پس‌از عرضه اولیه راه‌اندازی شد، برنامه را به حالت کامل R8 منتقل کرد که بهینه‌سازی‌های بیشتری را ممکن ساخت. While standard libraries like Retrofit, Firebase, and Jetpack Compose worked seamlessly with R8 using their built-in rules, migrating the rest of the massive codebase was achieved by resolving specific reflection-heavy code paths:

برای انتقال به حالت کامل R8، تیم قوانین نگهداری مصرف‌کننده را از جدیدترین نسخه Gson اتخاذ کرد تا تجزیه JSON بدون مشکل انجام شود. آن‌ها همچنین چالش‌های بهینه‌سازی را با پراکسی‌های پویای جاوا که از بازتاب برای تولید پیاده‌سازی‌های میانجی در زمان اجرا استفاده می‌کنند، برطرف کردند. چون R8 نمی‌تواند این استفاده از زمان اجرا را درطول کامپایل کردن شناسایی کند، کلاس‌های ضروری را حذف می‌کند یا نام آن‌ها را تغییر می‌دهد که باعث ازکار افتادن برنامه می‌شود. تیم به‌جای اعمال استثناهای گسترده که اندازه برنامه را افزایش می‌داد، کلاس‌های خاصی را که به بازتاب متکی بودند جدا کرد و قوانین نگهداری بسیار هدفمندی را اجرا کرد که هم پایداری برنامه و هم کارایی بهینه‌سازی را حفظ کرد

درنهایت، «حالت کامل R8» بهبودهای عظیم و ترکیبی ارائه داد، ازجمله کاهش ۱۴٪ بیشتر در اندازه بارگیری برنامه، بهبود ۱۰٪ بیشتر در زمان راه‌اندازی، و کاهش ۱۰٪ دیگر در خطاهای ANR مشهود برای کاربر. پس‌از انتقال به «حالت کامل R8»، پایگاه کد Grab به این امتیازهای بهینه‌سازی R8 دست یافت. 

  • امتیاز بهینه‌سازی: ۷۲٪
  • امتیاز فشرده‌سازی کد: ۷۳٪
  • امتیاز مبهم‌سازی کد: ۷۹٫۶٪

این امتیازها بازخوردی درباره پیکربندی R8 و نیاز به اصلاح قوانین نگهداری برای به‌حداکثر رساندن بهینه‌سازی R8 ارائه دادند. 

فاز ۳: پالایش قانون حفظ 

در مرحله نهایی سفر بهینه‌سازی، Grab بر ایجاد سلامت پایگاه کد بلندمدت، نظارت مستمر بر عملکرد، و حاکمیت سخت‌گیرانه بر کامپایل تمرکز دارد. برای اندازه‌گیری سیستماتیک بهداشت قانون و جلوگیری از پسرفت‌های پیکربندی در چرخه‌های انتشار مداوم، تیم درحال ادغام R8 Configuration Analyzer در گردش کار ارزیابی خود است. این ابزار دید عمیقی درباره اینکه قوانین نگهداری Grab چقدر به R8 اجازه می‌دهد تا برنامه را بهینه‌سازی، کوچک، و مبهم کند ارائه می‌دهد.

Grab با تکیه بر پایه «حالت کامل R8» و کاهش روشمند قوانین نگهداری قدیمی، به بهبودهای قابل‌توجهی در هر سه بعد اصلی ارزیابی‌شده توسط تحلیل‌گر دست یافته است:

  • امتیاز بهینه‌سازی: از ۷۲٪ به ۷۸٫۲٪ افزایش می‌یابد
  • امتیاز فشرده‌سازی کد: از ۷۳٪ به ۷۹٫۱٪ افزایش می‌یابد
  • امتیاز مبهم‌سازی کد: از ۷۹٫۶٪ به ۸۱٫۷٪ افزایش می‌یابد

علاوه‌بر تقویت این امتیازهای تشخیصی، این مرحله اختصاصی پالایش قانون حفظ، کاهش اضافی حدود ۳٫۶٪ در اندازه بارگیری برنامه را به‌همراه دارد. مهم‌تر اینکه، ردیابی این سنجه‌ها داده‌های عینی لازم را برای پیاده‌سازی حفاظ‌های CI پیش‌از ادغام خودکار دراختیار تیم مهندسی قرار می‌دهد تا اطمینان حاصل شود که کدهای جدیداً معرفی‌شده از استانداردهای سخت‌گیرانه قانون حفظ پیروی می‌کنند و دستاوردهای عملکردی که به‌سختی به‌دست آمده‌اند درطول زمان محافظت می‌شوند.

نتایج

بهبودهای کلیدی عملکرد پس‌از عرضه بهینه‌ساز R8:

مرحله بهینه‌سازیتعداد خطاهای «برنامه پاسخ نمی‌دهد»اندازهراه‌اندازی سرد
فاز ۱: بهینه‌ساز R8 (عرضه در ۲۰۲۵)~۲۵٪ کاهش‫٪۱۶ کاهش~٪۲۷ بهبود
فاز ۲: حالت کامل R8 (عرضه در ۲۰۲۶)‫٪۱۰ کاهش‫۱۴٪ کاهش‫٪۱۰ بهبود
بهینه‌سازی قانون حفظ-‫۳٫۶٪ کاهش-
درمجموع (تجمعی)~۳۲٫۵٪ کاهش~۳۰٫۴٪ کاهش~۳۵٪ بهبود

چالش‌های فنی 

فعال کردن بهینه‌ساز R8 در پایگاه کد ده‌ساله Grab موانع فنی کلیدی را در مقیاس بزرگ ایجاد کرد:

  • آزمایش: یک دهه بازتاب انباشته‌شده، موارد حاشیه‌ای را تحت بهینه‌سازی کامل ایجاد کرد. ازآنجایی‌که آزمایش جامع در میلیون‌ها خط کد غیرممکن بود، تیم رویکردی افزایشی و با اولویت بالا اتخاذ کرد—به‌طور خاص آزمایش را روی کدی متمرکز کرد که تحت پوشش قوانین نگهداری باریک‌شده قرار داشت، و به‌طور تکراری آن قوانین را اصلاح کرد تا فقط کلاس‌ها، روش‌ها، یا فیلدهای خاص موردنیاز برای بازتاب را هدف قرار دهد تا بهینه‌سازی R8 را به حداکثر برساند
  • تأخیر ساخت و معماری ترکیبی: سیستم ساخت ترکیبی Grab (‏Bazel برای اشکال‌زدایی، Gradle برای ساخت‌های نسخه پخش R8) به این معنی بود که تغییرات آزمایشی R8 به حداکثر ۲ ساعت زمان در CI ازراه‌دور نیاز داشت. برای پر کردن شکاف بازخورد، Grab نسخه‌های تضمین کیفیت روزانه خود را با رفتار R8 تولید هماهنگ کرد و جمع‌آوری مصنوعات کلیدی ساخت CI مانند mapping.txt و configuration.txt را برای ساده‌سازی بررسی خودکار کرد.
  • چرخه‌های اشکال‌زدایی کند: ردیابی علل ریشه‌ای در میان صدها کلاس بهینه‌سازی‌شده بار سنگینی را بر دوش تیم کوچک ما گذاشته بود. ‫Grab این مشکل را با خودکارسازی بارگذاری فایل‌های نگاشت در Firebase Crashlytics ازطریق Gradle و بسته‌بندی نگاشت‌های ابهام‌زدایی برای «کنسول Play» حل کرد، که امکان حل فوری و دقیق نمادها را برای مشکلات زمان اجرا فراهم می‌کند

تأثیر طولانی‌مدت کد با R8

فراتر از بهبودهای فوری عملکرد، فعال کردن بهینه‌ساز R8 مزیت ساختاری دائمی برای پایگاه کد عظیم Grab به‌همراه داشت. در Grab، تیم از ابزار متن‌باز app-sizer برای اندازه‌گیری تأثیر R8 استفاده کرد و مشاهده کرد که R8 به‌طور دائم هزینه هر خط کد در برنامه را ازنظر اندازه بارگیری کاهش می‌دهد.

ANDDM_Grab_Inline_Metric.png

در عصر توسعه با کمک هوش مصنوعی، که حجم کد به‌سرعت افزایش می‌یابد، این بهینه‌سازی اهمیت بیشتری پیدا می‌کند. بدون R8، گسترش کد به‌طور متناسب اندازه بارگیری را افزایش می‌دهد. R8 تضمین می‌کند که همان نرخ گسترش کد، ردپای حافظه را به‌شدت کاهش می‌دهد.

توجه: روش‌شناسی: سهم Kotlin/Java در app-sizer برای اندازه بارگیری برنامه (دستگاه نمونه)، تقسیم بر LOC (cloc؛ فقط مجموعه‌های منبع اصلی؛ به‌استثنای ماژول‌های آزمایشی، ساختگی، و نمونه) Kotlin تولید.

مدیریت قانون و حفاظ‌های Keep

برای جلوگیری از تنزل قانون و حفظ بهینه‌سازی‌های R8 درطول زمان، Grab قصد دارد حفاظ‌های خودکار پیش‌از ادغام را مستقیماً در خط لوله CI خود ادغام کند. طبق این خط‌مشی، قوانین نگهداری جدید به‌شدت محدود شده‌اند و هرگونه قانون اضافی نیاز به توجیه و بررسی صریح دارد. تیم Grab امتیازهای بهینه‌سازی R8 را در CI ردیابی می‌کند. با تکیه بر این، تیم قصد دارد از این سنجه‌ها به‌عنوان یک حلقه بازخورد مداوم استفاده کند تا رگرسیون‌ها قبل‌از ادغام شناسایی شوند و دستاوردهای بهینه‌سازی در هر چرخه انتشار حفظ شوند. 

R8-rules-score (1).png

شکل ۱: داشبورد CI که امتیازهای کوچک‌سازی، بهینه‌سازی، و مبهم‌سازی R8 را درطول زمان در ساخت‌های Grab ردیابی می‌کند تا به‌طور پیش‌گیرانه پس‌رفت‌ها را در نسخه‌های مختلف شناسایی و از آن‌ها جلوگیری کند.

نتیجه‌گیری 

تیم Pax App Core در Grab که با چالش‌های یک پایگاه کد عظیم و درحال بلوغ مواجه بود، با پیاده‌سازی Baseline Profiles و بهبود مداوم پیکربندی R8، به‌طور فعالانه با افزایش زمان راه‌اندازی و جهش‌های عملکردی پس‌از انتشار مقابله کرد. این تیم خطاهای ANR مشهود برای کاربر را حدود ۳۲٫۵٪ کاهش داد، حجم بارگیری برنامه را حدود ۳۰٫۳٪ کم کرد، و زمان شروع سرد را حدود ۳۵٪ بهبود بخشید، و درعین‌حال کارایی مقیاس‌بندی پایگاه کد خود را به‌طور اساسی بهبود داد.

این بهینه‌سازی‌ها تجربه اَبَربرنامه سریع‌تر و پایدارتری را برای میلیون‌ها کاربر در سراسر آسیای جنوب شرقی فراهم کرد و درعین‌حال ابزارهایی را دراختیار مهندسان Grab قرار داد تا ویژگی‌ها را سریع‌تر بسازند بدون اینکه با مشکل افزایش سریع حجم بارگیری مواجه شوند.

برای شروع و کسب اطلاعات بیشتر درباره R8، به - d.android.com/r8 مراجعه کنید.

نویسنده:
ادامه خواندن