Başlatma sürelerini optimize etme

Bu dokümanda, belirli Android cihazların önyükleme sürelerini iyileştirmeyle ilgili iş ortağı rehberliği sağlanmaktadır. Kullanıcıların cihazı kullanabilmek için ö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 arabalar gibi cihazlarda hızlı bir başlatma süresi kritik öneme sahiptir (Hiç kimse sadece bir navigasyon hedefini girmek için onlarca saniye beklemeyi sevmez).

Android 8.0, çeşitli bileşenlerde çeşitli iyileştirmeleri destekleyerek önyükleme sürelerini kısaltır. Aşağıdaki tabloda bu performans iyileştirmeleri (Google Pixel ve Pixel XL cihazlarda ölçülen değerler) özetlenmiştir.

Bileşen İyileştirme
Önyükleyici
  • UART günlüğü kaldırılarak 1,6 saniye tasarruf edildi
  • GZIP'den LZ4'e geçerek 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 optimizasyonuyla 0,3 saniye tasarruf edildi
  • Sürücüde gereksiz bekleme/testi kaldırarak 0,15 saniye tasarruf edildi
  • CONFIG_CC_OPTIMIZE_FOR_SIZE kaldırılarak 0,12 saniye tasarruf edildi
G/Ç ayarı
  • Normal önyüklemede 2 saniye tasarruf
  • İlk açılışta 25 saniye tasarruf
init.*.rc
  • Başlatma komutlarını paralelleştirerek 1,5 saniye tasarruf edildi
  • Zigotu erken başlatarak 0,25 saniye tasarruf edildi
  • cpuset ayarıyla 0,22 saniye tasarruf edildi
Açılış animasyonu
  • fsck tetiklenmeden önyükleme işlemi 2 saniye önce başladı, fsck tetiklenerek önyükleme işlemi çok daha büyük
  • Açılış animasyonunun hemen kapatılmasıyla Pixel XL'de 5 saniye tasarruf edildi
SELinux politikası genfscon tarafından 0,2 saniye tasarruf edildi

Bootloader'ı optimize etme

Önyükleyiciyi daha iyi önyükleme süreleri için optimize etmek üzere:

  • Günlük kaydı için:
    • Çok fazla günlük kaydı olduğunda uzun sürebileceği için UART'a günlük kaydını devre dışı bırakın. (Google Pixel cihazlarda, bootloader'ı 1,5 saniye yavaşlattığı tespit edildi).
    • Yalnızca hata durumlarını günlüğe kaydedin ve diğer bilgileri, ayrı bir mekanizmayla almak için bellekte saklayın.
  • Çekirdek sıkıştırma için, modern donanımlarda GZIP yerine LZ4 kullanmayı düşünün (örnek yama). Farklı çekirdek sıkıştırma seçeneklerinin farklı yükleme ve sıkıştırma sürelerine sahip olabileceğini ve bazı seçeneklerin belirli donanımınız için diğerlerinden daha iyi çalışabileceğini unutmayın.
  • Hata ayıklama/özel mod girişi için gereksiz bekleme sürelerini kontrol edin ve bunları en aza indirin.
  • Önyükleme makinesinde harcanan önyükleme süresini cmdline olarak çekirdeğe iletin.
  • Çekirdek yükleme ve G/Ç'yi başlatma için CPU saatini kontrol edin ve paralelleştirmeyi (çok çekirdekli destek gerekir) düşünün.

G/Ç verimliliğini optimize etme

