Bootzeiten optimieren

In diesem Dokument finden Partner Hinweise zur Verbesserung der Bootzeiten für bestimmte Android-Geräte. Die Bootzeit ist ein wichtiger Bestandteil der Systemleistung, da Nutzer warten müssen, bis der Bootvorgang abgeschlossen ist, bevor sie das Gerät verwenden können. Bei Geräten wie Autos, bei denen das Kaltstarten häufiger vorkommt, ist eine kurze Startzeit entscheidend. Niemand möchte mehrere Dutzend Sekunden warten müssen, nur um ein Navigationsziel einzugeben.

Android 8.0 ermöglicht kürzere Bootzeiten, da mehrere Verbesserungen an verschiedenen Komponenten unterstützt werden. In der folgenden Tabelle sind diese Leistungsverbesserungen zusammengefasst (gemessen auf Google Pixel und Google Pixel XL).

Komponente Verbesserung
Bootloader
  • 1,6 Sekunden eingespart, da UART-Protokoll entfernt wurde
  • Durch die Umstellung von GZIP auf LZ4 wurden 0,4 Sekunden eingespart.
Gerätekernel
  • 0,3 Sekunden eingespart, indem nicht verwendete Kernelkonfigurationen entfernt und die Größe des Treibers reduziert wurde
  • 0,3 Sekunden durch dm-verity-Prefetch-Optimierung gespart
  • 0,15 Sekunden eingespart, da unnötiges Warten/Testen im Treiber entfernt wurde
  • 0,12 Sekunden gespart, da CONFIG_CC_OPTIMIZE_FOR_SIZE entfernt wurde
I/O-Optimierung
  • 2 Sekunden beim normalen Start eingespart
  • 25 Sekunden beim ersten Start gespart
init.*.rc
  • 1,5 Sekunden durch parallele Ausführung von Init-Befehlen gespart
  • 0,25 Sekunden eingespart, da das Zygote frühzeitig gestartet wurde
  • Durch die Optimierung von cpuset wurden 0,22 Sekunden eingespart.
Boot-Animation
  • Startet beim Start ohne ausgelöstes fsck 2 Sekunden früher, beim Start mit ausgelöstem fsck viel länger
  • Auf Pixel XL werden 5 Sekunden gespart, da die Bootanimation sofort beendet wird
SELinux-Richtlinie 0,2 Sekunden durch genfscon gespart

Bootloader optimieren

So optimieren Sie den Bootloader für kürzere Bootzeiten:

  • Für die Protokollierung:
    • Deaktivieren Sie das Schreiben von Protokollen in UART, da dies bei vielen Protokollen sehr lange dauern kann. (Auf Google Pixel-Geräten wurde festgestellt, dass dadurch der Bootloader um 1,5 Sekunden verlangsamt wird.)
    • Protokollieren Sie nur Fehlersituationen und speichern Sie andere Informationen mit einem separaten Mechanismus zum Abrufen im Arbeitsspeicher.
  • Für die Kerneldekomprimierung sollten Sie auf aktueller Hardware LZ4 anstelle von GZIP verwenden (Beispiel Patch). Beachten Sie, dass verschiedene Kernelkomprimierungsoptionen unterschiedliche Lade- und Dekomprimierungszeiten haben können. Außerdem funktionieren einige Optionen für Ihre spezifische Hardware möglicherweise besser als andere.
  • Prüfen Sie unnötige Wartezeiten für das Entprellen/den Eintritt in den Sondermodus und minimieren Sie sie.
  • Übergeben Sie die im Bootloader verbrachte Bootzeit als cmdline an den Kernel.
  • Prüfen Sie die CPU-Taktfrequenz und ziehen Sie eine Parallelisierung (erfordert Multi-Core-Unterstützung) für das Laden des Kernels und das Initialisieren der E/A in Betracht.

E/A-Effizienz optimieren

