Önyükleme Sürelerini Optimize Etme

Bu belge, belirli Android cihazlarının önyükleme sürelerini iyileştirmeye yönelik iş ortaklarına rehberlik sağlar. Kullanıcıların aygıtı kullanmadan önce önyüklemenin tamamlanmasını beklemesi gerektiğinden, önyükleme süresi sistem performansının önemli bir bileşenidir. Soğuk başlatmanın daha sık gerçekleştiği otomobiller gibi cihazlar için, hızlı başlatma süresine sahip olmak kritik öneme sahiptir (hiç kimse yalnızca bir navigasyon hedefi girmek için onlarca saniye beklemekten hoşlanmaz).

Android 8.0, çeşitli bileşenlerdeki çeşitli iyileştirmeleri destekleyerek önyükleme sürelerinin azaltılmasına olanak tanır. Aşağıdaki tabloda bu performans iyileştirmeleri özetlenmektedir (Google Pixel ve Pixel XL cihazlarda ölçülmüştür).

Bileşen Gelişim
Önyükleyici
  • UART günlüğünü kaldırarak 1,6 saniye tasarruf edildi
  • GZIP'ten LZ4'e geçiş yapılarak 0,4 saniye tasarruf edildi
Cihaz çekirdeği
  • Kullanılmayan çekirdek yapılandırmalarını kaldırarak ve sürücü boyutunu azaltarak 0,3 saniye tasarruf edildi
  • Dm-verity ön getirme optimizasyonu ile 0,3 saniye tasarruf edildi
  • Sürücüdeki gereksiz bekleme/testi kaldırmak için 0,15 saniye tasarruf edildi
  • CONFIG_CC_OPTIMIZE_FOR_SIZE öğesini kaldırmak için 0,12 saniye kaydedildi
G/Ç ayarı
  • Normal önyüklemede 2 saniye kaydedildi
  • İlk açılışta 25 saniye tasarruf edildi
init.*.rc
  • Başlatma komutlarını paralelleştirerek 1,5 saniye tasarruf edildi
  • Zigotu erken başlatarak 0,25 saniye tasarruf edildi
  • cpuset tune tarafından 0,22 saniye kaydedildi
Önyükleme animasyonu
  • Fsck tetiklenmeden önyüklemede 2 saniye daha erken başlatıldı, fsck tetiklemeli önyüklemeyle önyüklemede çok daha büyük
  • Önyükleme animasyonunun anında kapatılmasıyla Pixel XL'de 5 saniye kaydedildi
SELinux politikası genfscon tarafından 0,2 sn tasarruf edildi

Bootloader'ı Optimize Etme

Daha iyi önyükleme süreleri için önyükleyiciyi optimize etmek için:

  • Günlüğe kaydetme için:
    • Çok sayıda günlük kaydı uzun zaman alabileceğinden, UART'a günlük yazmayı devre dışı bırakın. (Google Pixel cihazlarda önyükleyiciyi 1,5 saniye yavaşlattığını gördük).
    • Yalnızca hata durumlarını günlüğe kaydedin ve geri almak için diğer bilgileri ayrı bir mekanizmayla belleğe kaydetmeyi düşünün.
  • Çekirdek sıkıştırmasını açmak için, çağdaş donanımda GZIP yerine LZ4 kullanmayı düşünün (örnek yama ). Farklı çekirdek sıkıştırma seçeneklerinin farklı yükleme ve açma sürelerine sahip olabileceğini ve bazı seçeneklerin, özel donanımınız için diğerlerinden daha iyi çalışabileceğini unutmayın.
  • Geri dönüş/özel mod girişi için gereksiz bekleme sürelerini kontrol edin ve bunları en aza indirin.
  • Önyükleyicide harcanan önyükleme süresini çekirdeğe cmdline olarak aktarın.
  • CPU saatini kontrol edin ve çekirdek yükleme ve G/Ç'yi başlatma için paralelleştirmeyi (çok çekirdekli destek gerektirir) düşünün.

