Optimización de los tiempos de arranque

Este documento brinda orientación a los socios para mejorar los tiempos de arranque de dispositivos Android específicos. El tiempo de arranque es un componente importante del rendimiento del sistema, ya que los usuarios deben esperar a que se complete el arranque antes de poder usar el dispositivo. Para dispositivos como automóviles donde el arranque en frío ocurre con más frecuencia, tener un tiempo de arranque rápido es fundamental (a nadie le gusta esperar docenas 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 al eliminar el registro de UART
  • Ahorró 0.4s al cambiar a LZ4 desde GZIP
Núcleo del dispositivo
  • Ahorró 0.3 s al eliminar las configuraciones del kernel no utilizadas y reducir el tamaño del controlador
  • Ahorró 0,3 s con la optimización de captación previa de dm-verity
  • Se guardaron 0.15 s para eliminar esperas/pruebas innecesarias en el controlador
  • Guardó 0.12s para eliminar CONFIG_CC_OPTIMIZE_FOR_SIZE
ajuste de E/S
  • Ahorró 2 segundos en el arranque normal
  • Ahorró 25 s en el primer arranque
init.*.rc
  • Ahorró 1.5 s al poner en paralelo los comandos init
  • Ahorró 0.25 s al comenzar zygote temprano
  • Guardado 0.22s por cpuset tune
Animación de inicio
  • Comenzó 2 segundos antes en el arranque sin activar fsck, mucho más grande en el arranque con arranque activado por fsck
  • 5 segundos guardados en Pixel XL con apagado inmediato de la animación de arranque
Política de SELinux Guardado 0.2s 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 con muchos registros. (En los dispositivos Google Pixel, descubrimos que ralentiza el cargador de arranque 1.5s).
    • Registre solo situaciones de error y considere almacenar otra información en la memoria con un mecanismo separado para recuperar.
  • 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.
  • Compruebe los tiempos de espera innecesarios para la entrada en modo especial/antirrebote y minimícelos.
  • Pase el tiempo de arranque empleado en el gestor de arranque al kernel como cmdline.
  • Compruebe el reloj de la CPU y considere la paralelización (requiere compatibilidad con varios núcleos) para la carga del kernel y la inicialización de E/S.

Optimización del núcleo

Use los siguientes consejos para optimizar el kernel para mejorar los tiempos de arranque.

Minimizar la configuración predeterminada del dispositivo

Minimizar la configuración del kernel puede reducir el tamaño del kernel para una descompresión de carga más rápida, inicialización y superficies de ataque más pequeñas. Para optimizar la configuración predeterminada del dispositivo:

  • Identifique los controladores no utilizados . Revise los directorios /dev y /sys y busque nodos con etiquetas generales de SELinux (lo que indica que esos nodos no están configurados para ser accesibles desde el espacio del usuario). Elimine dichos nodos si los encuentra.
  • Desconfigurar las CONFIG no utilizadas . Revise el archivo .config generado por la compilación del kernel para desactivar explícitamente cualquier CONFIGURACIÓN no utilizada que se activó de forma predeterminada. Por ejemplo, eliminamos las siguientes CONFIG no utilizadas de Google Pixel:
    CONFIG_ANDROID_LOGGER=y
    CONFIG_IMX134=y
    CONFIG_IMX132=y
    CONFIG_OV9724=y
    CONFIG_OV5648=y
    CONFIG_GC0339=y
    CONFIG_OV8825=y
    CONFIG_OV8865=y
    CONFIG_s5k4e1=y
    CONFIG_OV12830=y
    CONFIG_USB_EHCI_HCD=y
    CONFIG_IOMMU_IO_PGTABLE_FAST_SELFTEST=y
    CONFIG_IKCONFIG=y
    CONFIG_RD_BZIP2=y
    CONFIG_RD_LZMA=y
    CONFIG_TI_DRV2667=y
    CONFIG_CHR_DEV_SCH=y
    CONFIG_MMC=y
    CONFIG_MMC_PERF_PROFILING=y
    CONFIG_MMC_CLKGATE=y
    CONFIG_MMC_PARANOID_SD_INIT=y
    CONFIG_MMC_BLOCK_MINORS=32
    CONFIG_MMC_TEST=y
    CONFIG_MMC_SDHCI=y
    CONFIG_MMC_SDHCI_PLTFM=y
    CONFIG_MMC_SDHCI_MSM=y
    CONFIG_MMC_SDHCI_MSM_ICE=y
    CONFIG_MMC_CQ_HCI=y
    CONFIG_MSDOS_FS=y
    # CONFIG_SYSFS_SYSCALL is not set
    CONFIG_EEPROM_AT24=y
    # CONFIG_INPUT_MOUSEDEV_PSAUX is not set
    CONFIG_INPUT_HBTP_INPUT=y
    # CONFIG_VGA_ARB is not set
    CONFIG_USB_MON=y
    CONFIG_USB_STORAGE_DATAFAB=y
    CONFIG_USB_STORAGE_FREECOM=y
    CONFIG_USB_STORAGE_ISD200=y
    CONFIG_USB_STORAGE_USBAT=y
    CONFIG_USB_STORAGE_SDDR09=y
    CONFIG_USB_STORAGE_SDDR55=y
    CONFIG_USB_STORAGE_JUMPSHOT=y
    CONFIG_USB_STORAGE_ALAUDA=y
    CONFIG_USB_STORAGE_KARMA=y
    CONFIG_USB_STORAGE_CYPRESS_ATACB=y
    CONFIG_SW_SYNC_USER=y
    CONFIG_SEEMP_CORE=y
    CONFIG_MSM_SMEM_LOGGING=y
    CONFIG_IOMMU_DEBUG=y
    CONFIG_IOMMU_DEBUG_TRACKING=y
    CONFIG_IOMMU_TESTS=y
    CONFIG_MOBICORE_DRIVER=y
    # CONFIG_DEBUG_PREEMPT is not set
    
  • Elimine las CONFIG que provocan ejecuciones de prueba innecesarias en cada arranque . Si bien son útiles en el desarrollo, tales configuraciones (es decir, CONFIG_IOMMU_IO_PGTABLE_FAST_SELFTEST) deben eliminarse en un kernel de producción.

Minimizar el tamaño del controlador

Algunos controladores en el kernel del dispositivo se pueden eliminar si la función no se usa para reducir aún más el tamaño del kernel. Por ejemplo, si la WLAN está conectada a través de PCIe, la compatibilidad con SDIO no se usa y debe eliminarse durante el tiempo de compilación. Para obtener más información, consulte el kernel de Google Pixel: net: wireless: cnss: agregue la opción para deshabilitar la compatibilidad con SDIO.

Eliminación de la optimización del compilador para el tamaño

Elimine la configuración del kernel para CONFIG_CC_OPTIMIZE_FOR_SIZE. Este indicador se introdujo originalmente cuando se suponía que un tamaño de código más pequeño produciría un acceso de caché activo (y, por lo tanto, sería más rápido). Sin embargo, esta suposición ya no es válida ya que los SoC móviles modernos se han vuelto más potentes.

Además, eliminar el indicador puede habilitar la advertencia del compilador para variables no inicializadas, que se suprime en los kernels de Linux cuando está presente el indicador CONFIG_CC_OPTIMIZE_FOR_SIZE (realizar este cambio por sí solo nos ayudó a descubrir muchos errores significativos en algunos controladores de dispositivos Android).

Aplazamiento de la inicialización

Muchos procesos se inician durante el arranque, pero solo los componentes en la ruta crítica (cargador de arranque > kernel > init > montaje del sistema de archivos > cigoto > servidor del sistema) afectan directamente el tiempo de arranque. Perfile initcall durante el arranque del kernel para identificar periféricos/componentes que son lentos y no críticos para iniciar el proceso de inicio, luego retrase esos periféricos/componentes hasta más adelante en el proceso de arranque moviéndose a módulos de kernel cargables. Pasar a una sonda de controlador/dispositivo asincrónico también puede ayudar a poner en paralelo componentes lentos en la ruta crítica kernel > init.

BoardConfig-common.mk:
    BOARD_KERNEL_CMDLINE += initcall_debug ignore_loglevel

driver:
    .probe_type = PROBE_PREFER_ASYNCHRONOUS,

Nota: Las dependencias de los controladores deben resolverse con cuidado agregando compatibilidad con EPROBEDEFER .

Optimización de la eficiencia de E/S

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

Afinando 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 las aplicaciones normales).

