Optimasi Waktu Booting

Halaman ini menyediakan serangkaian tips, yang dapat Anda pilih, untuk meningkatkan waktu boot.

Hapus simbol debug dari modul

Mirip dengan cara menghapus simbol debug dari kernel pada perangkat produksi, pastikan Anda juga menghapus simbol debug dari modul. Menghapus simbol debug dari modul membantu waktu boot dengan mengurangi hal berikut:

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

Menghapus simbol debug dari modul dapat menghemat beberapa detik selama boot.

Penghapusan simbol diaktifkan secara default di versi platform Android, tetapi untuk mengaktifkannya secara eksplisit, setel BOARD_DO_NOT_STRIP_VENDOR_RAMDISK_MODULES di konfigurasi khusus perangkat Anda di bawah device/ vendor / device .

Gunakan 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 platform Android yang dibangun melalui BOARD_RAMDISK_USE_LZ4 . Anda dapat mengatur opsi ini di konfigurasi khusus perangkat Anda. Kompresi kernel dapat diatur melalui kernel defconfig.

Beralih ke LZ4 akan memberikan waktu boot 500ms hingga 1000ms lebih cepat.

Hindari login berlebihan di driver Anda

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

Kernel Linux melakukan beberapa optimasi ukuran memori (seperti optimasi cache hit) ketika mengalokasikan PLT. Dengan komit hulu ini, skema pengoptimalan memiliki kompleksitas O(N^2), di mana N adalah jumlah RELA tipe R_AARCH64_JUMP26 atau R_AARCH64_CALL26 . Jadi memiliki lebih sedikit RELA dari jenis ini sangat membantu dalam mengurangi waktu muat modul.

Salah satu pola pengkodean umum yang meningkatkan jumlah R_AARCH64_CALL26 atau R_AARCH64_JUMP26 adalah masuknya driver secara berlebihan. Setiap panggilan ke printk() atau skema logging lainnya biasanya menambahkan CALL26 / JUMP26 RELA. Dalam teks komit di komit hulu , ,perhatikan bahwa bahkan dengan pengoptimalan, enam modul membutuhkan waktu sekitar 250 md untuk dimuat—itu karena enam modul tersebut adalah enam modul teratas dengan jumlah pencatatan terbanyak.

Mengurangi logging dapat menghemat dapat menghemat sekitar 100 - 300ms pada waktu boot tergantung seberapa berlebihan logging yang ada.

Aktifkan pemeriksaan asinkron, secara selektif

Ketika sebuah modul dimuat, jika perangkat yang didukungnya telah diisi dari DT (devicetree) dan ditambahkan ke inti driver, maka pemeriksaan perangkat dilakukan dalam konteks pemanggilan module_init() . Saat pemeriksaan perangkat dilakukan dalam konteks module_init() , modul tidak dapat menyelesaikan pemuatan hingga pemeriksaan selesai. Karena pemuatan modul sebagian besar serial, perangkat yang membutuhkan waktu yang relatif lama untuk menyelidiki memperlambat waktu boot.

Untuk menghindari waktu booting yang lebih lambat, aktifkan pemeriksaan asinkron untuk modul yang memerlukan waktu beberapa saat untuk memeriksa perangkatnya. Mengaktifkan probing asinkron untuk semua modul mungkin tidak bermanfaat karena waktu yang diperlukan untuk melakukan fork thread dan memulai probe mungkin sama tingginya dengan waktu yang diperlukan untuk memeriksa perangkat.

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

Untuk mengaktifkan pemeriksaan asinkron untuk sebuah modul, tidak cukup hanya menyetel flag PROBE_PREFER_ASYNCHRONOUS dalam kode driver. Untuk modul, Anda juga perlu menambahkan module_name .async_probe=1 di baris perintah kernel atau meneruskan async_probe=1 sebagai parameter modul saat memuat modul menggunakan modprobe atau insmod .

Mengaktifkan pemeriksaan asinkron dapat menghemat sekitar 100 - 500 md pada waktu boot tergantung pada perangkat keras/driver Anda.

Selidiki driver CPUfreq Anda sedini mungkin

Semakin awal driver CPUfreq Anda memeriksa, semakin cepat Anda dapat menskalakan frekuensi CPU ke maksimum (atau maksimum terbatas secara termal) selama boot. Semakin cepat CPU, semakin cepat boot. Pedoman ini juga berlaku untuk driver devfreq yang mengontrol DRAM, memori, dan frekuensi interkoneksi.

