Zoptymalizuj czas uruchamiania

Ten dokument zawiera wskazówki dla partnerów, które pomogą Ci skrócić czas uruchamiania w określonych przypadkach Urządzenia z Androidem. Czas uruchamiania to ważny element wydajności systemu, użytkownicy muszą poczekać na zakończenie uruchamiania, zanim będą mogli korzystać z urządzenia. Urządzenia np. w przypadku samochodów, które uruchamiają się częściej na zimno, czas jest krytyczny (nikt nie lubi czekać dziesiątki sekund tylko po to, aby wpisać miejsce docelowe nawigacji).

Android 8.0 umożliwia skrócenie czasu uruchamiania dzięki obsłudze kilku ulepszeń z wielu komponentów. Te dane są przedstawione w tabeli poniżej (zmierzone na urządzeniach Google Pixel i Pixel XL).

Komponent Poprawa
Program rozruchowy
  • Zapisano 1.6 s przez usunięcie logu UART
  • Zapisano 0,4 s po przejściu z GZIP na LZ4
Jądro urządzenia
  • Zaoszczędzono 0,3 s dzięki usunięciu nieużywanych konfiguracji jądra i zmniejszeniu rozmiaru sterownika
  • Zaoszczędzono 0,3 s z optymalizacją pobierania z wyprzedzeniem dm-verity
  • Zaoszczędzono 0,15 s, aby wyeliminować niepotrzebne oczekiwanie/testy u sterownika
  • Zapisano 0,12 s, aby usunąć CONFIG_CC_OPTIMIZE_FOR_SIZE
Dostrajanie I/O
  • Zaoszczędzono 2 sekundy przy normalnym uruchomieniu
  • Zaoszczędzono 25 s przy pierwszym uruchomieniu
init.*.rc
  • Zaoszczędzono 1,5 s dzięki równoległemu użyciu poleceń init
  • Zaoszczędzono 0,25 s dzięki wczesnemu uruchomieniu zygote
  • Zaoszczędzono 0,22 s przez cpuset tune
Animacja podczas uruchamiania
  • Uruchomiono 2 s wcześniej przy uruchamianiu bez aktywowania funkcji fsck; po uruchomieniu jest znacznie większa rozruch wyzwalany przez fsck
  • Zapisano 5s na Pixelu XL z natychmiastową animacją uruchamiania
Zasada SELinux Zapisane 0,2 s przez: genfscon

Optymalizacja programu rozruchowego

Aby zoptymalizować program rozruchowy i skrócić czas uruchamiania:

  • Do logowania:
    • Wyłącz zapisywanie dzienników do UART, ponieważ może to zająć dużo czasu z wieloma logowanie. Na urządzeniach Google Pixel spowalniają one program rozruchowy o 1, 5 s.
    • Rejestruj tylko błędy i rozważ zapisanie innych informacji w pamięci z osobnym mechanizmem ich pobierania.
  • Rozważenie dekompresji jądra systemu LZ4 w nowoczesnym sprzęcie zamiast GZIP (przykładowy plik patch). Pamiętaj, że: różne opcje kompresji jądra mogą mieć różne i niektóre opcje mogą działać lepiej niż inne i konkretnego sprzętu.
  • Sprawdź niepotrzebne czasy oczekiwania na odbijanie wiadomości/wprowadzanie w trybie specjalnym i minimalizuj .
  • Przekazuj czas rozruchu w programie rozruchowym do jądra jako cmdline.
  • Sprawdź zegar procesora i rozważ zastosowanie równoległego sterowania (wymaga obsługi wielu rdzeni) do wczytywania jądra i inicjowania operacji wejścia-wyjścia.

Optymalizuj wydajność I/O

Poprawa wydajności wejścia-wyjścia ma kluczowe znaczenie dla przyspieszenia rozruchu. wszystkie niepotrzebne niepotrzebne rzeczy należy odłożyć do czasu uruchomienia (w telefonie Google Pixel odczytywane jest około 1,2 GB danych podczas uruchamiania urządzenia).

Dostrój system plików

Odczyt jądra systemu Linux rozpoczyna się, gdy plik jest odczytywany od początku lub gdy bloki są odczytywane sekwencyjnie, co wymaga dostrojenia algorytmu szeregowania wejścia-wyjścia parametry służące do uruchamiania (który obejmuje inny zbiór zadań, niż zwykłe aplikacje).

Urządzenia obsługujące bezproblemowe aktualizacje typu A/B w dużym stopniu korzystają z systemu plików. dostrajanie przy pierwszym uruchomieniu (np. 20 s w przypadku Google Pixel). Na przykład stworzyliśmy te parametry dla telefonu 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
    ...

Różne

  • Włącz rozmiar szyfrowania z wyprzedzeniem dm-verity za pomocą konfiguracji jądra DM_VERITY_HASH_PREFETCH_MIN_SIZE (rozmiar domyślny to 128).
  • W celu zwiększenia stabilności systemu plików oraz rezygnacji z wymuszonego sprawdzania przy każdym rozruchu, używaj nowego narzędzia do generowania ext4, ustawiając TARGET_USES_MKE2FS w BoardConfig.mk

