Mengoptimalkan waktu booting

Dokumen ini memberikan panduan kepada partner guna meningkatkan waktu booting untuk Perangkat Android. Waktu {i>booting<i} adalah komponen penting dari kinerja sistem karena pengguna harus menunggu hingga selesai {i>booting<i} sebelum dapat menggunakan perangkat. Untuk perangkat seperti mobil yang lebih sering melakukan {i> cold boot-up<i}, memiliki {i>quick boot<i} waktu sangat penting (tidak ada yang suka menunggu lusinan detik hanya untuk memasukkan tujuan navigasi).

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

Komponen Peningkatan
{i>Bootloader<i}
  • Menghemat 1,6 detik dengan menghapus log UART
  • Dihemat 0.4s dengan beralih 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 waktu tunggu/pengujian yang tidak perlu pada driver
  • Menghemat 0,12 dtk untuk menghapus CONFIG_CC_OPTIMIZE_FOR_SIZE
penyesuaian I/O
  • Disimpan 2 detik saat booting normal
  • Menghemat 25 detik pada booting pertama
init.*.rc
  • Menghemat 1,5 detik dengan memparalelkan perintah init
  • Hemat 0,25 detik dengan memulai zygote lebih awal
  • Disimpan 0.22s oleh cpuset tune
Animasi boot
  • Dimulai 2 detik sebelumnya saat {i>booting<i} tanpa dipicu fsck, jauh lebih besar saat {i>booting<i} dengan boot yang dipicu fsck
  • Menghemat 5 detik pada Pixel XL dengan penonaktifan animasi boot langsung
Kebijakan SELinux Dihemat 0,2 dtk pada oleh genfscon

Optimalkan bootloader

Untuk mengoptimalkan bootloader demi waktu booting yang lebih baik:

  • Untuk logging:
    • Nonaktifkan penulisan log ke UART karena bisa memakan waktu lama dengan banyak pembuatan log. (Di perangkat Google Pixel, kami menemukannya memperlambat bootloader 1.5).
    • Hanya catat situasi kesalahan dan pertimbangkan untuk menyimpan informasi lainnya ke memori dengan mekanisme terpisah untuk diambil.
  • Untuk dekompresi kernel, pertimbangkan penggunaan LZ4 untuk hardware kontemporer bukan GZIP (contoh patch). Perlu diingat bahwa opsi kompresi {i>kernel<i} yang berbeda bisa memiliki fungsi pemuatan yang berbeda dan waktu dekompresi, dan beberapa opsi mungkin bekerja lebih baik daripada yang lain untuk perangkat keras tertentu.
  • Periksa waktu tunggu yang tidak perlu untuk entri mode debounan/khusus dan mereka.
  • Teruskan waktu booting di bootloader ke kernel sebagai cmdline.
  • Memeriksa clock CPU dan mempertimbangkan paralelisasi (memerlukan dukungan multi-core) untuk pemuatan {i>kernel<i} dan menginisialisasi I/O.

Mengoptimalkan efisiensi I/O

Meningkatkan efisiensi I/O sangat penting untuk mempercepat waktu booting, dan hal apa pun yang tidak diperlukan harus ditunda hingga setelah {i>booting<i} (di Google Pixel, sekitar 1,2 GB data yang dibaca saat {i>booting<i}).

Menyesuaikan sistem file

Kernel Linux {i>read forward<i} dimulai ketika file dibaca dari awal atau ketika blok dibaca secara berurutan, sehingga perlu untuk menyesuaikan penjadwal I/O parameter khusus untuk booting (yang memiliki beban kerja berbeda karakterisasi daripada aplikasi normal).

Perangkat yang mendukung update tanpa hambatan (A/B) sangat diuntungkan dengan adanya sistem file tuning pada booting pertama kali (misalnya 20 dtk di Google Pixel). Sebagai contoh, kita 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

  • Mengaktifkan ukuran pengambilan data hash dm-verity menggunakan konfigurasi kernel DM_VERITY_HASH_PREFETCH_MIN_SIZE (ukuran default adalah 128).
  • Untuk stabilitas sistem file yang lebih baik dan penurunan pemeriksaan paksa yang terjadi di setiap booting, gunakan alat pembuatan ext4 baru dengan menetapkan TARGET_USES_MKE2FS di {i>BoardConfig.mk.<i}

Analisis 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 memerinci akses file 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 setiap 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 {i>kernel<i} sampai kerangka kerja dibuat, dan perangkat biasanya menghabiskan beberapa detik pada berbagai tahap init.

Menjalankan tugas secara paralel

