Optimiza los tiempos de inicio

En este documento, se proporciona orientación a los socios para mejorar los tiempos de inicio de aplicaciones Dispositivos Android El tiempo de inicio es un componente importante del rendimiento del sistema, ya que los usuarios deben esperar a que se complete el inicio para poder usar el dispositivo. Para dispositivos como en automóviles en los que el inicio en frío ocurre con más frecuencia, el tiempo es crítico (a nadie le gusta esperar decenas de segundos solo para ingresar una destino de navegación).

Android 8.0 permite reducir los tiempos de inicio al admitir varias mejoras. en una variedad de componentes. En la siguiente tabla, se resumen estas métricas mejoras (según las mediciones de dispositivos Google Pixel y Pixel XL).

Componente Mejora
Bootloader
  • Se guardaron 1.6 s por la eliminación del registro de UART
  • Se guardaron 0.4 s cambiando de GZIP a LZ4.
Kernel del dispositivo
  • Se quitaron 0.3 s mediante la eliminación de las configuraciones de kernel sin usar y la reducción del tamaño del controlador.
  • Se guardaron 0.3 s con optimización de carga previa de dm-verity
  • Se guardaron 0.15 s para quitar las esperas o pruebas innecesarias en el controlador.
  • Se guardaron 0.12 s para quitar CONFIG_CC_OPTIMIZE_FOR_SIZE.
Ajuste de E/S
  • Se guardaron 2 s en el inicio normal
  • Se ahorran 25 s en el primer inicio
init.*.rc
  • Se guardaron 1.5 s mediante la sincronización de comandos init en paralelo.
  • Se ahorraron 0.25 s al iniciar Zygote antes.
  • Ahorro de 0.22 s por cpuset tune
Animación de inicio
  • Comenzó 2 s antes en el inicio sin activar fsck, y mucho más en el inicio con inicio activado por fsck
  • Se ahorran 5 s en Pixel XL y se cierra inmediatamente la animación de inicio.
Política de SELinux Se guardó 0.2 s por genfscon

Optimizar el bootloader

Para optimizar el bootloader y mejorar los tiempos de inicio, haz lo siguiente:

  • Para los registros:
    • Inhabilitar la escritura de registros en UART, ya que puede llevar mucho tiempo con muchas de los datos. (en los dispositivos Google Pixel, descubrimos que ralentiza el bootloader en 1.5 s).
    • Registra solo las situaciones de errores y considera almacenar otra información en la memoria con un mecanismo independiente de recuperación.
  • Para la descompresión de kernel, considera usar LZ4 para el hardware contemporáneo. en lugar de GZIP (ejemplo patch). Ten en cuenta que las diferentes opciones de compresión de kernel pueden tener distintas tiempos de descompresión y algunas opciones pueden funcionar mejor que otras en su un hardware específico.
  • Verifica los tiempos de espera innecesarios para la anulación de rebotes o la entrada del modo especial y minimiza de ellos.
  • Pasa el tiempo de inicio empleado en el bootloader al kernel como cmdline.
  • Verifica el reloj de la CPU y considera la paralelización (requiere compatibilidad con varios núcleos) para cargar el kernel e inicializar E/S.

Optimizar la eficiencia de E/S

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

Ajusta el sistema de archivos

Se inicia la lectura anticipada del kernel de Linux cuando se lee un archivo desde el principio o cuando los bloques se leen de forma secuencial, por lo que es necesario ajustar el programador de E/S parámetros específicos para el inicio (que tiene una carga de trabajo diferente más características que las apps normales).

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

Varios

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

Analizar E/S

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

trace_event=block,ext4 in BOARD_KERNEL_CMDLINE

Para desglosar el acceso de cada archivo a los archivos, realiza los siguientes cambios en el kernel (solo kernel de desarrollo; no usar 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);

Usa las siguientes secuencias de comandos para ayudar a analizar el rendimiento de inicio.

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

Optimiza init.*.rc

Init es el puente desde el kernel hasta que se establece el framework. suelen pasar unos segundos en diferentes etapas init.

Ejecuta tareas en paralelo

Si bien el init actual de Android es más o menos un proceso de un solo subproceso, pueden realizar algunas tareas en paralelo.

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

    Revisar este registro puede indicar oportunidades de mejora.

  • Inicia los servicios y habilita los dispositivos periféricos en rutas críticas de manera anticipada. Para Por ejemplo, algunos SOC requieren iniciar servicios relacionados con la seguridad antes de iniciar SurfaceFlinger. Revisar el registro del sistema cuando ServiceManager muestre "wait for servicio" (esto suele indicar que se debe iniciar un servicio dependiente) antes de empezar.
  • Quita los servicios y comandos que no se usen en init.*.rc. Todo lo que no se use en El init de la etapa inicial se debe aplazar hasta el inicio completado.

Nota: El servicio de propiedades es parte del proceso init, por lo que setproperty durante el inicio puede generar una larga demora si init está ocupado en comandos integrados.