Analizuj I/O

Aby zrozumieć działania wejścia-wyjścia podczas uruchamiania, użyj danych śledzenia jądra (służą one również systrace):

trace_event=block,ext4 in BOARD_KERNEL_CMDLINE

Aby podzielić dostęp do poszczególnych plików, wprowadź następujące zmiany w jądrze: (tylko jądro dla programistów; nie należy go używać w jądrze produkcyjnym):

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

Aby przeanalizować wydajność uruchamiania, użyj tych skryptów.

  • system/extras/boottime_tools/bootanalyze/bootanalyze.py Mierzy czas uruchamiania z podziałem na ważne kroki procesu uruchamiania.
  • system/extras/boottime_tools/io_analysis/check_file_read.py boot_trace – podaje informacje o dostępie do każdego pliku.
  • system/extras/boottime_tools/io_analysis/check_io_trace_all.py boot_trace Udostępnia podział na poziomie systemu.

Optymalizuj init.*.rc

Jest to most z jądra systemu aż do ustanowienia platformy. urządzenia zwykle spędzają kilka sekund na różnych etapach inicjowania.

Równoległe uruchamianie zadań

Bieżąca inicjalizacja Androida jest bardziej lub mniej niż procesem jednowątkowym, nadal mogą wykonywać niektóre zadania równolegle.

  • uruchamiać powolne polecenia w usłudze skryptów powłoki i łączyć je później, i czekania na określoną właściwość. Android 8.0 obsługuje ten przypadek użycia wait_for_property.
  • Zidentyfikuj powolne operacje w inicjowaniu. System zarejestruje polecenie init. exec/wait_for_prop lub inne, długotrwałe działanie (w Androidzie 8.0 dowolne polecenie trwa dłużej niż 50 ms). Na przykład:
    init: Command 'wait_for_coldboot_done' action=wait_for_coldboot_done returned 0 took 585.012ms

    Zaglądanie do tego dziennika może wskazywać możliwości wprowadzenia ulepszeń.

  • Szybko uruchom usługi i włącz urządzenia peryferyjne na ścieżce krytycznej. Dla: na przykład niektóre konta SOC wymagają uruchomienia usług związanych z zabezpieczeniami przed rozpoczęciem SurfaceFlinger. Sprawdź dziennik systemowy, gdy ServiceManager zwróci wartość „Poczekaj na” „usługa” – zwykle oznacza to, że trzeba uruchomić usługę zależną .
  • Usuń wszystkie nieużywane usługi i polecenia z pliku init.*.rc. Wszystkie elementy nieużywane inicjowanie na wczesnym etapie powinno być odroczone do ukończenia uruchamiania.

Uwaga: usługa obiektu jest częścią procesu inicjowania, więc wywołanie setproperty podczas uruchamiania może spowodować długie opóźnienie, jeśli inicjowanie jest zajęte z wbudowanymi poleceniami.

Używaj dostrajania algorytmu szeregowania

Użyj dostrajania algorytmu szeregowania na potrzeby wcześniejszego rozruchu. Przykład z telefonu 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

Niektóre usługi mogą wymagać wzmocnienia priorytetu podczas uruchamiania. Przykład:

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

Zacznij zygote wcześniej

Urządzenia z szyfrowaniem opartym na plikach mogą uruchomić zygote wcześniej podczas uruchamiania zygote-start aktywator (domyślnie zygote jest uruchamiana na poziomie głównej klasy, która jest znacznie późniejsza niż zygote-start). Wykonując tę czynność, zezwól na uruchomienie zygote na wszystkich procesorach ( nieprawidłowe ustawienie cpuset może wymusić działanie Zygote na określonych procesorach).

Wyłącz oszczędzanie energii

ustawienie oszczędzania energii przez komponenty takie jak UFS lub procesor podczas uruchamiania urządzenia; gubernatora można wyłączyć.

Uwaga: oszczędzanie energii powinno być włączone w trybu ładowania, aby oszczędzać energię.

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

Wstrzymaj inicjowanie niekrytyczne

Inicjowanie niekrytyczne, na przykład ZRAM, można odroczyć do boot_complete.

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

Optymalizacja animacji rozruchu

Skorzystaj z poniższych wskazówek, aby zoptymalizować animację rozruchu.

Skonfiguruj wczesny start

Android 8.0 umożliwia wcześniejsze uruchomienie animacji rozruchu, przed podłączeniem danych użytkownika partycji danych. Jednak nawet w przypadku korzystania z nowego łańcucha narzędzi ext4 w Androidzie 8.0, fsck jest nadal okresowo uruchamiana ze względów bezpieczeństwa, powodując opóźnienie uruchamianie usługi uruchamiania animacji.

