Optimización de los tiempos de arranque

Este documento proporciona orientación a los socios para mejorar los tiempos de inicio de dispositivos Android específicos. El tiempo de inicio es un componente importante del rendimiento del sistema, ya que los usuarios deben esperar a que se complete el inicio antes de poder utilizar el dispositivo. Para dispositivos como automóviles donde el arranque en frío ocurre con mayor frecuencia, tener un tiempo de arranque rápido es fundamental (a nadie le gusta esperar decenas de segundos solo para ingresar un destino de navegación).

Android 8.0 permite tiempos de arranque reducidos al admitir varias mejoras en una variedad de componentes. La siguiente tabla resume estas mejoras de rendimiento (medidas en dispositivos Google Pixel y Pixel XL).

Componente Mejora
Cargador de arranque
  • Ahorró 1,6 s eliminando el registro UART
  • Ahorró 0,4 s al cambiar a LZ4 desde GZIP
Núcleo del dispositivo
  • Ahorró 0,3 segundos eliminando configuraciones del kernel no utilizadas y reduciendo el tamaño del controlador
  • Ahorró 0,3 segundos con la optimización de captación previa de dm-verity
  • Se ahorraron 0,15 s para eliminar esperas/pruebas innecesarias en el conductor
  • Se guardaron 0,12 s para eliminar CONFIG_CC_OPTIMIZE_FOR_SIZE
ajuste de E/S
  • 2 segundos guardados en el arranque normal
  • Ahorró 25 segundos en el primer arranque
inicio.*.rc
  • Ahorró 1,5 s al poner en paralelo los comandos de inicio
  • Ahorró 0,25 s al iniciar zygote temprano
  • Ahorró 0,22 s por cpuset tune
Animación de inicio
  • Comenzó 2 segundos antes en el arranque sin fsck activado, mucho más grande en el arranque con fsck activado
  • Ahorró 5 segundos en Pixel XL con apagado inmediato de la animación de arranque
Política SELinux Guardado 0,2 s en por genfscon

Optimización del cargador de arranque

Para optimizar el gestor de arranque para mejorar los tiempos de arranque:

  • Para iniciar sesión:
    • Deshabilite la escritura de registros en UART, ya que puede llevar mucho tiempo debido a una gran cantidad de registros. (En los dispositivos Google Pixel, descubrimos que ralentiza el gestor de arranque 1,5 s).
    • Registre sólo situaciones de error y considere almacenar otra información en la memoria con un mecanismo independiente para recuperarla.
  • Para la descompresión del kernel, considere usar LZ4 para hardware contemporáneo en lugar de GZIP ( parche de ejemplo). Tenga en cuenta que las diferentes opciones de compresión del kernel pueden tener diferentes tiempos de carga y descompresión, y algunas opciones pueden funcionar mejor que otras para su hardware específico.
  • Verifique los tiempos de espera innecesarios para la entrada en modo antirrebote/especial y minimícelos.
  • Pase el tiempo de arranque transcurrido en el gestor de arranque al kernel como cmdline.
  • Verifique el reloj de la CPU y considere la paralelización (requiere soporte de múltiples núcleos) para la carga del kernel e inicialización de E/S.

Optimización de la eficiencia de E/S

Mejorar la eficiencia de E/S es fundamental para acelerar el tiempo de arranque, y la lectura de todo lo que no sea necesario debe posponerse hasta después del arranque (en un Google Pixel, se leen aproximadamente 1,2 GB de datos durante el arranque).

Ajustando el sistema de archivos

La lectura anticipada del kernel de Linux se activa cuando un archivo se lee desde el principio o cuando los bloques se leen secuencialmente, lo que hace necesario ajustar los parámetros del programador de E/S específicamente para el arranque (que tiene una caracterización de carga de trabajo diferente a la de las aplicaciones normales).

Los dispositivos que admiten actualizaciones fluidas (A/B) se benefician enormemente del ajuste del sistema de archivos en el primer arranque (por ejemplo, 20 en Google Pixel). A modo de ejemplo, ajustamos los siguientes parámetros para 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
    ...

