Bootzeiten optimieren

Dieses Dokument bietet Partneranleitungen zur Verbesserung der Startzeiten für bestimmte Android-Geräte. Die Startzeit ist eine wichtige Komponente der Systemleistung, da Benutzer warten müssen, bis der Startvorgang abgeschlossen ist, bevor sie das Gerät verwenden können. Bei Geräten wie Autos, bei denen ein Kaltstart häufiger erfolgt, ist eine schnelle Startzeit von entscheidender Bedeutung (niemand wartet gerne Dutzende Sekunden, nur um ein Navigationsziel einzugeben).

Android 8.0 ermöglicht kürzere Startzeiten, indem es mehrere Verbesserungen in einer Reihe von Komponenten unterstützt. Die folgende Tabelle fasst diese Leistungsverbesserungen zusammen (gemessen auf Google Pixel- und Pixel XL-Geräten).

Komponente Verbesserung
Bootloader
  • Durch Entfernen des UART-Protokolls wurden 1,6 Sekunden eingespart
  • Durch den Wechsel von GZIP zu LZ4 wurden 0,4 Sekunden eingespart
Gerätekernel
  • 0,3 Sekunden eingespart durch das Entfernen nicht verwendeter Kernelkonfigurationen und die Reduzierung der Treibergröße
  • 0,3 Sekunden eingespart mit dm-verity Prefetch-Optimierung
  • 0,15 Sekunden eingespart, um unnötiges Warten/Testen im Treiber zu vermeiden
  • 0,12 Sekunden eingespart, um CONFIG_CC_OPTIMIZE_FOR_SIZE zu entfernen
I/O-Tuning
  • 2 Sekunden beim normalen Booten gespart
  • 25 Sekunden beim ersten Start gespart
init.*.rc
  • Durch die Parallelisierung von Init-Befehlen wurden 1,5 Sekunden eingespart
  • Durch den frühen Start von Zygote wurden 0,25 Sekunden eingespart
  • 0,22 Sekunden durch CPU-Set-Optimierung eingespart
Boot-Animation
  • Beim Booten ohne FSCK-Auslösung 2 Sekunden früher gestartet, beim Booten mit FSCK-ausgelöstem Booten viel größer
  • 5 Sekunden gespart auf Pixel XL durch sofortiges Abschalten der Startanimation
SELinux-Richtlinie 0,2 Sekunden gespart am von genfscon

Optimieren des Bootloaders

So optimieren Sie den Bootloader für verbesserte Startzeiten:

  • Zur Protokollierung:
    • Deaktivieren Sie das Schreiben von Protokollen auf UART, da dies bei vielen Protokollierungen lange dauern kann. (Auf den Google Pixel-Geräten haben wir festgestellt, dass es den Bootloader um 1,5 Sekunden verlangsamt.)
    • Protokollieren Sie nur Fehlersituationen und erwägen Sie die Speicherung anderer Informationen im Speicher mit einem separaten Abrufmechanismus.
  • Erwägen Sie für die Kernel-Dekomprimierung die Verwendung von LZ4 für moderne Hardware anstelle von GZIP (Beispiel- Patch ). Beachten Sie, dass verschiedene Kernel-Komprimierungsoptionen unterschiedliche Lade- und Dekomprimierungszeiten haben können und einige Optionen für Ihre spezifische Hardware möglicherweise besser funktionieren als andere.
  • Überprüfen Sie unnötige Wartezeiten für die Entprellung/Eintritt in den Spezialmodus und minimieren Sie diese.
  • Übergeben Sie die im Bootloader verbrachte Bootzeit als cmdline an den Kernel.
  • Überprüfen Sie den CPU-Takt und erwägen Sie Parallelisierung (erfordert Multi-Core-Unterstützung) für das Laden des Kernels und die Initialisierung von E/A.

Optimierung der I/O-Effizienz

Die Verbesserung der I/O-Effizienz ist entscheidend, um die Startzeit zu verkürzen, und das Lesen nicht notwendiger Daten sollte bis nach dem Start verschoben werden (auf einem Google Pixel werden beim Start etwa 1,2 GB Daten gelesen).

Optimieren des Dateisystems

Das Vorauslesen des Linux-Kernels setzt ein, wenn eine Datei von Anfang an gelesen wird oder wenn Blöcke sequentiell gelesen werden. Dies macht es erforderlich, die I/O-Scheduler-Parameter speziell für das Booten zu optimieren (das eine andere Arbeitslastcharakterisierung aufweist als normale Anwendungen).

