Pengoptimalan waktu booting

Halaman ini memberikan tips untuk meningkatkan waktu booting.

Menghapus simbol debug dari modul

Serupa dengan cara simbol debug dihapus dari kernel di perangkat produksi, pastikan Anda juga menghapus simbol debug dari modul. Menghapus simbol debug dari modul membantu waktu booting dengan mengurangi hal berikut:

  • Waktu yang diperlukan untuk membaca biner dari flash.
  • Waktu yang diperlukan untuk mendekompresi ramdisk.
  • Waktu yang diperlukan untuk memuat modul.

Menghapus simbol debug dari modul dapat menghemat waktu beberapa detik selama booting.

Penghapusan simbol diaktifkan secara default dalam build platform Android, tetapi untuk mengaktifkannya secara eksplisit, tetapkan BOARD_DO_NOT_STRIP_VENDOR_RAMDISK_MODULES dalam konfigurasi khusus perangkat di bagian perangkat/vendor/device.

Menggunakan kompresi LZ4 untuk kernel dan ramdisk

Gzip menghasilkan output terkompresi yang lebih kecil dibandingkan dengan LZ4, tetapi LZ4 melakukan dekompresi lebih cepat daripada Gzip. Untuk kernel dan modul, pengurangan ukuran penyimpanan absolut dari penggunaan Gzip tidak terlalu signifikan dibandingkan dengan manfaat waktu dekompresi LZ4.

Dukungan untuk kompresi ramdisk LZ4 telah ditambahkan ke build platform Android melalui BOARD_RAMDISK_USE_LZ4. Anda dapat menetapkan opsi ini di konfigurasi khusus perangkat. Kompresi kernel dapat ditetapkan melalui defconfig kernel.

Beralih ke LZ4 akan memberikan waktu booting 500 md hingga 1000 md lebih cepat.

Menghindari logging yang berlebihan di driver

Di ARM64 dan ARM32, panggilan fungsi yang berjarak lebih dari jarak tertentu dari situs panggilan memerlukan jump table (disebut tabel penautan prosedur, atau PLT) agar dapat mengenkode alamat lompatan lengkap. Karena modul dimuat secara dinamis, tabel lompat ini perlu diperbaiki selama pemuatan modul. Panggilan yang memerlukan relokasi disebut entri relokasi dengan entri tambahan eksplisit (atau RELA, untuk singkatnya) dalam format ELF.

Kernel Linux melakukan beberapa pengoptimalan ukuran memori (seperti pengoptimalan hit cache) saat mengalokasikan PLT. Dengan commit upstream ini, skema pengoptimalan memiliki kompleksitas O(N^2), dengan N adalah jumlah RELA dari jenis R_AARCH64_JUMP26 atau R_AARCH64_CALL26. Jadi, memiliki lebih sedikit RELA dari jenis ini akan membantu mengurangi waktu pemuatan modul.

Salah satu pola coding umum yang meningkatkan jumlah RELA R_AARCH64_CALL26 atau R_AARCH64_JUMP26 adalah logging yang berlebihan di driver. Setiap panggilan ke printk() atau skema logging lainnya biasanya menambahkan entri RELA CALL26/JUMP26. Dalam teks commit dalam commit upstream, perhatikan bahwa meskipun dengan pengoptimalan, enam modul memerlukan waktu sekitar 250 md untuk dimuat—hal ini karena enam modul tersebut adalah enam modul teratas dengan jumlah logging terbanyak.

Mengurangi pencatatan log dapat menghemat waktu sekitar 100 - 300 md pada waktu booting, bergantung pada seberapa berlebihan logging yang ada.

Mengaktifkan probing asinkron, secara selektif

Saat modul dimuat, jika perangkat yang didukungnya telah diisi dari DT (devicetree) dan ditambahkan ke core driver, maka probe perangkat dilakukan dalam konteks panggilan module_init(). Jika pemeriksaan perangkat dilakukan dalam konteks module_init(), modul tidak dapat menyelesaikan pemuatan hingga pemeriksaan selesai. Karena pemuatan modul sebagian besar diserialisasi, perangkat yang memerlukan waktu yang relatif lama untuk diperiksa akan memperlambat waktu booting.

