Core dump seulement sur certains noeuds (72 à 78), différents modèles de CPU

Bonjour,

Nous avons rencontré un problème qui n'était pas facile à comprendre, que j'aimerais partager ici au cas où ça puisse être utile à quelqu'un (et au cas où des solutions existeraient). Nous avons eu de manière répétable des core dump systématiques sur un exécutable C++ "maison" compilé sur le cluster, mais seulement sur certains nœuds (cpu-node-72 à cpu-node-78).

Les erreurs sont du style:
srun: error: cpu-node-74: task 0: Illegal instruction (core dumped)

Le plantage n'est pas reproductible avec un exécutable compilé avec les symboles de débuggage, donc difficile de creuser exactement où le problème a lieu.

Après quelques heures de tentatives de débuggage, je me suis aperçu que les noeuds 72 à 78 n'étaient pas identiques aux autres :
Intel(R) Xeon(R) CPU E5-2650 v2 @ 2.60GHz

Alors que j'ai
Intel(R) Xeon(R) CPU E5-2695 v3 @ 2.30GHz
pour les autres noeuds (et Intel(R) Xeon(R) CPU E5-2620 v4 @ 2.10GHz pour le master).

Et le problème disparait si je retire l'option -march=native du compilateur. Il s'agit donc bien d'une incompatibilité des binaires compilés sur le le nœud master quand ils sont exécutés sur les nœuds 72-78. On ne peut pas exclure à la base que ça ne fait que révéler une gestion hasardeuse des pointeurs de fonctions dans mon code, mais en tout cas l'erreur est révélée par le changement de CPU.

J'avoue que je ne m'attendais pas à des incompatibilités binaires entre exécutables compilés sur des Xeon aussi similaires, mais pour information : c'est possible.

Question 1: est-ce que ces différences d'architectures sont documentées quelque part?
Question 2: faut-il renoncer aux optimisations cpu-spécifiques sur le cluster? Sinon, quel socle commun peut-on espérer? (Intel? x86?) Est-ce que l'homogénéité du hardware est dans le cahier des charges, ou est-ce qu'il est possible que la nouvelle génération de nœuds soit sur une architecture complètement différente (ARM?).
Question 3: savez-vous s'il est possible d'indiquer à slurm d'utiliser des exécutables différents en fonction des nœuds? Ou d'exploiter un espace disque non-mutualisé, spécifique au nœud? (par exemple, pour compiler avant d'exécuter?).

Deux solutions semblent fonctionner :
Solution 1: compiler sans l'option -march=native (je n'ai pas mesuré l'impact sur le temps d'exécution).
Solution 2: exclure les nœuds problématiques avec --exclude.

Le problème avec la solution 2 c'est qu'elle risque de devenir de plus en plus compliquée à mettre en place avec la croissance du cluster et la multiplication des petites différences entre les nœuds.

Bonjour Arnaud,

Merci pour ce retour intéressant.

L'option -march=native est plutôt déconseillé dès que l'on change de processeur (Optimisation de GCC - Gentoo wiki, gcc - Why is -march=native used so rarely? - Stack Overflow).
Mais j'avoue également que je suis étonnée du résultat avec ces processeurs relativement proches (Intel Xeon CPU E5-2620v4, E5-2650v2 et E5-2695v3).

Quelques éléments de réponses, ci-dessous.

Question 1: est-ce que ces différences d'architectures sont documentées quelque part?

Oui mais malheureusement pas à jours (et peut-être pas assez précis). Nous allons corriger ça.
https://ifb-elixirfr.gitlab.io/cluster/doc/cluster-desc/
https://ifb-elixirfr.gitlab.io/cluster/doc/slurm_at_ifb/#ifb-core-cluster-computing-nodes

Question 2: faut-il renoncer aux optimisations cpu-spécifiques sur le cluster? Sinon, quel socle commun peut-on espérer? (Intel? x86?) Est-ce que l'homogénéité du hardware est dans le cahier des charges, ou est-ce qu'il est possible que la nouvelle génération de nœuds soit sur une architecture complètement différente (ARM?).