Usa el ajuste del programador

Usa el ajuste del programador para el inicio 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

Es posible que algunos servicios necesiten un aumento de prioridad durante el inicio. Ejemplo:

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

Cómo iniciar Zygote con anticipación

Los dispositivos con encriptación basada en archivos pueden iniciar Zygote antes en el comando zygote-start. (de forma predeterminada, Zygote se inicia en la clase main, que es mucho más tarde que zygote-start). Cuando hagas esto, asegúrate de que Zygote se ejecute en todas las CPU (como una configuración incorrecta de cpuset puede forzar a Zygote a ejecutarse en CPU específicas).

Inhabilitar ahorro de energía

Parámetro de configuración de ahorro de energía durante el inicio del dispositivo para componentes como UFS o CPU puede inhabilitarse.

Precaución: El ahorro de energía debe habilitarse en de carga para ahorrar 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

Aplaza 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}

Cómo optimizar la animación de inicio

Usa las siguientes sugerencias para optimizar la animación de inicio.

Configura el inicio anticipado

Android 8.0 habilita el inicio de la animación de inicio antes, antes de activar los datos de usuario por cada partición. Sin embargo, incluso cuando se usa la nueva cadena de herramientas ext4 en Android 8.0, fsck se activa periódicamente por motivos de seguridad, lo que provoca un retraso en iniciar el servicio de animación de inicio.

Para hacer que la animación de inicio comience antes, divide el montaje fstab en dos fases:

  • En la fase inicial, activa solo las particiones (como system/ y vendor/) que no requieren ejecución y, luego, inicia los servicios de animación de inicio y sus dependencias (como servicemanager y Surfaceflinger).
  • En la segunda fase, activa las particiones (como data/) que requieren verificaciones de ejecución.

La animación de inicio se iniciará mucho más rápido (y en tiempo constante), independientemente de maldita sea.

Finalizar limpieza

Después de recibir la señal de salida, bootanimación reproduce la última parte, la duración que pueden ralentizar el tiempo de inicio. Un sistema que se inicia rápidamente no necesita animaciones que podrían ocultar eficazmente las mejoras realizadas. Recomendaciones y hacen que el bucle repetido y el final sean cortos.

Optimiza SELinux

Usa los siguientes consejos para optimizar SELinux y mejorar los tiempos de inicio.

  • Usa expresiones regulares limpias (regex). regex con formato incorrecto puede generar mucha sobrecarga cuando se hace coincidir la política SELinux sys/devices en file_contexts. Por ejemplo, la regex /sys/devices/.*abc.*(/.*)? fuerza por error un análisis de todos Subdirectorios de /sys/devices que contienen "abc", lo que habilita las coincidencias para /sys/devices/abc y /sys/devices/xyz/abc. Mejorar esta regex a /sys/devices/[^/]*abc[^/]*(/.*)? habilitar una coincidencia solo para /sys/devices/abc.
  • Mueve etiquetas a genfscon. Esta función de SELinux existente pasa prefijos de coincidencia de archivos al kernel en el objeto binario de SELinux, donde el kernel los aplica a las entradas sistemas de archivos. Esto también ayuda a corregir los archivos etiquetados incorrectamente creados por el kernel, lo que evita condiciones de carrera que pueden ocurrir entre los procesos del espacio del usuario que intentan acceder estos archivos antes de que se vuelvan a etiquetar.

Herramientas y métodos

Utiliza las siguientes herramientas para recopilar datos para los objetivos de optimización.

Diagrama de inicio

El gráfico de arranque proporciona un desglose de la carga de CPU y E/S de todos los procesos en todo el proceso en un sistema de archivos. No requiere volver a compilar la imagen del sistema y se puede usar como un antes de acceder a Systrace.

Para habilitar el gráfico de inicio, haz lo siguiente:

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

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

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

Cuando termines, borra /data/bootchart/enabled para evitar la recopilación los datos cada vez.

Si el gráfico de inicio no funciona y recibes un error que dice que bootchart.png no existe, lo siguiente:
  1. Ejecuta 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. Actualizar $ANDROID_BUILD_TOP/system/core/init/grab-bootchart.sh para dirigir a la copia local de pybootchartgui (ubicado en ~/Documents/bootchart/pybootchartgui.py)

Systrace

Systrace permite recopilar registros de kernel y Android durante el inicio. La visualización de Systrace puede ayudar a analizar problemas específicos durante la durante el inicio. (Sin embargo, para verificar el número promedio o el número acumulado durante el inicio completo, es más fácil consultar directamente el seguimiento del kernel).

Para habilitar Systrace durante el inicio, haz lo siguiente:

  • En frameworks/native/cmds/atrace/atrace.rc, cambia lo siguiente:
      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á inhabilitado de forma predeterminada).

  • En el archivo device.mk, agrega 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, agrega 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 las E/S, agrega también block, ext4 y f2fs.

  • En el archivo init.rc específico del dispositivo, agrega 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 inicio, recupera 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