Açılış süresini kısaltmak için G/Ç verimliliğini artırmak çok önemlidir.Gerekmeyen her şeyin okunması, önyükleme işleminden sonraya ertelenebilir (Google Pixel'de önyükleme sırasında yaklaşık 1,2 GB veri okunur).

Dosya sistemini ayarlama

Linux çekirdeği önceden okuma özelliği, bir dosya baştan okunduğunda veya bloklar sırayla okunduğunda devreye girer. Bu da I/O planlayıcı parametrelerinin özellikle önyükleme için (normal uygulamalardan farklı bir iş yükü karakterizasyonuna sahiptir) ayarlanmasını zorunlu kılar.

Sorunsuz (A/B) güncellemeleri destekleyen cihazlar, ilk kez başlatıldığında dosya sistemi ayarlarından (ör. Google Pixel'de 20 saniye) büyük ölçüde yararlanır. Örneğin, 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

  • DM_VERITY_HASH_PREFETCH_MIN_SIZE çekirdek yapılandırmasını kullanarak dm-verity karma ön getirme boyutunu etkinleştirin (varsayılan boyut 128'dir).
  • Daha iyi dosya sistemi kararlılığı ve her önyüklemede gerçekleşen zorunlu kontrolün kaldırılması için BoardConfig.mk dosyasında TARGET_USES_MKE2FS ayarını yaparak yeni ext4 oluşturma aracını kullanın.

G/Ç'yi analiz etme

Açılış sırasındaki G/Ç etkinliklerini anlamak için çekirdek ftrace verilerini (systrace tarafından da kullanılır) kullanın:

trace_event=block,ext4 in BOARD_KERNEL_CMDLINE

Her dosyanın dosya erişimini ayrıntılı olarak incelemek 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);

Açılış performansını analiz etmenize yardımcı olması için aşağıdaki komut dosyalarını kullanın.

  • system/extras/boottime_tools/bootanalyze/bootanalyze.py Başlatma işlemindeki önemli adımların dökümünü içeren bir başlangıç süresi ölçümü yapar.
  • system/extras/boottime_tools/io_analysis/check_file_read.py boot_trace Her dosya için erişim bilgilerini sağlar.
  • system/extras/boottime_tools/io_analysis/check_io_trace_all.py boot_trace Sistem düzeyinde döküm sağlar.

init.*.rc dosyasını optimize etme

Init, çekirdekten çerçevenin oluşturulmasına kadar olan köprüdür ve cihazlar genellikle farklı init aşamalarında birkaç saniye geçirir.

Görevleri paralel olarak çalıştırma

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

  • Kabuk komut dosyası hizmetinde yavaş komutları yürütün ve belirli bir mülkü bekleyerek daha sonra birleştirin. Android 8.0, bu kullanım alanını yeni bir wait_for_property komutuyla destekler.
  • init'te yavaş işlemleri belirleme Sistem, init komutunu (exec/wait_for_prop) veya uzun süren herhangi bir işlemi (Android 8.0'da 50 ms'den uzun süren tüm komutlar) günlüğe kaydeder. Örnek:
    init: Command 'wait_for_coldboot_done' action=wait_for_coldboot_done returned 0 took 585.012ms

    Bu günlüğü inceleyerek iyileştirme fırsatları bulabilirsiniz.

  • Kritik yoldaki hizmetleri başlatın ve çevre birim cihazlarını erkenden etkinleştirin. Örneğin, bazı SOC'ler SurfaceFlinger'ı başlatmadan önce güvenlikle ilgili hizmetlerin başlatılmasını gerektirir. ServiceManager "wait for service" (hizmeti bekle) değerini döndürdüğünde sistem günlüğünü inceleyin. Bu, genellikle bağımlı bir hizmetin önce başlatılması gerektiğinin bir işaretidir.
  • init.*.rc dosyasında kullanılmayan tüm hizmetleri ve komutları kaldırın. İlk aşamada başlatılırken kullanılmayan her şey, önyükleme tamamlanana kadar ertelenebilir.

Not: Mülk hizmeti, init sürecinin bir parçasıdır. Bu nedenle, init yerleşik komutlarda meşgulse önyükleme sırasında setproperty çağrısı uzun bir gecikmeye neden olabilir.

Planlayıcı ayarını kullanma

Erken başlatma için planlayıcı ayarları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ırması gerekebilir. Örnek:

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

Zigotu erken başlatın

Dosya tabanlı şifreleme kullanan cihazlar, zygote-start tetikleyicisinde zygote'u daha erken başlatabilir (varsayılan olarak zygote, zygote-start'tan çok daha sonra olan class main'de başlatılır). Bunu yaparken zygote'un tüm CPU'larda çalışmasına izin verdiğinizden emin olun (yanlış cpuset ayarı zygote'u belirli CPU'larda çalışmaya zorlayabilir).

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

Cihazın başlatılması sırasında UFS ve/veya CPU denetleyicisi 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ı erteleme

ZRAM gibi kritik olmayan başlatma işlemleri boot_complete'e ertelenebilir.

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

Açılış animasyonunu optimize etme

Açılış animasyonunu optimize etmek için aşağıdaki ipuçlarını kullanın.

Erken başlangıcı yapılandırma

Android 8.0, önyükleme animasyonunun userdata bölümünün monte edilmesinden önce başlatılmasını sağlar. Ancak Android 8.0'da yeni ext4 araç zinciri kullanıldığında bile fsck, güvenlik nedeniyle düzenli olarak tetiklenmeye devam eder ve bootanimation hizmetinin başlatılmasında gecikme yaşanmasına neden olur.

bootanimation'in erken başlamasını sağlamak için fstab bağlama işlemini iki aşamaya bölün:

  • İlk aşamada, yalnızca çalıştırma kontrolü gerektirmeyen bölümleri (system/ ve vendor/ gibi) monte edin, ardından önyükleme animasyonu hizmetlerini ve bunların bağımlılarını (ör. servicemanager ve surfaceflinger) başlatın.
  • İkinci aşamada, kontrol çalıştırmayı gerektiren bölümleri (data/ gibi) monte edin.

fsck'ye bakılmaksızın önyükleme animasyonu çok daha hızlı (ve sabit bir sürede) başlatılır.

Temiz bir şekilde bitirin

Çıkış sinyalini aldıktan sonra bootanimation son kısmı oynatır. Bu kısmın uzunluğu, önyükleme süresini uzatabilir. Hızlı bir şekilde açılan bir sistemde, yapılan iyileştirmeleri etkili bir şekilde gizleyebilecek uzun animasyonlara gerek yoktur. Hem tekrarlanan döngüyü hem de finali kısa tutmanız önerilir.

SELinux'u optimize etme

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

  • Net 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 neden olabilir. Örneğin, normal ifade /sys/devices/.*abc.*(/.*)?, "abc" içeren tüm /sys/devices alt dizinlerinin yanlışlıkla taranmasını zorunlu kılarak hem /sys/devices/abc hem de /sys/devices/xyz/abc için eşleşmeleri etkinleştirir. Bu normal ifadeyi /sys/devices/[^/]*abc[^/]*(/.*)? olarak iyileştirmek, yalnızca /sys/devices/abc ile eşleşmeyi sağlar.
  • Etiketleri genfscon'a taşıyın. Mevcut SELinux özelliği, dosya eşleştirme ön eklerini SELinux ikili dosyasındaki çekirdeğe iletir. Çekirdek, bu ön ekleri çekirdek tarafından oluşturulan dosya sistemlerine uygular. Bu işlem, yeniden etiketleme işleminden önce bu dosyalara erişmeye çalışan kullanıcı alanı süreçleri arasında oluşabilecek yarış koşullarını önleyerek, çekirdek tarafından oluşturulan yanlış etiketlenmiş dosyaların düzeltilmesine de yardımcı olur.

Araçlar ve yöntemler

Optimizasyon hedefleri için veri toplamanıza yardımcı olması amacıyla aşağıdaki araçları kullanın.

Bootchart

Bootchart, sistem genelindeki tüm işlemlerin CPU ve G/Ç yükü dökümünü sağlar. Sistem görüntüsünün yeniden oluşturulmasını gerektirmez ve systrace'e girmeden önce hızlı bir doğrulama kontrolü olarak kullanılabilir.

bootchart'ı etkinleştirmek için:

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

Açılıştan sonra önyükleme grafiğini alın:

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

İşlem tamamlandığında, verilerin her seferinde toplanmasını önlemek için /data/bootchart/enabled öğesini silin.

bootchart çalışmıyorsa ve bootchart.png dosyasının bulunmadığını belirten bir hata alırsanı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 değerini, pybootchartgui değerinin yerel kopyasını (~/Documents/bootchart/pybootchartgui.py konumunda) işaret edecek şekilde güncelleyin

Systrace

Systrace, önyükleme sırasında hem çekirdek hem de Android izlemelerinin toplanmasına olanak tanır. systrace'in görselleştirilmesi, önyükleme 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 izlemeye bakmak daha kolaydır).

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

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

    Şunları yapmak için:

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

  • device.mk dosyasına aşağıdaki satırı ekleyin:
    PRODUCT_PROPERTY_OVERRIDES +=    debug.atrace.tags.enableflags=802922
    PRODUCT_PROPERTY_OVERRIDES +=    persist.traced.enable=0
  • Cihaz BoardConfig.mk dosyasına aşağıdakileri 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 block, ext4 ve f2fs dosya sistemlerini de ekleyin.

  • Cihaza özgü init.rc dosyasına aşağıdakileri 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
  • Açılıştan sonra, izlemeyi alın:

    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