Les clusters sont souvent amenés à évoluer et grandir. Il est donc difficile de garantir l'homogénéité du hardware.
Certains sites (tel que les centres nationaux, les mésocentres, etc) peuvent offrir un cluster beaucoup plus homogène puisque acheté d'un seul "bloc".
Mais dès que l'on fait évoluer le cluster, c'est difficile, voire impossible.
Dans la même idée, si le cluster de l'IFB est entièrement "Intel/x86", cela pourrait évoluer avec de nouveaux achats. Il n'y a pas de raison de se limiter à Intel/x86 (AMD est compétitif, l'architecture ARM peut offrir d’intéressante perspectives, etc).
D'ailleurs, on notera que les noeuds de calcul "GPU" sont aujourd'hui assez répandus/sollicités.

Ceci dit, il y a souvent un nombre important de machines identiques (par exemple sur l'IFB de cpu-node-1 à cpu-node-65). On peut donc assez facilement s'en "contenter" (i.e. ne pas profiter de tous les noeuds).

On note également l'idée de pouvoir sélectionner des nœuds strictement identiques lors de la soumission de jobs (via des features pas exemple).

Question 3: savez-vous s'il est possible d'indiquer à slurm d'utiliser des exécutables différents en fonction des nœuds? Ou d'exploiter un espace disque non-mutualisé, spécifique au nœud? (par exemple, pour compiler avant d'exécuter?).

Le choix de l’exécutable suivant le nœud doit être géré au niveau du script soumis (en fonction de /proc/cpuinfo, du nom ou autre).

Les espaces /tmp sont propres à chaque nœud et peuvent être utilisé en ce sens (pensez à supprimer ce qui a été déposé !).

Deux solutions semblent fonctionner :
Solution 1: compiler sans l'option -march=native (je n'ai pas mesuré l'impact sur le temps d'exécution).
Solution 2: exclure les nœuds problématiques avec --exclude.

J’aurais tendance à privilégier la solution 1 (plus générique).
Mais je serais curieux de connaître les différences dans les temps d’exécution.

1 « J'aime »

Bonjour,

Merci pour votre réponse.

L'option -march=native est plutôt déconseillé dès que l'on change de processeur

Oui oui, c'est juste que je n'avais pas réalisé qu'on changeait de processeur en fonction des noeuds.

J’aurais tendance à privilégier la solution 1 (plus générique).

Je pense que c'est en effet bien plus stable.

Mais je serais curieux de connaître les différences dans les temps d’exécution.

Je viens de vérifier (sur ma machine, je n'ai pas trouvé l'utilitaire perf sur le cluster), et j'ai -1.44% en temps d'exécution, -0.49% en nombre de cycles CPU, et -0.55% en nombre d'instructions avec un exécutable compilé avec -march=native. C'est pas zéro, mais c'est quand même un peu dans l'épaisseur du trait. Je ne pense pas que ça vaille le coup de gérer plusieurs exécutables pour un gain de 1%.

J'imagine que ça dépend pas mal du code, du compilateur, et du processeur, mais dans mon cas, le rapport coût/bénéfice n'est pas impressionnant.

Bonjour;
Il y a sur SLURM la possibilité de spécifier sur quel processeur Feature= je veux faire tourner mon code.
Par exemple sur genotoul nous avons 2 générations de noeud : ivy et broadwell:

COMPUTE NODES

NodeName=node[101-148] sockets=2 CoresPerSocket=32 ThreadsPerCor
e=1 RealMemory=257672 MemSpecLimit=4096 Feature=broadwell Weight=1
NodeName=node[001-068] Sockets=2 CoresPerSocket=20 ThreadsPerCore=1 Real
Memory=257672 MemSpecLimit=4096 Feature=ivy Weight=2

Pour y accéder (sbatch ou srun), utiliser la directive suivante :

Add the following flag to address the ivy or broadwell processor architecture :

-C,
--constraint=ivy
(old compute nodes 001 to 068)

-C,
--constraint=broadwell
(new computes nodes 101 to 148)

1 « J'aime »