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 |
|
Núcleo del dispositivo |
|
ajuste de E/S |
|
init.*.rc |
|
Animación de inicio |
|
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/
yvendor/
) 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
enfile_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
- 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
- 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
Esto habilita el seguimiento (que está deshabilitado de forma predeterminada).
Para un análisis de E/S detallado, agregue también bloque y ext4 y f2fs.