بهینه‌سازی زمان‌های راه‌اندازی

این سند راهنمایی برای شریک ارائه می‌دهد تا زمان راه‌اندازی را برای دستگاه‌های Android خاصی بهبود بخشد. زمان راه‌اندازی جزء مهمی از عملکرد سیستم است زیرا کاربران باید منتظر بمانند تا راه‌اندازی کامل شود و سپس بتوانند از دستگاه استفاده کنند. برای دستگاه‌هایی مثل خودرو که راه‌اندازی مجدد در آن‌ها بیشتر اتفاق می‌افتد، داشتن زمان راه‌اندازی سریع بسیار مهم است (هیچ‌کس دوست ندارد ده‌ها ثانیه منتظر بماند تا بتواند مقصد ناوبری را وارد کند).

‫Android 8.0 با پشتیبانی از چندین بهبود در طیف وسیعی از عناصر، امکان کاهش زمان راه‌اندازی را فراهم می‌کند. جدول زیر این بهبودهای عملکرد را خلاصه می‌کند (همان‌گونه که در دستگاه‌های Google Pixel و Pixel XL اندازه‌گیری شده است).

مؤلفه بهبود
راه‌انداز سیستم
  • با برداشتن گزارش UART،‏ ۱٫۶ ثانیه ذخیره شد
  • با تغییر از GZIP به LZ4،‏ ۰٫۴ ثانیه صرفه‌جویی شد
هسته دستگاه
  • با برداشتن پیکربندی‌های هسته استفاده‌نشده و کاهش اندازه درایور، ۰٫۳ ثانیه صرفه‌جویی شد
  • ‫۰٫۳ ثانیه با بهینه‌سازی پیش‌واکشی dm-verity صرفه‌جویی شد
  • Saved 0.15s to remove unnecessary wait/test in driver
  • با برداشتن CONFIG_CC_OPTIMIZE_FOR_SIZE،‏ ۰٫۱۲ ثانیه صرفه‌جویی شد
تنظیم ورودی/خروجی
  • ‫۲ ثانیه در راه‌اندازی عادی صرفه‌جویی شد
  • ‫۲۵ ثانیه در اولین راه‌اندازی صرفه‌جویی شد
init.*.rc
  • با موازی کردن فرمان‌های آغازین ۱٫۵ ثانیه صرفه‌جویی شد
  • با زود شروع کردن زیگوت ۰٫۲۵ ثانیه صرفه‌جویی شد
  • با تنظیم cpuset،‏ ۰٫۲۲ ثانیه صرفه‌جویی شد
پویانمایی راه‌اندازی
  • در راه‌اندازی بدون راه‌اندازی fsck، ۲ ثانیه زودتر شروع شد، در راه‌اندازی با راه‌اندازی fsck بسیار بزرگ‌تر است
  • با خاموش کردن فوری پویانمایی راه‌اندازی، ۵ ثانیه در Pixel XL صرفه‌جویی شد
خط‌مشی SELinux ‫۰٫۲ ثانیه در genfscon ذخیره شد

بهینه‌سازی راه‌انداز سیستم

برای بهینه‌سازی راه‌انداز سیستم برای بهبود زمان‌های راه‌اندازی:

  • برای ورود به سیستم:
    • نوشتن گزارش در UART غیرفعال شود زیرا با گزارش‌گیری زیاد ممکن است زمان زیادی طول بکشد. (در دستگاه‌های Google Pixel، متوجه شدیم که این کار سرعت بارکننده را ۱٫۵ ثانیه کاهش می‌دهد).
    • فقط وضعیت‌های خطا را ثبت کنید و اطلاعات دیگر را با سازوکار جداگانه‌ای برای بازیابی در حافظه ذخیره کنید.
  • برای فشرده‌سازی هسته، برای سخت‌افزار معاصر به‌جای GZIP از LZ4 استفاده کنید (مثال وصله). به‌خاطر داشته باشید که گزینه‌های مختلف فشرده‌سازی هسته می‌توانند زمان‌های بارگیری و باز کردن فشرده‌سازی متفاوتی داشته باشند، و برخی‌از گزینه‌ها ممکن است برای سخت‌افزار خاص شما بهتر از بقیه عمل کنند.
  • زمان‌های انتظار غیرضروری برای ورود به حالت ویژه/لرزش‌گیری را بررسی کنید و آن‌ها را به حداقل برسانید.
  • زمان بوت صرف‌شده در bootloader را به‌عنوان cmdline به هسته منتقل کنید.
  • ساعت CPU را بررسی کنید و موازی‌سازی (نیازمند پشتیبانی چندهسته‌ای) را برای بار کردن هسته و مقداردهی اولیه I/O درنظر بگیرید.

بهینه‌سازی کارایی ورودی/خروجی

