Optimiser les temps de démarrage

Ce document fournit des conseils aux partenaires pour améliorer les temps de démarrage de certains appareils Android. Le temps de démarrage est un élément important des performances du système, car les utilisateurs doivent attendre que le démarrage soit terminé avant de pouvoir utiliser l'appareil. Pour les appareils tels que les voitures, où le démarrage à froid se produit plus fréquemment, un temps de démarrage rapide est essentiel (personne n'aime attendre des dizaines de secondes juste pour saisir une destination de navigation).

Android 8.0 permet de réduire les temps de démarrage en prenant en charge plusieurs améliorations sur une gamme de composants. Le tableau suivant récapitule ces améliorations de performances (mesurées sur des appareils Google Pixel et Pixel XL).

Component Amélioration
Bootloader (chargeur d'amorçage)
  • Gain de 1,6 s en supprimant le journal UART
  • Gain de 0,4 s en passant de GZIP à LZ4
Noyau de l'appareil
  • Gain de 0,3 s en supprimant les configurations de kernel inutilisées et en réduisant la taille du pilote
  • Économie de 0,3 s grâce à l'optimisation du préchargement dm-verity
  • Économie de 0,15 s pour supprimer l'attente/le test inutiles dans le pilote
  • 0,12 s économisés en supprimant CONFIG_CC_OPTIMIZE_FOR_SIZE
Réglage des E/S
  • Économie de 2 s au démarrage normal
  • Économie de 25 secondes au premier démarrage
init.*.rc
  • Économie de 1,5 s en parallélisant les commandes d'initialisation
  • Économie de 0,25 s en démarrant zygote plus tôt
  • Économie de 0,22 s grâce à l'ajustement du cpuset
Animation de démarrage
  • Démarrage 2 s plus tôt au démarrage sans fsck déclenché, beaucoup plus important au démarrage avec démarrage déclenché par fsck
  • Économie de 5 secondes sur le Pixel XL avec arrêt immédiat de l'animation de démarrage
Règle SELinux Économie de 0,2 s par genfscon

Optimiser le bootloader

Pour optimiser le bootloader afin d'améliorer les temps de démarrage:

  • Pour la journalisation :
    • Désactivez l'écriture des journaux dans UART, car cela peut prendre beaucoup de temps avec une grande quantité de journalisation. (Sur les appareils Google Pixel, nous avons constaté qu'il ralentit le bootloader de 1,5 s).
    • Consignez uniquement les situations d'erreur et envisagez de stocker d'autres informations en mémoire avec un mécanisme de récupération distinct.
  • Pour la décompression du noyau, envisagez d'utiliser LZ4 pour le matériel contemporain au lieu de GZIP (exemple de correctif). N'oubliez pas que les différentes options de compression du noyau peuvent avoir des temps de chargement et de décompression différents, et que certaines options peuvent fonctionner mieux que d'autres pour votre matériel spécifique.
  • Vérifiez les temps d'attente inutiles pour la désactivation du debounce/l'entrée en mode spécial, et réduisez-les.
  • Transmettez le temps de démarrage passé dans le bootloader au noyau en tant que cmdline.
  • Vérifiez la fréquence du processeur et envisagez la parallélisation (nécessite la prise en charge du multicœur) pour le chargement du noyau et l'initialisation des E/S.

Optimiser l'efficacité des E/S

L'amélioration de l'efficacité des E/S est essentielle pour accélérer le temps de démarrage.La lecture de tout élément inutile doit être différée jusqu'au démarrage (sur un Google Pixel, environ 1,2 Go de données sont lues au démarrage).

Ajuster le système de fichiers

La lecture anticipée du kernel Linux se déclenche lorsqu'un fichier est lu depuis le début ou lorsque des blocs sont lus de manière séquentielle, ce qui nécessite d'ajuster les paramètres du planificateur d'E/S spécifiquement pour le démarrage (qui a une caractérisation de la charge de travail différente de celle des applications normales).

Les appareils compatibles avec les mises à jour fluides (A/B) bénéficient grandement du réglage du système de fichiers au premier démarrage (par exemple, 20 secondes sur Google Pixel). Par exemple, nous avons affiné les paramètres suivants pour le 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
    ...

Divers

  • Activez la taille de préchargement du hachage dm-verity à l'aide de la configuration du noyau DM_VERITY_HASH_PREFETCH_MIN_SIZE (la taille par défaut est de 128).
  • Pour une meilleure stabilité du système de fichiers et une suppression de la vérification forcée qui se produit à chaque démarrage, utilisez le nouvel outil de génération ext4 en définissant TARGET_USES_MKE2FS dans BoardConfig.mk.

