Linux offre une possibilité intéressante : celle de configurer assez finement la vitesse du processeur depuis la ligne de commande. Il permet d’agir indépendamment (pour autant que l’architecture matérielle le permette) sur les différents coeurs des processeurs. On m’a demandé récemment une petite illustration pratique de ces possibilités : en voici un résumé.
Configurer la vitesse CPU avec /sys
La première opération nécessaire est de connaître la liste des fréquences supportées par un CPU. Cette liste est accessible grâce au pseudo système de fichier /sys
.
# ls /sys/devices/system/cpu/ cpu0 cpu1 cpu2 cpu3 cpufreq cpuidle kernel_max offline online possible present probe release sched_mc_power_savings # cat /sys/devices/system/cpu/online 0-3 # cat /sys/devices/system/cpu/possible 0-7 # cat /sys/devices/system/cpu/present 0-3 #
Ici, le noyau peut gérer sept CPU (paramètre possible
), mais il n’y en a que quatre connectés (present
). Ils sont tous actifs (online
). Choisissons, arbitrairement, le CPU numéro 1 et vérifions ses fréquences de fonctionnement.
# ls /sys/devices/system/cpu/cpu1/ cache cpufreq crash_notes online thermal_throttle topology # ls /sys/devices/system/cpu/cpu1/cpufreq/ affected_cpus related_cpus scaling_max_freq bios_limit scaling_available_frequencies scaling_min_freq cpuinfo_cur_freq scaling_available_governors scaling_setspeed cpuinfo_max_freq scaling_cur_freq stats cpuinfo_min_freq scaling_driver cpuinfo_transition_latency scaling_governor # cat /sys/devices/system/cpu/cpu1/cpufreq/scaling_available_frequencies 2400000 2133000 1867000 1600000 # cat /sys/devices/system/cpu/cpu1/cpufreq/scaling_cur_freq 2400000 #
Le CPU accepte quatre fréquences, il est actuellement configuré avec la plus rapide : 2,4GHz. On peut la modifier ainsi.
# echo userspace > /sys/devices/system/cpu/cpu1/cpufreq/scaling_governor # echo 1867000 > /sys/devices/system/cpu/cpu1/cpufreq/scaling_setspeed # cat /sys/devices/system/cpu/cpu1/cpufreq/scaling_cur_freq 1867000 #
Mesurer les effets de la vitesse CPU
Nous expliquerons la première ligne de l’exemple ci-dessus dans le prochain paragraphe.
Pour voir les effets de la configuration précédente, on peut utiliser un petit programme qui incrémente un compteur pendant une durée donnée (cinq secondes ici).
compte-iterations.c : #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/time.h> int main(void) { struct timeval tv; struct timeval tv_debut; long long int compteur; // Attendre le debut d'une nouvelle seconde gettimeofday(& tv_debut, NULL); do { gettimeofday(& tv, NULL); } while (tv.tv_sec == tv_debut.tv_sec); // Compter pendant cinq secondes compteur = 0; tv_debut = tv; do { compteur ++; gettimeofday(& tv, NULL); } while (tv.tv_sec < (tv_debut.tv_sec + 5)); printf("Resultats : %lldn", compteur); return EXIT_SUCCESS; }
Essayons de l’exécuter avec différentes fréquences.
# echo 1600000 > /sys/devices/system/cpu/cpu1/cpufreq/scaling_setspeed # taskset -pc 1 $$ pid 9778's current affinity list: 0-3 pid 9778's new affinity list: 1 # ./compte-iterations Resultats : 17263876 # ./compte-iterations Resultats : 16733090 # ./compte-iterations Resultats : 16810727 # echo 1867000 > /sys/devices/system/cpu/cpu1/cpufreq/scaling_setspeed # ./compte-iterations Resultats : 19926359 # ./compte-iterations Resultats : 19868890 # ./compte-iterations Resultats : 19681040 # echo 2133000 > /sys/devices/system/cpu/cpu1/cpufreq/scaling_setspeed # ./compte-iterations Resultats : 22517877 # ./compte-iterations Resultats : 23028619 # ./compte-iterations Resultats : 22310298 # echo 2400000 > /sys/devices/system/cpu/cpu1/cpufreq/scaling_setspeed # ./compte-iterations Resultats : 25317042 # ./compte-iterations Resultats : 25806554 # ./compte-iterations Resultats : 25916774 #
Nous voyons bien que le nombre d’itérations fluctue en fonction de la fréquence configurée.
Configurer le comportement du CPU
Plutôt qu’agir directement sur la fréquence, ce qui nécessite de lire la valeur et d’inscrire celle choisie, le noyau nous propose des comportements, des heuristiques, qu’il nomme governors, qui vont assurer la modification de la fréquence CPU afin de l’ajuster aux besoins de l’utilisateur.
On trouvera des explications plus détaillées sur les governors dans cet article. Il existe (suivant la configuration du noyau à la compilation) jusqu’à cinq governors, dont les noms sont visibles ainsi.
# cat /sys/devices/system/cpu/cpu1/cpufreq/scaling_available_governors conservative ondemand userspace powersave performance #
Leurs comportements :
- powersave : économiser la batterie (d’un ordinateur portable) en utilisant la fréquence la plus faible possible.
- performance : optimiser la vitesse de traitement en adoptant la fréquence disponible la plus élevée.
- ondemand : faire varier la fréquence en fonction de la charge système afin d’optimiser la vitesse de traitement lorsqu’il y a une forte demande, tout en économisant la batterie lorsqu’il y a peu de tâches en cours.
- conservative : adopter le même comportement que ondemand, en évitant les modifications trop fréquentes de la vitesse du CPU.
- userspace : laisser l’utilisateur fixer lui-même la vitesse qui lui convient, comme nous l’avons fait dans les paragraphes précédents.
Essayons les effets des principaux governors sur notre programme.
# echo performance > /sys/devices/system/cpu/cpu1/cpufreq/scaling_governor # cat /sys/devices/system/cpu/cpu1/cpufreq/scaling_cur_freq 2400000 # ./compte-iterations Resultats : 25932762 # ./compte-iterations Resultats : 25937413 # ./compte-iterations Resultats : 25335626 # echo powersave > /sys/devices/system/cpu/cpu1/cpufreq/scaling_governor # cat /sys/devices/system/cpu/cpu1/cpufreq/scaling_cur_freq 1600000 # ./compte-iterations Resultats : 17625101 # ./compte-iterations Resultats : 16788736 # ./compte-iterations Resultats : 17017337 # echo ondemand > /sys/devices/system/cpu/cpu1/cpufreq/scaling_governor # cat /sys/devices/system/cpu/cpu1/cpufreq/scaling_cur_freq 1600000 # ./compte-iterations Resultats : 25371803 # ./compte-iterations Resultats : 25294453 # ./compte-iterations Resultats : 25920801 #
Conclusion
Il est intéressant de pouvoir configurer facilement la vitesse du processeur indépendamment des fréquences exactes, en employant par exemple une heuristique powersave sur un portable, performance sur un serveur, ou ondemand sur un poste de travail généraliste.
Pour les systèmes embarqués, les choix ne sont pas toujours évidents, comme on peut le voir dans ces articles : Temps réel et économie d’énergie 1 et 2.
Bonjour,
Je fais des mesures de temps de latence avec cyclictest sur un noyau Linux 3.10.58 avec le patch RT. Ma cyble est un PC x86_64 4 cœurs.
Pour stresser mon système, j’utilise l’outil stress de l’ensemble rt-tests plus des pings flood venant d’un autre PC.
Je n’arrive pas à comprendre pourquoi les latences MAXIMALES quand mon système est chargé sont inférieurs (meilleurs) que celles quand mon système est libre.
Les tests ont étés réalisés pendant 48H chacun et dans les mêmes conditions.
Bonjour,
Il arrive fréquemment que les latences mesurées avec un système chargé soit meilleures que celles obtenues avec le même système au repos. En effet, lorsqu’il n’a rien à exécuter, le noyau Linux endort le processeur (voir le paragraphe « Processor states » de https://en.wikipedia.org/wiki/Advanced_Configuration_and_Power_Interface). Et dans ce cas le réveil du processeur à l’arrivée d’une interruption est plus long que si le processeur est déjà actif.
Durant mes sessions de formation, il m’arrive de montrer cet effet en comparant la durée d’un ping lorsque la machine cible est au repos et lorsqu’elle exécute une boucle active.
Bonjour,
Merci beaucoup, j’ai réactivé les ticks périodiques dans la configuration de mon noyau et le résultats semble plus logique avec des latences encore plus petites. Maintenant je suis heurté au problème dû au manque de support de driver NVIDIA pour un noyau Linux-RT car le but de faire une application temps réel qui traiterait des images en cuda sur GPU.