تمكّن فريق وقت تشغيل Android (ART) من تقليل وقت التجميع بنسبة% 18 بدون التأثير في الرموز المجمَّعة أو أي تراجع في الحد الأقصى للذاكرة. كان هذا التحسين جزءًا من مبادرتنا لعام 2025 التي تهدف إلى تحسين وقت التجميع بدون التأثير في استخدام الذاكرة أو جودة الرمز البرمجي المجمَّع.
يُعدّ تحسين سرعة وقت التجميع أمرًا بالغ الأهمية في ART. على سبيل المثال، عندما يؤثر تجميع التعليمات البرمجية في الوقت المناسب (JIT) بشكل مباشر في كفاءة التطبيقات وأداء الجهاز بشكل عام. تؤدي عمليات التجميع الأسرع إلى تقليل الوقت اللازم لبدء التحسينات، ما يؤدي إلى توفير تجربة مستخدم أكثر سلاسة واستجابة. علاوةً على ذلك، سواء كان التحويل إلى رمز أصلي يتم في الوقت المناسب أو قبل الوقت المناسب، فإنّ التحسينات في سرعة وقت التجميع تؤدي إلى تقليل استهلاك الموارد أثناء عملية التجميع، ما يؤدي إلى تحسين عمر البطارية ودرجة حرارة الجهاز، خاصةً على الأجهزة المنخفضة المواصفات.
تم طرح بعض هذه التحسينات على سرعة وقت التجميع في إصدار Android لشهر يونيو 2025، وستتوفّر بقية التحسينات في إصدار Android في نهاية العام. بالإضافة إلى ذلك، يمكن لجميع مستخدمي Android الذين يستخدمون الإصدار 12 والإصدارات الأحدث الاستفادة من هذه التحسينات من خلال تحديثات Mainline.
تحسين برنامج التحويل البرمجي المحسِّن
إنّ تحسين برنامج التجميع هو دائمًا لعبة مفاضلات. لا يمكنك الحصول على السرعة مجانًا، بل عليك التنازل عن شيء ما. لقد وضعنا لأنفسنا هدفًا واضحًا وصعبًا للغاية: تسريع عملية الترجمة البرمجية بدون حدوث أي تراجع في الذاكرة، والأهم من ذلك، بدون خفض جودة الرمز البرمجي الذي يتم إنتاجه. إذا كان المحوّل البرمجي أسرع ولكن التطبيقات تعمل بشكل أبطأ، نكون قد أخفقنا.
المورد الوحيد الذي كنّا على استعداد لاستخدامه هو وقت التطوير الخاص بنا للبحث والتحقيق والعثور على حلول ذكية تستوفي هذه المعايير الصارمة. دعونا نلقي نظرة فاحصة على كيفية عملنا للعثور على مجالات للتحسين، بالإضافة إلى إيجاد الحلول المناسبة للمشاكل المختلفة.
العثور على تحسينات ممكنة تستحقّ العناء
قبل أن تتمكّن من البدء في تحسين مقياس معيّن، يجب أن تتمكّن من قياسه. وبخلاف ذلك، لن تتمكّن أبدًا من التأكّد مما إذا كنت قد حسّنتها أم لا. لحسن الحظ، تكون سرعة وقت التجميع ثابتة إلى حدّ ما طالما اتّخذت بعض الاحتياطات، مثل استخدام الجهاز نفسه الذي تستخدمه للقياس قبل التغيير وبعده، والتأكّد من أنّك لا تحدّ من سرعة جهازك بسبب ارتفاع درجة حرارته. بالإضافة إلى ذلك، لدينا أيضًا قياسات قطعية، مثل إحصاءات المترجم، تساعدنا في فهم ما يحدث في الخلفية.
بما أنّ المورد الذي كنا نضحّي به من أجل إجراء هذه التحسينات هو وقت التطوير، أردنا أن نتمكّن من تكرار العملية بأسرع ما يمكن. وقد يعني ذلك أنّنا اخترنا مجموعة من التطبيقات التمثيلية (مزيج من تطبيقات الطرف الأول وتطبيقات الطرف الثالث ونظام التشغيل Android نفسه) لإنشاء نماذج أولية للحلول. بعد ذلك، تأكّدنا من أنّ عملية التنفيذ النهائية كانت تستحق الجهد من خلال إجراء اختبارات يدوية وتلقائية على نطاق واسع.
باستخدام مجموعة حِزم APK المحدّدة يدويًا، كنا نفعّل عملية تجميع يدوية على الجهاز، ونحصل على ملف تجميع، ونستخدم pprof لتصوّر الأماكن التي نستغرق فيها وقتًا طويلاً.
مثال على رسم بياني لملف شخصي في pprof
أداة pprof فعّالة جدًا وتتيح لنا تقسيم البيانات وتصفيتها وترتيبها لمعرفة، على سبيل المثال، مراحل أو طرق المترجم التي تستغرق معظم الوقت. لن نتناول تفاصيل حول pprof، ولكن يجب أن تعرف أنّه إذا كان الشريط أكبر، يعني ذلك أنّ عملية التجميع استغرقت وقتًا أطول.
أحد هذه العروض هو "من الأسفل إلى الأعلى" حيث يمكنك معرفة الطرق التي تستغرق معظم الوقت. في الصورة أدناه، يمكننا الاطّلاع على طريقة تُسمى Kill، وهي تمثّل أكثر من% 1 من وقت التجميع. سنتناول أيضًا بعض الطرق الأخرى الأفضل لاحقًا في مشاركة المدونة.
عرض الملف الشخصي من الأسفل إلى الأعلى
في برنامج التحويل البرمجي المحسّن، هناك مرحلة تُعرف باسم "الترقيم العالمي للقيم" (GVN). ليس عليك القلق بشأن ما يفعله بشكل عام، ولكن الجزء المهم هو معرفة أنّه يتضمّن طريقة تُسمى Kill، وهي تحذف بعض العُقد وفقًا لفلتر. يستغرق ذلك وقتًا طويلاً لأنّه يجب تكرار جميع العُقد والتحقّق منها واحدة تلو الأخرى. لاحظنا أنّ هناك بعض الحالات التي نعرف فيها مسبقًا أنّ عملية التحقّق ستكون غير صحيحة، بغض النظر عن العُقد النشطة لدينا في تلك المرحلة. في هذه الحالات، يمكننا تخطّي التكرار تمامًا، ما يؤدي إلى خفض معدّل الخطأ من% 1.023 إلى% 0.3 تقريبًا وتحسين وقت تشغيل GVN بنسبة %15 تقريبًا.
تنفيذ عمليات تحسين مفيدة
تطرّقنا إلى كيفية قياس الوقت وكيفية رصد الأماكن التي يتم فيها استهلاك الوقت، ولكن هذه مجرد البداية. الخطوة التالية هي كيفية تحسين الوقت المستغرَق في التجميع.
في العادة، في حالة مثل حالة `Kill` المذكورة أعلاه، سنلقي نظرة على كيفية تكرار العُقد وتنفيذها بشكل أسرع، مثلاً من خلال تنفيذ الإجراءات بالتوازي أو تحسين الخوارزمية نفسها. في الواقع، هذا ما حاولنا فعله في البداية، وعندما لم نتمكّن من العثور على أي شيء، أدركنا أنّ الحلّ يكمن في عدم تكرار العملية على الإطلاق (في بعض الحالات). عند إجراء هذا النوع من التحسينات، من السهل أن يغيب عن ذهنك الهدف الأكبر.
في حالات أخرى، استخدمنا بعض الأساليب المختلفة، بما في ذلك:
- استخدام طرق تجريبية لتحديد ما إذا كان التحسين لن يؤدي إلى نتائج جديرة بالاهتمام وبالتالي يمكن تخطّيه
- استخدام بنى بيانات إضافية لتخزين البيانات المحسوبة مؤقتًا
- تغيير بنى البيانات الحالية للحصول على زيادة في السرعة
- حساب النتائج بشكل غير مباشر لتجنُّب الحلقات في بعض الحالات
- استخدام التجريد المناسب، لأنّ الميزات غير الضرورية يمكن أن تبطئ عمل الرمز
- تجنُّب تتبُّع مؤشر مستخدَم بشكل متكرّر خلال عمليات تحميل عديدة
كيف نعرف ما إذا كانت عمليات التحسين تستحق المتابعة؟
والجميل في الأمر أنّك لست بحاجة إلى ذلك. بعد رصد أنّ أحد الأقسام يستهلك الكثير من وقت التجميع وبعد تخصيص وقت التطوير لمحاولة تحسينه، قد لا تتمكّن أحيانًا من العثور على حلّ. قد لا يكون هناك ما يمكن فعله، أو قد يستغرق التنفيذ وقتًا طويلاً جدًا، أو قد يؤدي إلى تراجع مقياس آخر بشكل كبير، أو زيادة تعقيد قاعدة الرموز، وما إلى ذلك. مقابل كل عملية تحسين ناجحة يمكنك الاطّلاع عليها في مشاركة المدونة هذه، هناك عمليات تحسين أخرى لا حصر لها لم تؤتِ ثمارها.
إذا كنت في موقف مشابه، حاوِل تقدير مقدار التحسّن الذي ستحقّقه في المقياس بأقل جهد ممكن. ويعني ذلك ما يلي:
- التقدير باستخدام المقاييس التي جمعتها سابقًا أو مجرد شعور
- التقدير باستخدام نموذج أوّلي سريع وغير دقيق
- تنفيذ حلّ
لا تنسَ تقدير عيوب الحلّ الذي تقدّمه. على سبيل المثال، إذا كنت ستعتمد على بنى بيانات إضافية، ما مقدار الذاكرة التي تريد استخدامها؟
نظرة مفصَّلة
لنلقِ نظرة على بعض التغييرات التي أجريناها.
أجرينا تغييرًا لتحسين طريقة تُعرف باسم FindReferenceInfoOf. كانت هذه الطريقة تجري بحثًا خطيًا عن متّجه للعثور على إدخال. عدّلنا بنية البيانات هذه لتتم فهرستها حسب معرّف التعليمات، وبالتالي تصبح FindReferenceInfoOf من النوع O(1) بدلاً من O(n). بالإضافة إلى ذلك، خصّصنا مسبقًا مساحة للخط المتجه لتجنُّب تغيير حجمه. لقد زدنا حجم الذاكرة قليلاً لأنّنا اضطررنا إلى إضافة حقل إضافي يحسب عدد الإدخالات التي أدرجناها في المتّجه، ولكنّها كانت تضحية بسيطة لأنّ الحد الأقصى للذاكرة لم يزد. وقد أدّى ذلك إلى تسريع مرحلة LoadStoreAnalysis بنسبة تتراوح بين %34 و%66، ما أدّى بدوره إلى تحسين وقت التجميع بنسبة تتراوح بين %0.5 و%1.8.
لدينا عملية تنفيذ مخصّصة لـ HashSet نستخدمها في عدة مواضع. كان إنشاء بنية البيانات هذه يستغرق وقتًا طويلاً، وقد عرفنا السبب. قبل سنوات عديدة، تم استخدام بنية البيانات هذه في عدد قليل من الأماكن التي كانت تستخدم مجموعات HashSets كبيرة جدًا، وتم تعديلها لتحسينها لهذا الغرض. ومع ذلك، تم استخدامها في الوقت الحالي في الاتجاه المعاكس مع عدد قليل من الإدخالات وعمر قصير. وهذا يعني أنّنا كنا نضيّع دورات المعالجة من خلال إنشاء HashSet كبير، ولكنّنا لم نستخدمه إلا لعدد قليل من الإدخالات قبل تجاهله. من خلال هذا التغيير، تمكّنا من تحسين وقت التجميع بنسبة تتراوح بين %1.3 و%2 تقريبًا. بالإضافة إلى ذلك، انخفض استخدام الذاكرة بنسبة تتراوح بين %0.5 و%1 تقريبًا لأنّنا لم نعد نستخدم بنى بيانات كبيرة كما في السابق.
لقد حسّنّا وقت التجميع بنسبة تتراوح بين %0.5 و%1 تقريبًا من خلال تمرير بنى البيانات حسب المرجع إلى تعبير lambda لتجنُّب نسخها. هذا خطأ لم ننتبه إليه في المراجعة الأصلية وبقي في قاعدة الرموز البرمجية لسنوات. وبفضل إلقاء نظرة على الملفات الشخصية في pprof، لاحظنا أنّ هذه الطرق كانت تنشئ وتتلف الكثير من بُنى البيانات، ما دفعنا إلى التحقيق فيها وتحسينها.
لقد سرّعنا المرحلة التي تكتب الناتج المجمَّع من خلال تخزين القيم المحسوبة مؤقتًا، ما أدّى إلى تحسين إجمالي وقت التجميع بنسبة تتراوح بين %1.3 و%2.8 تقريبًا. للأسف، كان تتبُّع البيانات الإضافية أكثر من اللازم، وقد نبّهتنا الاختبارات الآلية إلى انخفاض أداء الذاكرة. في وقت لاحق، ألقينا نظرة ثانية على الرمز نفسه وطرحنا إصدارًا جديدًا لم يعالج مشكلة تراجع الذاكرة فحسب، بل حسّن أيضًا وقت التجميع بنسبة تتراوح بين %0.5 و%1.8 إضافية تقريبًا. في التغيير الثاني، كان علينا إعادة تصميم طريقة عمل هذه المرحلة من أجل التخلص من أحد بنى البيانات.
لدينا مرحلة في برنامج التحويل البرمجي المحسّن تعمل على تضمين استدعاءات الدوال من أجل الحصول على أداء أفضل. لاختيار الطرق التي سيتم تضمينها، نستخدم كلتا الطريقتين التجريبية قبل إجراء أي عملية حسابية، وعمليات التحقّق النهائية بعد إنجاز العمل ولكن قبل الانتهاء من عملية التضمين مباشرةً. إذا رصد أيّ من هذه الإجراءات أنّ عملية التضمين لا تستحق العناء (على سبيل المثال، سيتمّ إضافة الكثير من التعليمات الجديدة)، لن نضمّن طلب الإجراء.
نقلنا عمليتَي تحقّق من فئة "عمليات التحقّق النهائية" إلى فئة "الاستدلال" لتقدير ما إذا كان التضمين المباشر سينجح أم لا قبل إجراء أي عمليات حسابية تستغرق وقتًا طويلاً. بما أنّ هذا تقدير، فهو ليس مثاليًا، ولكن تأكّدنا من أنّ الإرشادات الجديدة تغطي% 99.9 من المحتوى الذي تم تضمينه سابقًا بدون التأثير في الأداء. أحد مقاييس التجربة الجديدة هذه كان حول تسجيلات DEX المطلوبة (تحسين بنسبة تتراوح بين 0.2% و1.3%)، والآخر حول عدد التعليمات (تحسين بنسبة %2 تقريبًا).
لدينا تنفيذ مخصّص لـ BitVector نستخدمه في عدة مواضع. استبدلنا فئة BitVector القابلة لتغيير الحجم بفئة BitVectorView أبسط لبعض متجهات البت ذات الحجم الثابت. يؤدي ذلك إلى إلغاء بعض عمليات التوجيه غير المباشر وعمليات التحقّق من النطاق في وقت التشغيل، كما يؤدي إلى تسريع إنشاء عناصر متّجه البت.
بالإضافة إلى ذلك، تم إنشاء نموذج لفئة BitVectorView استنادًا إلى نوع التخزين الأساسي (بدلاً من استخدام uint32_t دائمًا مثل BitVector القديم). ويتيح ذلك تنفيذ بعض العمليات، مثل Union()، على ضعف عدد البتات معًا على الأنظمة الأساسية 64 بت. تم تقليل عيّنات الدوال المتأثرة بنسبة تزيد عن% 1 إجمالاً عند تجميع نظام التشغيل Android. تم إجراء ذلك على مستوى عدة تغييرات [1 و2 و3 و4 و5 و6]
إذا أردنا التحدّث بالتفصيل عن جميع التحسينات، سيستغرق ذلك وقتًا طويلاً. إذا كنت مهتمًا بإجراء المزيد من التحسينات، يمكنك الاطّلاع على بعض التغييرات الأخرى التي أجريناها:
- أضِف عملية حفظ السجلات لتحسين أوقات التجميع بنسبة تتراوح بين %0.6 و%1.6 تقريبًا.
- احتساب البيانات بشكل غير مباشر لتجنُّب الدورات، إذا أمكن ذلك
- إعادة تصميم الرمز لتجنُّب العمليات الحسابية المسبقة عندما لا يتم استخدامها
- تجنُّب بعض سلاسل التحميل التابعة عندما يمكن الحصول على أداة التخصيص بسهولة من أماكن أخرى
- هذه حالة أخرى لإضافة عملية تحقّق لتجنُّب العمل غير الضروري.
- تجنُّب التفرّع المتكرّر في نوع السجلّ (أساسي/FP) في أداة تخصيص السجلّ.
- تأكَّد من تهيئة بعض المصفوفات في وقت الترجمة البرمجية. لا تعتمد على clang لإجراء ذلك.
- تنظيف بعض المقاطع الصوتية المتكرّرة استخدِم حلقات النطاق التي يمكن لـ Clang تحسينها بشكل أفضل لأنّها لا تحتاج إلى إعادة تحميل المؤشرات الداخلية للحاوية بسبب الآثار الجانبية للحلقة. تجنَّب استدعاء الدالة الوهمية `HInstruction::GetInputRecords()` في الحلقة من خلال الدالة المضمّنة `InputAt(.)` لكل إدخال.
- تجنَّب استخدام دوال Accept() لنمط الزائر من خلال الاستفادة من تحسينات المترجم.
الخاتمة
وقد أدّى تفانينا في تحسين سرعة وقت التجميع في ART إلى تحقيق تحسينات كبيرة، ما جعل نظام التشغيل Android أكثر سلاسة وكفاءة، وساهم أيضًا في إطالة عمر البطارية وتحسين درجة حرارة الجهاز. ومن خلال تحديد التحسينات وتنفيذها بعناية، أثبتنا أنّه يمكن تحقيق مكاسب كبيرة في وقت التجميع بدون التأثير سلبًا في استخدام الذاكرة أو جودة الرمز البرمجي.
تضمّنت رحلتنا إنشاء ملفات تعريف باستخدام أدوات مثل pprof، والاستعداد للتكرار، وأحيانًا حتى التخلي عن الطرق الأقل جدوى. لم تؤدِّ الجهود الجماعية لفريق ART إلى تقليل وقت التجميع بنسبة ملحوظة فحسب، بل وضعت أيضًا الأساس لتحقيق تحسينات مستقبلية.
تتوفّر كل هذه التحسينات في تحديث Android لنهاية العام 2025، وعلى الإصدار Android 12 والإصدارات الأحدث من خلال تحديثات mainline. نأمل أن يكون هذا التحليل المتعمّق لعملية التحسين قد قدّم إحصاءات قيّمة حول تعقيدات هندسة المترجم ومكافآتها.
متابعة القراءة
-
أخبار المنتجات
تختلف احتياجات كل مطوّر وسير عمله المرتبط بالذكاء الاصطناعي، ومن المهم أن يتمكّن من اختيار الطريقة التي يساعد بها الذكاء الاصطناعي في عملية التطوير. في يناير، أتحنا إمكانية اختيار أي نموذج ذكاء اصطناعي محلي أو عن بُعد لتشغيل وظائف الذكاء الاصطناعي في استوديو Android
Matthew Warner • مدة القراءة: دقيقتان
-
أخبار المنتجات
أصبح الإصدار 3 من استوديو Android Panda ثابتًا وجاهزًا للاستخدام في الإنتاج. يمنحك هذا الإصدار المزيد من التحكّم والتخصيص في مهام سير العمل المستندة إلى الذكاء الاصطناعي، ما يسهّل عليك إنشاء تطبيقات Android عالية الجودة أكثر من أي وقت مضى.
Matt Dyor • قراءة لمدة 3 دقائق
-
أخبار المنتجات
في Google، نحن ملتزمون بتوفير نماذج الذكاء الاصطناعي الأكثر تطورًا مباشرةً على أجهزة Android التي تحملها في جيبك. يسرّنا اليوم الإعلان عن إطلاق أحدث نماذجنا المفتوحة والمتطورة: Gemma 4.
Caren Chang, David Chou • قراءة لمدة 3 دقائق
البقاء على اطّلاع على آخر التحديثات
يمكنك تلقّي أحدث الإحصاءات حول تطوير تطبيقات Android في بريدك الوارد أسبوعيًا.