Die Verbesserung der E/A-Effizienz ist entscheidend, um die Bootzeit zu verkürzen. Nicht notwendige Daten sollten erst nach dem Start gelesen werden. Auf einem Google Pixel werden beim Start etwa 1,2 GB an Daten gelesen.

Dateisystem optimieren

Die Vorab-Lesefunktion des Linux-Kernels wird aktiviert, wenn eine Datei von Anfang an gelesen wird oder Blöcke sequenziell gelesen werden. Daher müssen die I/O-Scheduler-Parameter speziell für das Booten optimiert werden, da das eine andere Arbeitslastcharakteristik als normale Apps hat.

Geräte, die nahtlose (A/B)-Updates unterstützen, profitieren beim ersten Start erheblich von der Optimierung des Dateisystems (z.B. 20 Sekunden bei Google Pixel). Als Beispiel haben wir die folgenden Parameter für Google Pixel optimiert:

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

Sonstiges

  • Aktivieren Sie die Größe des dm-verity-Hash-Prefetch mit der Kernelkonfiguration DM_VERITY_HASH_PREFETCH_MIN_SIZE (Standardgröße ist 128).
  • Für eine bessere Dateisystemstabilität und eine entfallene erzwungene Prüfung, die bei jedem Start ausgeführt wird, verwenden Sie das neue Tool zum Erstellen von ext4-Dateisystemen. Legen Sie dazu in BoardConfig.mk TARGET_USES_MKE2FS fest.

E/A analysieren

Verwenden Sie Kernel-ftrace-Daten (auch von systrace verwendet), um I/O-Aktivitäten während des Bootens zu analysieren:

trace_event=block,ext4 in BOARD_KERNEL_CMDLINE

Nehmen Sie die folgenden Änderungen am Kernel vor, um den Dateizugriff für jede Datei aufzuschlüsseln (nur Entwicklungskernel; nicht in Produktionskerneln verwenden):

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

Mit den folgenden Scripts können Sie die Bootleistung analysieren.

  • system/extras/boottime_tools/bootanalyze/bootanalyze.py Misst die Bootzeit mit einer Aufschlüsselung der wichtigen Schritte im Bootvorgang.
  • system/extras/boottime_tools/io_analysis/check_file_read.py boot_trace Enthält Zugriffsinformationen für jede Datei.
  • system/extras/boottime_tools/io_analysis/check_io_trace_all.py boot_trace Eine Aufschlüsselung auf Systemebene.

init.*.rc optimieren

Init ist die Brücke vom Kernel bis zur Einrichtung des Frameworks. Geräte verbringen in der Regel einige Sekunden in verschiedenen Init-Phasen.

Aufgaben parallel ausführen

Die aktuelle Android-init ist zwar mehr oder weniger ein einzeiliger Prozess, Sie können aber einige Aufgaben parallel ausführen.

  • Langsame Befehle in einem Shell-Script-Dienst ausführen und später darauf warten, dass eine bestimmte Property erfüllt ist. Android 8.0 unterstützt diesen Anwendungsfall mit einem neuen Befehl wait_for_property.
  • Langsame Vorgänge in init identifizieren Das System protokolliert den Befehl „init exec/wait_for_prop“ oder jede Aktion, die lange dauert (unter Android 8.0 jeder Befehl, der länger als 50 ms dauert). Beispiel:
    init: Command 'wait_for_coldboot_done' action=wait_for_coldboot_done returned 0 took 585.012ms

    Die Prüfung dieses Protokolls kann Verbesserungsmöglichkeiten aufzeigen.

  • Starten Sie Dienste und aktivieren Sie Peripheriegeräte im kritischen Pfad frühzeitig. So müssen beispielsweise bei einigen SOCs sicherheitsrelevante Dienste gestartet werden, bevor SurfaceFlinger gestartet wird. Sehen Sie sich das Systemprotokoll an, wenn der ServiceManager „Warten auf Dienst“ zurückgibt. Dies ist in der Regel ein Zeichen dafür, dass ein abhängiger Dienst zuerst gestartet werden muss.
  • Entfernen Sie alle nicht verwendeten Dienste und Befehle in init.*.rc. Alles, was in der frühen Phase der Initialisierung nicht verwendet wird, sollte bis zum Abschluss des Bootens verschoben werden.

