Optimisation des 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 la fin du démarrage avant de pouvoir utiliser le périphérique. Pour les appareils tels que les voitures où les démarrages à froid sont plus fréquents, il est essentiel d'avoir un temps de démarrage rapide (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ésume ces améliorations de performances (telles que mesurées sur les appareils Google Pixel et Pixel XL).

Composant Amélioration
Chargeur de démarrage
  • Économisé 1,6 s en supprimant le journal UART
  • Économisé 0,4 seconde en passant à LZ4 à partir de GZIP
Noyau de l'appareil
  • Gain de 0,3 s en supprimant les configurations de noyau inutilisées et en réduisant la taille du pilote
  • Gain de 0,3 seconde grâce à l'optimisation de la prélecture dm-verity
  • Gain de 0,15 s pour supprimer les attentes/tests inutiles dans le pilote
  • Économisé 0,12 s pour supprimer CONFIG_CC_OPTIMIZE_FOR_SIZE
Réglage des E/S
  • 2 secondes enregistrées au démarrage normal
  • Économisé 25 secondes au premier démarrage
init.*.rc
  • Économisé 1,5 seconde en mettant en parallèle les commandes d'initialisation
  • Économisé 0,25 seconde en démarrant zygote tôt
  • Économisé 0,22 s grâce à cpuset tune
Animation de démarrage
  • Démarré 2 secondes plus tôt au démarrage sans déclenchement de fsck, beaucoup plus gros au démarrage avec démarrage déclenché par fsck
  • 5 secondes enregistrées sur Pixel XL avec arrêt immédiat de l'animation de démarrage
Politique SELinux Économisé 0,2 seconde par genfscon

Optimisation du chargeur de démarrage

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

  • Pour la journalisation :
    • Désactivez l'écriture du journal sur UART car cela peut prendre beaucoup de temps avec beaucoup de journalisation. (Sur les appareils Google Pixel, nous avons constaté que cela ralentissait le chargeur de démarrage de 1,5 seconde).
    • Enregistrez uniquement les situations d'erreur et envisagez de stocker d'autres informations en mémoire avec un mécanisme distinct à récupérer.
  • Pour la décompression du noyau, envisagez d'utiliser LZ4 pour le matériel contemporain au lieu de GZIP (exemple patch ). Gardez à l’esprit que différentes options de compression du noyau peuvent avoir des temps de chargement et de décompression différents, et que certaines options peuvent mieux fonctionner que d’autres pour votre matériel spécifique.
  • Vérifiez les temps d’attente inutiles pour l’entrée en mode anti-rebond/spécial et minimisez-les.
  • Transmettez le temps de démarrage passé dans le chargeur de démarrage au noyau en tant que ligne de commande.
  • Vérifiez l'horloge du processeur et envisagez la parallélisation (nécessite une prise en charge multicœur) pour le chargement du noyau et l'initialisation des E/S.

Optimisation de 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, et la lecture de tout ce qui n'est pas nécessaire doit être différée jusqu'après le démarrage (sur un Google Pixel, environ 1,2 Go de données sont lues au démarrage).

Réglage du système de fichiers

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

Les appareils prenant en charge les mises à jour transparentes (A/B) bénéficient grandement du réglage du système de fichiers au premier démarrage (par exemple, 20 s sur Google Pixel). À titre d'exemple, nous avons ajusté 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électure du hachage dm-verity à l'aide de la configuration du noyau DM_VERITY_HASH_PREFETCH_MIN_SIZE (la taille par défaut est 128).
  • Pour une meilleure stabilité du système de fichiers et une vérification forcée abandonné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.

Analyse des E/S

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

trace_event=block,ext4 in BOARD_KERNEL_CMDLINE

Pour répartir 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 vous aider à 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 Donne une ventilation au niveau du système.

Optimisation de init.*.rc

Init est le pont entre le noyau et l'établissement du framework, et les appareils passent généralement quelques secondes à différentes étapes d'initialisation.

Exécuter des tâches en parallèle