Misceláneas

  • Active el tamaño de captación previa de hash de dm-verity usando la configuración del kernel DM_VERITY_HASH_PREFETCH_MIN_SIZE (el tamaño predeterminado es 128).
  • Para una mejor estabilidad del sistema de archivos y una verificación forzada descartada que ocurre en cada arranque, use la nueva herramienta de generación ext4 configurando TARGET_USES_MKE2FS en BoardConfig.mk.

Analizando E/S

Para comprender las actividades de E/S durante el arranque, utilice los datos de ftrace del kernel (también utilizados por systrace):

trace_event=block,ext4 in BOARD_KERNEL_CMDLINE

Para desglosar el acceso a cada archivo, realice los siguientes cambios en el kernel (solo kernel de desarrollo; no utilizar en kernels de producción):

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

Utilice los siguientes scripts para ayudar a analizar el rendimiento del arranque.

  • system/extras/boottime_tools/bootanalyze/bootanalyze.py Mide el tiempo de arranque con un desglose de los pasos importantes en el proceso de arranque.
  • system/extras/boottime_tools/io_analysis/check_file_read.py boot_trace Proporciona información de acceso por cada archivo.
  • system/extras/boottime_tools/io_analysis/check_io_trace_all.py boot_trace Proporciona un desglose a nivel del sistema.

Optimizando init.*.rc

Init es el puente desde el kernel hasta que se establece el marco, y los dispositivos suelen pasar unos segundos en diferentes etapas de inicio.

Ejecutar tareas en paralelo

Si bien el inicio actual de Android es más o menos un proceso de un solo subproceso, aún puedes realizar algunas tareas en paralelo.

  • Ejecute comandos lentos en un servicio de script de shell y únase a ellos más tarde esperando una propiedad específica. Android 8.0 admite este caso de uso con un nuevo comando wait_for_property .
  • Identificar operaciones lentas en init. El sistema registra el comando init exec/wait_for_prop o cualquier acción que demore mucho tiempo (en Android 8.0, cualquier comando que demore más de 50 ms). Por ejemplo:
    init: Command 'wait_for_coldboot_done' action=wait_for_coldboot_done returned 0 took 585.012ms

    La revisión de este registro puede indicar oportunidades de mejora.

  • Inicie servicios y habilite dispositivos periféricos en rutas críticas con anticipación. Por ejemplo, algunos SOC requieren iniciar servicios relacionados con la seguridad antes de iniciar SurfaceFlinger. Revise el registro del sistema cuando ServiceManager devuelva "esperar servicio"; esto suele ser una señal de que primero se debe iniciar un servicio dependiente.
  • Elimine los servicios y comandos no utilizados en init.*.rc. Todo lo que no se utilice en la etapa inicial de inicio debe posponerse hasta que se complete el inicio.

Nota: El servicio de propiedad es parte del proceso de inicio, por lo que llamar setproperty durante el arranque puede provocar un gran retraso si init está ocupado en los comandos integrados.

Usando el ajuste del programador

Utilice el ajuste del programador para un arranque temprano. Ejemplo de 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

Es posible que algunos servicios necesiten un impulso de prioridad durante el arranque. Ejemplo:

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

Iniciar el cigoto temprano

Los dispositivos con cifrado basado en archivos pueden iniciar zygote antes en el activador de inicio de zygote (de forma predeterminada, zygote se inicia en la clase principal, que es mucho más tarde que zygote-start). Al hacer esto, asegúrese de permitir que zygote se ejecute en todas las CPU (ya que una configuración incorrecta de cpuset puede forzar a zygote a ejecutarse en CPU específicas).

Desactivar el ahorro de energía

Durante el arranque del dispositivo, se puede desactivar la configuración de ahorro de energía para componentes como UFS y/o el regulador de CPU.

Precaución: El ahorro de energía debe habilitarse en el modo cargador para mayor eficiencia.

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

Aplazar la inicialización no crítica

La inicialización no crítica, como ZRAM, se puede aplazar hasta boot_complete.

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

Optimización de la animación de arranque

Utilice los siguientes consejos para optimizar la animación de arranque.

Configurar el inicio temprano

Android 8.0 permite iniciar la animación de inicio temprano, antes de montar la partición de datos del usuario. Sin embargo, incluso cuando se utiliza la nueva cadena de herramientas ext4 en Android 8.0, fsck todavía se activa periódicamente por razones de seguridad, lo que provoca un retraso en el inicio del servicio de animación de arranque.