Analyser les E/S

Pour comprendre les activités d'E/S au démarrage, utilisez les données ftrace du kernel (également utilisées par systrace):

trace_event=block,ext4 in BOARD_KERNEL_CMDLINE

Pour décomposer l'accès aux fichiers pour chaque fichier, apportez les modifications suivantes au noyau (noyau de développement uniquement, ne pas utiliser dans les noyaux de production):

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

Utilisez les scripts suivants pour analyser les performances de démarrage.

  • system/extras/boottime_tools/bootanalyze/bootanalyze.py Mesure le temps de démarrage avec une répartition des étapes importantes du processus de démarrage.
  • system/extras/boottime_tools/io_analysis/check_file_read.py boot_trace Fournit des informations d'accès pour chaque fichier.
  • system/extras/boottime_tools/io_analysis/check_io_trace_all.py boot_trace Fournit une répartition au niveau du système.

Optimiser init.*.rc

Init est le pont entre le noyau et le framework. Les appareils passent généralement quelques secondes dans différentes étapes d'initialisation.

Exécuter des tâches en parallèle

Bien que l'init Android actuel soit plus ou moins un processus à thread unique, vous pouvez toujours effectuer certaines tâches en parallèle.

  • Exécutez des commandes lentes dans un service de script shell, puis rejoignez-le plus tard en attendant une propriété spécifique. Android 8.0 prend en charge ce cas d'utilisation avec une nouvelle commande wait_for_property.
  • Identifier les opérations lentes dans init. Le système consigne la commande init exec/wait_for_prop ou toute action qui prend beaucoup de temps (sous Android 8.0, toute commande qui prend plus de 50 ms). Exemple :
    init: Command 'wait_for_coldboot_done' action=wait_for_coldboot_done returned 0 took 585.012ms

    L'examen de ce journal peut vous indiquer des pistes d'amélioration.

  • Démarrez les services et activez les périphériques sur le chemin critique dès que possible. Par exemple, certains SOC nécessitent de démarrer des services liés à la sécurité avant de démarrer SurfaceFlinger. Examinez le journal système lorsque ServiceManager renvoie "wait for service" (Attendre le service). Il s'agit généralement d'un signe indiquant qu'un service dépendant doit être démarré en premier.
  • Supprimez tous les services et commandes inutilisés dans init.*.rc. Tout élément non utilisé lors de l'initialisation de l'étape initiale doit être différé jusqu'à la fin du démarrage.

Remarque:Le service de propriété fait partie du processus d'initialisation. L'appel de setproperty au démarrage peut donc entraîner un long délai si init est occupé dans les commandes intégrées.

Utiliser le réglage du planificateur

Utilisez le réglage du planificateur pour le démarrage anticipé. Exemple sur un 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

Certains services peuvent avoir besoin d'une priorité renforcée au démarrage. Exemple :

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

Démarrer le zygote plus tôt