Aby uruchomić animację rozruchu wcześniej, podziel element montowania fstab na 2 fazy:

  • Na wczesnym etapie podłącz tylko partycje (np. system/ i vendor/), które nie wymagają uruchomienia a następnie uruchomić usługi animacji i ich zależności (takie jak Servicemanager i Surfaceflinger).
  • W drugim etapie podłącz partycje (takie jak data/), które ale wymagają uruchamiania testów.

Animacja uruchamiania uruchamia się znacznie szybciej (i w stałym czasie) niezależnie od do kurwy.

Wykończ czyszczenie

Po otrzymaniu sygnału wyjściowego animacja uruchamiana odtworzy ostatnią część, co może spowolnić uruchamianie. System, który uruchamia się szybko, nie wymaga animacje, które mogłyby ukryć wprowadzone ulepszenia. Zalecamy przez co pętla powtarzania i koniec są krótkie.

Optymalizacja SELinux

Skorzystaj z poniższych wskazówek, aby zoptymalizować SELinux i skrócić czas uruchamiania.

  • Używaj przejrzystych wyrażeń regularnych. Niepoprawnie sformułowane wyrażenie regularne może znacznie zwiększyć koszty podczas dopasowywania zasad SELinux dla sys/devices w file_contexts. Na przykład wyrażenie regularne Funkcja /sys/devices/.*abc.*(/.*)? błędnie wymusza skanowanie wszystkich elementów /sys/devices podkatalogów zawierających słowo „abc”, co umożliwia dopasowanie zarówno dla /sys/devices/abc, jak i /sys/devices/xyz/abc. Rozszerzenie tego wyrażenia regularnego do wersji /sys/devices/[^/]*abc[^/]*(/.*)? sprawi, włącz dopasowanie tylko dla adresu /sys/devices/abc.
  • Przenieś etykiety do genfscon. Ta istniejąca funkcja SELinux przekazuje prefiksy dopasowania plików do jądra systemu pliku binarnego SELinux, w którym jądro stosuje go do wygenerowanych w jądrze funkcji systemów plików. Pomaga to również w naprawianiu błędnie oznaczonych plików utworzonych w jądrze, warunki wyścigu, które mogą wystąpić między procesami przestrzeni użytkownika usiłującymi uzyskać dostęp przed zmianą etykiet.

Narzędzia i metody

Opisane poniżej narzędzia pomogą Ci zbierać dane na potrzeby celów optymalizacji.

Wykres rozruchowy

Wykres rozruchowy udostępnia zestawienie obciążenia procesora i wejścia-wyjścia dla wszystkich procesów w całym procesie systemu. Nie wymaga odbudowy obrazu systemu i można go użyć należy sprawdzić poprawność przed przystąpieniem do systrace.

Aby włączyć wykres rozruchowy:

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

Po uruchomieniu pobierz wykres rozruchowy:

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

Gdy skończysz, usuń /data/bootchart/enabled, aby zapobiec gromadzeniu dane za każdym razem.

Jeśli schemat rozruchowy nie działa i pojawia się błąd z informacją, że bootchart.png nie istnieje, zrób następujące:
  1. Uruchom te polecenia:
          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. Zaktualizuj urządzenie $ANDROID_BUILD_TOP/system/core/init/grab-bootchart.sh aby wskazać lokalną kopię pliku pybootchartgui (lokalizacja: ~/Documents/bootchart/pybootchartgui.py)

Sístrače

Systrace umożliwia zbieranie logów czasu jądra i Androida podczas uruchamiania. Wizualizacja systrace może pomóc w analizie konkretnego problemu podczas uruchamiania. (Aby jednak sprawdzić średnią lub skumulowaną liczbę w okresie podczas rozruchu, łatwiej jest bezpośrednio zajrzeć do logu jądra).

Aby włączyć systrace podczas uruchamiania:

  • W frameworks/native/cmds/atrace/atrace.rc zmień:
      write /sys/kernel/debug/tracing/tracing_on 0
      write /sys/kernel/tracing/tracing_on 0

    Aby:

      #    write /sys/kernel/debug/tracing/tracing_on 0
      #    write /sys/kernel/tracing/tracing_on 0
  • Umożliwia to śledzenie (domyślnie jest ono wyłączone).

  • W pliku device.mk dodaj ten wiersz:
    PRODUCT_PROPERTY_OVERRIDES +=    debug.atrace.tags.enableflags=802922
    PRODUCT_PROPERTY_OVERRIDES +=    persist.traced.enable=0
  • W pliku BoardConfig.mk urządzenia dopisz:
    BOARD_KERNEL_CMDLINE := ... trace_buf_size=64M trace_event=sched_wakeup,sched_switch,sched_blocked_reason,sched_cpu_hotplug
  • Aby przeprowadzić szczegółową analizę wejścia-wyjścia, dodaj też bloki oraz ext4 i f2fs.

  • W pliku init.rc powiązanym z konkretnym urządzeniem dodaj:
    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
    
  • Po uruchomieniu pobierz log czasu:

    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
    
.