تحسين أوقات التشغيل

يوفّر هذا المستند إرشادات للشركاء لتحسين أوقات بدء التشغيل لأجهزة Android معيّنة. وقت بدء التشغيل هو مكوّن مهم من أداء النظام، لأنّه على المستخدمين الانتظار حتى اكتمال عملية بدء التشغيل قبل أن يتمكّنوا من استخدام الجهاز. بالنسبة إلى الأجهزة، مثل السيارات التي يتم فيها تشغيل الجهاز من جديد بشكل متكرّر، من المهم أن يكون وقت التشغيل سريعًا (لا أحد يحب الانتظار لعدّة عشرات من الثواني لمجرد إدخال وجهةnavigate).

يتيح نظام التشغيل Android 8.0 تقليل أوقات بدء التشغيل من خلال توفير العديد من التحسينات على مستوى مجموعة من المكوّنات. يلخّص الجدول التالي تحسينات الأداء هذه (كما تم قياسها على أجهزة Google Pixel وPixel XL).

المكوّن تحسين
برنامج الإقلاع
  • تم توفير 1.6 ثانية من خلال إزالة سجلّ UART
  • تم توفير 0.4 ثانية من خلال التبديل إلى LZ4 من GZIP
نواة الجهاز
  • تم توفير 0.3 ثانية من خلال إزالة إعدادات النواة غير المستخدَمة وتقليل حجم برنامج التشغيل
  • تم توفير 0.3 ثانية من خلال تحسين أداة التحميل المُسبَق لنظام dm-verity
  • تم توفير 0.15 ثانية لإزالة الانتظار/الاختبار غير الضروري في برنامج التشغيل
  • تم توفير 0.12 ثانية لإزالة CONFIG_CC_OPTIMIZE_FOR_SIZE
ضبط وحدات الإدخال والإخراج
  • تم توفير ثانيتَين في عملية التشغيل العادية
  • تم توفير 25 ثانية في عملية التشغيل الأولى
init.*.rc
  • تم توفير 1.5 ثانية من خلال تنفيذ أوامر التشغيل بالتوازي
  • تم توفير 0.25 ثانية من خلال بدء zygote مبكرًا
  • تم توفير 0.22 ثانية من خلال ضبط cpuset
الصورة المتحركة لبدء التشغيل
  • بدأ قبل ثانيتين من بدء التشغيل بدون بدء fsck، وبدأ قبل وقت أطول بكثير من بدء التشغيل مع بدء التشغيل الذي بدأه fsck
  • تم توفير 5 ثوانٍ على هاتف Pixel XL من خلال إيقاف الصورة المتحركة لبدء التشغيل على الفور
سياسة SELinux تم توفير 0.2 ثانية من خلال genfscon

تحسين برنامج الإقلاع

لتحسين أداة تحميل البرامج لتحسين أوقات التشغيل:

  • بالنسبة إلى التسجيل:
    • أوقِف كتابة السجلّ إلى UART لأنّ ذلك قد يستغرق وقتًا طويلاً مع الكثير من عمليات التسجيل. (على أجهزة Google Pixel، تبيّن لنا أنّه يبطئ برنامج الإقلاع لمدة 1.5 ثانية).
    • سجِّل حالات الخطأ فقط، وفكِّر في تخزين المعلومات الأخرى في الذاكرة باستخدام آلية منفصلة لاستردادها.
  • فك ضغط النواة: ننصحك باستخدام LZ4 للأجهزة الحديثة بدلاً من GZIP (مثال على تصحيح). يُرجى العِلم أنّه قد تختلف أوقات التحميل و فك الضغط للخيارات المختلفة لضغط النواة، وقد تعمل بعض الخيارات بشكل أفضل من غيرها مع الأجهزة المحدّدة.
  • تحقّق من أوقات الانتظار غير الضرورية لإزالة الارتعاش/دخول الوضع الخاص وتجنَّب تلك الأوقات قدر الإمكان.
  • تمرير وقت التمهيد الذي تم قضاؤه في أداة التمهيد إلى النواة كسلسلة أوامر
  • تحقَّق من ساعة وحدة المعالجة المركزية (CPU) وفكِّر في استخدام تقنية التوازي (يتطلب ذلك دعم المعالجات متعددة النوى) لتحميل النواة وبدء عمليات الإدخال/الإخراج.