Geräte, die nahtlose (A/B) Updates unterstützen, profitieren stark von der Optimierung des Dateisystems beim ersten Start (z. B. 20s 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
    ...

Verschiedenes

  • Aktivieren Sie die dm-verity-Hash-Prefetch-Größe mithilfe der Kernel-Konfiguration DM_VERITY_HASH_PREFETCH_MIN_SIZE (Standardgröße ist 128).
  • Für eine bessere Dateisystemstabilität und eine entfallende erzwungene Prüfung, die bei jedem Start erfolgt, verwenden Sie das neue ext4-Generierungstool, indem Sie TARGET_USES_MKE2FS in BoardConfig.mk festlegen.

I/O analysieren

Um die E/A-Aktivitäten während des Startvorgangs zu verstehen, verwenden Sie Kernel-FTrace-Daten (die auch von Systrace verwendet werden):

trace_event=block,ext4 in BOARD_KERNEL_CMDLINE

Um den Dateizugriff für jede Datei aufzuschlüsseln, nehmen Sie die folgenden Änderungen am Kernel vor (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);

Verwenden Sie die folgenden Skripte, um bei der Analyse der Startleistung zu helfen.

  • system/extras/boottime_tools/bootanalyze/bootanalyze.py Misst die Startzeit mit einer Aufschlüsselung wichtiger Schritte im Startvorgang.
  • system/extras/boottime_tools/io_analysis/check_file_read.py boot_trace Stellt Zugriffsinformationen für jede Datei bereit.
  • system/extras/boottime_tools/io_analysis/check_io_trace_all.py boot_trace Gibt eine Aufschlüsselung auf Systemebene an.

Optimierung von init.*.rc

Init ist die Brücke vom Kernel bis zur Einrichtung des Frameworks, und Geräte verbringen normalerweise einige Sekunden in verschiedenen Init-Stufen.

Parallele Ausführung von Aufgaben

Während es sich bei der aktuellen Android-Initialisierung mehr oder weniger um einen Single-Thread-Prozess handelt, können Sie dennoch einige Aufgaben parallel ausführen.

  • Führen Sie langsame Befehle in einem Shell-Skriptdienst aus und schließen Sie sich diesem später an, indem Sie auf eine bestimmte Eigenschaft warten. Android 8.0 unterstützt diesen Anwendungsfall mit einem neuen wait_for_property Befehl.
  • Identifizieren Sie langsame Vorgänge in init. Das System protokolliert den Init-Befehl exec/wait_for_prop oder alle Aktionen, die lange dauern (in Android 8.0 alle Befehle, die länger als 50 ms dauern). Zum Beispiel:
    init: Command 'wait_for_coldboot_done' action=wait_for_coldboot_done returned 0 took 585.012ms

    Die Durchsicht dieses Protokolls kann Hinweise auf Verbesserungsmöglichkeiten geben.

  • Starten Sie Dienste und aktivieren Sie Peripheriegeräte im kritischen Pfad frühzeitig. Beispielsweise erfordern einige SOCs den Start sicherheitsrelevanter Dienste vor dem Start von SurfaceFlinger. Überprüfen Sie das Systemprotokoll, wenn ServiceManager „Warten auf Dienst“ zurückgibt. Dies ist normalerweise ein Zeichen dafür, dass zuerst ein abhängiger Dienst 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 zurückgestellt werden, bis der Startvorgang abgeschlossen ist.

Hinweis: Der Eigenschaftsdienst ist Teil des Init-Prozesses, daher kann der Aufruf von setproperty während des Startvorgangs zu einer langen Verzögerung führen, wenn Init mit integrierten Befehlen beschäftigt ist.

Verwenden der Scheduler-Optimierung

Verwenden Sie die Scheduler-Optimierung für einen frühen Start. Beispiel von einem 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 möglicherweise eine Prioritätserhöhung während des Startvorgangs. 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 beginnen

Geräte mit dateibasierter Verschlüsselung können Zygote beim Zygote-Start-Trigger früher starten (standardmäßig wird Zygote bei der Klasse „Main“ gestartet, was viel später als Zygote-Start erfolgt). Stellen Sie dabei sicher, dass Zygote auf allen CPUs ausgeführt werden kann (da die falsche CPU-Set-Einstellung dazu führen kann, dass Zygote auf bestimmten CPUs ausgeführt wird).

Deaktivieren Sie den Energiesparmodus

Während des Gerätestarts kann die Energiespareinstellung für Komponenten wie UFS und/oder CPU-Governor deaktiviert werden.

Achtung: Aus Effizienzgründen sollte der Energiesparmodus im Lademodus aktiviert sein.

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

Verschieben Sie die nicht kritische Initialisierung

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}

Optimierung der Boot-Animation

Nutzen Sie die folgenden Tipps, um die Boot-Animation zu optimieren.

Frühstart konfigurieren

