بهینه سازی زمان بوت

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

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

جزء بهبود
بوت لودر
  • با حذف گزارش UART، 1.6 ثانیه ذخیره شد
  • با تغییر به LZ4 از GZIP، 0.4 ثانیه ذخیره شد
هسته دستگاه
  • 0.3 ثانیه با حذف تنظیمات هسته استفاده نشده و کاهش اندازه درایور ذخیره شد
  • 0.3 ثانیه با بهینه سازی پیش واکشی dm-verity ذخیره شد
  • 0.15 ثانیه برای حذف انتظار/تست غیر ضروری در درایور ذخیره شد
  • 0.12 ثانیه برای حذف CONFIG_CC_OPTIMIZE_FOR_SIZE ذخیره شد
تنظیم I/O
  • 2s در بوت معمولی ذخیره شد
  • 25 ثانیه در اولین بوت ذخیره شد
init.*.rc
  • 1.5 ثانیه با موازی کردن دستورات init ذخیره شد
  • با شروع زودهنگام زیگوت، 0.25 ثانیه ذخیره شد
  • 0.22 ثانیه توسط cpuset tune ذخیره شد
پویانمایی بوت
  • 2 ثانیه زودتر در بوت بدون راه‌اندازی fsck شروع شد، در بوت بسیار بزرگ‌تر با بوت راه‌اندازی fsck
  • 5s در Pixel XL با خاموش شدن فوری انیمیشن بوت ذخیره شد
خط مشی SELinux 0.2 ثانیه توسط genfscon ذخیره شد

بهینه سازی بوت لودر

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

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

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

بهبود کارایی ورودی/خروجی برای سریع‌تر کردن زمان راه‌اندازی بسیار مهم است و خواندن هر چیزی که لازم نیست باید به بعد از راه‌اندازی موکول شود (در Google Pixel، حدود 1.2 گیگابایت داده هنگام بوت خوانده می‌شود).

تنظیم فایل سیستم

خواندن پیشخوان هسته لینوکس زمانی شروع می شود که یک فایل از ابتدا خوانده می شود یا زمانی که بلوک ها به صورت متوالی خوانده می شوند، تنظیم پارامترهای زمانبندی I/O را به طور خاص برای بوت کردن ضروری می کند (که مشخصات بار کاری متفاوتی نسبت به برنامه های معمولی دارد).

دستگاه‌هایی که از به‌روزرسانی‌های یکپارچه (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 روشن کنید (اندازه پیش فرض 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 فعلی اندروید کم و بیش یک فرآیند رشته ای است، هنوز هم می توانید برخی از کارها را به صورت موازی انجام دهید.

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

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

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

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

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

از تنظیم زمانبندی برای راه اندازی اولیه استفاده کنید. مثالی از گوگل پیکسل:

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 در کلاس اصلی راه‌اندازی می‌شود که بسیار دیرتر از 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 در اندروید 8.0، fsck همچنان به دلایل ایمنی به طور دوره‌ای فعال می‌شود و باعث تاخیر در شروع سرویس بوتانیمیشن می‌شود.

برای اینکه بوتانیمیشن زودتر شروع شود، mount fstab را به دو مرحله تقسیم کنید:

  • در مرحله اولیه، فقط پارتیشن‌هایی (مانند system/ و vendor/ ) را که نیازی به بررسی اجرا ندارند، نصب کنید، سپس سرویس‌های پویانمایی بوت و وابستگی‌های آن (مانند servicemanager و surfaceflinger) را شروع کنید.
  • در مرحله دوم، پارتیشن‌هایی (مانند data/ ) را که نیاز به بررسی اجرا دارند، سوار کنید.

انیمیشن بوت بدون در نظر گرفتن fsck بسیار سریعتر (و در زمان ثابت) شروع می شود.

اتمام تمیز

پس از دریافت سیگنال خروج، بوتانیمیشن آخرین قسمت را اجرا می کند که طول آن می تواند زمان بوت را کاهش دهد. سیستمی که به سرعت بوت می‌شود، نیازی به انیمیشن‌های طولانی ندارد که می‌تواند به‌طور موثری هر گونه پیشرفت انجام شده را پنهان کند. ما توصیه می کنیم که هم حلقه تکرار و هم آخر را کوتاه کنید.

بهینه سازی SELinux

از نکات زیر برای بهینه سازی SELinux برای بهبود زمان بوت استفاده کنید.

  • از عبارات منظم تمیز (regex) استفاده کنید . Regex با شکل‌دهی ضعیف می‌تواند هنگام تطبیق خط‌مشی SELinux برای sys/devices در file_contexts منجر به سربار زیادی شود. به عنوان مثال، regex /sys/devices/.*abc.*(/.*)? به اشتباه همه زیرشاخه‌های /sys/devices را که حاوی "abc" هستند، اسکن می‌کند و مطابقت را برای /sys/devices/abc و /sys/devices/xyz/abc فعال می‌کند. آیا این regex به /sys/devices/[^/]*abc[^/]*(/.*)? تطابق را فقط برای /sys/devices/abc فعال می کند.
  • برچسب ها را به genfscon منتقل کنید. این ویژگی SELinux موجود، پیشوندهای تطبیق فایل را به هسته در باینری SELinux منتقل می کند، جایی که هسته آنها را برای فایل سیستم های تولید شده توسط هسته اعمال می کند. این همچنین کمک می‌کند تا فایل‌های دارای برچسب نادرست ایجاد شده توسط هسته را برطرف کرده و از شرایط مسابقه‌ای که می‌تواند بین فرآیندهای فضای کاربر که تلاش می‌کنند به این فایل‌ها دسترسی داشته باشند قبل از برچسب‌گذاری مجدد رخ دهد، جلوگیری می‌کند.

ابزار و روش ها

از ابزارهای زیر برای کمک به جمع آوری داده ها برای اهداف بهینه سازی استفاده کنید.

بوتچارت

Bootchart تفکیک بار CPU و I/O همه فرآیندها را برای کل سیستم فراهم می کند. این نیازی به بازسازی تصویر سیستم ندارد و می تواند به عنوان یک بررسی سریع سلامت عقل قبل از فرو رفتن در systrace استفاده شود.

برای فعال کردن بوتچارت:

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

پس از راه‌اندازی، نمودار بوت را واکشی کنید:

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

پس از اتمام، /data/bootchart/enabled را حذف کنید تا از جمع آوری داده ها در هر بار جلوگیری کنید.

اگر بوتچارت کار نمی کند و با خطای اینکه 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 می تواند در تجزیه و تحلیل یک مشکل خاص در هنگام بوت آپ کمک کند. (با این حال، برای بررسی میانگین تعداد یا تعداد انباشته شده در کل بوت، ساده تر است که مستقیماً به ردیابی هسته نگاه کنید).

برای فعال کردن 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
  • برای تجزیه و تحلیل دقیق ورودی/خروجی، بلوک و 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