(Cet article est un extrait de la version préparatoire de mon livre « Applications temps-réel avec Linux » en cours d’écriture)
Économie ou performance : arbitrage pour le temps-réel embarqué (Partie 2)
Nous avons examiné dans le précédent billet le fonctionnement des governors, les modules qui permettent au noyau Linux de régler la fréquence du processeur, en tenant compte de certaines heuristiques (privilégier les économies d’énergie, la performance du calcul, ou s’adapter automatiquement).
Pour un système embarqué, il est habituellement important d’économiser l’énergie électrique, que ce soit pour préserver la batterie ou pour éviter d’incorporer un système de refroidissement encombrant. Durant certaines phases de fonctionnement (attente de commande utilisateur, entrées-sorties sur des fichiers, communication réseau, etc.) la réactivité et la vitesse de traitement du processeur importent peu, et il est possible d’envisager de ralentir le CPU. À d’autres moments (attente d’interruption, phase de calcul urgente…) on préférera pousser le processeur au maximum de sa puissance de traitement.
Pourquoi utiliser un governor (« powersave » ou « performance » par exemple) plutôt que de fixer directement soi-même les fréquences de fonctionnement (en remplissant /sys/devices/system/cpu/
numéro de cpu/cpufreq/scaling_cur_freq
) ? Tout simplement pour améliorer la portabilité de notre code, qui pourra ainsi évoluer sur de nouveaux processeurs sans être dépendant des fréquences absolues supportées par tel ou tel CPU.
Nous devons auparavant vérifier la réactivité du système lors d’une demande de basculement entre un governor (par exemple « powersave ») et un autre (disons « performance »). Pour cela, on peut utiliser le programme test-changement-governor.c
, qui se trouve dans l’archive suivante : https://www.blaess.fr/christophe/files/test-governors-2.tar.bz2. Il permet de lancer deux threads : le premier active, sur le processeur où l’autre thread s’exécute, un governor indiqué en argument sur la ligne de commande. Le second démarre une série de boucles actives au bout d’une seconde. Arrivé à la moitié de sa série, il notifie le premier thread qui bascule alors sur un second governor (indiqué également sur la ligne de commande). Nous pourrons ainsi observer le temps de transition.
Voici un exemple d’exécution (à faire fonctionner avec les droits root) :
# ./test-changement-governor usage: ./test-changement-governor governor_1 governor_2 # ./test-changement-governor powersave performance 0 1788 1 1778 2 1789 [...] 48 1770 49 1795 50 914 51 761 [...] 97 760 98 754 99 760 #
Ce que l’on peut représenter par le graphique suivant (en abcisse les boucles actives, en ordonnées les durées) :
Le temps de basculement est plutôt rapide, dès que le premier thread a écrit le nom du governor dans le fichier /sys/devices/system/cpu/
<numero de cpu>/cpufreq/scaling_governor
, la fréquence du cpu augmente immédiatement ce qui réduit le temps d’exécution d’une boucle active.
On peut donc très bien imaginer, dans une application temps-réel d’augmenter la fréquence CPU avant d’entrer dans une portion de code relativement critique en terme de temps d’exécution avec un :
fd = open("/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor, O_WRONLY); if (fd < 0) { perror("cpufreq"); exit(1); } write(fd, "performance", 11); /* 11 = strlen("performance") */
Puis, une fois le besoin en puissance CPU amoindri :
write(fd, "powersave", 9); /* 9 = strlen("powersave") */
NB : il faut penser à adapter le numéro de processeur dans le chemin fourni à open()
.
Avant d’employer réellement cette technique dans une application temps-réel, il est nécessaire de mesure le temps pris par la modification du governor. Pour cela nous pouvons utiliser le programme mesure-changement-governor.c
, présent dans l’archive mentionnée plus haut.
# ./mesure-changement-governor powersave performance Duree de modification : 51us #
Sur cette plateforme (Intel dual-core 2×1.86 GHz), le temps nécessaire pour modifier le governor est d’une cinquantaine de micro-secondes. Si les contraintes temporelles sont de l’ordre de la milliseconde, nous pouvons tout à fait intégrer cette variation dynamique, contrôlée par notre application. Il est important de vérifier cette durée (et l’ensemble des autres éléments que nous avons mesuré dans ces deux articles) sur la plateforme cible, pour s’assurer que l’économie d’énergie obtenue en exécutant les tâches de moindre importance à faible vitesse est conciliable avec la réactivité rapide nécessaire pour les parties critiques du système temps-réel.
tres interessant, merci