Mengoptimalkan waktu booting

Dokumen ini memberikan panduan partner untuk meningkatkan waktu booting untuk perangkat Android tertentu. Waktu booting adalah komponen penting dari performa sistem karena pengguna harus menunggu booting selesai sebelum dapat menggunakan perangkat. Untuk perangkat seperti mobil yang sering melakukan cold boot, memiliki waktu booting yang cepat sangatlah penting (tidak ada yang suka menunggu puluhan detik hanya untuk memasukkan tujuan navigasi).

Android 8.0 memungkinkan pengurangan waktu booting dengan mendukung beberapa peningkatan di berbagai komponen. Tabel berikut meringkas peningkatan performa ini (seperti yang diukur pada perangkat Google Pixel dan Pixel XL).

Komponen Peningkatan
Bootloader
  • Menghemat 1,6 detik dengan menghapus log UART
  • Menghemat 0,4 detik dengan mengubah ke LZ4 dari GZIP
Kernel perangkat
  • Menghemat 0,3 detik dengan menghapus konfigurasi kernel yang tidak digunakan dan mengurangi ukuran driver
  • Menghemat 0,3 detik dengan pengoptimalan pengambilan data dm-verity
  • Menghemat 0,15 detik untuk menghapus penantian/pengujian yang tidak perlu di driver
  • Menghemat 0,12 detik untuk menghapus CONFIG_CC_OPTIMIZE_FOR_SIZE
Penyesuaian I/O
  • Hemat 2 detik saat booting normal
  • Hemat 25 detik saat booting pertama
init.*.rc
  • Menghemat 1,5 detik dengan menjalankan perintah init secara paralel
  • Menghemat 0,25 detik dengan memulai zygote lebih awal
  • Menghemat 0,22 detik dengan penyesuaian cpuset
Animasi booting
  • Dimulai 2 detik lebih awal saat booting tanpa fsck dipicu, jauh lebih besar saat booting dengan boot yang dipicu fsck
  • Menghemat waktu 5 detik di Pixel XL dengan segera menonaktifkan animasi booting
Kebijakan SELinux Menyimpan 0,2 detik dengan genfscon

Mengoptimalkan bootloader

Untuk mengoptimalkan bootloader agar waktu booting lebih baik:

  • Untuk logging:
    • Nonaktifkan penulisan log ke UART karena dapat memerlukan waktu lama dengan banyak logging. (Di perangkat Google Pixel, kami mendapati bahwa hal ini memperlambat bootloader 1,5 detik).
    • Catat hanya situasi error dan pertimbangkan untuk menyimpan informasi lain ke memori dengan mekanisme terpisah untuk diambil.
  • Untuk dekompresi kernel, pertimbangkan untuk menggunakan LZ4 untuk hardware kontemporer, bukan GZIP (contoh patch). Perlu diingat bahwa opsi kompresi kernel yang berbeda dapat memiliki waktu pemuatan dan dekompresi yang berbeda, dan beberapa opsi mungkin berfungsi lebih baik daripada yang lain untuk hardware tertentu.
  • Periksa waktu tunggu yang tidak perlu untuk debouncing/entri mode khusus dan minimalkan waktu tunggu tersebut.
  • Teruskan waktu booting yang dihabiskan di bootloader ke kernel sebagai cmdline.
  • Periksa clock CPU dan pertimbangkan paralelisasi (memerlukan dukungan multi-core) untuk pemuatan kernel dan inisialisasi I/O.

Mengoptimalkan efisiensi I/O

Meningkatkan efisiensi I/O sangat penting untuk mempercepat waktu booting, dan membaca apa pun yang tidak diperlukan harus ditangguhkan hingga setelah booting (di Google Pixel, sekitar 1,2 GB data dibaca saat booting).

Menyesuaikan sistem file

Pembacaan di depan kernel Linux dimulai saat file dibaca dari awal atau saat blok dibaca secara berurutan, sehingga perlu menyesuaikan parameter penjadwal I/O secara khusus untuk booting (yang memiliki karakterisasi beban kerja yang berbeda dari aplikasi normal).

Perangkat yang mendukung update lancar (A/B) sangat diuntungkan dari penyesuaian sistem file saat pertama kali melakukan booting (misalnya, 20 detik di Google Pixel). Sebagai contoh, kami menyesuaikan parameter berikut untuk 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
    ...

Lain-lain

  • Aktifkan ukuran prefetch hash dm-verity menggunakan konfigurasi kernel DM_VERITY_HASH_PREFETCH_MIN_SIZE (ukuran default adalah 128).
  • Untuk stabilitas sistem file yang lebih baik dan pemeriksaan paksa yang dihapus yang terjadi pada setiap booting, gunakan alat pembuatan ext4 baru dengan menetapkan TARGET_USES_MKE2FS di BoardConfig.mk.