Meskipun init Android saat ini kurang lebih merupakan proses thread tunggal, 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.
  • Mengidentifikasi operasi lambat dalam init. Sistem mencatat perintah init ke dalam log {i>exec/wait_for_prop<i} atau tindakan apa pun yang memakan waktu lama (di Android 8.0, perintah apa pun 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.

  • Memulai layanan dan mengaktifkan perangkat periferal di jalur kritis lebih awal. Sebagai misalnya, beberapa SOC mengharuskan memulai layanan terkait keamanan sebelum memulai SurfaceFlinger. Tinjau log sistem saat ServiceManager menampilkan "wait for 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 di init tahap awal harus ditunda hingga proses {i>booting<i} 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 scheduler

Menggunakan penyesuaian scheduler 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
...

Mulai zygote lebih awal

Perangkat dengan enkripsi berbasis file dapat memulai zygote lebih awal pada zygote-start (secara default, zygote diluncurkan di class main, yang berlangsung lebih lama dari zygote-start). Ketika melakukannya, pastikan untuk mengizinkan zygote berjalan di semua CPU (seperti pengaturan cpuset yang salah dapat memaksa zygote untuk berjalan di CPU tertentu).

Nonaktifkan mode hemat daya

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

Perhatian: Mode hemat daya akan diaktifkan di 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 yang tidak penting

Inisialisasi tidak penting seperti ZRAM dapat dialihkan ke boot_complete.

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

Mengoptimalkan animasi boot

Gunakan tips berikut untuk mengoptimalkan animasi booting.

Konfigurasi mulai awal

Android 8.0 memungkinkan animasi booting lebih awal, sebelum memasang data pengguna partisi. Namun, bahkan saat menggunakan rantai alat ext4 baru di Android 8.0, {i>fsck<i} masih dipicu secara berkala karena alasan keamanan, menyebabkan keterlambatan dalam memulai layanan {i>boot<i}.

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

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

Animasi {i>booting<i} akan dimulai jauh lebih cepat (dan dalam waktu yang konstan) terlepas dari {i>fsck<i}.

Finishing bersih

Setelah menerima sinyal keluar, bootanimasi memainkan bagian terakhir, panjang yang dapat memperlambat waktu booting. Sistem yang melakukan {i>booting<i} dengan cepat tidak perlu waktu yang lama animasi yang secara efektif dapat menyembunyikan setiap peningkatan yang dilakukan. Saran dari kami membuat pengulangan dan pengulangan menjadi pendek.

Mengoptimalkan SELinux

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

  • Gunakan ekspresi reguler yang bersih (regex). Format ekspresi reguler tidak tepat dapat menyebabkan banyak {i>overhead<i} saat mencocokkan kebijakan SELinux untuk sys/devices dalam file_contexts. Misalnya, ekspresi reguler /sys/devices/.*abc.*(/.*)? keliru memaksa pemindaian semua /sys/devices subdirektori yang berisi "abc", mengaktifkan kecocokan untuk /sys/devices/abc dan /sys/devices/xyz/abc. Meningkatkan ekspresi reguler ini ke /sys/devices/[^/]*abc[^/]*(/.*)? akan mengaktifkan kecocokan hanya untuk /sys/devices/abc.
  • Pindahkan label ke genfscon. Fitur SELinux yang ada ini meneruskan awalan pencocokan file ke dalam kernel di biner SELinux, di mana {i>kernel<i} menerapkannya ke file yang dihasilkan {i>kernel<i} sistem file. Hal ini juga membantu memperbaiki file yang dibuat {i>kernel<i} yang salah label, kondisi perlombaan yang dapat terjadi di antara proses ruang pengguna yang mencoba mengakses file ini sebelum dilakukan pelabelan ulang.

Alat dan metode

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

Diagram Booting

Bootchart menyediakan perincian beban CPU dan I/O dari semua proses untuk keseluruhan sistem file. Tidak memerlukan pembangunan ulang image sistem dan dapat digunakan sebagai pemeriksaan kesehatan sebelum mendalami 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 pesan 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 untuk 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 komputer {i>booting<i}. (Namun, untuk memeriksa jumlah rata-rata atau jumlah akumulasi selama sehingga 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).

  • Dalam 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 hal berikut:
    BOARD_KERNEL_CMDLINE := ... trace_buf_size=64M trace_event=sched_wakeup,sched_switch,sched_blocked_reason,sched_cpu_hotplug
  • Untuk analisis I/O terperinci, tambahkan juga blok, ext4 dan f2fs.

  • Di file init.rc khusus perangkat, tambahkan hal 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 trace:

    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