Untuk menghindari waktu booting yang lebih lambat, aktifkan pemindaian asinkron untuk modul yang memerlukan waktu untuk memindai perangkatnya. Mengaktifkan pemeriksaan asinkron untuk semua modul mungkin tidak bermanfaat karena waktu yang diperlukan untuk melakukan fork thread dan memulai pemeriksaan mungkin setinggi waktu yang diperlukan untuk menyelidiki perangkat.

Perangkat yang terhubung melalui bus lambat seperti I2C, perangkat yang melakukan pemuatan firmware dalam fungsi probe, dan perangkat yang melakukan banyak inisialisasi hardware dapat menyebabkan masalah pengaturan waktu. Cara terbaik untuk mengidentifikasi kapan hal ini terjadi adalah dengan mengumpulkan waktu pemeriksaan untuk setiap driver dan mengurutkannya.

Untuk mengaktifkan pemindaian asinkron untuk modul, tidak cukup hanya menetapkan tanda PROBE_PREFER_ASYNCHRONOUS dalam kode driver. Untuk modul, Anda juga perlu menambahkan module_name.async_probe=1 di command line kernel atau meneruskan async_probe=1 sebagai parameter modul saat memuat modul menggunakan modprobe atau insmod.

Mengaktifkan probing asinkron dapat menghemat sekitar 100 - 500 md pada waktu booting, bergantung pada hardware/driver Anda.

Periksa driver CPUfreq sedini mungkin

Semakin awal driver CPUfreq melakukan pemeriksaan, semakin cepat Anda dapat menskalakan frekuensi CPU ke maksimum (atau beberapa maksimum yang dibatasi secara termal) selama booting. Makin cepat CPU, makin cepat booting. Panduan ini juga berlaku untuk driver devfreq yang mengontrol DRAM, memori, dan frekuensi interkoneksi.

Dengan modul, urutan pemuatan dapat bergantung pada level initcall dan urutan kompilasi atau penautan driver. Gunakan alias MODULE_SOFTDEP() untuk memastikan driver cpufreq adalah salah satu dari beberapa modul pertama yang dimuat.

Selain memuat modul lebih awal, Anda juga harus memastikan semua dependensi untuk memeriksa driver CPUfreq juga telah diperiksa. Misalnya, jika Anda memerlukan handle regulator atau clock untuk mengontrol frekuensi CPU, pastikan handle tersebut diuji terlebih dahulu. Atau, Anda mungkin memerlukan driver termal dimuat sebelum driver CPUfreq jika CPU Anda menjadi terlalu panas selama booting. Jadi, lakukan apa yang Anda bisa untuk memastikan CPUfreq dan driver devfreq yang relevan menyelidiki sedini mungkin.

Penghematan dari menyelidiki driver CPUfreq lebih awal dapat sangat kecil hingga sangat besar, bergantung pada seberapa awal Anda dapat menyelidikinya dan pada frekuensi apa bootloader meninggalkan CPU.

Memindahkan modul ke partisi init tahap kedua, vendor, atau vendor_dlkm

Karena proses init tahap pertama diserialisasi, tidak ada banyak kesempatan untuk melakukan paralelisasi proses booting. Jika modul tidak diperlukan agar inisialisasi tahap pertama selesai, pindahkan modul ke inisialisasi tahap kedua dengan menempatkannya di partisi vendor atau vendor_dlkm.

Inisialisasi tahap pertama tidak memerlukan pemeriksaan beberapa perangkat untuk sampai ke inisialisasi tahap kedua. Hanya kemampuan konsol dan penyimpanan flash yang diperlukan untuk alur booting normal.

Muat driver penting berikut:

  • watchdog
  • reset
  • cpufreq

Untuk mode fastbootd pemulihan dan ruang pengguna, inisialisasi tahap pertama memerlukan lebih banyak perangkat untuk diselidiki (seperti USB), dan ditampilkan. Simpan salinan modul ini di ramdisk tahap pertama dan di partisi vendor atau vendor_dlkm. Hal ini memungkinkannya dimuat dalam inisialisasi tahap pertama untuk pemulihan atau alur booting fastbootd. Namun, jangan memuat modul mode pemulihan dalam inisialisasi tahap pertama selama alur booting normal. Modul mode pemulihan dapat ditangguhkan ke init tahap kedua untuk mengurangi waktu booting. Semua modul lain yang tidak diperlukan pada init tahap pertama harus dipindahkan ke partisi vendor_dlkm atau vendor.