Menganalisis I/O

Untuk memahami aktivitas I/O selama booting, gunakan data ftrace kernel (juga digunakan oleh systrace):

trace_event=block,ext4 in BOARD_KERNEL_CMDLINE

Untuk menguraikan akses file untuk setiap file, buat perubahan berikut pada kernel (khusus kernel pengembangan; jangan gunakan dalam kernel produksi):

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);

Gunakan skrip berikut untuk membantu menganalisis performa booting.

  • system/extras/boottime_tools/bootanalyze/bootanalyze.py Mengukur waktu booting dengan perincian langkah-langkah penting dalam proses booting.
  • system/extras/boottime_tools/io_analysis/check_file_read.py boot_trace Memberikan informasi akses per file.
  • system/extras/boottime_tools/io_analysis/check_io_trace_all.py boot_trace Memberikan perincian tingkat sistem.

Mengoptimalkan init.*.rc

Init adalah jembatan dari kernel hingga framework dibuat, dan perangkat biasanya menghabiskan beberapa detik dalam berbagai tahap init.

Menjalankan tugas secara paralel

Meskipun init Android saat ini kurang lebih merupakan proses dengan satu thread, Anda masih dapat melakukan beberapa tugas secara paralel.

  • Jalankan perintah lambat di layanan skrip shell dan gabungkan nanti dengan menunggu properti tertentu. Android 8.0 mendukung kasus penggunaan ini dengan perintah wait_for_property baru.
  • Identifikasi operasi yang lambat di init. Sistem mencatat log perintah init exec/wait_for_prop atau tindakan apa pun yang memerlukan waktu lama (di Android 8.0, perintah apa pun yang memerlukan waktu lebih dari 50 md). Contoh:
    init: Command 'wait_for_coldboot_done' action=wait_for_coldboot_done returned 0 took 585.012ms

    Meninjau log ini dapat menunjukkan peluang untuk peningkatan.

  • Mulai layanan dan aktifkan perangkat periferal di jalur kritis lebih awal. Misalnya, beberapa SOC memerlukan layanan terkait keamanan yang dimulai sebelum memulai SurfaceFlinger. Tinjau log sistem saat ServiceManager menampilkan "tunggu layanan" — ini biasanya merupakan tanda bahwa layanan dependen harus dimulai terlebih dahulu.
  • Hapus layanan dan perintah yang tidak digunakan di init.*.rc. Apa pun yang tidak digunakan dalam inisialisasi tahap awal harus ditangguhkan hingga booting selesai.

Catatan: Layanan properti adalah bagian dari proses init, sehingga memanggil setproperty selama booting dapat menyebabkan penundaan yang lama jika init sibuk dalam perintah bawaan.

Menggunakan penyesuaian penjadwal

Gunakan penyesuaian penjadwal untuk booting awal. Contoh dari 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

Beberapa layanan mungkin memerlukan peningkatan prioritas selama booting. Contoh:

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

Memulai zygote lebih awal

Perangkat dengan enkripsi berbasis file dapat memulai zygote lebih awal pada pemicu zygote-start (secara default, zygote diluncurkan di class utama, yang jauh lebih lambat daripada zygote-start). Saat melakukannya, pastikan untuk mengizinkan zygote berjalan di semua CPU (karena setelan cpuset yang salah dapat memaksa zygote berjalan di CPU tertentu).

Menonaktifkan mode hemat daya

Selama booting perangkat, setelan hemat daya untuk komponen seperti UFS dan/atau pengontrol CPU dapat dinonaktifkan.

Perhatian: Hemat daya harus diaktifkan dalam mode pengisi daya untuk efisiensi.

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

Menunda inisialisasi non-penting

Inisialisasi non-penting seperti ZRAM dapat ditangguhkan ke boot_complete.

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

Mengoptimalkan animasi booting

Gunakan tips berikut untuk mengoptimalkan animasi booting.

Mengonfigurasi awal mulai

Android 8.0 memungkinkan animasi booting dimulai lebih awal, sebelum memasang partisi userdata. Namun, meskipun menggunakan rantai alat ext4 baru di Android 8.0, fsck masih dipicu secara berkala karena alasan keamanan, sehingga menyebabkan penundaan dalam memulai layanan bootanimation.

Agar animasi booting dimulai lebih awal, bagi pemasangan fstab menjadi dua fase:

  • Pada fase awal, hanya pasang partisi (seperti system/ dan vendor/) yang tidak memerlukan pemeriksaan operasi, lalu mulai layanan animasi booting dan dependensinya (seperti servicemanager dan surfaceflinger).
  • Pada fase kedua, pasang partisi (seperti data/) yang memerlukan pemeriksaan run.