Les appareils avec chiffrement basé sur des fichiers peuvent démarrer zygote plus tôt au déclencheur zygote-start (par défaut, zygote est lancé au niveau de la classe principale, qui est beaucoup plus tard que zygote-start). Dans ce cas, assurez-vous d'autoriser zygote à s'exécuter sur tous les processeurs (car un mauvais paramètre cpuset peut forcer zygote à s'exécuter sur des processeurs spécifiques).

Désactiver l'économie d'énergie

Lors du démarrage de l'appareil, le paramètre d'économie d'énergie pour des composants tels que UFS et/ou le gouverneur du processeur peut être désactivé.

Attention:L'économie d'énergie doit être activée en mode chargeur pour plus d'efficacité.

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

Différer l'initialisation non critique

L'initialisation non critique, telle que ZRAM, peut être différée à boot_complete.

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

Optimiser l'animation de démarrage

Suivez les conseils ci-dessous pour optimiser l'animation de démarrage.

Configurer le démarrage anticipé

Android 8.0 permet de démarrer l'animation de démarrage plus tôt, avant le montage de la partition userdata. Toutefois, même lorsque vous utilisez la nouvelle chaîne d'outils ext4 dans Android 8.0, fsck est toujours déclenché périodiquement pour des raisons de sécurité, ce qui retarde le démarrage du service bootanimation.

Pour que l'animation de démarrage démarre plus tôt, divisez le montage fstab en deux phases:

  • Dans la phase initiale, ne montez que les partitions (telles que system/ et vendor/) qui ne nécessitent pas de vérifications d'exécution, puis démarrez les services d'animation de démarrage et ses dépendances (telles que servicemanager et surfaceflinger).
  • Dans la deuxième phase, montez les partitions (telles que data/) qui nécessitent des vérifications d'exécution.

L'animation de démarrage sera lancée beaucoup plus rapidement (et en temps constant) quel que soit fsck.

Nettoyer la fin

Après avoir reçu le signal de sortie, l'animation de démarrage lit la dernière partie, dont la longueur peut ralentir le temps de démarrage. Un système qui démarre rapidement n'a pas besoin d'animations longues qui pourraient masquer efficacement les améliorations apportées. Nous vous recommandons de faire en sorte que la boucle répétée et la finale soient courtes.

Optimiser SELinux

Suivez les conseils ci-dessous pour optimiser SELinux afin d'améliorer les temps de démarrage.

  • Utilisez des expressions régulières (regex) propres. Une expression régulière mal formée peut entraîner beaucoup de frais généraux lors de la mise en correspondance de la stratégie SELinux pour sys/devices dans file_contexts. Par exemple, l'expression régulière /sys/devices/.*abc.*(/.*)? force par erreur l'analyse de tous les sous-répertoires /sys/devices contenant "abc", ce qui permet de faire correspondre à la fois /sys/devices/abc et /sys/devices/xyz/abc. Si vous améliorez cette expression régulière en /sys/devices/[^/]*abc[^/]*(/.*)?, une correspondance ne sera possible que pour /sys/devices/abc.
  • Déplacez les libellés vers genfscon. Cette fonctionnalité SELinux existante transmet des préfixes de correspondance de fichiers au noyau dans le binaire SELinux, où le noyau les applique aux systèmes de fichiers générés par le noyau. Cela permet également de corriger les fichiers créés par le noyau mal étiquetés, ce qui évite les conditions de course qui peuvent se produire entre les processus d'espace utilisateur qui tentent d'accéder à ces fichiers avant le nouveau libellé.

Outils et méthodes

Utilisez les outils suivants pour collecter des données pour les cibles d'optimisation.

Bootchart

Bootchart fournit une répartition de la charge CPU et des E/S de tous les processus pour l'ensemble du système. Il ne nécessite pas de reconstruire l'image système et peut être utilisé comme vérification rapide avant de se plonger dans systrace.

Pour activer bootchart:

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

Après le démarrage, récupérez le graphique de démarrage:

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

Lorsque vous avez terminé, supprimez /data/bootchart/enabled pour éviter de collecter les données à chaque fois.

Si bootchart ne fonctionne pas et qu'un message d'erreur indique que bootchart.png n'existe pas, procédez comme suit :
  1. Exécutez les commandes suivantes :
          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. Mettre à jour $ANDROID_BUILD_TOP/system/core/init/grab-bootchart.sh pour qu'il pointe vers la copie locale de pybootchartgui (située à ~/Documents/bootchart/pybootchartgui.py)

Systrace

Systrace permet de collecter des traces du noyau et d'Android au démarrage. La visualisation de systrace peut vous aider à analyser un problème spécifique au démarrage. (Cependant, pour vérifier le nombre moyen ou le nombre cumulé pendant tout le démarrage, il est plus facile d'examiner directement la trace du noyau.)

Pour activer systrace au démarrage:

  • Dans frameworks/native/cmds/atrace/atrace.rc, remplacez:
      write /sys/kernel/debug/tracing/tracing_on 0
      write /sys/kernel/tracing/tracing_on 0

    À :

      #    write /sys/kernel/debug/tracing/tracing_on 0
      #    write /sys/kernel/tracing/tracing_on 0
  • Cela active le traçage (qui est désactivé par défaut).

  • Dans le fichier device.mk, ajoutez la ligne suivante:
    PRODUCT_PROPERTY_OVERRIDES +=    debug.atrace.tags.enableflags=802922
    PRODUCT_PROPERTY_OVERRIDES +=    persist.traced.enable=0
  • Dans le fichier BoardConfig.mk de l'appareil, ajoutez les éléments suivants:
    BOARD_KERNEL_CMDLINE := ... trace_buf_size=64M trace_event=sched_wakeup,sched_switch,sched_blocked_reason,sched_cpu_hotplug
  • Pour une analyse détaillée des E/S, ajoutez également "block", "ext4" et "f2fs".

  • Dans le fichier init.rc spécifique à l'appareil, ajoutez les éléments suivants:
    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
  • Après le démarrage, récupérez la trace:

    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