Optimización extrema del arranque de una Pi
(kittenlabs.de)- SolarCamPi es una cámara solar off-grid que enciende y apaga una Raspberry Pi Zero 2 W solo cuando toma fotos, por lo que el tiempo de arranque determina la duración de la batería
- Con Debian 12 arm64 Lite, midiendo
/init.shcomo el primer código en espacio de usuario, el arranque inicial consumía unos 12 segundos y 9.5 Ws - Al reducir HDMI, LED, HAT/PoE/LCD, la detección automática de cámara y pantalla, e initramfs, se eliminaron esperas y detecciones innecesarias, y la corriente bajó de 136.7 mA → 120.6 mA
- Tras pasar a Buildroot 2024.02.1 y un kernel personalizado, se quitaron drivers, compresión, KASLR y algunas mitigaciones, llegando al programa en espacio de usuario de Linux en menos de 3.5 segundos
- Al bajar el voltaje de entrada de 5.0 V a 3.6 V, la energía total se redujo de 1.754 Ws → 1.438 Ws, aunque queda validar estabilidad y confiabilidad porque opera fuera de especificación
Por qué SolarCamPi necesitaba reducir el tiempo de arranque
- SolarCamPi es una cámara WiFi alimentada por energía solar que arranca periódicamente una Raspberry Pi Zero 2 W, toma una foto, la sube por WiFi a un servicio en la nube y luego se apaga de nuevo
- En entornos con poca energía, como el invierno en Europa occidental, cada segundo que la Pi permanece encendida se convierte directamente en un costo energético
- La conexión al servidor y la subida de fotos de la aplicación en espacio de usuario ya estaban optimizadas al máximo posible, y el circuito electrónico también estaba diseñado para reducir el consumo durante el reposo
- Quedaban dos caminos de ahorro
- Reducir el consumo de corriente
- Reducir el tiempo de ejecución
- Bajar solo la corriente no siempre trae beneficios
- Si se desactiva CPU turbo y baja la corriente, pero aumenta el tiempo de ejecución, la energía total puede terminar siendo mayor
- El objetivo es minimizar el área bajo la curva de corriente y tiempo
Equipo de medición y método de prueba
- En la optimización de arranque en sistemas embebidos, es importante contar con herramientas que permitan verificar rápidamente la ejecución real tras cada cambio, reduciendo la extracción de la tarjeta SD y la manipulación de energía
- El equipo usado fue el siguiente
- Nordic Power Profiler Kit II
- USB-SD-Mux Fast
- Conversor USB-UART
- Power Profiler Kit II alimenta el dispositivo bajo prueba mientras mide el consumo de energía en el tiempo junto con el estado de 8 entradas digitales
- Se conectó un pin GPIO de la Raspberry Pi a una entrada digital
- Como primera acción de la aplicación, se alterna el GPIO para medir el tiempo desde la aplicación de energía hasta la ejecución del código en espacio de usuario
- USB-SD-Mux es un interposer que se coloca entre la tarjeta microSD y el dispositivo, permitiendo que la computadora tome la tarjeta, reescriba su contenido y se la devuelva al dispositivo
- Elimina la tarea repetitiva de sacar la tarjeta, insertarla en un lector y volver a ponerla en el dispositivo
- Con GPIO integrado también puede automatizar el reset o el control de energía del dispositivo bajo prueba
- La consola USB-UART es necesaria para revisar el estado cuando el sistema se rompe, por ejemplo ante fallas de arranque o problemas de WiFi
Medición de arranque con Debian como referencia
- Sobre una imagen limpia de Debian 12 bookworm arm64 Lite, se agregó
init=/init.shen/boot/firmware/cmdline.txt - Esta configuración hace que el kernel ejecute
/init.shcomo primer proceso en espacio de usuario antes que systemd - El
init.shde ejemplo alterna GPIO4 y luego continúa el arranque de systemd conexec /sbin/init - En la medición inicial, la entrada digital 0 pasa a low tras unos 12 segundos, confirmando la ejecución de
init.sh - Este proceso de arranque usó un total de 1.90 coulomb
1.9As * 5.0V = 9.5Ws- Una batería alcalina AA puede entregar alrededor de 13500 Ws de energía
Reducir el consumo de corriente
- Al desactivar por completo el codificador HDMI, la corriente bajó de 136.7 mA a 122.6 mA
- Como era necesario codificar datos de cámara, no se pudo apagar la GPU en sí
- Si la aplicación no necesita cámara ni GPU, también se podría intentar desactivar la GPU
- Solo desactivar el activity LED ahorró 2 mA, reduciendo la corriente de 122.6 mA a 120.6 mA
- Si hay un LED de cámara, se puede desactivar
- También se reduce la posibilidad de que el LED se refleje en la imagen
- En la prueba de cambio de configuración turbo, la Pi en el estado modificado actual usaba 1.62 As
- Al ejecutarla sin turbo forzado, usaba 1.58 As
- Por razones desconocidas, al apagar el modo turbo/boost, el estado predeterminado de GPIO4 se invirtió, por lo que se cambió la polaridad en
init.sh
Reducir el tiempo de arranque
- La corriente se redujo alrededor de 13%, pero todavía pasaban unos 8 segundos antes de que apareciera la primera salida de Linux en la consola, y en ese tramo se consumía alrededor de 1 As
- En la familia Raspberry Pi, la GPU se inicializa primero
- La GPU busca
bootcode.binen la tarjeta SD - Pi 4 y posteriores usan EEPROM
- La GPU busca
- Al activar
BOOT_UARTenbootcode.bin, se pueden ver logs UART detallados- Es necesario respaldar el
bootcode.binoriginal, y el proceso de modificación es potencialmente destructivo
- Es necesario respaldar el
- El bootloader espera una respuesta EDID para detectar automáticamente los parámetros de video del monitor HDMI conectado
- Como no se usa HDMI, se hardcodeó la cadena EDID para evitar la detección
- También se desactivó la detección de EEPROM I2C relacionada con HAT, ventilador PoE, LCD y pantalla táctil
- Estas opciones esperan respuestas en el bus I2C, por lo que se pueden apagar si no son necesarias
- La detección automática de cámaras y pantallas MIPI también consume tiempo
- Como la cámara usada está fijada a una HQ Camera IMX477, se apagó con
camera_auto_detect=0,display_auto_detect=0y se cargó directamentedtoverlay=imx477
- Como la cámara usada está fijada a una HQ Camera IMX477, se apagó con
- Con los cambios anteriores, el tiempo de arranque reportado internamente bajó de 5.38 segundos a 4.75 segundos, y al desactivar initramfs eliminando
auto_initramfs=1bajó hasta 4.47 segundos - El método
sdtweak, que overclockea el periférico SD a 100 MHz, no produjo una diferencia medible en el rendimiento de arranque- Existe riesgo de corrupción de datos en accesos de escritura, por lo que no es deseable para dispositivos IoT remotos
Cuello de botella en la carga del kernel y migración a Buildroot
- En este punto, la carga del kernel era una de las tareas más lentas
- Cargar
kernel8.imgde 9,276,375 bytes tomaba alrededor de 1.54 segundos - La velocidad de transferencia era de unos 6 MiB/s
- Cargar
- La carga del kernel la realiza la GPU, que usa el procesador interno propietario VideoCore IV
- Es posible que el código del loader sea ineficiente o use configuraciones conservadoras, pero al ser una caja negra no fue posible ajustar registros o parámetros de forma útil
- El overclock del core del procesador GPU era teóricamente posible y redujo el tiempo de carga del kernel en 20%
- No se recomienda porque se desconocen efectos secundarios como la confiabilidad
- El sistema se migró de Raspbian/Debian a una distribución personalizada basada en Buildroot 2024.02.1
- Uso de toolchain aarch64 nativa
- Se mantuvieron glibc y las herramientas userland de Raspberry Pi
- En el kernel personalizado se eliminaron o redujeron los siguientes elementos
- Soporte de sonido
- La mayoría de drivers de dispositivos de bloque y sistemas de archivos, manteniendo solo SD/MMC y ext4
- RAID, USB, HID, DVB
- Soporte de video y framebuffer
- Funciones avanzadas de red como túneles, bridging y firewall
- Compresión Gzip del kernel y compresión de módulos
- Mantener tanto el kernel como los módulos sin comprimir fue beneficioso desde el punto de vista de energía total, aunque la GPU tardara más en cargarlos
- La descompresión Gzip consume mucha energía y en la práctica incluye una etapa adicional de reubicación
- También se desactivó KASLR
- KASLR aleatoriza la dirección de carga del kernel en memoria para dificultar la creación de exploits, pero requiere reubicar el kernel después de la carga por la GPU
- En este caso de uso, la superficie de ataque de red es muy limitada y todo el software de la aplicación se ejecuta como root
- También se desactivaron mitigaciones para vulnerabilidades de ejecución especulativa como Spectre
- El tamaño resultante del kernel fue de 8.5 MiB sin comprimir, y el tamaño Gzip para comparación fue de 4.1 MiB
- El kernel original de Raspbian era de 25 MiB sin comprimir y 8.9 MiB comprimido con Gzip
Resultado final y optimización del voltaje de entrada
- Finalmente se llegó al programa en espacio de usuario de Linux en menos de 3.5 segundos
- El tiempo usado dentro del kernel de Linux fue de unos 400 ms
- El consumo total de energía fue de 0.364 As * 5.0 V = 1.82 Ws
- Comparado con los 9.5 Ws para llegar a espacio de usuario en Debian stock, es aproximadamente una quinta parte
- Tras la publicación, Graham Sutherland / Polynomial señaló que el regulador de la Pi Zero no es eficiente con entrada de 5.0 V
- En el escenario de prueba y en el producto terminado fue posible bajar el voltaje de entrada hasta 4.0 V
- 5.0 V:
350.94mAs * 5.0V = 1.754Ws - 4.0 V:
390.77mAs * 4.0V = 1.563Ws - 3.6 V:
399.60mAs * 3.6V = 1.438Ws
- 5.0 V:
- Al bajar el voltaje, aumentan los mC, es decir los mAs, por el incremento de corriente, pero la energía total baja de forma importante
- Operar a 3.6 V redujo la energía en alrededor de 20% adicional al usar el regulador conmutado en un punto de operación más ideal, pero técnicamente queda fuera de especificación, por lo que aún se necesita más validación de estabilidad y confiabilidad
Configuración y código publicados
- SolarCamPi config.txt:
config.txtcompleto - SolarCamPi Linux kernel defconfig: configuración reducida del kernel
- Rama v2 de SolarCamPi-Buildroot: árbol completo de Buildroot; es un trabajo en progreso
1 comentarios
Opiniones de Hacker News
La gestión de energía de la familia Raspberry Pi sin duda es uno de sus puntos débiles. Por eso me entusiasma la nueva Pico 2, porque parece que será más fácil entrar en estados de ahorro de energía relativamente profundos incluso sin hardware externo.
Una vez hice una cámara para un uso similar con una Google Coral mini; la cámara no es tan buena como la HQ cam, pero con el RTC integrado soportaba muy fácilmente el modo de espera/despertar, así que era ideal para una app que toma fotos periódicamente. También tenía el rendimiento y los 2 GB de memoria suficientes para procesar imágenes de alta resolución.
Es posible conectar físicamente la HQ camera, pero hasta donde sé no existe en Coral una canalización de software para gestionarla.
El ecosistema de Raspberry Pi es mucho más maduro, y confío más en la disponibilidad futura de Raspberry Pi que en que Google siga suministrando la línea Coral. Aun así, sentí claramente lo útil que puede ser contar con buen soporte de energía en el hardware.
Irónicamente, la siguiente versión de la cámara la hizo un proveedor externo con Raspberry Pi, y compensó el problema de energía poniendo una batería mucho más grande. Gracias a eso ahora tengo acumuladas 100 Coral dev mini y cámaras sin siquiera abrir, así que estoy en la situación de tener que encontrar una buena idea o venderlas.
Busqué "google coral twitter" para encontrar la cuenta oficial de Twitter, y fue desconcertante que el segundo resultado fuera tu tuit intentando vender 100 placas sobrantes.
Por lo que mencionas de gestionar la energía con hardware externo, parece que no es solo un problema de software.
Si empaquetas la aplicación como un initramfs enlazado al kernel, en casos simples puedes reducir bastante el arranque porque ya no hace falta montar un sistema de archivos.
En algunos casos, incluso algo como el init de BusyBox puede reemplazarse por un script bash sencillo que solo haga el arranque mínimo. También vale la pena considerar montar devtmpfs, proc, sysfs, etc., y, si es posible, deshacerse de glibc.
Antes de enlazar el initramfs al kernel, conviene probar con chroot que estén todas las dependencias necesarias de la aplicación. Si corre dentro de chroot, el kernel también podrá ejecutarlo durante el arranque, y los ciclos de desarrollo se vuelven mucho más rápidos.
Si desactivas los módulos del kernel y enlazas dentro del kernel solo las funciones necesarias, puedes reducir aún más el espacio y el tiempo de arranque. También vale la pena probar la compresión zstd en lugar de gzip.
Esto aplica cuando las etapas tempranas del arranque no aprovechan suficiente el rendimiento del hardware, o cuando la imagen es grande y conviene hacer otras cosas en paralelo con la carga.
Entonces lo único que queda es un initramfs con un único binario enlazado estáticamente.
Hay otros dos buenos artículos sobre cómo reducir el tiempo de arranque de la Pi:
https://www.furkantokac.com/rpi3-fast-boot-less-than-2-secon...
http://himeshp.blogspot.com/2018/08/fast-boot-with-raspberry...
Tomé estos dos artículos como referencia para hacer un marco de fotos digital con una Pi, y logré que arrancara muy rápido incluso hasta el navegador en modo kiosco. Si los requisitos son muy pocos, se pueden conseguir tiempos de arranque bastante impresionantes.
La verdadera tragedia es que el código de GPU bootcode.bin es una caja negra propietaria y no hay código fuente.
Es terrible que los proyectos de hacking y hobby tengan que cargar con una caja negra secreta y oculta que no se puede modificar.
Ahora es software libre y de código abierto.
https://www.theregister.com/2023/11/28/microsoft_opens_sourc...
Eso no hace que todo el firmware de la Pi se vuelva automáticamente libre y de código abierto. No es un driver. Pero si quisieran, podrían hacerlo.
Me da curiosidad qué habrá adentro que necesiten mantener cerrado.
Me gusta el artículo en general, pero no estoy seguro de esta parte
Me refiero a la parte de que desactivar el turbo de la CPU para ahorrar un poco de consumo de corriente es una mala decisión, y que por el tiempo más lento se gasta más energía que si se termina rápido y se apaga
En clases de ciencias de la computación aprendí que el consumo de energía es proporcional al cuadrado de la frecuencia de reloj. Es decir, si duplicas el reloj, la potencia se cuadruplica
Entonces, para comprobar si el incremento cuadrático al subir el reloj es mayor que el producto del consumo fijo de energía que aparece al aumentar el tiempo de trabajo, creo que habría que medir la diferencia real de potencia
Relacionado con eso, sería bueno que la CPU de la Pi ofreciera información detallada de consumo de energía que se pudiera derivar de la hoja de datos o exponer en tiempo real mediante registros
La mayoría de los chips necesitan un voltaje más alto para alcanzar velocidades de reloj más altas, y de ahí viene la relación cuadrática
Pero tengo entendido que la Raspberry Pi no tiene control dinámico de voltaje, así que si solo bajas el reloj sin bajar el voltaje, no creo que afecte el consumo total de energía de conmutación
Claro que también hay un punto óptimo. Si haces demasiado overclock a la CPU, el rendimiento por watt cae tanto que el race to idle deja de funcionar
Por eso los sistemas embebidos modernos a menudo usan estrategias de "race-to-sleep" o "race-to-halt": ejecutan la tarea muy rápido y luego apagan la mayoría de los componentes hasta que llegue el siguiente evento
Si hacer cierto cálculo en 1 segundo cuesta 1 J, por ejemplo 1 W a 1 GHz, supongo que en un modelo esférico perfecto de vaca, hacer el mismo cálculo en 0.5 segundos costaría 2 J. Porque a 2 GHz serían 4 W
Pero eso solo considera el consumo de la CPU; si el sistema completo tiene un consumo fijo de 4 W, a 1 GHz serían 1 J de CPU más 4 J del sistema, 5 J en total, y a 2 GHz serían 2 J de CPU más 2 J del sistema, 4 J en total
Si lo entendí bien, ¿eso significa que conviene usar turbo cuando el consumo del sistema completo es similar al consumo de la CPU en estado turbo, y si no, no?
Impresionante. Pero cada vez que leo algo así me acuerdo de cuando grabé el arranque de Plan 9 en una Pi Zero: https://taoofmac.com/space/blog/2020/09/02/1900#resurrecting
El GIF es salida en tiempo real
Pero otra cosa es si puede cargar los drivers de cámara y Wi‑Fi que necesita el proyecto original
Personalmente, los tiempos de arranque de las distribuciones Linux en general me parecen bastante decepcionantes, y en hardware tan débil el problema se vuelve mucho mayor
Hice optimizaciones parecidas en una SBC MQ-Pro. También lo noto bastante en laptops. Las MacBook quizá sean la excepción, pero de todos modos es algo molesto
Una vez encendida, volver a iniciar sesión es instantáneo, pero reiniciar sí toma bastante tiempo
Pero las distribuciones, por motivos razonables, crean kernels e initramfs muy genéricos, y esa combinación no es especialmente rápida para arrancar
[0]: https://blog.davidv.dev/posts/minimizing-linux-boot-times/
Salvo configuraciones horribles, como que NetworkManager espere 90 segundos por una Wi‑Fi inexistente. Mi caja Linux tarda unos 4 segundos hasta graphical.target, y la mayor parte es por la conexión Wi‑Fi y ntpd, que en principio son opcionales
Si de verdad quieres un arranque rápido, basta con tirar por la borda toda la capacidad de configuración dinámica como capas de compatibilidad del bootloader, abstracciones e initramfs. Pero entonces quedas a merced del proveedor de hardware, así que no vale la pena
Por ejemplo, una MacBook Air 11 con Linux llega a la pantalla de login tan rápido que casi no alcanzo a ver los logs de arranque. Según systemd-analyze, tarda menos de 4 segundos hasta el graphical target
La clave parecen ser dos cosas. No usar un entorno de escritorio; arrancar en modo texto y luego ejecutar startx cuando hace falta, o arrancar X con un gestor de login ligero (lightdm). Sin entorno de escritorio, la cantidad de servicios se reduce varias veces, a un solo dígito, y en hardware viejo baja mucho la presión de I/O durante el arranque. Incluso ejecutando X, el sistema arrancado queda por debajo de 200 MB
La otra es que EFI stub puede mejorar la velocidad: https://wiki.archlinux.org/title/EFISTUB
Mi primera intuición fue si no se podría usar otro núcleo. ¿De verdad hace falta Linux para tomar una foto y enviarla a la nube?
No soy del área de hardware, así que me da curiosidad cómo se podría completar esta tarea con el presupuesto mínimo. Fue un artículo interesante.
Tal vez sea solo porque ahora tengo dos ESP32-CAM sobre la mesa del comedor, pero me dio curiosidad cómo se compararía una configuración con ESP32-CAM. Probablemente solo sirva bien hasta imágenes de 2 megapíxeles, pero creo que tanto el tiempo de arranque como el consumo de energía serían casi un orden de magnitud menores.
Si les interesa, aquí hay detalles: https://components101.com/modules/esp32-cam-camera-module
En principio también sería posible hacerlo en bare metal, pero no es fácil hacer funcionar los periféricos necesarios.
Me preguntaba por qué el kernel personalizado apareció tan tarde. Si se quiere optimizar, ¿no se empieza con LFS o con una distribución basada en código fuente? Tampoco parece que las actualizaciones autónomas de software sean imprescindibles en un dispositivo así.
También me pregunto si se puede optimizar EFI/BIOS en este tipo de dispositivos. Al menos en mi escritorio común con Arch Linux representa una parte considerable del tiempo de arranque.
$ systemd-analyzeStartup finished in 10.076s (firmware) + 1.339s (loader) + 1.569s (kernel) + 2.974s (initrd) + 3.894s (userspace) = 19.854sNo conozco exactamente el hardware de Raspberry Pi, pero muchos otros SoC embebidos tienen un bootloader bastante mínimo que funciona con u-boot y normalmente es muy rápido. Sobre todo si configuras en 0 el retraso para esperar entrada del usuario.
Un kernel mínimo y un sistema con busybox es mucho menos doloroso, y Gentoo tampoco es una mala opción.
> systemd-analyzeStartup finished in 3.259s (firmware) + 35.127s (loader) + 1.823s (kernel) + 2.927s (userspace) = 43.138s3.5 segundos es genial, pero si el escenario completo realmente consiste en conectarse a Wi-Fi cada pocos minutos para subir una imagen, ESP32 habría sido una opción mucho mejor en consumo de energía.
Salvo que haga falta alguna función específica del módulo de cámara para Pi que no tengan las cámaras compatibles con ESP32-CAM.
Parece demasiado trabajo para hacer algo que un microcontrolador puede hacer casi sin esfuerzo.