بهبود کارایی ورودی/خروجی برای سریع‌تر کردن زمان راه‌اندازی بسیار مهم است، و خواندن هر چیزی که ضروری نیست باید تا بعداز راه‌اندازی به‌تعویق بیفتد (در Google Pixel، حدود ۱٫۲ گیگابایت داده در زمان راه‌اندازی خوانده می‌شود).

تنظیم کردن سیستم فایل

وقتی فایلی از ابتدا خوانده می‌شود یا وقتی بلوک‌ها به‌ترتیب خوانده می‌شوند، پیش‌خوان هسته Linux فعال می‌شود، که باعث می‌شود لازم شود پارامترهای زمان‌بندی ورودی/خروجی به‌طور خاص برای راه‌اندازی تنظیم شود (که مشخصه‌های بار کاری متفاوتی نسبت‌به برنامه‌های عادی دارد).

دستگاه‌هایی که از به‌روزرسانی‌های یکپارچه (A/B) پشتیبانی می‌کنند، از تنظیم سیستم فایل در اولین راه‌اندازی (مثلاً ۲۰ ثانیه در 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 را روشن کنید DM_VERITY_HASH_PREFETCH_MIN_SIZE (اندازه پیش‌فرض ۱۲۸ است).
  • برای پایداری بهتر سیستم فایل و بررسی اجباری حذف‌شده‌ای که در هر راه‌اندازی رخ می‌دهد، از ابزار نسل جدید 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, const struct open_how *how, 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, how->flags, how->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 فعلی کم‌وبیش یک فرایند تک‌رشته‌ای است، اما همچنان می‌توانید برخی‌از وظایف را به‌صورت موازی انجام دهید.

  • فرمان‌های کند را در سرویس دستورگان پوسته‌ای اجرا کنید و بعداً با انتظار برای دارایی خاص به آن بپیوندید. ‫Android 8.0 از این مورد استفاده با فرمان جدید wait_for_property پشتیبانی می‌کند.
  • شناسایی عملیات‌های کند در init. سیستم فرمان init exec/wait_for_prop یا هر اقدامی را که زمان زیادی طول می‌کشد (در Android 8.0، هر فرمانی که بیشتر از ۵۰ میلی‌ثانیه طول می‌کشد) ثبت می‌کند. برای مثال:
    init: Command 'wait_for_coldboot_done' action=wait_for_coldboot_done returned 0 took 585.012ms

    مرور این گزارش می‌تواند فرصت‌هایی برای بهبود را نشان دهد.

  • سرویس‌ها را شروع کنید و دستگاه‌های جانبی را در مسیر بحرانی زودتر فعال کنید. برای مثال، برخی‌از SOCها نیاز دارند که سرویس‌های مرتبط با امنیت قبل‌از شروع SurfaceFlinger شروع شوند. وقتی ServiceManager پیام «انتظار برای سرویس» برمی‌گرداند، گزارش سیستم را مرور کنید — این معمولاً نشانه‌ای است که سرویس وابسته باید ابتدا شروع شود.
  • هرگونه سرویس و فرمان استفاده‌نشده را در init.*.rc بردارید. هر چیزی که در راه‌اندازی اولیه مرحله اولیه استفاده نمی‌شود باید به تکمیل بوت موکول شود.

توجه: سرویس دارایی بخشی از فرایند init است، بنابراین اگر init در فرمان‌های داخلی مشغول باشد، فراخوانی setproperty درطول راه‌اندازی می‌تواند منجر به تأخیر طولانی شود.

استفاده از تنظیم زمان‌بندی

از تنظیم زمان‌بند برای راه‌اندازی اولیه استفاده کنید. مثال از 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 را زودتر در محرک zygote-start شروع کنند (به‌طور پیش‌فرض، zygote در کلاس اصلی راه‌اندازی می‌شود که بسیار دیرتر از zygote-start است). هنگام انجام این کار، مطمئن شوید که به zygote اجازه می‌دهید در همه CPUها اجرا شود (زیرا تنظیم cpuset اشتباه ممکن است zygote را مجبور کند در CPUهای خاصی اجرا شود).

غیرفعال کردن صرفه‌جویی در مصرف برق

درطول راه‌اندازی دستگاه، تنظیمات صرفه‌جویی در مصرف باتری برای مؤلفه‌هایی مانند 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 امکان شروع زودهنگام پویانمایی راه‌اندازی را، قبل‌از نصب پارتیشن داده‌های کاربر، فراهم می‌کند. بااین‌حال، حتی هنگام استفاده از زنجیره ابزار ext4 جدید در Android 8.0، fsck همچنان به‌دلیل مسائل ایمنی به‌صورت دوره‌ای راه‌اندازی می‌شود و باعث تأخیر در شروع سرویس 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 تفکیک بار CPU و I/O همه فرایندها را برای کل سیستم ارائه می‌دهد. این کار نیازی به بازسازی تصویر سیستم ندارد و می‌توان از آن به‌عنوان یک بررسی سریع قبل‌از پرداختن به 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 امکان جمع‌آوری ردگیری‌های هسته و 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