تحسين كفاءة الإدخال/الإخراج

إنّ تحسين كفاءة الإدخال/الإخراج أمر مهم لتسريع وقت التشغيل، ويجب تأجيل قراءة أي شيء غير ضروري إلى ما بعد التشغيل (على هاتف Google Pixel، تتم قراءة حوالي 1.2 غيغابايت من البيانات عند التشغيل).

ضبط نظام الملفات

يتم تفعيل ميزة "القراءة المسبقة" لنظام التشغيل Linux عند قراءة ملف من البداية أو عند قراءة وحدات متسلسلة، ما يجعل من الضروري ضبط مَعلمات جدولة الإدخال/الإخراج لبدء التشغيل على وجه التحديد (الذي يختلف عن التطبيقات العادية من حيث تحديد الحمولة).

تستفيد الأجهزة التي تتيح التحديثات السلسة (A/B) بشكل كبير من ملف النظام الترميم عند التمهيد لأول مرة (مثلاً 20 ثانية على Google Pixel). على سبيل المثال، عدّلنا المَعلمات التالية لجهاز Google Pixel:

on late-fs
  # boot time fs tune
    # boot time fs tune
    write /sys/block/sda/queue/iostats 0
    write /sys/block/sda/queue/scheduler cfq
    write /sys/block/sda/queue/iosched/slice_idle 0
    write /sys/block/sda/queue/read_ahead_kb 2048
    write /sys/block/sda/queue/nr_requests 256
    write /sys/block/dm-0/queue/read_ahead_kb 2048
    write /sys/block/dm-1/queue/read_ahead_kb 2048

on property:sys.boot_completed=1
    # end boot time fs tune
    write /sys/block/sda/queue/read_ahead_kb 512
    ...

متنوعة

  • فعِّل حجم التخزين المؤقت المُسبَق لتجزئة dm-verity باستخدام إعدادات kernel DM_VERITY_HASH_PREFETCH_MIN_SIZE (الحجم التلقائي هو 128).
  • لتحسين ثبات نظام الملفات وإيقاف عملية التحقّق الإجباري التي تحدث عند كل عملية تشغيل، استخدِم أداة إنشاء ext4 الجديدة من خلال ضبط TARGET_USES_MKE2FS فيملف BoardConfig.mk.

تحليل I/O

لفهم أنشطة الإدخال/الإخراج أثناء عملية التمهيد، استخدِم بيانات ftrace الخاصة بالنواة (التي تستخدمها أيضًا أداة systrace):

trace_event=block,ext4 in BOARD_KERNEL_CMDLINE

لتقسيم أذونات الوصول إلى الملفات لكل ملف، عليك إجراء التغييرات التالية على النواة (نواة التطوير فقط، ولا تستخدمها في نواة الإنتاج):

diff --git a/fs/open.c b/fs/open.c
index 1651f35..a808093 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -981,6 +981,25 @@
 }
 EXPORT_SYMBOL(file_open_root);
 