Animasi booting akan dimulai jauh lebih cepat (dan dalam waktu konstan) terlepas dari fsck.

Selesaikan pembersihan

Setelah menerima sinyal keluar, bootanimation akan memutar bagian terakhir, yang durasinya dapat memperlambat waktu booting. Sistem yang melakukan booting dengan cepat tidak memerlukan animasi yang panjang yang dapat secara efektif menyembunyikan peningkatan apa pun yang dilakukan. Sebaiknya buat loop berulang dan akhiran singkat.

Mengoptimalkan SELinux

Gunakan tips berikut untuk mengoptimalkan SELinux agar waktu booting lebih baik.

  • Gunakan ekspresi reguler (regex) yang bersih. Regex yang dibentuk dengan buruk dapat menyebabkan banyak overhead saat mencocokkan kebijakan SELinux untuk sys/devices di file_contexts. Misalnya, ekspresi reguler /sys/devices/.*abc.*(/.*)? secara keliru memaksa pemindaian semua subdirektori /sys/devices yang berisi "abc", sehingga memungkinkan pencocokan untuk /sys/devices/abc dan /sys/devices/xyz/abc. Meningkatkan ekspresi reguler ini ke /sys/devices/[^/]*abc[^/]*(/.*)? akan memungkinkan kecocokan hanya untuk /sys/devices/abc.
  • Pindahkan label ke genfscon. Fitur SELinux yang ada ini meneruskan awalan pencocokan file ke kernel dalam biner SELinux, tempat kernel menerapkannya ke sistem file yang dibuat kernel. Hal ini juga membantu memperbaiki file yang dibuat kernel yang salah diberi label, sehingga mencegah kondisi perlombaan yang dapat terjadi antara proses ruang pengguna yang mencoba mengakses file ini sebelum pemberian label ulang terjadi.

Alat dan metode

Gunakan alat berikut untuk membantu Anda mengumpulkan data untuk target pengoptimalan.

Bootchart

Bootchart memberikan perincian beban CPU dan I/O dari semua proses untuk seluruh sistem. Tindakan ini tidak memerlukan pembuatan ulang image sistem dan dapat digunakan sebagai pemeriksaan kewarasan cepat sebelum mempelajari systrace.

Untuk mengaktifkan bootchart:

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

Setelah booting, ambil diagram booting:

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

Setelah selesai, hapus /data/bootchart/enabled untuk mencegah pengumpulan data setiap saat.

Jika bootchart tidak berfungsi dan Anda mendapatkan error yang menyatakan bahwa bootchart.png tidak ada, lakukan hal berikut:
  1. Jalankan perintah berikut:
          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. Perbarui $ANDROID_BUILD_TOP/system/core/init/grab-bootchart.sh agar mengarah ke salinan lokal pybootchartgui (terletak di ~/Documents/bootchart/pybootchartgui.py)

Systrace

Systrace memungkinkan pengumpulan rekaman aktivitas kernel dan Android selama booting. Visualisasi systrace dapat membantu menganalisis masalah tertentu selama proses booting. (Namun, untuk memeriksa jumlah rata-rata atau jumlah akumulasi selama seluruh booting, akan lebih mudah untuk melihat pelacakan kernel secara langsung).

Untuk mengaktifkan systrace selama booting:

  • Di frameworks/native/cmds/atrace/atrace.rc, ubah:
      write /sys/kernel/debug/tracing/tracing_on 0
      write /sys/kernel/tracing/tracing_on 0

    Untuk:

      #    write /sys/kernel/debug/tracing/tracing_on 0
      #    write /sys/kernel/tracing/tracing_on 0
  • Tindakan ini akan mengaktifkan pelacakan (yang dinonaktifkan secara default).

  • Di file device.mk, tambahkan baris berikut:
    PRODUCT_PROPERTY_OVERRIDES +=    debug.atrace.tags.enableflags=802922
    PRODUCT_PROPERTY_OVERRIDES +=    persist.traced.enable=0
  • Di file BoardConfig.mk perangkat, tambahkan kode berikut:
    BOARD_KERNEL_CMDLINE := ... trace_buf_size=64M trace_event=sched_wakeup,sched_switch,sched_blocked_reason,sched_cpu_hotplug
  • Untuk analisis I/O mendetail, tambahkan juga blok dan ext4 serta f2fs.

  • Dalam file init.rc khusus perangkat, tambahkan kode berikut:
    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
  • Setelah booting, ambil rekaman aktivitas:

    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