Android 8.0 ermöglicht das frühzeitige Starten der Boot-Animation, bevor die Benutzerdatenpartition bereitgestellt wird. Selbst wenn die neue ext4-Toolkette in Android 8.0 verwendet wird, wird fsck aus Sicherheitsgründen immer noch regelmäßig ausgelöst, was zu einer Verzögerung beim Start des Bootanimationsdienstes führt.

Damit die Bootanimation früher beginnt, teilen Sie den fstab-Mount in zwei Phasen auf:

  • Mounten Sie in der frühen Phase nur die Partitionen (wie system/ und vendor/ “), für die keine Ausführungsprüfungen erforderlich sind, und starten Sie dann die Boot-Animationsdienste und ihre Abhängigkeiten (wie „servicemanager“ und „surfaceflinger“).
  • In der zweiten Phase mounten Sie Partitionen (z. B. data/ ), für die Ausführungsprüfungen erforderlich sind.

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

Sauberer Abschluss

Nach dem Empfang des Exit-Signals spielt die Bootanimation den letzten Teil, dessen Länge die Bootzeit verlangsamen kann. Ein System, das schnell hochfährt, benötigt keine langwierigen Animationen, die vorgenommene Verbesserungen effektiv verbergen könnten. Wir empfehlen, sowohl die Wiederholungsschleife als auch das Finale kurz zu machen.

Optimierung von SELinux

Verwenden Sie die folgenden Tipps, um SELinux für verbesserte Startzeiten zu optimieren.

  • Verwenden Sie saubere reguläre Ausdrücke (Regex) . Ein schlecht geformter regulärer Ausdruck kann zu viel Overhead führen, wenn die SELinux-Richtlinie für sys/devices in file_contexts abgeglichen wird. Beispielsweise ist der reguläre Ausdruck /sys/devices/.*abc.*(/.*)? erzwingt versehentlich einen Scan aller /sys/devices Unterverzeichnisse, die „abc“ enthalten, wodurch Übereinstimmungen sowohl für /sys/devices/abc als auch /sys/devices/xyz/abc ermöglicht werden. Wird dieser reguläre Ausdruck auf /sys/devices/[^/]*abc[^/]*(/.*)? aktiviert eine Übereinstimmung nur für /sys/devices/abc .
  • Beschriftungen nach genfscon verschieben . Diese vorhandene SELinux-Funktion übergibt dateiabgleichende Präfixe in der SELinux-Binärdatei an den Kernel, wo der Kernel sie auf vom Kernel generierte Dateisysteme anwendet. Dies trägt auch dazu bei, falsch gekennzeichnete, vom Kernel erstellte Dateien zu beheben und verhindert so Race Conditions, die zwischen Userspace-Prozessen auftreten können, die versuchen, auf diese Dateien zuzugreifen, bevor eine Neukennzeichnung erfolgt.

Werkzeug und Methoden

Nutzen Sie die folgenden Tools, um Daten für Optimierungsziele zu sammeln.

Bootchart

Bootchart bietet eine Aufschlüsselung der CPU- und E/A-Last aller Prozesse für das gesamte System. Es erfordert keine Neuerstellung des Systemabbilds und kann als schnelle Überprüfung der Integrität vor dem Eintauchen in Systrace verwendet werden.

So aktivieren Sie Bootchart:

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

Rufen Sie nach dem Hochfahren das Boot-Diagramm ab:

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

Wenn Sie fertig sind, löschen Sie /data/bootchart/enabled um zu verhindern, dass die Daten jedes Mal erfasst werden.

Wenn Bootchart nicht funktioniert und Sie eine Fehlermeldung erhalten, dass bootchart.png nicht existiert, gehen Sie wie folgt vor:
  1. Führen Sie die folgenden 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 , um auf die lokale Kopie von pybootchartgui zu verweisen (zu finden unter ~/Documents/bootchart/pybootchartgui.py ).

Systrace

Systrace ermöglicht das Sammeln von Kernel- und Android-Traces während des Startvorgangs. Die Visualisierung von Systrace kann bei der Analyse spezifischer Probleme während des Startvorgangs hilfreich sein. (Um jedoch die durchschnittliche oder akkumulierte Anzahl während des gesamten Startvorgangs zu überprüfen, ist es einfacher, direkt in den Kernel-Trace zu schauen.)

So aktivieren Sie Systrace während des Startvorgangs:

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

    Zu:

      #    write /sys/kernel/debug/tracing/tracing_on 0
      #    write /sys/kernel/tracing/tracing_on 0
  • Dadurch wird die Ablaufverfolgung aktiviert (die 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 Gerätedatei BoardConfig.mk 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 E/A-Analyse fügen Sie außerdem Block sowie ext4 und f2fs hinzu.

  • Fügen Sie in der gerätespezifischen Datei init.rc 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
    
  • Rufen Sie nach dem Hochfahren den Trace ab:

    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