Aller au contenu

6 · Entrées-sorties et stockage

Complément de SYFP24. Ce chapitre traite les formats de données, le benchmark IO500, les couches de stockage intermédiaires, et la méthodologie de diagnostic.

Difficulté : ★★ · ⏱ 20 h

Le rapport qui explique tout

La puissance de calcul des supercalculateurs a progressé beaucoup plus vite que la bande passante de leur stockage. Un calcul qui produisait autrefois ses résultats en un temps négligeable les produit aujourd'hui en une fraction significative de son temps total.

Conséquence pratique : sur un code de production à grande échelle, les entrées-sorties consomment couramment 10 à 50 % du temps total, et c'est souvent le premier poste d'optimisation disponible — parce que personne ne s'en occupe.

Les deux régimes, rappel

Le point central de SYFP24, assez important pour être répété.

Régime bande passante Régime métadonnées
Profil Peu de fichiers, très gros, gros blocs Beaucoup de fichiers, petits
Goulot Bande passante agrégée des serveurs de données Le serveur de métadonnées, souvent unique
Levier principal Striping, E-S collectives, alignement Réduire le nombre de fichiers
Antipatron Striping par défaut sur un fichier de 100 Go Un fichier par processus à 10 000 rangs

La majorité des désastres viennent d'un code écrit pour le premier régime qui se retrouve dans le second en montant en échelle, sans que personne ne l'ait vu.

Les formats de données scientifiques

Choisir le format est une décision d'architecture qui se paie pendant des années.

Format Nature Points forts Limites
Texte (CSV, ASCII) — Lisible, universel 3 à 5× plus gros, 10 à 50× plus lent, perte de précision
Binaire brut Aucune structure Rapide, simple Aucune métadonnée, non portable (boutisme, alignement), illisible dans six mois
HDF5 Hiérarchique, autodécrit Standard de fait en simulation, mode parallèle sur MPI-IO, métadonnées riches, compression Complexe, un seul fichier = point de défaillance unique
NetCDF-4 Au-dessus de HDF5 Standard en météorologie et climat, conventions établies (CF) Moins souple que HDF5 brut
Parquet Colonnaire Excellent pour les requêtes analytiques, compression forte Mauvais pour les tranches multidimensionnelles
Zarr Tableaux en blocs, un objet par bloc Adapté au stockage objet et au cloud, écriture parallèle sans verrou Beaucoup de petits objets
ADIOS2 Abstraction d'E-S Conçu pour l'exascale, streaming entre codes couplés, plusieurs moteurs Moins répandu, courbe d'apprentissage

La recommandation par défaut

Pour un code de simulation : HDF5, en mode parallèle, un fichier par instant de sauvegarde, avec des métadonnées complètes (version du code, hash Git, paramètres, unités, date, machine).

Pour des données destinées à l'analyse statistique ou à l'apprentissage : Parquet.

Pour un code exascale ou un couplage entre plusieurs codes : regarder ADIOS2.

Pour du texte : jamais, sauf pour un fichier de configuration.

Les E-S collectives, et pourquoi elles gagnent

Le mécanisme central de MPI-IO, et la raison pour laquelle il existe.

Quand N rangs écrivent chacun une petite partie non contiguë d'un grand tableau, les écritures individuelles sont désastreuses : de nombreuses petites requêtes dispersées, avec des conflits de verrous sur les mêmes blocs du système de fichiers.

L'optimisation, appelée two-phase I/O, fonctionne ainsi :

  1. Phase de communication : les rangs s'échangent les données pour que chacun détienne une portion contiguë du fichier final. Un sous-ensemble de rangs, les aggregators, collecte les données.
  2. Phase d'écriture : seuls les aggregators écrivent, en grosses requêtes contiguës et alignées.

Le coût est une communication MPI supplémentaire ; le gain est un facteur qui atteint couramment dix à cent sur un système de fichiers parallèle. C'est pour cela qu'on écrit MPI_File_write_all et non MPI_File_write.

Les paramètres à connaître : le nombre d'aggregators et la taille de tampon d'agrégation sont réglables par hints MPI (cb_nodes, cb_buffer_size, striping_factor, striping_unit), passés dans un MPI_Info. Les grands centres documentent les valeurs recommandées pour leur machine.

Le benchmark IO500

Devenu la référence pour mesurer un système de stockage HPC, et présent dans le paysage des compétitions.

Sa particularité est de mesurer les deux régimes dans un seul score composite :

  • la partie bande passante utilise IOR dans plusieurs configurations : écriture et lecture, en easy (motif favorable, fichier par processus, alignement idéal) et en hard (motif défavorable, fichier partagé, décalages non alignés) ;
  • la partie métadonnées utilise mdtest : création, statistique, suppression de fichiers, également en easy et en hard.