Hinweis:Der Property-Dienst ist Teil des Init-Prozesses. Wenn Sie setproperty während des Bootens aufrufen, kann es zu einer langen Verzögerung kommen, wenn Init in den integrierten Befehlen beschäftigt ist.

Scheduler-Optimierung verwenden

Verwenden Sie die Scheduler-Optimierung für einen schnellen Start. Beispiel von 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

Einige Dienste benötigen beim Starten möglicherweise eine höhere Priorität. Beispiel:

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

Zygote früh starten

Auf Geräten mit dateibasierter Verschlüsselung kann der Zygote früher beim Trigger „zygote-start“ gestartet werden. Standardmäßig wird der Zygote bei „class main“ gestartet, was viel später als „zygote-start“ ist. Achten Sie dabei darauf, dass der Zygote auf allen CPUs ausgeführt werden darf, da er bei einer falschen cpuset-Einstellung möglicherweise auf bestimmten CPUs ausgeführt wird.

Energiesparmodus deaktivieren

Während des Startens des Geräts können die Energiespareinstellungen für Komponenten wie UFS und/oder CPU-Taktregler deaktiviert werden.

Achtung:Im Lademodus sollte der Energiesparmodus aktiviert sein, um effizient zu arbeiten.

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

Nicht kritische Initialisierung verschieben

Nicht kritische Initialisierungen wie ZRAM können auf boot_complete verschoben werden.

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

Bootanimation optimieren

Mit den folgenden Tipps können Sie die Bootanimation optimieren.

Frühzeitigen Start konfigurieren

Unter Android 8.0 kann die Bootanimation schon gestartet werden, bevor die userdata-Partition bereitgestellt wird. Auch wenn die neue ext4-Tool-Chain in Android 8.0 verwendet wird, wird fsck aus Sicherheitsgründen weiterhin regelmäßig ausgelöst, was zu einer Verzögerung beim Starten des Bootanimationsdiensts führt.

Damit die Bootanimation frühzeitig gestartet wird, teilen Sie die Bereitstellung über die Datei „fstab“ in zwei Phasen auf:

  • In der Anfangsphase sollten Sie nur die Partitionen bereitstellen (z. B. system/ und vendor/), für die keine Ausführungsüberprüfungen erforderlich sind. Starten Sie dann die Boot-Animationen und die zugehörigen Abhängigkeiten (z. B. servicemanager und surfaceflinger).
  • In der zweiten Phase werden Partitionen wie data/ bereitgestellt, für die Laufzeitprüfungen erforderlich sind.

Die Boot-Animation wird unabhängig von fsck viel schneller (und in konstanter Zeit) gestartet.

Finish clean

Nach Erhalt des Beendigungssignals wird der letzte Teil der Bootanimation abgespielt. Die Länge dieses Teils kann die Bootzeit verlängern. Bei einem System, das schnell startet, sind keine langen Animationen erforderlich, die eventuelle Verbesserungen verdecken könnten. Wir empfehlen, sowohl die sich wiederholende Schleife als auch das Finale kurz zu halten.

SELinux optimieren