Dengan modul, urutan beban dapat bergantung pada level initcall dan kompilasi atau urutan tautan driver. Gunakan alias MODULE_SOFTDEP() untuk memastikan driver cpufreq termasuk di antara beberapa modul pertama yang dimuat.

Selain memuat modul lebih awal, Anda juga perlu memastikan semua dependensi untuk menyelidiki driver CPUfreq juga telah diperiksa. Misalnya, jika Anda memerlukan pegangan jam atau pengatur untuk mengontrol frekuensi CPU Anda, pastikan mereka diperiksa terlebih dahulu. Atau Anda mungkin memerlukan driver termal untuk dimuat sebelum driver CPUfreq jika mungkin CPU Anda menjadi terlalu panas saat boot up. Jadi, lakukan apa yang Anda bisa untuk memastikan CPUfreq dan driver devfreq yang relevan menyelidiki sedini mungkin.

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

Pindahkan modul ke partisi init tahap kedua, vendor atau vendor_dlkm

Karena proses init tahap pertama diserialisasi, tidak banyak peluang untuk memparalelkan proses boot. Jika modul tidak diperlukan untuk menyelesaikan init tahap pertama, pindahkan modul ke init tahap kedua dengan menempatkannya di partisi vendor atau vendor_dlkm .

Init tahap pertama tidak memerlukan pemeriksaan beberapa perangkat untuk sampai ke init tahap kedua. Hanya fungsi konsol dan penyimpanan flash yang diperlukan untuk aliran boot normal.

Muat driver penting berikut:

  • penjaga
  • mengatur ulang
  • frekuensi cpu

Untuk pemulihan dan mode fastbootd ruang pengguna, init tahap pertama membutuhkan lebih banyak perangkat untuk diselidiki (seperti USB), dan ditampilkan. Simpan salinan modul ini di ramdisk tahap pertama dan di partisi vendor atau vendor_dlkm . Ini memungkinkan mereka untuk dimuat di init tahap pertama untuk pemulihan atau aliran boot fastbootd . Namun, jangan memuat modul mode pemulihan di tahap pertama init selama aliran boot normal. Modul mode pemulihan dapat ditunda ke tahap kedua init untuk mengurangi waktu boot. Semua modul lain yang tidak diperlukan pada tahap pertama ini harus dipindahkan ke partisi vendor atau vendor_dlkm .

Diberikan daftar perangkat daun (misalnya, UFS atau serial), skrip dev needs.sh menemukan semua driver, perangkat, dan modul yang diperlukan untuk dependensi atau pemasok (misalnya, jam, regulator, atau gpio ) untuk diselidiki.

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

  • Pengurangan ukuran ramdisk.
    • Ini menghasilkan pembacaan flash yang lebih cepat ketika bootloader memuat ramdisk (langkah boot serial).
    • Ini menghasilkan kecepatan dekompresi yang lebih cepat ketika kernel mendekompresi ramdisk (langkah boot serial).
  • Init tahap kedua bekerja secara paralel, yang menyembunyikan waktu pemuatan modul dengan pekerjaan yang dilakukan di init tahap kedua.

Memindahkan modul ke tahap kedua dapat menghemat 500 - 1000 ms pada waktu boot tergantung pada berapa banyak modul yang dapat Anda pindahkan ke init tahap kedua.

Logistik pemuatan modul

Versi Android terbaru menampilkan konfigurasi papan 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 . Ini daftar modul yang akan disalin ke ramdisk.
  • BOARD_VENDOR_RAMDISK_KERNEL_MODULES_LOAD . Ini daftar modul yang akan dimuat di init tahap pertama.
  • BOARD_VENDOR_RAMDISK_RECOVERY_KERNEL_MODULES_LOAD . Daftar modul yang akan dimuat saat pemulihan atau fastbootd dipilih dari ramdisk.
  • BOARD_VENDOR_KERNEL_MODULES . Daftar modul yang akan disalin ke partisi vendor atau vendor_dlkm di /vendor/lib/modules/ .
  • BOARD_VENDOR_KERNEL_MODULES_LOAD . Ini daftar modul yang akan dimuat di init tahap kedua.

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

