Nous cherchons à mettre à niveau le système d'exploitation de nos serveurs d'Ubuntu 10.04 LTS vers Ubuntu 12.04 LTS. Malheureusement, il semble que la latence pour exécuter un thread qui est devenu exécutable a considérablement augmenté du noyau 2.6 au noyau 3.2. En fait, les chiffres de latence que nous obtenons sont difficiles à croire.
Permettez-moi d'être plus précis sur le test. Nous avons un programme qui exécute deux threads. Le premier thread obtient l'heure actuelle (en ticks en utilisant RDTSC) puis signale une variable de condition une fois par seconde. Le deuxième thread attend la variable de condition et se réveille quand il est signalé. Il obtient ensuite l'heure actuelle (en ticks en utilisant RDTSC). La différence entre l'heure du deuxième thread et l'heure du premier thread est calculée et affichée sur la console. Après cela, le deuxième thread attend une fois de plus la variable de condition. Il sera à nouveau signalé par le premier thread après environ un deuxième passage.
Donc, en un mot, nous obtenons une communication thread à thread via une mesure de latence de variable de condition une fois par seconde.
Dans le noyau 2.6.32, cette latence est quelque part de l'ordre de 2,8-3,5 us, ce qui est raisonnable. Dans le noyau 3.2.0, cette latence a augmenté à quelque part de l'ordre de 40-100 us. J'ai exclu toute différence de matériel entre les deux hôtes. Ils fonctionnent sur un matériel identique (processeurs double socket X5687 {Westmere-EP} fonctionnant à 3,6 GHz avec hyperthreading, speedstep et tous les états C désactivés). L'application de test modifie l'affinité des threads pour les exécuter sur des cœurs physiques indépendants du même socket (c'est-à-dire que le premier thread est exécuté sur Core 0 et le deuxième thread est exécuté sur Core 1), il n'y a donc pas de rebond des threads sur cœurs ou rebondissement / communication entre les sockets.
La seule différence entre les deux hôtes est que l'un exécute Ubuntu 10.04 LTS avec le noyau 2.6.32-28 (la boîte de changement de contexte rapide) et l'autre exécute le dernier Ubuntu 12.04 LTS avec le noyau 3.2.0-23 (le contexte lent boîte de commutation). Tous les paramètres du BIOS et le matériel sont identiques.
Y a-t-il eu des changements dans le noyau qui pourraient expliquer ce ralentissement ridicule du temps qu'il faut pour qu'un thread soit programmé pour s'exécuter?
Mise à jour: Si vous souhaitez exécuter le test sur votre hôte et la version Linux, j'ai publié le code dans pastebin pour votre lecture. Compiler avec:
g++ -O3 -o test_latency test_latency.cpp -lpthread
Exécutez avec (en supposant que vous ayez au moins un boîtier double cœur):
./test_latency 0 1 # Thread 1 on Core 0 and Thread 2 on Core 1
Mise à jour 2 : Après de nombreuses recherches dans les paramètres du noyau, des articles sur les modifications du noyau et des recherches personnelles, j'ai compris quel est le problème et j'ai publié la solution en réponse à cette question.
la source
/proc/sys/kernel/*
peut fonctionner? Si vous trouvez quelque chose qui fonctionne, placez cette configuration/etc/sysctl.conf
ou un fichier/etc/sysctl.d/
pour le faire persister lors des redémarrages.Réponses:
La solution au problème de performances de réveil des threads défectueux dans les noyaux récents a à voir avec le passage au
intel_idle
pilote cpuidle deacpi_idle
, le pilote utilisé dans les noyaux plus anciens. Malheureusement, leintel_idle
pilote ignore la configuration du BIOS de l'utilisateur pour les états C et danse sur sa propre mélodie . En d'autres termes, même si vous désactivez complètement tous les états C dans le BIOS de votre PC (ou serveur), ce pilote les forcera toujours pendant les périodes de brève inactivité, qui se produisent presque toujours à moins d'un benchmark synthétique consommant tout le cœur (par exemple, stress ) est en cours d'exécution. Vous pouvez surveiller les transitions d'état C, ainsi que d'autres informations utiles liées aux fréquences du processeur, à l'aide du merveilleux outil Google i7z sur la plupart des matériels compatibles.Pour voir quel pilote cpuidle est actuellement actif dans votre configuration, il suffit de saisir le
current_driver
fichier dans lacpuidle
section ci-/sys/devices/system/cpu
dessous:Si vous souhaitez que votre système d'exploitation Linux moderne ait la latence de changement de contexte la plus faible possible, ajoutez les paramètres de démarrage du noyau suivants pour désactiver toutes ces fonctionnalités d'économie d'énergie:
Sur Ubuntu 12.04, vous pouvez le faire en les ajoutant à l'
GRUB_CMDLINE_LINUX_DEFAULT
entrée dans/etc/default/grub
puis en les exécutantupdate-grub
. Les paramètres de démarrage à ajouter sont:Voici les détails sanglants sur ce que font les trois options de démarrage:
La mise
intel_idle.max_cstate
à zéro rétablira soit votre pilote cpuidleacpi_idle
(au moins selon la documentation de l'option), soit le désactivera complètement. Sur ma boîte, il est complètement désactivé (c'est-à-dire que l'affichage ducurrent_driver
fichier dans/sys/devices/system/cpu/cpuidle
produit une sortie denone
). Dans ce cas , la deuxième option de démarrage,processor.max_cstate=0
est inutile. Cependant, la documentation indique que la définition de max_cstate à zéro pour leintel_idle
pilote doit rétablir le système d'exploitation vers leacpi_idle
pilote. Par conséquent, je mets la deuxième option de démarrage au cas où.L'
processor.max_cstate
option définit l'état C maximum duacpi_idle
pilote sur zéro, en le désactivant également. Je n'ai pas de système sur lequel je puisse tester cela, car ilintel_idle.max_cstate=0
supprime complètement le pilote du cpuidle sur tout le matériel dont je dispose. Cependant, si votre installation vous ramène deintel_idle
àacpi_idle
avec uniquement la première option de démarrage, veuillez me faire savoir si la deuxième option aprocessor.max_cstate
fait ce qu'elle a été documentée dans les commentaires afin que je puisse mettre à jour cette réponse.Enfin, le dernier des trois paramètres,
idle=poll
est un vrai porc de puissance. Il désactivera C1 / C1E, ce qui supprimera le dernier bit de latence restant au détriment d'une consommation d'énergie beaucoup plus élevée, alors n'utilisez celui-ci que lorsque c'est vraiment nécessaire. Pour la plupart, ce sera exagéré, car la latence C1 * n'est pas si grande. En utilisant mon application de test fonctionnant sur le matériel que j'ai décrit dans la question initiale, la latence est passée de 9 us à 3 us. Il s'agit certainement d'une réduction significative pour les applications très sensibles à la latence (par exemple, le trading financier, la télémétrie / suivi de haute précision, l'acquisition de données haute fréquence, etc.), mais cela ne vaut peut-être pas le coup de puissance électrique encouru pour la grande majorité des applications de bureau. La seule façon de savoir avec certitude est de profiler l'amélioration des performances de votre application par rapport àMettre à jour:
Après des tests supplémentaires avec divers
idle=*
paramètres, je l' ai découvert que la miseidle
àmwait
si votre matériel est une bien meilleure idée. Il semble que l'utilisation desMWAIT/MONITOR
instructions permette au CPU d'entrer en C1E sans qu'aucune latence notable ne soit ajoutée au temps de réveil du thread. Avecidle=mwait
, vous obtiendrez des températures de processeur plus fraîches (par rapport àidle=poll
), une consommation d'énergie moindre et vous conserverez toujours les excellentes latences faibles d'une boucle d'inactivité d'interrogation. Par conséquent, mon ensemble recommandé mis à jour de paramètres de démarrage pour la latence de réveil des threads à faible processeur basé sur ces résultats est:L'utilisation de
idle=mwait
au lieu deidle=poll
peut également aider à l'initiation de Turbo Boost (en aidant le processeur à rester en dessous de son TDP [Thermal Design Power]) et de l'hyperthreading (pour lequel MWAIT est le mécanisme idéal pour ne pas consommer tout un noyau physique tout en étant en même temps. temps en évitant les états C supérieurs). Cela n'a pas encore été prouvé lors des tests, ce que je continuerai de faire.Mise à jour 2:
L'
mwait
option idle a été supprimée des noyaux 3.x plus récents (merci à l'utilisateur ck_ pour la mise à jour). Cela nous laisse avec deux options:idle=halt
- Doit fonctionner aussi bien quemwait
, mais testez pour être sûr que c'est le cas avec votre matériel. L'HLT
instruction est presque équivalente à uneMWAIT
indication d'état avec 0. Le problème réside dans le fait qu'une interruption est nécessaire pour sortir d'un état HLT, alors qu'une écriture mémoire (ou une interruption) peut être utilisée pour sortir de l'état MWAIT. En fonction de ce que le noyau Linux utilise dans sa boucle d'inactivité, cela peut rendre MWAIT potentiellement plus efficace. Donc, comme je l'ai dit test / profil et voyez si cela répond à vos besoins de latence ...et
idle=poll
- L'option la plus performante, au détriment de la puissance et de la chaleur.la source
Peut-être que ce qui est devenu plus lent, c'est le futex, la pierre angulaire des variables de condition. Cela éclairera un peu:
puis
qui montrera les microsecondes prises pour les appels système intéressants, triées par temps.
Sur le noyau 2.6.32
Sur le noyau 3.1.9
J'ai trouvé ce rapport de bogue vieux de 5 ans qui contient un test de performance "ping-pong" qui compare
Je devais ajouter
afin de compiler, ce que j'ai fait avec cette commande
Sur le noyau 2.6.32
Sur le noyau 3.1.9
Je conclus qu'entre les noyaux 2.6.32 et 3.1.9, le changement de contexte a effectivement ralenti, mais pas autant que vous l'observez dans le noyau 3.2. Je me rends compte que cela ne répond pas encore à votre question, je vais continuer à creuser.
Edit: J'ai trouvé que changer la priorité en temps réel du processus (les deux threads) améliore les performances sur 3.1.9 pour correspondre à 2.6.32. Cependant, définir la même priorité sur 2.6.32 le ralentit ... allez comprendre - je vais l'examiner plus en détail.
Voici mes résultats maintenant:
Sur le noyau 2.6.32
Sur le noyau 3.1.9
la source
Vous pouvez également voir les processeurs cliqueter dans les processus plus récents et les noyaux Linux en raison du pilote pstate qui est séparé des états c. Donc, en plus, pour désactiver cela, vous le paramètre de noyau suivant:
intel_pstate=disable
la source