Optimisation extrême du démarrage d’un Pi
(kittenlabs.de)- SolarCamPi est une caméra solaire hors réseau qui n’allume le Raspberry Pi Zero 2 W qu’au moment de prendre une photo avant de l’éteindre, donc le temps de démarrage lui-même détermine l’autonomie de la batterie
- Sur une base Debian 12 arm64 Lite, en mesurant
/init.shcomme premier code en espace utilisateur, le démarrage initial prend environ 12 secondes et consomme 9,5 Ws - En réduisant HDMI, les LED, la détection automatique HAT/PoE/LCD, caméra et écran, ainsi que l’initramfs, les attentes et détections inutiles sont supprimées, et le courant descend de 136,7 mA à 120,6 mA
- Après migration vers Buildroot 2024.02.1 avec un noyau personnalisé, en supprimant des pilotes, la compression, KASLR et certaines fonctions de mitigation, Linux atteint un programme en espace utilisateur en moins de 3,5 secondes
- En abaissant la tension d’entrée de 5,0 V à 3,6 V, l’énergie totale passe de 1,754 Ws à 1,438 Ws, mais cela reste un fonctionnement hors spécifications et la stabilité comme la fiabilité doivent encore être validées
Pourquoi SolarCamPi devait réduire son temps de démarrage
- SolarCamPi est une caméra Wi‑Fi alimentée par énergie solaire qui démarre périodiquement un Raspberry Pi Zero 2 W, prend une photo, l’envoie à un service cloud via le Wi‑Fi, puis s’éteint à nouveau
- Dans des environnements où l’énergie manque, comme l’hiver en Europe de l’Ouest, chaque seconde pendant laquelle le Pi reste allumé représente un coût énergétique
- L’application en espace utilisateur, pour la connexion au serveur et l’envoi des photos, était déjà optimisée autant que possible, et l’électronique était elle aussi conçue pour réduire la consommation en veille
- Il restait donc deux leviers
- réduire le courant consommé
- réduire le temps d’exécution
- Réduire seulement le courant n’est pas toujours avantageux
- désactiver le turbo CPU peut baisser le courant, mais si l’exécution dure plus longtemps, l’énergie totale peut au contraire augmenter
- l’objectif est de minimiser l’aire sous la courbe courant/temps
Matériel de mesure et méthode de test
- En optimisation de démarrage embarqué, il faut pouvoir vérifier rapidement l’exécution réelle après chaque changement, donc des outils limitant les retraits de carte SD et les manipulations d’alimentation sont importants
- Le matériel utilisé est le suivant
- Nordic Power Profiler Kit II
- USB-SD-Mux Fast
- un convertisseur USB-UART
- Le Power Profiler Kit II alimente l’appareil testé tout en mesurant la consommation électrique dans le temps ainsi que l’état de 8 entrées numériques
- des broches GPIO du Raspberry Pi sont reliées aux entrées numériques
- la première action de l’application consiste à basculer un GPIO afin de mesurer le temps écoulé entre la mise sous tension et l’exécution du code en espace utilisateur
- Le USB-SD-Mux est un interposeur entre la carte microSD et l’appareil, permettant à l’ordinateur de reprendre la carte, de réécrire son contenu, puis de la rendre à l’appareil
- cela évite les manipulations répétitives consistant à retirer la carte, la brancher à un lecteur, puis la remettre dans l’appareil
- son GPIO embarqué permet aussi d’automatiser le reset ou l’alimentation de l’appareil testé
- Une console USB-UART est nécessaire pour diagnostiquer l’état du système quand il casse, par exemple en cas d’échec du démarrage ou de problème Wi‑Fi
Mesure du démarrage avec Debian
- Sur une image Debian 12 bookworm arm64 Lite propre,
init=/init.sha été ajouté à/boot/firmware/cmdline.txt - Ce réglage fait exécuter
/init.shpar le noyau comme premier processus en espace utilisateur, avant systemd - Le
init.shd’exemple bascule GPIO4, puis poursuit le démarrage systemd avecexec /sbin/init - Dans la mesure initiale, l’entrée numérique 0 passe à low au bout d’environ 12 secondes, ce qui confirme l’exécution de
init.sh - Ce processus de démarrage consomme au total 1,90 coulomb
1.9As * 5.0V = 9.5Ws- une pile alcaline AA peut fournir environ 13 500 Ws d’énergie
Réduire le courant consommé
- En désactivant complètement l’encodeur HDMI, le courant baisse de 136,7 mA à 122,6 mA
- le GPU ne pouvait pas être désactivé, car il faut encoder les données de la caméra
- pour une application n’ayant besoin ni de caméra ni de GPU, on pourrait aussi tenter de désactiver le GPU
- La seule désactivation de la LED d’activité économise 2 mA, faisant passer le courant de 122,6 mA à 120,6 mA
- S’il existe une LED caméra, elle peut elle aussi être désactivée
- cela réduit aussi le risque de reflets sur les images
- Dans le test de modification du réglage turbo, le Pi dans son état actuel consomme 1,62 As
- sans turbo forcé, il consomme 1,58 As
- pour une raison inconnue, désactiver le mode turbo/boost inverse l’état par défaut de GPIO4, ce qui a conduit à inverser la polarité dans
init.sh
Réduire le temps de démarrage
- Le courant a baissé d’environ 13 %, mais il fallait encore près de 8 secondes avant que la première sortie Linux n’apparaisse sur la console, et cette phase consommait environ 1 As
- Sur les Raspberry Pi, le GPU s’initialise en premier
- le GPU cherche
bootcode.binsur la carte SD - sur les Pi 4 et plus récents, l’EEPROM est utilisée
- le GPU cherche
- En activant
BOOT_UARTdansbootcode.bin, il est possible d’obtenir des logs UART détaillés- il faut sauvegarder le
bootcode.bind’origine, et la modification est potentiellement destructrice
- il faut sauvegarder le
- Le bootloader tente de détecter automatiquement les paramètres vidéo du moniteur HDMI connecté en attendant une réponse EDID
- comme HDMI n’est pas utilisé, la chaîne EDID a été codée en dur pour éviter cette détection
- La détection des EEPROM I2C liées aux HAT, ventilateurs PoE, LCD et écrans tactiles a aussi été désactivée
- ces options attendent une réponse sur le bus I2C, donc il vaut mieux les couper si elles sont inutiles
- La détection automatique des caméras et écrans MIPI prend également du temps
- comme la caméra utilisée est une HQ Camera IMX477,
camera_auto_detect=0etdisplay_auto_detect=0ont été désactivés, etdtoverlay=imx477est chargé directement
- comme la caméra utilisée est une HQ Camera IMX477,
- Avec ces changements, le temps de démarrage auto-reporté passe de 5,38 secondes à 4,75 secondes, puis tombe à 4,47 secondes en supprimant
auto_initramfs=1pour désactiver l’initramfs - La méthode
sdtweak, qui overclocke le périphérique SD à 100 MHz, n’a pas produit de différence mesurable sur les performances de démarrage- elle présente un risque de corruption des données lors des accès en écriture et n’est donc pas souhaitable pour un appareil IoT distant
Goulot d’étranglement du chargement du noyau et passage à Buildroot
- À ce stade, le chargement du noyau était l’une des opérations les plus lentes
- le chargement de
kernel8.img, avec ses 9 276 375 octets, prenait environ 1,54 seconde - le débit de transfert était d’environ 6 MiB/s
- le chargement de
- Le chargement du noyau est effectué par le GPU, qui utilise le processeur interne propriétaire VideoCore IV
- le code du chargeur était peut-être inefficace ou configuré de façon conservatrice, mais comme il s’agit d’une boîte noire, il n’était pas possible d’ajuster utilement les registres ou paramètres
- Un overclock du cœur processeur GPU était théoriquement possible et réduisait le temps de chargement du noyau de 20 %
- ce n’est pas recommandé, car les effets secondaires sur la fiabilité sont inconnus
- Le système a été migré de Raspbian/Debian vers une distribution personnalisée basée sur Buildroot 2024.02.1
- avec une toolchain native aarch64
- en conservant glibc et les outils userland Raspberry Pi
- Dans le noyau personnalisé, les éléments suivants ont été supprimés ou réduits
- le support audio
- la plupart des pilotes de périphériques bloc et systèmes de fichiers, en conservant seulement SD/MMC et ext4
- RAID, USB, HID, DVB
- le support vidéo et framebuffer
- les fonctions réseau avancées comme les tunnels, le bridging et le pare-feu
- la compression Gzip du noyau et la compression des modules
- Le choix de ne compresser ni le noyau ni les modules était plus avantageux en énergie globale, même si le GPU devait charger plus longtemps
- la décompression Gzip consomme beaucoup d’énergie et implique en pratique une étape supplémentaire de relocalisation
- KASLR a aussi été désactivé
- KASLR randomise l’adresse de chargement du noyau en mémoire pour compliquer l’écriture d’exploits, mais impose une relocalisation du noyau après le chargement par le GPU
- dans ce cas d’usage, la surface d’attaque réseau est très limitée et tout le logiciel applicatif s’exécute en root
- les mitigations des vulnérabilités d’exécution spéculative comme Spectre ont elles aussi été désactivées
- Le noyau obtenu mesure 8,5 MiB non compressé, contre 4,1 MiB en Gzip pour comparaison
- le noyau Raspbian d’origine faisait 25 MiB non compressé et 8,9 MiB en Gzip
Résultat final et optimisation de la tension d’entrée
- Au final, un programme Linux en espace utilisateur est atteint en moins de 3,5 secondes
- Le temps passé à l’intérieur du noyau Linux est d’environ 400 ms
- La consommation totale d’énergie est de 0.364As * 5.0V = 1.82Ws
- soit environ un cinquième des 9,5 Ws nécessaires pour atteindre l’espace utilisateur avec Debian d’origine
- Après publication, Graham Sutherland / Polynomial a signalé que le régulateur du Pi Zero n’est pas efficace avec une entrée à 5,0 V
- Dans le scénario de test comme dans le produit fini, il était possible d’abaisser la tension d’entrée jusqu’à 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 :
- Quand la tension baisse, les mC, donc les mAs, augmentent avec la hausse du courant, mais l’énergie totale diminue fortement
- Le fonctionnement à 3,6 V, en faisant travailler le régulateur à découpage dans une zone plus idéale, a réduit l’énergie d’environ 20 % supplémentaires, mais c’est techniquement hors spécifications et cela demande encore des validations de stabilité et de fiabilité
Configuration et code publiés
- SolarCamPi config.txt :
config.txtcomplet - SolarCamPi Linux kernel defconfig : configuration allégée du noyau
- SolarCamPi-Buildroot v2 branch : arborescence Buildroot complète, encore en cours de développement
1 commentaires
Avis sur Hacker News
La gestion de l’alimentation de la gamme Raspberry Pi est clairement l’un de ses points faibles. C’est pourquoi le nouveau Pico 2 m’intéresse : il semble qu’il soit devenu plus facile d’entrer dans un état de veille relativement profonde sans matériel externe.
J’ai déjà fabriqué une caméra pour un usage similaire avec un Google Coral mini ; la caméra n’est pas aussi bonne que la HQ cam, mais le RTC intégré rendait la mise en veille/réveil très simple, ce qui convenait parfaitement à une app qui prend des photos périodiquement. Il avait aussi assez de performances et 2 Go de mémoire pour traiter des images haute résolution.
On peut connecter physiquement la HQ camera, mais à ma connaissance il n’existe pas de pipeline logiciel sur Coral pour la gérer.
L’écosystème Raspberry Pi est bien plus mature, et je fais davantage confiance à la disponibilité future du Raspberry Pi qu’à la poursuite de l’approvisionnement de la gamme Coral par Google. Cela dit, j’ai vraiment compris à quel point un bon support matériel de l’alimentation pouvait être utile.
Ironiquement, la version suivante de la caméra a été réalisée par un sous-traitant avec un Raspberry Pi, et le problème d’alimentation a été compensé par une batterie beaucoup plus grosse. Résultat : je me retrouve avec un Coral dev mini et 100 caméras encore jamais déballés, et je dois soit trouver une bonne idée, soit les vendre.
J’ai cherché "google coral twitter" pour trouver le compte Twitter officiel, et le deuxième résultat était ton tweet où tu essaies de vendre 100 cartes restantes ; c’est déconcertant.
Vu qu’il est question de gérer l’alimentation avec du matériel externe, on dirait que ce n’est pas seulement un problème logiciel.
Si l’on regroupe l’application dans un initramfs lié au kernel, on peut, dans les cas simples, supprimer le besoin de monter un système de fichiers et gagner pas mal de temps.
Dans certains cas, des choses comme l’init BusyBox peuvent aussi être remplacées par un simple script bash qui n’effectue que le démarrage minimal. Monter devtmpfs, proc, sysfs, etc., et si possible abandonner glibc, vaut aussi la peine d’être envisagé.
Avant d’intégrer l’initramfs au kernel, il est utile de tester dans un chroot que toutes les dépendances nécessaires à l’application sont présentes. Si elle s’exécute dans le chroot, le kernel pourra aussi la lancer au démarrage, et les itérations de développement seront beaucoup plus rapides.
Désactiver les modules kernel et lier directement au kernel uniquement les fonctionnalités nécessaires peut encore réduire l’espace occupé et le temps de démarrage. Cela vaut aussi la peine de tester la compression zstd au lieu de gzip.
C’est le cas lorsque les premières étapes de démarrage n’exploitent pas pleinement les performances du matériel, ou lorsque l’image est grosse et qu’il vaut mieux faire autre chose en parallèle de son chargement.
Il ne reste alors qu’un initramfs contenant un seul binaire lié statiquement.
Il y a deux autres bons articles sur la réduction du temps de démarrage du Pi :
https://www.furkantokac.com/rpi3-fast-boot-less-than-2-secon...
http://himeshp.blogspot.com/2018/08/fast-boot-with-raspberry...
En m’appuyant sur ces deux articles, j’ai fabriqué un cadre photo numérique avec un Pi, et je l’ai fait démarrer très rapidement jusqu’au navigateur en mode kiosque. Quand les exigences sont très limitées, on peut obtenir des temps de démarrage assez impressionnants.
La vraie tragédie, c’est que le code GPU bootcode.bin soit une boîte noire propriétaire, sans code source.
C’est terrible que des projets de hack et de loisir doivent contenir une boîte noire secrète et cachée qu’on ne peut pas modifier.
C’est désormais un logiciel libre et open source.
https://www.theregister.com/2023/11/28/microsoft_opens_sourc...
Cela ne rend pas automatiquement tout le firmware du Pi libre et open source. Ce ne sont pas des drivers. Mais ils pourraient le faire s’ils le voulaient.
Je me demande ce qu’il y a dedans qui nécessite de le garder fermé.
J’aime l’article dans son ensemble, mais je ne suis pas convaincu par cette partie
Celle qui dit que désactiver le turbo du CPU pour économiser un peu de courant est un mauvais choix, parce que le temps supplémentaire consomme plus d’énergie que finir vite puis éteindre
En cours d’informatique, j’ai appris que la consommation électrique est proportionnelle au carré de la fréquence d’horloge. Autrement dit, doubler la fréquence quadruple la puissance
Dans ce cas, il faudrait mesurer l’écart réel de puissance pour vérifier si l’augmentation quadratique liée à la fréquence dépasse le produit de la consommation fixe par le temps de travail supplémentaire
À ce sujet, ce serait bien que le CPU du Pi fournisse des informations de consommation électrique détaillées, déductibles de la fiche technique ou exposées en temps réel via des registres
La plupart des puces ont besoin d’une tension plus élevée pour atteindre des fréquences d’horloge plus hautes, et c’est là que la relation quadratique apparaît
Mais, à ma connaissance, le Raspberry Pi n’a pas de contrôle dynamique de la tension ; donc si l’on baisse seulement la fréquence sans baisser la tension, cela ne devrait pas avoir d’effet sur l’énergie totale consommée par la commutation
Bien sûr, il existe aussi un point optimal. Si l’on overclocke trop le CPU, les performances par watt chutent trop et le race to idle ne fonctionne plus
C’est pourquoi les systèmes embarqués modernes utilisent souvent des stratégies de type « race-to-sleep » ou « race-to-halt » : exécuter la tâche très rapidement, puis éteindre la plupart des composants jusqu’au prochain événement
Si un calcul effectué en 1 seconde coûte 1 J, par exemple 1 W à 1 GHz, alors, dans un modèle parfaitement sphérique de vache, faire le même calcul en 0,5 seconde coûterait 2 J, j’imagine. Puisqu’à 2 GHz on passerait à 4 W
Mais cela ne regarde que la consommation du CPU ; si le système complet a une consommation fixe de 4 W, alors à 1 GHz on a 1 J pour le CPU et 4 J pour le système, soit 5 J au total, tandis qu’à 2 GHz on a 2 J pour le CPU et 2 J pour le système, soit 4 J
Si j’ai bien compris, cela veut dire que si la consommation du système complet est comparable à celle du CPU en mode turbo, il faut utiliser le turbo ; sinon, non ?
Impressionnant. Mais chaque fois que je lis ce genre d’article, ça me rappelle l’enregistrement que j’avais fait du démarrage de Plan 9 sur un Pi Zero : https://taoofmac.com/space/blog/2020/09/02/1900#resurrecting
Le GIF montre la sortie en temps réel
Mais savoir si cela peut charger les pilotes caméra et Wi‑Fi nécessaires au projet de l’article original, c’est une autre question
Personnellement, je trouve les temps de démarrage des distributions Linux globalement assez décevants, et le problème est bien plus marqué sur ce type de matériel peu puissant
J’ai fait des optimisations similaires sur un SBC MQ-Pro. Je le ressens aussi pas mal sur les ordinateurs portables. Les MacBook sont peut-être l’exception, mais dans tous les cas c’est agaçant
Une fois allumé, se reconnecter est instantané, mais un redémarrage prend quand même pas mal de temps
Cela dit, les distributions construisent, pour de bonnes raisons, des noyaux et initramfs très génériques, et cette combinaison ne démarre pas particulièrement vite
[0]: https://blog.davidv.dev/posts/minimizing-linux-boot-times/
Sauf en cas de configuration catastrophique, du genre NetworkManager qui attend 90 secondes un Wi‑Fi inexistant. Ma machine Linux met environ 4 secondes jusqu’à graphical.target, et l’essentiel vient de la connexion Wi‑Fi et de ntpd, qui sont tous deux en principe optionnels
Si l’on veut un démarrage vraiment rapide, il suffit de supprimer toute la configurabilité dynamique comme les couches de compatibilité du bootloader, les abstractions et l’initramfs. Mais on devient alors dépendant du fournisseur du matériel, donc ça n’en vaut pas la peine
Par exemple, mon MacBook Air 11 sous Linux affiche l’écran de connexion si rapidement que je vois à peine les logs de démarrage. D’après systemd-analyze, il met moins de 4 secondes jusqu’à la cible graphique
La clé semble être double. Ne pas utiliser d’environnement de bureau, démarrer en mode texte puis lancer startx quand c’est nécessaire, ou démarrer X avec un gestionnaire de connexion léger comme lightdm. Sans environnement de bureau, le nombre de services est réduit d’un facteur à un chiffre, ce qui diminue fortement la pression I/O au démarrage sur du vieux matériel. Même avec X lancé, le système démarré reste sous les 200 Mo
L’autre point, c’est que l’EFI stub peut accélérer les choses : https://wiki.archlinux.org/title/EFISTUB
Ma première intuition a été de me demander si on ne pouvait pas utiliser un autre cœur. A-t-on vraiment besoin de Linux pour prendre une photo et l’envoyer dans le cloud ?
Je ne suis pas spécialiste hardware, donc je me demande comment réaliser cette tâche avec un budget minimal. C’était un article intéressant
C’est peut-être seulement parce que j’ai deux ESP32-CAM sur la table en ce moment, mais je me demandais comment une configuration ESP32-CAM se comparerait. Elle serait probablement limitée à des images de 2 mégapixels, mais le temps de démarrage et la consommation électrique seraient tous deux sans doute inférieurs d’environ un ordre de grandeur
Pour les curieux, il y a des détails ici : https://components101.com/modules/esp32-cam-camera-module
En principe, c’est possible en bare metal, mais faire fonctionner les périphériques nécessaires n’est pas simple
Je me demandais pourquoi le noyau personnalisé arrivait si tard. Quand on veut optimiser, on ne part pas de LFS ou d’une distribution basée sur les sources ? Il ne me semble pas non plus que ce genre d’appareil ait absolument besoin de mises à jour logicielles autonomes
Je me demande aussi s’il est possible d’optimiser l’EFI/BIOS sur ce type d’appareil. Au moins sur mon desktop Arch Linux classique, cela représente une part importante du temps de démarrage
$ systemd-analyzeStartup finished in 10.076s (firmware) + 1.339s (loader) + 1.569s (kernel) + 2.974s (initrd) + 3.894s (userspace) = 19.854sJe ne connais pas exactement le matériel Raspberry Pi, mais beaucoup d’autres SoC embarqués ont un bootloader assez minimal qui fonctionne avec u-boot et qui est généralement très rapide. C’est particulièrement vrai si l’on met à 0 le délai d’attente d’une saisie utilisateur
Un noyau minimal avec un système busybox est bien moins pénible, et Gentoo n’est pas non plus un mauvais choix
> systemd-analyzeStartup finished in 3.259s (firmware) + 35.127s (loader) + 1.823s (kernel) + 2.927s (userspace) = 43.138s3,5 secondes, c’est impressionnant, mais si le scénario complet consiste vraiment à se connecter au Wi-Fi toutes les quelques minutes pour téléverser une image, ESP32 aurait été un bien meilleur choix en matière de consommation électrique
À moins qu’une fonctionnalité précise du module caméra pour Pi soit indispensable et absente des caméras compatibles ESP32-CAM
On dirait beaucoup trop de travail pour faire quelque chose qu’un microcontrôleur pourrait faire presque sans effort