Système entier
Avertissement : La version de
perf
actuellement installée sursrv-calcul-ambulant
a des difficultés à nommer les threads secondaires du démonslurmctld
, qui sont juste identifiés par un identifiant numérique du genre:2301
. Il faut lancerperf trace
en tant qu’administrateur pour que les noms soient résolus correctement. Ce comportement n’était pas observé avec d’anciennes versions deperf
, et il est possible qu’il disparaisse à nouveau à l’avenir, mais en attendant, je garde les captures d’écran de l’ancienne version, qui sont plus informatives. Donc ne vous étonnez pas si vos propres exécutions deperf stat
ont des sorties texte différentes et moins claires.
Introduction
Nous l’avons mentionné précédemment, perf trace
ne représente pas une avancée
majeure par rapport à strace
pour suivre les appels système d’un processus
unique. Son intérêt principal par rapport à strace
est de donner accès à des
informations inaccessibles via ce dernier :
- L’activité du système entier
- L’activité noyau à grain plus fin que les appels système
Commençons par le suivi de l’activité du système entier. Celui-ci, qui peut être
déclenché avec le flag --all-cpus
, qui s’abbrévie en -a
, peut être riche en
enseignement. Mais il y a certaines précautions à prendre quand on a recours à
cette approche.
Pour comprendre pourquoi, essayons d’abord de le faire naïvement pendant un bref
instant. Notez au passage l’utilisation d’une commande sleep
pour limiter la
durée du suivi, cette astuce est applicable à de nombreuses commandes perf
.
srun --pty \
perf trace -a \
sleep 0.001
Sortie de perf trace
Vous ne vous attendiez peut être pas à une sortie aussi volumineuse en surveillant l’activité d’un système Linux minimaliste pendant une milliseconde. Et en effet, comme ma formulation précédente vous l’aura fait deviner, quelque chose s’est mal passé.
Un examen rapide de la sortie de perf trace
permettra de comprendre la nature
du problème. En agissant trop naïvement, nous avons violé une règle d’or du
suivi de l’activité système :
Tu ne provoqueras pas, une fois par événement système surveillé, un événement système que tu surveilles également.
Ici, à chaque fois qu’un appel système est observé, perf trace
écrit une
sortie dans la console. Cette écriture console nécessite un appel système,
lui-même détecté par perf trace
, ce qui déclenche une autre écriture console,
et donc un autre appel système… Une boucle infinie s’amorce, générant un
énorme volume d’activité système qui masquera tout ce qu’on aurait pu vouloir
observer d’autre que l’activité de perf
.
Vous devrez garder en tête ce principe à chaque fois que vous utiliserez les
fonctionnalités de perf
pour surveiller l’activité système de façon
exhaustive. Le piège ci-dessus peut vous sembler grossier après coup, mais
parfois il sera plus subtil, par exemple si vous surveillez l’activité disque ou
système de fichier avec une commande comme perf record
qui génère de
l’activité disque.
Mais dans notre cas simple d’un suivi global des appels système, perf trace
fournit plusieurs manières de contourner le problème.
Approche par résumé et détails ciblés
Une première approche est de commencer par demander un résumé des appels système
observés pendant une certaine durée avec l’option --summary
, qui s’abbrévie en
-s
et désactive la sortie “live” de perf trace
…
srun --pty \
perf trace -a -s \
sleep 5
Sortie de perf trace
…puis de relancer perf trace
en ne suivant que les appels système qui,
d’après le résumé, sont appelés particulièrement souvent ou bloquent
l’application pour une durée particulièrement longue. On peut effectuer ce
filtrage avec l’habituelle option --event
/-e
:
srun --pty \
perf trace -a -e futex,clock_nanosleep,epoll_wait \
sleep 3
Sortie de perf trace
Nous remarquons ainsi plusieurs choses intéressantes :
- Le démon
slurmctld
(un des composants de l’implémentation de Slurm) semble avoir une stratégie de synchronisation basée sur l’attente active, car il fait desclock_nanosleep
de 100ms avec une régularité de métronome, et ne fait aucun autre appel système aussi fréquemment. Il est probable qu’à cet intervalle de temps, il communique avec d’autres composants de Slurm par un canal autre que le noyau Linux, peut-être des opérations atomiques en mémoire partagée. - Le démon
sstate
(un autre composant de Slurm) semble utiliser une stratégie extrêmement agressive pour synchroniser ses threads. En effet, toutes les secondes, il fait des salves extrêmement rapides et prolongées d’appels àfutex()
, la brique de base d’implémentation des primitives de synchronisationpthread
sous Linux. Deux futex sont utilisés:- L’un d’eux, situé à l’adresse mémoire 0x55558d761da0, est utilisé pour réveiller un thread toutes les 20µs. Cette fréquence semble étonamment élevée.
- L’autre, situé à l’adresse mémoire 0x55558d761d8c, est utilisé pour
attendre une notification qui ne vient pas, encore une fois toutes les
20µs. Il semblerait qu’un timeout soit utilisé, mais vu la fréquence
d’appel celui-ci est probablement trop court pour servir à quelque chose.
Cependant, de temps en temps, un timeout plus long de 1s est utilisé.
On voit que le pointeur vers la structure
timespec
associé est toujours le même, ce qui suggère qu’elle a été modifiée en place, mais malheureusementperf trace
ne déréférence pas les pointeurs donc on ne peut pas en savoir plus sur les valeurs de timeout utilisées avec cet outil-là.
- Il arrive occasionnellement à
perf trace
de se tromper sur la durée d’un appel système et sortir une valeur franchement aberrante. Je n’ai pas d’explication à ce phénomène à l’heure actuelle, je constate juste qu’il est heureusement assez rare pour peu gêner en pratique.
Cette approche d’analyse d’activité système (combiner un premier résumé des appels systèmes avec une étude plus détaillée de ceux qui semblent intéressants) fonctionne bien tant que le phénomène que nous cherchons à observer…
- Est reproductible ou de longue durée (pour avoir le temps de faire deux
appels à
perf trace
) - N’implique pas un des appels système utilisés par
perf trace
.
Exercice : L’option --summary
/-s
peut aussi être intéressante pour
faire une analyse “haut niveau” de l’activité des commandes/processus
individuels.
Reprenez la commande tree /usr >/dev/null
étudiée plus haut, et utilisez cette
option pour faire un résumé de ses appels système. Remarquez la plus grande
clarté du résultat, mais aussi l’impact moins grand de perf stat
sur les
performances de la commande dans cette configuration.
Vous pouvez aussi tester et comparer l’équivalent strace
de cette option,
strace -c
. Si vous appliquez les deux utilitaires au processus bash
(avec
l’option -f
pour strace
), vous constaterez que leur comportement diffère un
peu : perf trace
fournit des statistiques pour chaque processus, alors que
strace
ne fournit qu’un total accumulé sur tous les processus enfants.
Approche par spécialisation des CPUs
Cette seconde approche nécessite un peu plus de préparation, mais elle vous
offrira en contrepartie une sortie plus “épurée” et focalisée sur l’information
que vous recherchez, ainsi qu’un plus faible impact de perf trace
sur les
performances des programmes étudiés.
L’idée est d’utiliser un outil comme taskset
pour isoler les processus que
vous voulez étudier sur certains coeurs CPU dans un premier temps, puis pour
lancer perf trace
sur d’autres coeurs CPU dans un second temps, en lui
indiquant de ne surveiller que les coeurs CPU où les tâches isolées se trouvent.
Cela peut être fait avec l’option --cpu
/-C
, comme dans perf stat
.
Sur srv-calcul-ambulant
, une partie du travail a déjà été fait pour vous,
puisque les tâches interactives et services systèmes sont paramétrés pour
s’exécuter sur les coeurs CPU 0,1,18 et 19 tandis que les jobs Slurm s’exécutent
sur les coeurs CPU restants (2-17 et 20-35).
Vous pouvez donc, sans préparation supplémentaire, surveiller l’activité système des coeurs de benchmark quand vous lancez un job Slurm, comme ceci :
# Dans un shell
perf trace -C 2-17,20-35
# Dans un autre shell
srun true
Il y a cependant deux limites importantes à cette approche :
- Vous voyez également l’activité système associée aux jobs Slurm de tous les
autres utilisateurs. Si vous êtes malchanceux, quelqu’un d’autre va faire un
srun
au même moment et compliquer grandement l’interprétation de votre mesure. - Vous prenez du temps CPU sur une session interactive pour faire fonctionner
perf trace
, alors que les ressources CPU associées aux sessions interactives sont très limitées.
Je vous recommande donc de la réserver aux situations où l’approche précédente est inapplicable.
Approche par filtrage de l’activité associée à perf
Une dernière manière d’éviter de suivre les appels systèmes associés à
l’exécution de perf trace
avec perf trace
est de demander à celui-ci de les
ignorer avec l’option --filter-pids
, qui prend en paramètre une liste de PIDs
(identifiants de processus) séparés par des virgules et fait en sorte que ni
l’activité de ces processus, ni celle de perf
ne sera présente dans la sortie
de perf trace
.
Pour alléchante que soit cette dernière approche, elle a plusieurs problèmes :
- Dès que la sortie de
perf trace
suit un trajet compliqué à travers le système (comme c’est le cas lorsqu’on la redirige ou quand on lanceperf trace
viasrun
), le nombre de processus à exclure augmente, et la représentativité des mesures diminue d’autant. - Les PIDs des processus à exclure ne sont pas forcément connus d’avance, et les déterminer peut nécessiter des acrobaties shell complexes.