Los dispositivos que admiten actualizaciones continuas (A/B) se benefician enormemente del ajuste del sistema de archivos en el primer arranque (por ejemplo, 20 s en Google Pixel). Como 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 mediante 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 eliminada que se produce en cada arranque, use la nueva herramienta de generación ext4 configurando TARGET_USES_MKE2FS en BoardConfig.mk.

Análisis de E/S

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

trace_event=block,ext4 in BOARD_KERNEL_CMDLINE

Para desglosar el acceso a los archivos para 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 de 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 un desglose a nivel del sistema.

Optimización de init.*.rc

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

Ejecutar tareas en paralelo

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

  • Ejecute comandos lentos en un servicio de script de shell y únase a eso 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 demora 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 los servicios y habilite los dispositivos periféricos en la ruta crítica 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. Cualquier cosa que no se use en la etapa inicial de inicio debe posponerse para que se complete el arranque.

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

Uso del ajuste del programador

Use el ajuste del programador para el arranque anticipado. 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

Algunos servicios pueden necesitar un aumento 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
...

Comenzando 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 la configuración incorrecta de cpuset puede obligar a zygote a ejecutarse en CPU específicas).

Deshabilitar 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 controlador de CPU.

Precaución: el ahorro de energía debe estar habilitado en el modo de 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 diferir a 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.

Configuración de inicio anticipado

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

Para hacer que bootanimation comience temprano, divida el montaje fstab en dos fases:

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

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

Terminando limpio

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

Optimización de SELinux

Use los siguientes consejos para optimizar SELinux para mejorar los tiempos de arranque.

  • Utilice expresiones regulares limpias (regex) . Las expresiones regulares mal formadas pueden 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 para /sys/devices/abc y /sys/devices/xyz/abc . ¿Mejorando esta expresión regular a /sys/devices/[^/]*abc[^/]*(/.*)? habilitará una coincidencia solo para /sys/devices/abc .
  • Mover 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 los archivos creados por el kernel mal etiquetados, lo que evita las condiciones de carrera que pueden ocurrir entre los procesos del espacio de usuario que intentan acceder a estos archivos antes de que se vuelva a etiquetar.

Herramienta y métodos

Use las siguientes herramientas para ayudarlo a recopilar datos para objetivos de optimización.

Diagrama de arranque

Bootchart proporciona un desglose de carga de CPU y E/S de todos los procesos para todo el sistema. No requiere la reconstrucción de la imagen del sistema y se puede utilizar como una comprobación rápida de cordura antes de sumergirse en systrace.

Para habilitar el gráfico de arranque:

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

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

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

Cuando haya terminado, elimine /data/bootchart/enabled para evitar la recopilación de datos cada vez.

Systrace

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

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 seguimiento (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 del dispositivo BoardConfig.mk , 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 de E/S detallado, agregue también bloque 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 de arrancar, 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