Le score final est une moyenne géométrique, ce qui interdit d'optimiser une seule dimension : un système excellent en bande passante et mauvais en métadonnées obtient un score médiocre. C'est un choix de conception délibéré et pertinent.

Pour une équipe de compétition : savoir installer et exécuter IO500 est une compétence à acquérir à l'avance. Le règlement du benchmark est précis et les soumissions publiques contiennent les configurations, ce qui en fait une source d'apprentissage.

Les couches de stockage intermédiaires

L'écart croissant entre la vitesse des nœuds et celle du stockage principal a conduit à insérer des couches intermédiaires.

Les burst buffers. Une couche de stockage rapide, à base de mémoire flash, placée entre les nœuds de calcul et le système de fichiers principal. Un code écrit ses sauvegardes sur le burst buffer à pleine vitesse, et les données sont recopiées en arrière-plan vers le stockage permanent. Le gain est réel sur les codes qui écrivent par rafales — ce qui est le cas de la plupart des codes à points de reprise.

Le stockage local aux nœuds. Certains nœuds disposent de disques NVMe locaux. Utiles pour les fichiers temporaires, les données de travail, ou comme cache. Le piège : ces disques ne sont pas partagés, donc pas visibles par les autres nœuds, et ils sont vidés à la fin du travail.

Les hiérarchies gérées. Des systèmes comme DAOS (conçu pour la NVMe et le stockage objet) ou les fonctionnalités de gestion de niveaux de Lustre permettent de placer automatiquement les données chaudes sur le support rapide et les données froides sur le support lent.

La conséquence pour un développeur : il faut savoir quels espaces de stockage existent sur une machine, lequel est rapide, lequel est persistant, lequel est purgé, et quels quotas s'appliquent. Les guides utilisateurs des centres documentent cela systématiquement, et c'est la première chose à lire.

La méthodologie de diagnostic

En cinq étapes, dans l'ordre.

1. Mesurer la part des E-S dans le temps total. Sans cela, on optimise peut-être 3 % du temps. Instrumentez les phases.

2. Profiler avec Darshan. C'est l'outil décisif : il s'insère par préchargement de bibliothèque, sans recompilation, et produit un rapport complet — nombre de fichiers, tailles de requête, séquentialité, déséquilibre entre rangs, temps passé en E-S par rang.

3. Identifier le régime. Bande passante ou métadonnées ? La réponse détermine tout le reste.

4. Formuler une hypothèse chiffrée avant de modifier. « En passant de 4 096 fichiers à un fichier partagé avec un striping de 16, je devrais passer de 200 Mo/s à environ 3 Go/s. »

5. Mesurer, et expliquer l'écart.

Mesurer une E-S honnêtement

Le cache de page du système d'exploitation rend la seconde lecture d'un fichier quasi instantanée. Une mesure d'E-S qui ne tient pas compte de cela est sans valeur.

Trois remèdes :

  • vider les caches entre les mesures : sync && echo 3 | sudo tee /proc/sys/vm/drop_caches (nécessite les privilèges, donc rarement possible sur un cluster partagé) ;
  • utiliser un volume de données beaucoup plus grand que la mémoire du nœud ;
  • ouvrir avec O_DIRECT, qui contourne le cache — mais impose des contraintes d'alignement.

La troisième est la plus propre pour un banc d'essai, la deuxième la plus simple en pratique.

Les points de reprise

Sujet connexe et important : un calcul de plusieurs jours sur des milliers de nœuds sera interrompu. La probabilité qu'aucun composant ne défaille pendant plusieurs jours sur dix mille nœuds est faible.

L'arbitrage fondamental : plus on sauvegarde souvent, moins on perd de travail en cas de panne, mais plus on passe de temps à sauvegarder. Il existe un intervalle optimal, donné approximativement par la formule de Young puis affinée par Daly :

\[ \tau_{\text{opt}} \approx \sqrt{2\,C\,M} \]

où \(\tau_{\text{opt}}\) est l'intervalle optimal entre deux sauvegardes, \(C\) le coût d'une sauvegarde et \(M\) le temps moyen entre pannes, tous en secondes.

Exemple numérique. Avec une sauvegarde coûtant \(C = 60\) s et un temps moyen entre pannes de \(M = 10\) h \(= 36\,000\) s :

\[ \tau_{\text{opt}} \approx \sqrt{2 \times 60 \times 36\,000} \approx 2\,078\ \text{s} \approx 35\ \text{minutes} \]

Sauvegarder toutes les cinq minutes gaspillerait du temps ; toutes les trois heures en perdrait trop en moyenne.

Les techniques d'atténuation : sauvegarde asynchrone (écriture en arrière-plan pendant que le calcul continue), sauvegarde multi-niveaux (une copie en mémoire d'un nœud voisin pour les pannes légères, une copie sur disque pour les pannes lourdes — c'est le principe de la bibliothèque SCR, Scalable Checkpoint Restart), et compression.