G/Ç verimliliğini optimize etme

G/Ç verimliliğini artırmak, önyükleme süresini hızlandırmak açısından kritik öneme sahiptir ve gerekli olmayan herhangi bir şeyin okunması, önyükleme sonrasına ertelenmelidir (Google Pixel'de, önyükleme sırasında yaklaşık 1,2 GB veri okunur).

Dosya sistemini ayarlama

Linux çekirdeği ileri okuma, bir dosya baştan okunduğunda veya bloklar sırayla okunduğunda devreye girerek, özellikle önyükleme için (normal uygulamalardan farklı bir iş yükü karakterizasyonuna sahip olan) G/Ç zamanlayıcı parametrelerinin ayarlanmasını gerekli kılar.

Kesintisiz (A/B) güncellemeleri destekleyen cihazlar, ilk açılışta dosya sistemi ayarlamasından büyük ölçüde yararlanır (örneğin, Google Pixel'de 20'ler). Örnek olarak Google Pixel için aşağıdaki parametreleri ayarladık:

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
    ...

Çeşitli

  • Çekirdek yapılandırması DM_VERITY_HASH_PREFETCH_MIN_SIZE (varsayılan boyut 128'dir) kullanarak dm-verity karma önceden getirme boyutunu açın.
  • Daha iyi dosya sistemi kararlılığı ve her önyüklemede meydana gelen zorunlu denetimin düşürülmesi için BoardConfig.mk'de TARGET_USES_MKE2FS ayarını yaparak yeni ext4 oluşturma aracını kullanın.

G/Ç analiz ediliyor

Önyükleme sırasında G/Ç etkinliklerini anlamak için çekirdek ftrace verilerini kullanın (aynı zamanda systrace tarafından da kullanılır):

trace_event=block,ext4 in BOARD_KERNEL_CMDLINE

Her bir dosyanın dosya erişimini dökümlemek için çekirdekte aşağıdaki değişiklikleri yapın (yalnızca geliştirme çekirdeği; üretim çekirdeklerinde kullanmayın):

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

Önyükleme performansını analiz etmeye yardımcı olması için aşağıdaki komut dosyalarını kullanın.

  • system/extras/boottime_tools/bootanalyze/bootanalyze.py Önyükleme sürecindeki önemli adımların dökümüyle önyükleme süresini ölçer.
  • system/extras/boottime_tools/io_analysis/check_file_read.py boot_trace Her dosya için erişim bilgisi sağlar.
  • system/extras/boottime_tools/io_analysis/check_io_trace_all.py boot_trace Sistem düzeyinde döküm verir.

init.*.rc optimizasyonu

Init, çekirdekten çerçevenin kurulmasına kadar geçen köprüdür ve cihazlar genellikle farklı başlatma aşamalarında birkaç saniye harcar.

Görevleri paralel çalıştırma

Mevcut Android init aşağı yukarı tek iş parçacıklı bir işlem olsa da, yine de bazı görevleri paralel olarak gerçekleştirebilirsiniz.

  • Bir kabuk komut dosyası hizmetinde yavaş komutları yürütün ve daha sonra belirli bir özelliği bekleyerek buna katılın. Android 8.0, bu kullanım durumunu yeni wait_for_property komutuyla destekler.
  • İnit'teki yavaş işlemleri tanımlayın. Sistem, exec/wait_for_prop başlatma komutunu veya uzun süren herhangi bir eylemi (Android 8.0'da, 50 ms'den uzun süren herhangi bir komut) günlüğe kaydeder. Örneğin:
    init: Command 'wait_for_coldboot_done' action=wait_for_coldboot_done returned 0 took 585.012ms

    Bu günlüğün incelenmesi iyileştirme fırsatlarına işaret edebilir.

  • Hizmetleri başlatın ve kritik yoldaki çevresel aygıtları erkenden etkinleştirin. Örneğin, bazı SOC'ler SurfaceFlinger'ı başlatmadan önce güvenlikle ilgili hizmetlerin başlatılmasını gerektirir. ServiceManager "hizmet için bekle" ifadesini döndürdüğünde sistem günlüğünü inceleyin; bu genellikle önce bağımlı bir hizmetin başlatılması gerektiğinin bir işaretidir.
  • init.*.rc dosyasındaki kullanılmayan hizmetleri ve komutları kaldırın. Başlangıç ​​aşamasında kullanılmayan herhangi bir şey, önyüklemenin tamamlanmasına ertelenmelidir.

Not: Özellik hizmeti, init işleminin bir parçasıdır, bu nedenle, init yerleşik komutlarla meşgulse, önyükleme sırasında setproperty çağrılması uzun bir gecikmeye neden olabilir.

Zamanlayıcı ayarını kullanma

Erken önyükleme için zamanlayıcı ayarını kullanın. Google Pixel'den örnek:

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

Bazı hizmetlerin önyükleme sırasında öncelik artışına ihtiyacı olabilir. Örnek:

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

Zigotun erken başlaması

Dosya tabanlı şifrelemeye sahip cihazlar, zigot başlatma tetikleyicisinde zigotu daha erken başlatabilir (varsayılan olarak zigot, zigot başlangıcından çok daha sonra olan ana sınıfta başlatılır). Bunu yaparken zigotun tüm CPU'larda çalışmasına izin verdiğinizden emin olun (çünkü yanlış cpuset ayarı zigotun belirli CPU'larda çalışmasına neden olabilir).

Güç tasarrufunu devre dışı bırak

Cihaz önyüklemesi sırasında UFS ve/veya CPU regülatörü gibi bileşenler için güç tasarrufu ayarı devre dışı bırakılabilir.

Dikkat: Verimlilik için şarj cihazı modunda güç tasarrufu etkinleştirilmelidir.

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

Kritik olmayan başlatmayı ertele

ZRAM gibi kritik olmayan başlatma, boot_complete'a ertelenebilir.

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

Önyükleme animasyonunu optimize etme

Önyükleme animasyonunu optimize etmek için aşağıdaki ipuçlarını kullanın.

Erken başlatmayı yapılandırma

Android 8.0, kullanıcı verileri bölümünü bağlamadan önce önyükleme animasyonunun erken başlatılmasını sağlar. Bununla birlikte, Android 8.0'da yeni ext4 araç zincirini kullanırken bile, güvenlik nedeniyle fsck hala periyodik olarak tetikleniyor ve bu da önyükleme animasyonu hizmetinin başlatılmasında gecikmeye neden oluyor.

Önyükleme animasyonunun erken başlamasını sağlamak için fstab montajını iki aşamaya bölün:

  • İlk aşamada, yalnızca çalıştırma denetimi gerektirmeyen bölümleri ( system/ ve vendor/ gibi) bağlayın, ardından önyükleme animasyon hizmetlerini ve bağımlılıklarını (servicemanager ve Surfaceflinger gibi) başlatın.
  • İkinci aşamada, çalışma kontrolleri gerektiren bölümleri ( data/ gibi) bağlayın.

Önyükleme animasyonu, fsck'ten bağımsız olarak çok daha hızlı (ve sabit sürede) başlatılacaktır.

Temiz bitirme

Çıkış sinyalini aldıktan sonra, önyükleme animasyonu, uzunluğu önyükleme süresini yavaşlatabilen son kısmı oynatır. Hızlı bir şekilde önyüklenen bir sistemin, yapılan iyileştirmeleri etkili bir şekilde gizleyebilecek uzun animasyonlara ihtiyacı yoktur. Hem tekrar eden döngüyü hem de finali kısa tutmanızı öneririz.

SELinux'u Optimize Etme

Daha iyi önyükleme süreleri için SELinux'u optimize etmek amacıyla aşağıdaki ipuçlarını kullanın.

  • Temiz normal ifadeler (regex) kullanın . Kötü biçimlendirilmiş normal ifade, file_contexts içindeki sys/devices için SELinux politikasını eşleştirirken çok fazla ek yüke yol açabilir. Örneğin, /sys/devices/.*abc.*(/.*)? normal ifadesi yanlışlıkla "abc" içeren tüm /sys/devices alt dizinlerinin taranmasını zorlar ve hem /sys/devices/abc hem de /sys/devices/xyz/abc için eşleşmeleri etkinleştirir. Bu normal ifade /sys/devices/[^/]*abc[^/]*(/.*)? yalnızca /sys/devices/abc için eşleşmeyi etkinleştirir.
  • Etiketleri genfscon'a taşıyın . Bu mevcut SELinux özelliği, dosya eşleştirme öneklerini SELinux ikili dosyasındaki çekirdeğe aktarır ve burada çekirdek bunları çekirdek tarafından oluşturulan dosya sistemlerine uygular. Bu aynı zamanda çekirdek tarafından oluşturulan yanlış etiketlenmiş dosyaların düzeltilmesine de yardımcı olur ve yeniden etiketleme gerçekleşmeden önce bu dosyalara erişmeye çalışan kullanıcı alanı işlemleri arasında oluşabilecek yarış koşullarını önler.

Araç ve yöntemler

Optimizasyon hedeflerine yönelik verileri toplamanıza yardımcı olması için aşağıdaki araçları kullanın.

Önyükleme şeması

Bootchart, tüm sistem için tüm süreçlerin CPU ve I/O yük dökümünü sağlar. Sistem görüntüsünün yeniden oluşturulmasını gerektirmez ve systrace'e dalmadan önce hızlı bir sağlık kontrolü olarak kullanılabilir.

Önyükleme grafiğini etkinleştirmek için:

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

Önyüklemeden sonra önyükleme grafiğini getirin:

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

İşiniz bittiğinde, verilerin her seferinde toplanmasını önlemek için /data/bootchart/enabled silin.

Bootchart çalışmıyorsa ve bootchart.png mevcut olmadığını belirten bir hata mesajı alıyorsanız aşağıdakileri yapın:
  1. Aşağıdaki komutları çalıştırın:
          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 yerel kopyasına işaret edecek şekilde güncelleyin ( ~/Documents/bootchart/pybootchartgui.py konumunda bulunur)

Systrace

Systrace, başlatma sırasında hem çekirdek hem de Android izlerinin toplanmasına olanak tanır. Systrace'in görselleştirilmesi, başlatma sırasında belirli bir sorunun analiz edilmesine yardımcı olabilir. (Ancak, tüm önyükleme sırasındaki ortalama sayıyı veya birikmiş sayıyı kontrol etmek için doğrudan çekirdek izine bakmak daha kolaydır).

Önyükleme sırasında systrace'i etkinleştirmek için:

  • frameworks/native/cmds/atrace/atrace.rc şunu değiştirin:
      write /sys/kernel/debug/tracing/tracing_on 0
      write /sys/kernel/tracing/tracing_on 0

    İle:

      #    write /sys/kernel/debug/tracing/tracing_on 0
      #    write /sys/kernel/tracing/tracing_on 0
  • Bu, izlemeyi etkinleştirir (varsayılan olarak devre dışıdır).

  • device.mk dosyasına şu satırı ekleyin:
    PRODUCT_PROPERTY_OVERRIDES +=    debug.atrace.tags.enableflags=802922
    PRODUCT_PROPERTY_OVERRIDES +=    persist.traced.enable=0
  • Aygıtın BoardConfig.mk dosyasına aşağıdakini ekleyin:
    BOARD_KERNEL_CMDLINE := ... trace_buf_size=64M trace_event=sched_wakeup,sched_switch,sched_blocked_reason,sched_cpu_hotplug
  • Ayrıntılı G/Ç analizi için ayrıca blok ve ext4 ve f2fs'yi ekleyin.

  • Cihaza özel init.rc dosyasına aşağıdakini ekleyin:
    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
    
  • Önyüklemeden sonra izlemeyi getir:

    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