Modifier facilement la fréquence processeur

Publié par cpb
Avr 30 2012

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.

3 Réponses

  1. Nicanord dit :

    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.

    • cpb dit :

      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.

      • Nicanord dit :

        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.

URL de trackback pour cette page