Écrire son propre code : à faire, à éviter, astuces

De Wiki de Calcul Québec
Aller à : Navigation, rechercher
Autres langues :anglais 100% • ‎français 100%

Quand on doit écrire soi-même le programme servant à faire ses simulations numériques, la façon de le faire peut avoir une influence majeure sur la performance du programme et le temps nécessaire pour obtenir ses résultats. On peut obtenir un programme parallèle où chaque processus tire avantage du matériel du serveur de calcul et produit des résultats en quelques jours ou à l'inverse un programme difficilement parallélisable dont les processus inefficaces produiront des résultats dans quelques années ou décennies.

Voici quelques conseils pour éviter de se retrouver dans la deuxième situation.

Sommaire

À faire

Choisir le bon algorithme

L'algorithme doit être adapté à la taille des données. Si la taille des données peut augmenter sans bornes, il est primordial d'utiliser un algorithme dont le temps de calcul augmente lentement avec la taille. Par exemple, si on doit trier un tableau de taille nn est grand, il est préférable d'utiliser un algorithme de tri fusion (merge sort), tri rapide (quick sort) ou tri par tas (heap sort), qui ont tous une complexité algorithmique O(n log(n)), plutôt que le tri à bulles, le tri par sélection ou le tri par insertion, qui ont une complexité algorithmique O(n2).

D'autre critères que la complexité algorithmique entre en ligne de compte : la précision des résultats, la facilité à paralléliser. Pour des algorithmes de tri, on pourrait aussi ajouter la stabilité, la complexité algorithmique dans le pire cas et le traitement en place ou non.

Tirer avantage des bibliothèques logicielles existantes

Pourquoi réinventer la roue? Il existe un grand nombre de bibliothèques logicielles optimisées : des bibliothèque d'algèbre linéaire (MKL, SuperLU), de transformées de Fourier rapides (FFTW) et autres. Pour ce qui n'est pas installé sur nos serveurs, on peut consulter le guide des logiciels mathématiques disponibles (GAMS ou Guide to Available Mathematical Software).

Utiliser un langage compilé

Les langages compilés (Fortran, C/C++) sont en général plus performants que les langages interprétés (Java, Python, Perl, R). On peut généralement écrire les portions critiques dans un langage compilé et les appeler d'un langage interprété, au besoin.

Catégoriser l'algorithme

La performance d'un algorithme peut être limitée par le processeur, par l'accès à la mémoire ou par le réseau. La conduite à suivre pour l'optimiser en dépend.

Profiler une simulation représentative

Il est inutile de tenter d'optimiser une portion de code qui prend un temps négligeable. Pour s'attaquer aux portions coûteuses, il faut d'abord les identifier avec un profileur (par exemple, gprof, Tau, Open SpeedShop). On peut alors les optimiser en modifiant la façon de calculer (algorithme ou autre) ou en trouvant une façon d'appeler ces sections de code moins fréquemment.

Respecter la hiérarchie de la mémoire

En ordre croissant de taille et en ordre décroissant de performance, on peut écrire des variables en registre, en mémoire cache de niveau 1, 2 ou 3, en mémoire principale (locale ou non) et sur disque. Pour maximiser la performance, on doit réutiliser au maximum les données lorsqu'elles se trouvent en registre et en mémoire cache. Les calculs doivent donc être ordonnés avec soin dans les portions du code ou la performance est critique.

Utiliser les bonnes options de compilation

Consulter la page Compilation de code pour plus de détails.

À éviter

Calculer la même chose plusieurs fois pour économiser de la mémoire

Les serveurs de calcul de Calcul Québec ont de 16 Go à 512 Go de mémoire par nœud, selon les serveurs. Comme cette mémoire est souvent dédiée à une seule simulation à la fois, il vaut mieux s'en servir si ça peut améliorer la performance.

Utiliser tant de mémoire que le système doit écrire la mémoire sur disque (swap)

L'écriture sur disque est beaucoup plus lente que l'écriture en mémoire. Laisser la mémoire déborder sur disque est une mauvaise idée; mieux vaut choisir un serveur ou des nœuds de calcul appropriés pour sa simulation ou utiliser un algorithme moins gourmand en mémoire dans ce cas.

Écrire les résultats sur disque inefficacement ou trop fréquemment

Les systèmes de fichiers parallèles ont une latence élevée, il faut faire ses écritures en blocs pour éviter de surcharger le système de fichiers et ainsi ralentir sa simulation et parfois aussi celles des autres utilisateurs. Voir la page Utiliser l'espace de stockage.

Astuces

C'est là où les instructions sont répétées qu'un programme passe le plus de temps. On doit donc porter une grande attention aux boucles à l'intérieur du code, particulièrement s'il y a plusieurs niveaux d'imbrication.

Voici quelques astuces qui améliorent parfois la performance des boucles :

  • sortir de la boucle les calculs qui sont constants d'une itération à l'autre;
  • balayer les tableaux de façon contiguë en mémoire en bouclant sur la première dimension du tableau dans la boucle interne en Fortran (en C et C++, on boucle sur la dernière dimension du tableau dans la boucle interne);
  • éviter les tests et les entrées-sorties à l'intérieur des boucles de calcul, si possible;
  • faire du déroulement de boucle (loop unrolling) (dans certaines circonstances, c'est le compilateur qui le fait);
  • traiter les données par blocs (loop blocking) (ici aussi, le compilateur le fait pour vous dans les cas simples).
Outils personnels
Espaces de noms

Variantes
Actions
Navigation
Ressources de Calcul Québec
Outils
Partager