Mit den folgenden Tipps können Sie SELinux für kürzere Bootzeiten optimieren.

  • Verwenden Sie saubere reguläre Ausdrücke (Regex). Unzureichend formatierte reguläre Ausdrücke können zu viel Overhead führen, wenn die SELinux-Richtlinie für sys/devices in file_contexts abgeglichen wird. Beispiel: Der reguläre Ausdruck /sys/devices/.*abc.*(/.*)? erzwingt fälschlicherweise einen Scan aller /sys/devices-Unterverzeichnisse, die „abc“ enthalten, sodass sowohl /sys/devices/abc als auch /sys/devices/xyz/abc übereinstimmen. Wenn Sie diesen regulären Ausdruck zu /sys/devices/[^/]*abc[^/]*(/.*)? verbessern, wird nur eine Übereinstimmung für /sys/devices/abc aktiviert.
  • Verschieben Sie Labels zu genfscon. Diese vorhandene SELinux-Funktion übergibt Dateiabgleichspräfixe in der SELinux-Binärdatei an den Kernel, wo sie auf vom Kernel generierte Dateisysteme angewendet werden. Außerdem können so falsch beschriftete vom Kernel erstellte Dateien korrigiert werden, um Race-Zustände zu verhindern, die zwischen Userspace-Prozessen auftreten können, die versuchen, auf diese Dateien zuzugreifen, bevor die Neubeschriftung erfolgt.

Tools und Methoden

Mit den folgenden Tools können Sie Daten für Optimierungsziele erheben.

Bootchart

Bootchart bietet eine Aufschlüsselung der CPU- und E/A-Auslastung aller Prozesse für das gesamte System. Es ist nicht erforderlich, das System-Image neu zu erstellen, und es kann als Schnellüberprüfung verwendet werden, bevor Sie sich mit systrace befassen.

So aktivieren Sie „bootchart“:

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

Nach dem Starten das Boot-Diagramm abrufen:

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

Wenn Sie fertig sind, löschen Sie /data/bootchart/enabled, damit die Daten nicht jedes Mal erfasst werden.

Wenn bootchart nicht funktioniert und Sie die Fehlermeldung erhalten, dass bootchart.png nicht vorhanden ist, gehen Sie so vor:
  1. Führen Sie folgende Befehle aus:
          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. Aktualisieren Sie $ANDROID_BUILD_TOP/system/core/init/grab-bootchart.sh, sodass es auf die lokale Kopie von pybootchartgui (~/Documents/bootchart/pybootchartgui.py) verweist.

Systrace

Mit Systrace können Sie sowohl Kernel- als auch Android-Traces während des Bootens erfassen. Die Visualisierung von systrace kann bei der Analyse eines bestimmten Problems beim Starten helfen. Wenn Sie jedoch die durchschnittliche Anzahl oder die Gesamtzahl während des gesamten Bootvorgangs prüfen möchten, ist es einfacher, direkt in den Kernel-Trace zu schauen.

So aktivieren Sie systrace beim Starten:

  • Ändern Sie in frameworks/native/cmds/atrace/atrace.rc Folgendes:
      write /sys/kernel/debug/tracing/tracing_on 0
      write /sys/kernel/tracing/tracing_on 0

    Sie haben folgende Optionen:

      #    write /sys/kernel/debug/tracing/tracing_on 0
      #    write /sys/kernel/tracing/tracing_on 0
  • Dadurch wird das Tracing aktiviert, das standardmäßig deaktiviert ist.

  • Fügen Sie in der Datei device.mk die folgende Zeile hinzu:
    PRODUCT_PROPERTY_OVERRIDES +=    debug.atrace.tags.enableflags=802922
    PRODUCT_PROPERTY_OVERRIDES +=    persist.traced.enable=0
  • Fügen Sie in der Datei BoardConfig.mk des Geräts Folgendes hinzu:
    BOARD_KERNEL_CMDLINE := ... trace_buf_size=64M trace_event=sched_wakeup,sched_switch,sched_blocked_reason,sched_cpu_hotplug
  • Für eine detaillierte I/O-Analyse fügen Sie auch „block“, „ext4“ und „f2fs“ hinzu.

  • Fügen Sie in der gerätespezifischen init.rc-Datei Folgendes hinzu:
    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
  • Nach dem Starten den Ablauf erfassen:

    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