Dengan daftar perangkat leaf (misalnya, UFS atau serial), skrip dev needs.sh menemukan semua driver, perangkat, dan modul yang diperlukan untuk dependensi atau penyedia (misalnya, clock, regulator, atau gpio) untuk diselidiki.

Memindahkan modul ke init tahap kedua akan mengurangi waktu booting dengan cara berikut:

  • Pengurangan ukuran Ramdisk.
    • Hal ini menghasilkan pembacaan flash yang lebih cepat saat bootloader memuat ramdisk (langkah booting serial).
    • Hal ini menghasilkan kecepatan dekompresi yang lebih cepat saat kernel mendekompresi ramdisk (langkah booting serial).
  • Init tahap kedua berfungsi secara paralel, yang menyembunyikan waktu pemuatan modul dengan tugas yang dilakukan pada init tahap kedua.

Memindahkan modul ke tahap kedua dapat menghemat 500 - 1000 md pada waktu booting, bergantung pada jumlah modul yang dapat Anda pindahkan ke inisialisasi tahap kedua.

Logistik pemuatan modul

Build Android terbaru menampilkan konfigurasi board yang mengontrol modul mana yang disalin ke setiap tahap, dan modul mana yang dimuat. Bagian ini berfokus pada subset berikut:

  • BOARD_VENDOR_RAMDISK_KERNEL_MODULES. Daftar modul ini akan disalin ke ramdisk.
  • BOARD_VENDOR_RAMDISK_KERNEL_MODULES_LOAD. Daftar modul ini akan dimuat dalam inisialisasi tahap pertama.
  • BOARD_VENDOR_RAMDISK_RECOVERY_KERNEL_MODULES_LOAD. Daftar modul ini yang akan dimuat saat pemulihan atau fastbootd dipilih dari ramdisk.
  • BOARD_VENDOR_KERNEL_MODULES. Daftar modul ini akan disalin ke partisi vendor atau vendor_dlkm di direktori /vendor/lib/modules/.
  • BOARD_VENDOR_KERNEL_MODULES_LOAD. Daftar modul ini yang akan dimuat dalam init tahap kedua.

Modul booting dan pemulihan di ramdisk juga harus disalin ke partisi vendor atau vendor_dlkm di /vendor/lib/modules. Menyalin modul ini ke partisi vendor akan memastikan modul tidak terlihat selama proses init tahap kedua, yang berguna untuk men-debug dan mengumpulkan modinfo untuk laporan bug.

Duplikasi ini akan memerlukan sedikit ruang pada vendor atau partisi vendor_dlkm selama kumpulan modul booting diminimalkan. Pastikan file modules.list vendor memiliki daftar modul yang difilter di /vendor/lib/modules. Daftar yang difilter memastikan waktu booting tidak terpengaruh oleh modul yang dimuat lagi (yang merupakan proses yang mahal).

Pastikan modul mode pemulihan dimuat sebagai satu grup. Memuat modul mode pemulihan dapat dilakukan dalam mode pemulihan, atau di awal inisialisasi tahap kedua dalam setiap alur booting.

Anda dapat menggunakan file Board.Config.mk perangkat untuk melakukan tindakan ini seperti yang terlihat dalam contoh berikut:

