Ten dokument zawiera wskazówki dla partnerów dotyczące skracania czasu uruchamiania określonych urządzeń z Androidem. Czas uruchamiania jest ważnym elementem wydajności systemu, ponieważ użytkownicy muszą czekać na zakończenie uruchamiania, zanim będą mogli korzystać z urządzenia. W przypadku urządzeń takich jak samochody, w których przypadku uruchamianie z zimnego stanu jest częstsze, szybki czas uruchamiania jest kluczowy (nikt nie lubi czekać kilkadziesiąt sekund tylko po to, aby podać cel nawigacji).
Android 8.0 umożliwia skrócenie czasu uruchamiania dzięki obsłudze kilku ulepszeń w różnych komponentach. W tabeli poniżej podsumowano te ulepszenia wydajności (zmierzone na urządzeniach Google Pixel i Pixel XL).
Komponent | Ulepszenie |
---|---|
Program rozruchowy |
|
Jądro urządzenia |
|
Dostosowywanie I/O |
|
init.*.rc |
|
Animacja uruchamiania |
|
Zasady SELinux | Zaoszczędzono 0,2 s dzięki genfscon |
Optymalizacja programu rozruchowego
Aby zoptymalizować bootloader pod kątem skrócenia czasu uruchamiania:
- Logowanie:
- Wyłącz zapisywanie logów na porcie UART, ponieważ może to zająć dużo czasu przy dużej ilości logów. (na urządzeniach Google Pixel okazało się, że spowalnia ono bootloader o 1,5 s).
- Rejestruj tylko sytuacje wystąpienia błędów i rozważ przechowywanie innych informacji w pamięci z użyciem osobnego mechanizmu wyszukiwania.
- W przypadku dekompresji jądra zastanów się nad użyciem LZ4 na nowoczesnym sprzęcie zamiast GZIP (np. patch). Pamiętaj, że różne opcje kompresji jądra mogą mieć różne czasy wczytywania i dekompresji, a niektóre opcje mogą działać lepiej niż inne w przypadku Twojego konkretnego sprzętu.
- Sprawdź niepotrzebne czasy oczekiwania na debouncing/special mode entry i zminimalizuj je.
- Przekazywanie czasu rozruchu spędzonego w programie rozruchowym do jądra jako cmdline.
- Sprawdź zegar procesora i rozważ użycie równoległości (wymaga obsługi wielu rdzeni) do ładowania jądra i inicjowania operacji wejścia/wyjścia.
Optymalizacja wydajności we/wy
Zwiększanie wydajności we/wy to kluczowy element przyspieszania czasu uruchamiania, a odczytywanie niepotrzebnych danych powinno być odkładane na czas po uruchomieniu (na telefonie Google Pixel podczas uruchamiania odczytywanych jest około 1,2 GB danych).
Dostosowywanie systemu plików
Funkcja odczytu w przód w jądrze Linuksa włącza się, gdy plik jest odczytywany od początku lub gdy bloki są odczytywane sekwencyjnie, co wymaga dostosowania parametrów harmonogramu we/wy w szczególny sposób na potrzeby uruchamiania (które ma inną charakterystykę obciążenia niż zwykłe aplikacje).
Urządzenia obsługujące płynne aktualizacje (A/B) korzystają z dostosowywania systemu plików podczas pierwszego uruchamiania (np. 20 s na Google Pixel). Na przykład w przypadku Google Pixel zoptymalizowaliśmy te parametry:
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 wyprzedzającego pobierania hasha dm-verity za pomocą konfiguracji jądra (DM_VERITY_HASH_PREFETCH_MIN_SIZE). Domyślny rozmiar to 128.
- Aby zwiększyć stabilność systemu plików i wyeliminować wymuszone sprawdzanie, które występuje przy każdym uruchomieniu, użyj nowego narzędzia do generowania ext4, ustawiając TARGET_USES_MKE2FS w pliku BoardConfig.mk.
Analiza I/O
Aby poznać aktywność we/wy w trakcie uruchamiania, użyj danych ftrace jądra (używanych też przez systrace):
trace_event=block,ext4 in BOARD_KERNEL_CMDLINE
Aby określić dostęp do poszczególnych plików, wprowadź w jądrze te zmiany (tylko w jądrze deweloperskim; nie używaj ich w jądrach produkcyjnych):
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 poniższych skryptów.
system/extras/boottime_tools/bootanalyze/bootanalyze.py
Pomiary czasu uruchamiania z podziałem na ważne etapy procesu uruchamiania.system/extras/boottime_tools/io_analysis/check_file_read.py boot_trace
Podaje informacje o dostępie do poszczególnych plików.system/extras/boottime_tools/io_analysis/check_io_trace_all.py boot_trace
Pokazuje podział na poziomie systemu.
Optymalizacja pliku init.*.rc
Inicjowanie to proces, który łączy jądro z ramami. Na różnych etapach inicjowania urządzenia zwykle spędzają kilka sekund.
równoległe wykonywanie zadań;
Chociaż bieżący proces inicjalizacji Androida jest mniej więcej jednowątkowy, niektóre zadania można wykonywać równolegle.
- Wykonuj powolne polecenia w usłudze skryptu powłoki i dołącz do niej później, oczekując na określoną właściwość. Android 8.0 obsługuje ten przypadek użycia za pomocą nowego polecenia
wait_for_property
. - Identyfikowanie wolnych operacji w init. System rejestruje polecenie init exec/wait_for_prop lub każde działanie, które zajmuje dużo czasu (w Androidzie 8.0 każde polecenie, które zajmuje więcej niż 50 ms). Przykład:
init: Command 'wait_for_coldboot_done' action=wait_for_coldboot_done returned 0 took 585.012ms
Przejrzyj ten dziennik, aby znaleźć możliwości poprawy.
- Wczesna aktywacja usług i urządzeń peryferyjnych na ścieżce krytycznej. Na przykład niektóre zespoły SOC wymagają uruchomienia usług związanych z bezpieczeństwem przed uruchomieniem narzędzia SurfaceFlinger. Sprawdź dziennik systemowy, gdy ServiceManager zwróci „wait for service” (zaczekaj na usługę) – zwykle oznacza to, że najpierw należy uruchomić usługę zależną.
- Usuń wszystkie nieużywane usługi i polecenia w pliku init.*.rc. Wszystko, czego nie używa się na wczesnym etapie inicjalizacji, powinno zostać odroczone do momentu zakończenia uruchamiania.
Uwaga: usługa Property jest częścią procesu inicjowania, więc wywołanie funkcji setproperty
podczas uruchamiania może spowodować długie opóźnienie, jeśli inicjowanie jest zajęte wykonywaniem wbudowanych poleceń.
Używanie dostosowywania algorytmu szeregowania
Użyj dostrajania harmonogramu do wczesnego uruchamiania. Przykład na telefonie 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ć podwyższenia 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 ...
Wczesna zygota
Urządzenia z szyfrowaniem na poziomie pliku mogą uruchomić zygote wcześniej, gdy zostanie wywołany przez zygote-start (domyślnie zygote jest uruchamiany w ramach metody main, czyli znacznie później niż zygote-start). Pamiętaj, aby zezwolić na uruchamianie zygote na wszystkich procesorach (nieprawidłowe ustawienie cpuset może spowodować uruchomienie zygote na określonych procesorach).
Wyłączanie oszczędzania energii
Podczas uruchamiania urządzenia ustawienie oszczędzania energii dla komponentów takich jak UFS lub sterownik procesora może być wyłączone.
Ostrzeżenie: w trybie ładowania należy włączyć oszczędzanie energii.
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
Opóźnij niekrytyczne inicjalizację
Niekrytyczna inicjalizacja, np. ZRAM, może zostać odroczona do boot_complete
.
on property:sys.boot_completed=1 # Enable ZRAM on boot_complete swapon_all /vendor/etc/fstab.${ro.hardware}
Optymalizowanie animacji uruchamiania
Aby zoptymalizować animację uruchamiania, skorzystaj z podanych niżej wskazówek.
Konfigurowanie wczesnego rozpoczęcia
Android 8.0 umożliwia wcześniejsze rozpoczęcie animacji uruchamiania, jeszcze przed zamontowaniem partycji danych użytkownika. Jednak nawet przy użyciu nowego łańcucha narzędzi ext4 w Androidzie 8.0, fsck jest nadal okresowo uruchamiany ze względów bezpieczeństwa, co powoduje opóźnienie uruchamiania usługi bootanimation.
Aby animacja uruchamiania się systemu zaczęła się wcześniej, podziel punkt zamontowania fstab na 2 fazy:
- Na wczesnym etapie zamontuj tylko partycje (np.
system/
ivendor/
), które nie wymagają sprawdzania podczas uruchamiania, a następnie uruchom usługi animacji rozruchu i ich zależności (np. servicemanager i surfaceflinger). - W drugiej fazie zamontuj partycje (np.
data/
), które wymagają wykonania kontroli.
Animacja uruchamiania będzie uruchamiana znacznie szybciej (i w ciągłym czasie) niezależnie od chksumy.
Zakończ czyszczenie
Po otrzymaniu sygnału wyjścia animacja uruchamiania odtwarza ostatnią część, której długość może wydłużać czas uruchamiania. System, który szybko się uruchamia, nie potrzebuje długich animacji, które mogłyby skutecznie ukryć wprowadzone ulepszenia. Zalecamy, aby pętla powtarzająca się i finał były krótkie.
Optymalizacja SELinux
Aby zoptymalizować SELinux pod kątem skrócenia czasu uruchamiania, skorzystaj z podanych niżej wskazówek.
- Używaj prostych wyrażeń regularnych. Nieprawidłowo sformułowany wyrażenie regularne może powodować duże obciążenie podczas dopasowywania zasad SELinux do
sys/devices
wfile_contexts
. Na przykład wyrażenie regularne/sys/devices/.*abc.*(/.*)?
błędnie wymusza skanowanie wszystkich/sys/devices
podkatalogów zawierających „abc”, co umożliwia dopasowanie zarówno do/sys/devices/abc
, jak i/sys/devices/xyz/abc
. Poprawione wyrażenie regularne/sys/devices/[^/]*abc[^/]*(/.*)?
umożliwi dopasowanie tylko do/sys/devices/abc
. - Przenieś etykiety do genfscon. Ta istniejąca funkcja SELinux przekazuje prefiksy dopasowywania plików do jądra w binarnym SELinux, gdzie jądro stosuje je do generowanych przez siebie systemów plików. Pomaga to również naprawiać błędnie oznaczone pliki utworzone przez jądro, zapobiegając błędom, które mogą wystąpić między procesami w przestrzeni użytkownika próbującymi uzyskać dostęp do tych plików przed ponownym oznaczeniem.
Narzędzia i metody
Korzystaj z tych narzędzi, aby zbierać dane na potrzeby celów optymalizacji.
Bootchart
Bootchart zawiera podział obciążenia procesora i wejścia/wyjścia wszystkich procesów w całym systemie. Nie wymaga odbudowywania obrazu systemu i może służyć jako szybka kontrola sanityzacji przed rozpoczęciem korzystania z systrace.
Aby włączyć bootchart:
adb shell 'touch /data/bootchart/enabled'
adb reboot
Po uruchomieniu pobierz wykres uruchamiania:
$ANDROID_BUILD_TOP/system/core/init/grab-bootchart.sh
Po zakończeniu usuń /data/bootchart/enabled
, aby zapobiec gromadzeniu danych za każdym razem.
bootchart.png
nie istnieje, wykonaj te czynności:
- 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
- Zaktualizuj plik
$ANDROID_BUILD_TOP/system/core/init/grab-bootchart.sh
, aby wskazywał on lokalną kopię plikupybootchartgui
(znajdującego się w folderze~/Documents/bootchart/pybootchartgui.py
).
Systrace
Systrace umożliwia zbieranie podczas uruchamiania zarówno śladów jądra, jak i Androida. Wizualizacja systrace może pomóc w analizie konkretnego problemu podczas uruchamiania. (Jednak aby sprawdzić średnią liczbę lub nagromadzoną liczbę podczas całego uruchamiania, łatwiej jest bezpośrednio przejrzeć ślad 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
Do:
# write /sys/kernel/debug/tracing/tracing_on 0 # write /sys/kernel/tracing/tracing_on 0
- 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 dodaj:BOARD_KERNEL_CMDLINE := ... trace_buf_size=64M trace_event=sched_wakeup,sched_switch,sched_blocked_reason,sched_cpu_hotplug
- W pliku
init.rc
dla konkretnego urządzenia 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 ślad:
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
Włącza śledzenie (domyślnie jest ono wyłączone).
Aby przeprowadzić szczegółową analizę wejść i wyjść, dodaj też blok i ext4 oraz f2fs.