+static void _trace_do_sys_open(struct file *filp, int flags, int mode, long fd)
+{
+       char *buf;
+       char *fname;
+
+       buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
+       if (!buf)
+               return;
+       fname = d_path(&filp-<f_path, buf, PAGE_SIZE);
+
+       if (IS_ERR(fname))
+               goto out;
+
+       trace_printk("%s: open(\"%s\", %d, %d) fd = %ld, inode = %ld\n",
+                     current-<comm, fname, flags, mode, fd, filp-<f_inode-<i_ino);
+out:
+       kfree(buf);
+}
+
long do_sys_open(int dfd, const char __user *filename, int flags, umode_t mode)
 {
 	struct open_flags op;
@@ -1003,6 +1022,7 @@
 		} else {
 			fsnotify_open(f);
 			fd_install(fd, f);
+			_trace_do_sys_open(f, flags, mode, fd);

استخدِم النصوص البرمجية التالية للمساعدة في تحليل أداء عملية التمهيد.

  • system/extras/boottime_tools/bootanalyze/bootanalyze.py يقيس وقت بدء التشغيل مع تقسيم الخطوات المهمة في عملية بدء التشغيل.
  • system/extras/boottime_tools/io_analysis/check_file_read.py boot_trace يوفّر معلومات الوصول لكل ملف.
  • system/extras/boottime_tools/io_analysis/check_io_trace_all.py boot_trace يوفّر تفاصيل على مستوى النظام.

تحسين init.*.rc

إنّ Init هو الجسر الذي يربط بين النواة وإطار العمل، ويقضي الأجهزة عادةً بضع ثوانٍ في مراحل مختلفة من عملية Init.

تنفيذ المهام بالتوازي

على الرغم من أنّ عملية Android init الحالية هي عملية أحادية السلسلة تقريبًا، فإنه لا يزال بإمكانك تنفيذ بعض المهام بالتوازي.

  • تنفيذ الأوامر البطيئة في خدمة نص برمجي لنظام التشغيل وبدء تنفيذها لاحقًا من خلال الانتظار لخاصية معيّنة يتيح نظام التشغيل Android 8.0 حالة الاستخدام هذه باستخدام الأمر الجديد wait_for_property.
  • تحديد العمليات البطيئة في init يسجِّل النظام الأمر init exec/wait_for_prop أو أي إجراء يستغرق وقتًا طويلاً (في Android 8.0، أي أمر يستغرق أكثر من 50 ملي ثانية). مثلاً:
    init: Command 'wait_for_coldboot_done' action=wait_for_coldboot_done returned 0 took 585.012ms

    قد تشير مراجعة هذا السجلّ إلى فرص لإجراء تحسينات.

  • ابدأ الخدمات وفعِّل الأجهزة الطرفية في المسار الحرج مبكرًا. على سبيل المثال، تتطلّب بعض وحدات التحكّم في العمليات الأمنية بدء الخدمات ذات الصلة بالأمان قبل بدء SurfaceFlinger. راجِع سجلّ النظام عندما يعرض ServiceManager رسالة "الانتظار لبدء الخدمة"، فهذا عادةً ما يشير إلى أنّه يجب أولاً بدء الخدمة التابعة.
  • أزِل أي خدمات وأوامر غير مستخدَمة في init.*.rc. يجب تأجيل أيّ عملية غير مستخدَمة في مرحلة الإعداد المبكرة إلى أن تكتمل عملية التشغيل.

ملاحظة: خدمة Property هي جزء من عملية init، لذا قد يؤدي استدعاء setproperty أثناء عملية التمهيد إلى تأخير طويل إذا كانت init مشغولة في الطلبات المضمّنة.

استخدام ميزة "ضبط المخطِّط"

استخدِم ميزة "ضبط المخطِّط" لبدء التشغيل المبكر. مثال من هاتف Google Pixel:

on init
    # boottime stune
    write /dev/stune/schedtune.prefer_idle 1
    write /dev/stune/schedtune.boost 100
    on property:sys.boot_completed=1
    # reset stune
    write /dev/stune/schedtune.prefer_idle 0
    write /dev/stune/schedtune.boost 0

    # or just disable EAS during boot
    on init
    write /sys/kernel/debug/sched_features NO_ENERGY_AWARE
    on property:sys.boot_completed=1
    write /sys/kernel/debug/sched_features ENERGY_AWARE

قد تحتاج بعض الخدمات إلى زيادة الأولوية أثناء التشغيل. مثال:

init.zygote64.rc:
service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server
    class main
    priority -20
    user root
...

بدء تكوين الجنين مبكرًا

يمكن للأجهزة التي تستخدم التشفير المستند إلى الملفات بدء عملية zygote في وقت أبكر من خلال بدء zygote-start (يتم تشغيل zygote تلقائيًا في فئة main، وهي لاحقة بكثير من zygote-start). عند إجراء ذلك، احرص على السماح بتشغيل zygote في جميع وحدات المعالجة المركزية (لأنّه قد يؤدي ضبط cpuset بشكلٍ خاطئ إلى فرض تشغيل zygote في وحدات معالجة مركزية معيّنة).

إيقاف ميزة "توفير الطاقة"

أثناء تشغيل الجهاز، يمكن إيقاف إعدادات توفير الطاقة للمكوّنات، مثل وحدة UFS و/أو وحدة التحكّم في سرعة وحدة المعالجة المركزية (CPU).

تحذير: يجب تفعيل ميزة "توفير الطاقة" في وضع الشاحن لزيادة الكفاءة.

on init
    # Disable UFS powersaving
    write /sys/devices/soc/${ro.boot.bootdevice}/clkscale_enable 0
    write /sys/devices/soc/${ro.boot.bootdevice}/clkgate_enable 0
    write /sys/devices/soc/${ro.boot.bootdevice}/hibern8_on_idle_enable 0
    write /sys/module/lpm_levels/parameters/sleep_disabled Y
on property:sys.boot_completed=1
    # Enable UFS powersaving
    write /sys/devices/soc/${ro.boot.bootdevice}/clkscale_enable 1
    write /sys/devices/soc/${ro.boot.bootdevice}/clkgate_enable 1
    write /sys/devices/soc/${ro.boot.bootdevice}/hibern8_on_idle_enable 1
    write /sys/module/lpm_levels/parameters/sleep_disabled N
on charger
    # Enable UFS powersaving
    write /sys/devices/soc/${ro.boot.bootdevice}/clkscale_enable 1
    write /sys/devices/soc/${ro.boot.bootdevice}/clkgate_enable 1
    write /sys/devices/soc/${ro.boot.bootdevice}/hibern8_on_idle_enable 1
    write /sys/class/typec/port0/port_type sink
    write /sys/module/lpm_levels/parameters/sleep_disabled N

تأجيل الإعداد غير المهم

يمكن تأجيل الإعداد غير المهم، مثل ZRAM، إلى boot_complete.

on property:sys.boot_completed=1
   # Enable ZRAM on boot_complete
   swapon_all /vendor/etc/fstab.${ro.hardware}

تحسين الصورة المتحركة لبدء التشغيل

اتّبِع النصائح التالية لتحسين الصورة المتحركة لبدء التشغيل.

ضبط ميزة "البدء المبكر"

يتيح نظام التشغيل Android 8.0 بدء عرض صورة التمهيد مبكرًا، قبل تركيب قسم userdata. ومع ذلك، حتى عند استخدام سلسلة أدوات ext4 الجديدة في Android 8.0، يستمر تشغيل fsck بشكل دوري لأسباب تتعلق بالسلامة، ما يؤدي إلى تأخير بدء خدمة bootanimation.

لبدء عرض bootanimation مبكرًا، يمكنك تقسيم عملية تحميل fstab إلى مرحلتين:

  • في المرحلة المبكرة، يجب تركيب الأقسام فقط (مثل system/ وvendor/) التي لا تتطلّب بدء عمليات التحقّق، ثم بدء خدمات الرسوم المتحرّكة لبدء التشغيل والمكوّنات التابعة لها (مثل servicemanager وsurfaceflinger).
  • في المرحلة الثانية، يمكنك تركيب الأقسام (مثل data/) التي تتطلّب إجراء عمليات التحقّق.

سيتم تشغيل صورة التمهيد المتحركة بشكل أسرع بكثير (وبوقت ثابت) بغض النظر عن fsck.

إنهاء التنظيف

بعد تلقّي إشارة الخروج، يشغِّل bootanimation الجزء الأخير، ويمكن أن يؤدي طوله إلى إبطاء وقت التشغيل. لا يحتاج النظام الذي يتم تشغيله بسرعة إلى استخدام مؤثرات متحركة مديدة يمكن أن تحجب بشكل فعال أي تحسينات تم إجراؤها. ننصحك بجعل كلّ من القسم المتكرّر والقسم الأخير قصيرَين.

تحسين SELinux

اتّبِع النصائح التالية لتحسين SELinux من أجل تقليل أوقات بدء التشغيل.

  • استخدِم تعبيرات عادية (regex) نظيفة. يمكن أن يؤدي التعبير العادي الذي تم إنشاؤه بشكلٍ سيئ إلى زيادة كبيرة في وقت التشغيل عند مطابقة سياسة SELinux لملف sys/devices في file_contexts. على سبيل المثال، يفرض التعبير العادي /sys/devices/.*abc.*(/.*)? عن طريق الخطأ فحص كل الدلائل الفرعية /sys/devices التي تحتوي على "abc"، ما يؤدي إلى تفعيل المطابقات لكل من /sys/devices/abc و/sys/devices/xyz/abc. سيؤدي تحسين هذا التعبير العادي إلى /sys/devices/[^/]*abc[^/]*(/.*)? إلى السماح بمطابقة /sys/devices/abc فقط.
  • انقل التصنيفات إلى genfscon. تنقل ميزة SELinux الحالية بادئات مطابقة الملفات إلى النواة في الملف الثنائي لنظام SELinux، حيث تطبّقها النواة على أنظمة الملفات التي أنشأتها. يساعد ذلك أيضًا في إصلاح الملفات التي أنشأها نواة النظام والتي تم تصنيفها بشكل خاطئ، ما يمنع حالات السباق التي يمكن أن تحدث بين عمليات مساحة المستخدم التي تحاول الوصول إلى هذه الملفات قبل إعادة تصنيفها.

الأدوات والأساليب

استخدِم الأدوات التالية لمساعدتك في جمع البيانات لأهداف التحسين.

Bootchart

يوفّر Bootchart تفاصيل عن حمولة وحدة المعالجة المركزية وعمليات الإدخال/الإخراج لجميع العمليات في النظام بأكمله. ولا يتطلّب هذا الإجراء إعادة إنشاء صورة النظام، ويمكن استخدامه كفحص سريع للتحقّق من صحة النظام قبل الانتقال إلى systrace.

لتفعيل bootchart:

adb shell 'touch /data/bootchart/enabled'
adb reboot

بعد التشغيل، يمكنك جلب الرسم البياني لبدء التشغيل:

$ANDROID_BUILD_TOP/system/core/init/grab-bootchart.sh

عند الانتهاء، احذف /data/bootchart/enabled لمنع جمع البيانات في كل مرة.

إذا لم يعمل bootchart وظهرت لك رسالة خطأ تفيد بأنّ bootchart.png غير متوفّر، عليك تنفيذ الخطوات التالية:
  1. نفِّذ الأوامر التالية:
          sudo apt install python-is-python3
          cd ~/Documents
          git clone https://github.com/xrmx/bootchart.git
          cd bootchart/pybootchartgui
          mv main.py.in main.py
        
  2. عدِّل $ANDROID_BUILD_TOP/system/core/init/grab-bootchart.sh للإشارة إلى النسخة المحلية من pybootchartgui (الموجودة في ~/Documents/bootchart/pybootchartgui.py).

Systrace

يتيح Systrace جمع عمليات تتبُّع كل من kernel وAndroid أثناء عملية التشغيل. يمكن أن يساعدك عرض systrace في تحليل مشكلة معيّنة أثناء التشغيل. (ومع ذلك، للتحقّق من متوسّط العدد أو العدد التراكمي أثناء عملية التمهيد بالكامل، من الأسهل الاطّلاع على تتبُّع النواة مباشرةً).

لتفعيل systrace أثناء بدء التشغيل:

  • في frameworks/native/cmds/atrace/atrace.rc، غيِّر ما يلي:
      write /sys/kernel/debug/tracing/tracing_on 0
      write /sys/kernel/tracing/tracing_on 0

    إلى:

      #    write /sys/kernel/debug/tracing/tracing_on 0
      #    write /sys/kernel/tracing/tracing_on 0
  • يؤدي ذلك إلى تفعيل التتبُّع (الذي يكون غير مفعَّل تلقائيًا).

  • في ملف device.mk، أضِف السطر التالي:
    PRODUCT_PROPERTY_OVERRIDES +=    debug.atrace.tags.enableflags=802922
    PRODUCT_PROPERTY_OVERRIDES +=    persist.traced.enable=0
  • في ملف الجهاز BoardConfig.mk، أضِف ما يلي:
    BOARD_KERNEL_CMDLINE := ... trace_buf_size=64M trace_event=sched_wakeup,sched_switch,sched_blocked_reason,sched_cpu_hotplug
  • للحصول على تحليل مفصّل لعمليات الإدخال/الإخراج، أضِف أيضًا block وext4 وf2fs.

  • في ملف init.rc الخاص بالجهاز، أضِف ما يلي:
    on property:sys.boot_completed=1          // This stops tracing on boot complete
    write /d/tracing/tracing_on 0
    write /d/tracing/events/ext4/enable 0
    write /d/tracing/events/f2fs/enable 0
    write /d/tracing/events/block/enable 0
  • بعد بدء التشغيل، يمكنك جلب التتبُّع:

    adb root && adb shell atrace --async_stop -z -c -o /data/local/tmp/boot_trace
    adb pull /data/local/tmp/boot_trace
    $ANDROID_BUILD_TOP/external/chromium-trace/systrace.py --from-file=boot_trace