Duplikasi harus menghabiskan ruang minimal pada vendor atau partisi vendor_dlkm selama set modul boot diminimalkan. Pastikan file modules.list vendor memiliki daftar modul yang difilter di /vendor/lib/modules . Daftar yang difilter memastikan waktu booting tidak terpengaruh oleh pemuatan modul lagi (yang merupakan proses yang mahal).

Pastikan modul mode pemulihan dimuat sebagai grup. Memuat modul mode pemulihan dapat dilakukan baik dalam mode pemulihan, atau di awal tahap kedua init di setiap aliran boot.

Anda dapat menggunakan file Board.Config.mk perangkat untuk melakukan tindakan ini seperti yang terlihat pada 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 di file konfigurasi papan. Skrip sebelumnya menemukan dan mengisi setiap modul subset dari modul kernel yang tersedia yang dipilih, meninggalkan modul reamining untuk init tahap kedua.

Untuk init tahap kedua, kami merekomendasikan menjalankan pemuatan modul sebagai layanan sehingga tidak memblokir aliran boot. Gunakan skrip shell untuk mengelola pemuatan modul sehingga logistik lainnya, seperti penanganan dan mitigasi kesalahan, atau penyelesaian pemuatan modul, dapat dilaporkan kembali (atau diabaikan) jika perlu.

Anda dapat mengabaikan kegagalan memuat modul debug yang tidak ada pada build pengguna. Untuk mengabaikan kegagalan ini, setel properti vendor.device.modules.ready untuk memicu tahap selanjutnya dari init rc scripting bootflow untuk melanjutkan ke layar peluncuran. Referensikan 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 perangkat keras, 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

Optimalisasi tambahan dapat dilakukan setelah modul berpindah dari tahap pertama ke tahap kedua. Anda dapat menggunakan fitur daftar blokir modprobe untuk membagi aliran boot tahap kedua untuk menyertakan pemuatan modul yang ditangguhkan dari modul yang tidak penting. Pemuatan modul yang digunakan secara eksklusif oleh HAL tertentu dapat ditunda untuk memuat modul hanya ketika HAL dimulai.

Untuk meningkatkan waktu boot yang terlihat, Anda dapat secara khusus memilih modul dalam layanan pemuatan modul yang lebih kondusif untuk memuat setelah layar peluncuran. Misalnya, Anda dapat secara eksplisit memuat modul untuk dekoder video atau wifi setelah aliran boot init dihapus ( sys.boot_complete sinyal properti Android, misalnya). Pastikan HAL untuk modul pemuatan yang terlambat memblokir cukup lama ketika driver kernel tidak ada.

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

Inisialisasi frekuensi CPU ke nilai yang wajar di bootloader

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

Inisialisasi frekuensi CPU dari CPU besar di bootloader

Sebelum driver CPUfreq dimuat, kernel tidak menyadari frekuensi CPU kecil dan besar dan tidak menskalakan kapasitas terjadwal CPU untuk frekuensi mereka saat ini. Kernel mungkin memigrasikan utas ke CPU besar jika bebannya cukup tinggi pada CPU kecil.

Pastikan CPU besar setidaknya memiliki kinerja yang sama dengan CPU kecil untuk frekuensi di mana bootloader membiarkannya masuk. Misalnya, jika CPU besar 2x lebih baik daripada CPU kecil untuk frekuensi yang sama, tetapi bootloader menyetel frekuensi CPU kecil menjadi 1,5 GHz dan frekuensi CPU besar menjadi 300 MHz, maka kinerja boot akan turun jika kernel memindahkan utas ke CPU besar. Dalam contoh ini, jika aman untuk mem-boot CPU besar pada 750 MHz, Anda harus melakukannya meskipun Anda tidak berencana untuk menggunakannya secara eksplisit.

Driver tidak boleh memuat firmware di tahap pertama ini

Mungkin ada beberapa kasus yang tidak dapat dihindari di mana firmware perlu dimuat di init tahap pertama. Tetapi secara umum, driver tidak boleh memuat firmware apa pun di init tahap pertama, terutama dalam konteks pemeriksaan perangkat. Memuat firmware di init tahap pertama menyebabkan seluruh proses boot terhenti jika firmware tidak tersedia di ramdisk tahap pertama. Dan bahkan jika firmware ada di ramdisk tahap pertama, itu masih menyebabkan penundaan yang tidak perlu.