Para que la animación de arranque comience temprano, divida el montaje de fstab en dos fases:

  • En la fase inicial, monte solo las particiones (como system/ y vendor/ ) que no requieren ejecutar comprobaciones, luego inicie los servicios de animación de arranque y sus dependencias (como servicemanager y Surfaceflinger).
  • En la segunda fase, monte particiones (como data/ ) que requieran ejecutar comprobaciones.

La animación de arranque se iniciará mucho más rápido (y en tiempo constante) independientemente de fsck.

Acabado limpio

Después de recibir la señal de salida, la animación de arranque desempeña la última parte, cuya duración puede ralentizar el tiempo de arranque. Un sistema que arranca rápidamente no necesita largas animaciones que podrían ocultar eficazmente las mejoras realizadas. Recomendamos que tanto el bucle repetido como el final sean breves.

Optimización de SELinux

Utilice los siguientes consejos para optimizar SELinux y mejorar los tiempos de arranque.

  • Utilice expresiones regulares limpias (regex) . Una expresión regular mal formada puede generar una gran sobrecarga al hacer coincidir la política de SELinux para sys/devices en file_contexts . Por ejemplo, la expresión regular /sys/devices/.*abc.*(/.*)? fuerza por error un análisis de todos los subdirectorios /sys/devices que contienen "abc", lo que permite coincidencias tanto para /sys/devices/abc como /sys/devices/xyz/abc . ¿Mejorando esta expresión regular a /sys/devices/[^/]*abc[^/]*(/.*)? habilitará una coincidencia solo para /sys/devices/abc .
  • Mueve etiquetas a genfscon . Esta característica existente de SELinux pasa prefijos de coincidencia de archivos al kernel en el binario de SELinux, donde el kernel los aplica a los sistemas de archivos generados por el kernel. Esto también ayuda a corregir archivos creados por el kernel mal etiquetados, evitando condiciones de carrera que pueden ocurrir entre procesos del espacio de usuario que intentan acceder a estos archivos antes de que se vuelva a etiquetar.

Herramienta y métodos

Utilice las siguientes herramientas para ayudarle a recopilar datos para objetivos de optimización.

Gráfico de arranque

Bootchart proporciona un desglose de la carga de CPU y E/S de todos los procesos de todo el sistema. No requiere reconstruir la imagen del sistema y puede usarse como una verificación rápida de estado antes de sumergirse en systrace.

Para habilitar el diagrama de arranque:

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

Después del inicio, obtenga el gráfico de inicio:

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

Cuando termine, elimine /data/bootchart/enabled para evitar que se recopilen datos cada vez.

Si bootchart no funciona y aparece un error que dice que bootchart.png no existe, haga lo siguiente:
  1. Ejecute los siguientes comandos:
          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. Actualice $ANDROID_BUILD_TOP/system/core/init/grab-bootchart.sh para que apunte a la copia local de pybootchartgui (ubicada en ~/Documents/bootchart/pybootchartgui.py )

Systrace

Systrace permite recopilar rastros del kernel y de Android durante el arranque. La visualización de systrace puede ayudar a analizar un problema específico durante el arranque. (Sin embargo, para comprobar el número promedio o el número acumulado durante todo el arranque, es más fácil buscar directamente en el seguimiento del kernel).

Para habilitar systrace durante el arranque:

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

    A:

      #    write /sys/kernel/debug/tracing/tracing_on 0
      #    write /sys/kernel/tracing/tracing_on 0
  • Esto habilita el rastreo (que está deshabilitado de forma predeterminada).

  • En el archivo device.mk , agregue la siguiente línea:
    PRODUCT_PROPERTY_OVERRIDES +=    debug.atrace.tags.enableflags=802922
    PRODUCT_PROPERTY_OVERRIDES +=    persist.traced.enable=0
  • En el archivo BoardConfig.mk del dispositivo, agregue lo siguiente:
    BOARD_KERNEL_CMDLINE := ... trace_buf_size=64M trace_event=sched_wakeup,sched_switch,sched_blocked_reason,sched_cpu_hotplug
  • Para un análisis detallado de E/S, agregue también block y ext4 y f2fs.

  • En el archivo init.rc específico del dispositivo, agregue lo siguiente:
    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
    
  • Después del arranque, busque el seguimiento:

    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