Exercices

E1 · ★ ⏱ 2 h — La taille de requête. Écrire 1 Go par appels write de taille croissante, de 1 octet à 16 Mo. Tracer le débit. Refaire avec fwrite (tamponné par la libc) et comparer : la libc masque partiellement le problème des petites requêtes, ce qui explique pourquoi certains codes s'en sortent par accident.

E2 · ★★ ⏱ 3 h — Les deux régimes, mesurés. Écrire 10 Go en un fichier, puis en cent mille fichiers. Mesurer l'écriture, puis un ls -l, un du -sh, et la suppression. Sur un système de fichiers parallèle, l'écart est spectaculaire. Consigner les quatre chiffres.

E3 · ★★ ⏱ 3 h — IOR et mdtest. Installer et exécuter IOR en mode fichier partagé et fichier par processus, puis mdtest, pour 1, 4 et 16 processus. Reproduire approximativement la structure des tests IO500 et calculer un score composite maison.

E4 · ★★★ ⏱ 4 h — Les quatre stratégies d'écriture. Sur le stencil 2D MPI de PRPA23, implémenter les quatre stratégies : un fichier par rang, rassemblement sur le rang 0, MPI_File_write_all avec une vue par subarray, et Parallel HDF5 avec hyperslabs. Mesurer les quatre pour 4, 16 et 64 rangs. Mesurer aussi la relecture par un nombre différent de processus — c'est le test qui distingue un format utilisable d'un format jetable.

E5 · ★★★ ⏱ 3 h — Darshan sur un code inconnu. Exécuter un code de simulation que vous n'avez pas écrit sous Darshan et produire le rapport. Répondre par écrit à cinq questions : combien de fichiers, quelle taille de requête médiane, quelle proportion d'accès séquentiels, quelle part du temps total en E-S, existe-t-il un rang qui écrit pour tous les autres.

E6 · ★★★ ⏱ 3 h — L'intervalle de sauvegarde optimal. Mesurer le coût réel d'une sauvegarde de votre code, estimer un temps moyen entre pannes (les guides des centres donnent parfois cette information), calculer l'intervalle optimal par la formule ci-dessus, puis simuler numériquement le temps total d'exécution attendu pour différents intervalles afin de vérifier que l'optimum est bien là.

E7 · ★★★★ ⏱ 4 h — Parquet contre HDF5 contre Zarr. Écrire un même jeu de données scientifique dans les trois formats et mesurer : taille sur disque, temps d'écriture, lecture complète, lecture d'une seule variable, lecture d'une tranche spatiale, lecture depuis un stockage objet si accessible. Produire un tableau de recommandation par cas d'usage.

Ressources

Priorité 1 :

  • La documentation Lustre (Lustre Operations Manual), chapitres architecture et réglage des performances.
  • Les guides d'E-S des grands centres : NERSC (docs.nersc.gov) est particulièrement pédagogique ; l'IDRIS documente les espaces disque de Jean Zay en français.
  • La documentation du HDF Group, y compris sur le Parallel HDF5.

Priorité 2 :

  • IO500 (io500.org) : règlement, listes, soumissions avec configurations.
  • IOR et mdtest (github.com/hpc/ior).
  • Darshan (wordpress.cels.anl.gov/darshan/).
  • Le tutoriel Parallel I/O in Practice, donné régulièrement à la conférence SC par Argonne et le NCSA. Les diapositives sont publiques et excellentes : c'est le meilleur document unique sur le sujet.

Priorité 3 :

  • ADIOS2 (adios2.readthedocs.io) et les articles associés.
  • Zarr (zarr.dev) et Apache Arrow / Parquet.
  • SCR (Scalable Checkpoint Restart, Lawrence Livermore) et la littérature sur la tolérance aux pannes : Young (1974) et Daly (2006) pour l'intervalle optimal.
  • Sur DAOS et les burst buffers : chercher les articles de conception correspondants.
  • Ghemawat, Gobioff, Leung, « The Google File System », SOSP 2003, pour le contraste avec l'approche big data.

À retenir

Les six règles des entrées-sorties

  1. Jamais un fichier par processus à grande échelle. Jamais.
  2. Jamais de rassemblement sur le rang 0 non plus : les deux extrêmes sont mauvais.
  3. E-S collectives (MPI_File_write_all, Parallel HDF5), qui déclenchent l'optimisation en deux phases.
  4. Grosses requêtes, contiguës, alignées. La taille de requête est le premier facteur.
  5. Régler le striping selon le régime : large pour un gros fichier partagé, étroit pour beaucoup de petits fichiers.
  6. Profiler avec Darshan avant de toucher au code. L'intuition est systématiquement fausse sur les E-S.

Chapitre suivant : Build et environnement.