Système entier

Avertissement : La version de perf actuellement installée sur srv-calcul-ambulant a des difficultés à nommer les threads secondaires du démon slurmctld, qui sont juste identifiés par un identifiant numérique du genre :2301. Il faut lancer perf trace en tant qu’administrateur pour que les noms soient résolus correctement. Ce comportement n’était pas observé avec d’anciennes versions de perf, 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 de perf 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 des clock_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 synchronisation pthread 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 malheureusement perf 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…

  1. Est reproductible ou de longue durée (pour avoir le temps de faire deux appels à perf trace)
  2. 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 lance perf trace via srun), 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.