Après 26 itérations, Linux augmente la vitesse du processeur jusqu'à la vitesse d'horloge maximale, car votre processus utilise sa tranche de temps plein plusieurs fois de suite.
Si vous vérifiiez avec des compteurs de performances au lieu de l'horloge murale, vous verriez que les cycles d'horloge de base par boucle de retard sont restés constants, confirmant qu'il s'agit simplement d'un effet de DVFS (que tous les processeurs modernes utilisent pour fonctionner à une énergie plus élevée). fréquence et tension efficaces la plupart du temps).
Si vous testiez sur un Skylake avec la prise en charge du noyau pour le nouveau mode de gestion de l'alimentation (où le matériel prend le contrôle total de la vitesse d'horloge), la montée en puissance se produirait beaucoup plus rapidement.
Si vous le laissez fonctionner pendant un certain temps sur un processeur Intel avec Turbo, vous verrez probablement le temps par itération augmenter à nouveau légèrement une fois que les limites thermiques obligeront la vitesse d'horloge à revenir à la fréquence maximale soutenue. (Consultez Pourquoi mon CPU ne peut-il pas maintenir des performances optimales dans HPC pour en savoir plus sur Turbo permettant au CPU de fonctionner plus vite qu'il ne peut le supporter pour des charges de travail à haute puissance.)
Présentation d'un usleep
empêche le régulateur de fréquence du processeur de Linux d'augmenter la vitesse d'horloge, car le processus ne génère pas une charge de 100 % même à la fréquence minimale. (C'est-à-dire que l'heuristique du noyau décide que le processeur fonctionne assez rapidement pour la charge de travail qui s'exécute dessus.)
commentaires sur d'autres théories :
re :La théorie de David selon laquelle un changement de contexte potentiel de usleep
pourrait polluer les caches :ce n'est pas une mauvaise idée en général, mais cela n'aide pas à expliquer ce code.
La pollution du cache/TLB n'est pas du tout importante pour cette expérience . Il n'y a pratiquement rien à l'intérieur de la fenêtre de synchronisation qui touche la mémoire autre que la fin de la pile. La plupart du temps est passé dans une petite boucle (1 ligne de cache d'instructions) qui ne touche qu'un int
de mémoire de pile. Toute pollution potentielle du cache pendant usleep
est une infime fraction du temps pour ce code (le vrai code sera différent) !
Plus en détail pour x86 :
L'appel au clock()
elle-même peut manquer de cache, mais une erreur de cache de récupération de code retarde la mesure de l'heure de démarrage, plutôt que de faire partie de ce qui est mesuré. Le deuxième appel au clock()
ne sera presque jamais retardé, car il devrait encore être chaud dans le cache.
Le run
la fonction peut être dans une ligne de cache différente de main
(puisque gcc marque main
comme "froid", il est donc moins optimisé et placé avec d'autres fonctions/données froides). Nous pouvons nous attendre à un ou deux échecs du cache d'instructions. Ils sont probablement toujours dans la même page 4k, donc main
aura déclenché l'échec potentiel du TLB avant d'entrer dans la région chronométrée du programme.
gcc -O0 compilera le code de l'OP en quelque chose comme ceci (l'explorateur Godbolt Compiler) :garder le compteur de boucle en mémoire sur la pile.
La boucle vide conserve le compteur de boucle dans la mémoire de la pile, donc sur un processeur Intel x86 typique, la boucle s'exécute à une itération par ~ 6 cycles sur le processeur IvyBridge de l'OP, grâce à la latence de transfert de magasin qui fait partie de add
avec une destination mémoire (lecture-modification-écriture). 100k iterations * 6 cycles/iteration
est de 600 000 cycles, ce qui domine la contribution d'au plus quelques échecs de cache (~ 200 cycles chacun pour les échecs de récupération de code qui empêchent d'autres instructions d'être émises jusqu'à ce qu'elles soient résolues).
L'exécution dans le désordre et la redirection de magasin devraient principalement masquer le manque potentiel de cache lors de l'accès à la pile (dans le cadre du call
instructions).
Même si le compteur de boucles était conservé dans un registre, 100 000 cycles, c'est beaucoup.
Un appel au usleep
peut entraîner ou non un changement de contexte. Si c'est le cas, cela prendra plus de temps que si ce n'est pas le cas.