# All kernel modules
KERNEL_MODULES := $(wildcard $(KERNEL_MODULE_DIR)/*.ko)
KERNEL_MODULES_LOAD := $(strip $(shell cat $(KERNEL_MODULE_DIR)/modules.load)

# First stage ramdisk modules
BOOT_KERNEL_MODULES_FILTER := $(foreach m,$(BOOT_KERNEL_MODULES),%/$(m))

# Recovery ramdisk modules
RECOVERY_KERNEL_MODULES_FILTER := $(foreach m,$(RECOVERY_KERNEL_MODULES),%/$(m))
BOARD_VENDOR_RAMDISK_KERNEL_MODULES += \
     $(filter $(BOOT_KERNEL_MODULES_FILTER) \
                $(RECOVERY_KERNEL_MODULES_FILTER),$(KERNEL_MODULES))

# ALL modules land in /vendor/lib/modules so they could be rmmod/insmod'd,
# and modules.list actually limits us to the ones we intend to load.
BOARD_VENDOR_KERNEL_MODULES := $(KERNEL_MODULES)
# To limit /vendor/lib/modules to just the ones loaded, use:
# BOARD_VENDOR_KERNEL_MODULES := $(filter-out \
#     $(BOOT_KERNEL_MODULES_FILTER),$(KERNEL_MODULES))

# Group set of /vendor/lib/modules loading order to recovery modules first,
# then remainder, subtracting both recovery and boot modules which are loaded
# already.
BOARD_VENDOR_KERNEL_MODULES_LOAD := \
        $(filter-out $(BOOT_KERNEL_MODULES_FILTER), \
        $(filter $(RECOVERY_KERNEL_MODULES_FILTER),$(KERNEL_MODULES_LOAD)))
BOARD_VENDOR_KERNEL_MODULES_LOAD += \
        $(filter-out $(BOOT_KERNEL_MODULES_FILTER) \
            $(RECOVERY_KERNEL_MODULES_FILTER),$(KERNEL_MODULES_LOAD))

# NB: Load order governed by modules.load and not by $(BOOT_KERNEL_MODULES)
BOARD_VENDOR_RAMDISK_KERNEL_MODULES_LOAD := \
        $(filter $(BOOT_KERNEL_MODULES_FILTER),$(KERNEL_MODULES_LOAD))

# Group set of /vendor/lib/modules loading order to boot modules first,
# then the remainder of recovery modules.
BOARD_VENDOR_RAMDISK_RECOVERY_KERNEL_MODULES_LOAD := \
    $(filter $(BOOT_KERNEL_MODULES_FILTER),$(KERNEL_MODULES_LOAD))
BOARD_VENDOR_RAMDISK_RECOVERY_KERNEL_MODULES_LOAD += \
    $(filter-out $(BOOT_KERNEL_MODULES_FILTER), \
    $(filter $(RECOVERY_KERNEL_MODULES_FILTER),$(KERNEL_MODULES_LOAD)))

Contoh ini menampilkan subset BOOT_KERNEL_MODULES dan RECOVERY_KERNEL_MODULES yang lebih mudah dikelola untuk ditentukan secara lokal dalam file konfigurasi board. Skrip sebelumnya menemukan dan mengisi setiap modul subset dari modul kernel yang tersedia dan dipilih, sehingga modul yang tersisa untuk tahap init kedua.

Untuk init tahap kedua, sebaiknya jalankan pemuatan modul sebagai layanan sehingga tidak memblokir alur booting. Gunakan skrip shell untuk mengelola pemuatan modul sehingga logistik lainnya, seperti penanganan dan mitigasi error, atau penyelesaian pemuatan modul, dapat dilaporkan kembali (atau diabaikan) jika diperlukan.

Anda dapat mengabaikan kegagalan pemuatan modul debug yang tidak ada di build pengguna. Untuk mengabaikan kegagalan ini, tetapkan properti vendor.device.modules.ready untuk memicu bootflow skrip init rc tahap selanjutnya untuk melanjutkan ke layar peluncuran. Lihat contoh skrip berikut, jika Anda memiliki kode berikut di /vendor/etc/init.insmod.sh:

#!/vendor/bin/sh
. . .
if [ $# -eq 1 ]; then
  cfg_file=$1
else
  # Set property even if there is no insmod config
  # to unblock early-boot trigger
  setprop vendor.common.modules.ready
  setprop vendor.device.modules.ready
  exit 1
fi

if [ -f $cfg_file ]; then
  while IFS="|" read -r action arg
  do
    case $action in
      "insmod") insmod $arg ;;
      "setprop") setprop $arg 1 ;;
      "enable") echo 1 > $arg ;;
      "modprobe") modprobe -a -d /vendor/lib/modules $arg ;;
     . . .
    esac
  done < $cfg_file
fi

Dalam file rc hardware, layanan one shot dapat ditentukan dengan:

service insmod-sh /vendor/etc/init.insmod.sh /vendor/etc/init.insmod.<hw>.cfg
    class main
    user root
    group root system
    Disabled
    oneshot

Pengoptimalan tambahan dapat dilakukan setelah modul berpindah dari tahap pertama ke kedua. Anda dapat menggunakan fitur daftar blokir modprobe untuk memisahkan alur booting tahap kedua guna menyertakan pemuatan modul yang ditangguhkan dari modul yang tidak penting. Pemuatan modul yang digunakan secara eksklusif oleh HAL tertentu dapat ditangguhkan untuk memuat modul hanya saat HAL dimulai.

Untuk meningkatkan waktu booting yang terlihat, Anda dapat memilih modul secara khusus di layanan pemuatan modul yang lebih kondusif untuk pemuatan setelah layar peluncuran. Misalnya, Anda dapat memuat modul secara eksplisit untuk decoder video atau Wi-Fi setelah alur booting init dihapus (misalnya, sinyal properti Android sys.boot_complete). Pastikan HAL untuk modul pemuatan terlambat memblokir cukup lama saat driver kernel tidak ada.

Atau, Anda dapat menggunakan perintah wait<file>[<timeout>] init dalam skrip rc alur booting untuk menunggu entri sysfs yang dipilih guna menunjukkan bahwa modul driver telah menyelesaikan operasi pemeriksaan. Contohnya adalah menunggu driver tampilan menyelesaikan pemuatan di latar belakang pemulihan atau fastbootd, sebelum menampilkan grafik menu.

Menginisialisasi frekuensi CPU ke nilai yang wajar di bootloader

Tidak semua SoC/produk mungkin dapat mem-booting CPU pada frekuensi tertinggi karena masalah termal atau daya selama pengujian loop booting. Namun, pastikan bootloader menetapkan frekuensi semua CPU online ke setinggi mungkin untuk SoC atau produk. Hal ini sangat penting karena, dengan kernel modular sepenuhnya, dekompresi ramdisk init terjadi sebelum driver CPUfreq dapat dimuat. Jadi, jika CPU dibiarkan di bagian bawah frekuensinya oleh bootloader, waktu dekompresi ramdisk dapat memerlukan waktu lebih lama daripada kernel yang dikompilasi secara statis (setelah menyesuaikan perbedaan ukuran ramdisk) karena frekuensi CPU akan sangat rendah saat melakukan pekerjaan intensif CPU (dekompresi). Hal yang sama berlaku untuk memori dan frekuensi interkoneksi.

Menginisialisasi frekuensi CPU CPU berukuran besar di bootloader

Sebelum driver CPUfreq dimuat, kernel tidak mengetahui frekuensi CPU dan tidak menskalakan kapasitas penjadwalan CPU untuk frekuensi saat ini. Kernel mungkin memigrasikan thread ke CPU besar jika bebannya cukup tinggi pada CPU kecil.

Pastikan CPU besar setidaknya memiliki performa yang sama dengan CPU kecil untuk frekuensi yang digunakan bootloader. Misalnya, jika CPU besar memiliki performa 2x lebih baik daripada CPU kecil untuk frekuensi yang sama, tetapi bootloader menetapkan frekuensi CPU kecil ke 1,5 GHz dan frekuensi CPU besar ke 300 MHz, performa booting akan menurun jika kernel memindahkan thread ke CPU besar. Dalam contoh ini, jika aman untuk mem-booting CPU besar pada 750 MHz, Anda harus melakukannya meskipun tidak berencana menggunakannya secara eksplisit.

Driver tidak boleh memuat firmware dalam inisialisasi tahap pertama

Mungkin ada beberapa kasus yang tidak dapat dihindari ketika firmware perlu dimuat dalam init tahap pertama. Namun secara umum, driver tidak boleh memuat firmware apa pun pada tahap pertama ini, terutama dalam konteks pemeriksaan perangkat. Memuat firmware di init tahap pertama menyebabkan seluruh proses booting terhenti jika firmware tidak tersedia di ramdisk tahap pertama. Dan meskipun firmware ada di tahap pertama ramdisk, firmware tersebut masih menyebabkan penundaan yang tidak perlu.