Bien que l'initialisation Android actuelle 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 et 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 .
  • Identifiez les opérations lentes dans init. Le système enregistre la commande init exec/wait_for_prop ou toute action prenant beaucoup de temps (sous Android 8.0, toute commande prenant plus de 50 ms). Par exemple :
    init: Command 'wait_for_coldboot_done' action=wait_for_coldboot_done returned 0 took 585.012ms

    L'examen de ce journal peut indiquer des opportunités d'amélioration.

  • Démarrez les services et activez les périphériques dans le chemin critique le plus tôt possible. Par exemple, certains SOC nécessitent le démarrage de services liés à la sécurité avant de démarrer SurfaceFlinger. Consultez le journal système lorsque ServiceManager renvoie « attendre le service » : il s'agit généralement d'un signe qu'un service dépendant doit être démarré en premier.
  • Supprimez tous les services et commandes inutilisés dans init.*.rc. Tout ce qui n'est pas utilisé au début de l'initialisation doit être différé jusqu'à la fin du démarrage.

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

Utilisation du réglage du planificateur

Utilisez le réglage du planificateur pour un démarrage anticipé. Exemple d'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 nécessiter une priorité lors du 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 tôt

Les appareils dotés d'un chiffrement basé sur les fichiers peuvent démarrer zygote plus tôt au niveau du déclencheur zygote-start (par défaut, zygote est lancé au niveau de la classe principale, ce qui est bien plus tard que zygote-start). Ce faisant, 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

Pendant le démarrage de l'appareil, les paramètres d'économie d'énergie pour les composants tels que l'UFS et/ou le gouverneur du processeur peuvent être désactivés.

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

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

Optimisation de l'animation de démarrage

Utilisez les conseils suivants pour optimiser l’animation de démarrage.

Configuration du démarrage anticipé

Android 8.0 permet de démarrer l'animation de démarrage plus tôt, avant de monter la partition de données utilisateur. Cependant, même lors de l'utilisation de la nouvelle chaîne d'outils ext4 dans Android 8.0, fsck est toujours déclenché périodiquement pour des raisons de sécurité, entraînant un retard dans le démarrage du service d'animation de démarrage.

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

  • Au début, montez uniquement les partitions (telles que system/ et vendor/ ) qui ne nécessitent pas d'exécution de vérifications, 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.

Finition propre

Après avoir reçu le signal de sortie, bootanimation joue la dernière partie, dont la durée peut ralentir le temps de démarrage. Un système qui démarre rapidement n’a pas besoin de longues animations qui pourraient effectivement masquer les améliorations apportées. Nous vous recommandons de rendre la boucle répétitive et la finale courtes.

Optimisation de SELinux

Utilisez les conseils suivants pour optimiser SELinux afin d'améliorer les temps de démarrage.

  • Utilisez des expressions régulières propres (regex) . Une expression régulière mal formée peut entraîner beaucoup de surcharge lors de la mise en correspondance de la politique SELinux pour sys/devices dans file_contexts . Par exemple, l'expression régulière /sys/devices/.*abc.*(/.*)? force par erreur une analyse de tous les sous-répertoires /sys/devices qui contiennent "abc", permettant des correspondances pour /sys/devices/abc et /sys/devices/xyz/abc . Améliorer cette expression régulière en /sys/devices/[^/]*abc[^/]*(/.*)? permettra une correspondance uniquement pour /sys/devices/abc .
  • Déplacez les étiquettes vers genfscon . Cette fonctionnalité SELinux existante transmet les 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 mal étiquetés créés par le noyau, évitant ainsi les conditions de concurrence qui peuvent survenir entre les processus de l'espace utilisateur tentant d'accéder à ces fichiers avant que le réétiquetage ne se produise.

Outil et méthodes

Utilisez les outils suivants pour vous aider à collecter des données pour les objectifs d'optimisation.

Tableau de démarrage

Bootchart fournit une répartition de la charge CPU et E/S de tous les processus pour l'ensemble du système. Il ne nécessite pas de reconstruction de l'image système et peut être utilisé comme un contrôle rapide avant de plonger dans Systrace.

Pour activer le diagramme de démarrage :

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

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

Si bootchart ne fonctionne pas et que vous obtenez une erreur indiquant 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. Mettez à jour $ANDROID_BUILD_TOP/system/core/init/grab-bootchart.sh pour pointer vers la copie locale de pybootchartgui (située dans ~/Documents/bootchart/pybootchartgui.py )

Systrace

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

Pour activer Systrace lors du démarrage :

  • Dans frameworks/native/cmds/atrace/atrace.rc , modifiez :
      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 du périphérique, ajoutez ce qui suit :
    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 et ext4 et f2fs.

  • Dans le fichier init.rc spécifique au périphérique, ajoutez ce qui suit :
    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