Turing : introduction à l'optimisation

Introduction

L'optimisation est une étape essentielle dans la vie d'un code de calcul. Celle-ci est malheureusement rarement menée à son terme, voire jamais entreprise. Le but de ce document est de vous donner une idée de ce que peut vous apporter ce type de travail et comment procéder. Dans ce domaine, il n'y a pas de recette miracle, mais de nombreuses pistes et méthodes de travail peuvent être suggérées.

Que signifie optimiser ?

Optimiser un code de calcul pour un supercalculateur tel que Turing consiste à réduire ses besoins en ressources. Ces dernières sont plurielles mais généralement on se focalise sur temps de restitution, mais la consommation de mémoire ou d'espace disque entrent également dans cette catégorie.

Pourquoi optimiser ?

Optimiser une application peut vous apporter un certain nombre d'avantages (si vos efforts sont fructueux !) :

  • obtenir plus rapidement des résultats par une diminution du temps de restitution ;
  • obtenir plus de résultats avec les heures qui vous ont été attribuées ;
  • effectuer de plus gros calculs ;
  • obtenir un avantage compétitif par rapport à d'autres équipes ;
  • mieux comprendre votre code, l'architecture de la machine et leurs interactions ;
  • découvrir et corriger des bugs grâce à la relecture des sources du code.

L'amélioration des performances d'une application permet également :

  • une réduction de la consommation d'énergie de vos calculs (les nœuds de calcul de Turing ne consomment presque rien lorsqu'ils ne sont pas utilisés) ;
  • une meilleure utilisation de la machine (le coût d'achat, de maintenance et d'utilisation d'un supercalculateur n'est pas négligeable). Chaque heure qui vous est attribuée représente une dépense pour toute la communauté scientifique ;
  • la libération des ressources pour d'autres groupes de recherche.

Pourquoi ne pas optimiser ?

Optimiser n'est pas toujours la meilleure stratégie. Tout code de calcul ne nécessite pas d'être optimisé (c'est rarement vrai pour des machines massivement parallèles), ou bien les gains peuvent être insuffisants par rapport aux efforts à fournir.

On peut citer comme raisons pour ne pas optimiser :

  • l'insuffisance de ressources ou moyens pour le faire (manque de personnel, de temps, de compétences…). L'IDRIS peut vous aider en cela ;
  • la diminution de la portabilité du code (beaucoup d'optimisations sont spécifiques à l'architecture de la machine) ;
  • la diminution de la lisibilité des sources et une maintenance plus délicate ;
  • les risque de pertes de performances sur d'autres architectures ;
  • le risque d'introduction de nouveaux bugs ;
  • un code non pérenne ;
  • un code suffisamment rapide (il ne sert à rien d'optimiser un code qui fournit déjà des résultats dans un temps raisonnable) ;
  • un code déjà optimisé.

Certaines personnes préfèrent également attendre que les machines évoluent : un calculateur plus rapide permet un gain en performances sans efforts. Ce raisonnement n'est plus valable car la tendance actuelle de l'évolution du matériel va vers un parallélisme de plus en plus massif, mais avec des coeurs à peine plus rapides. De plus, les performances pour les entrées/sorties croissent beaucoup moins vite que les puissances de calcul. Sans oublier que la durée de vie d'un supercalculateur est de plusieurs années ; il s'agirait donc d'être patient !

Quand optimiser ?

Une application ne doit être optimisée que lorsqu'elle fonctionne correctement. Il ne faut jamais commencer ce travail trop tôt (hormis pour le choix des algorithmes). En effet, le risque est grand que vous ajoutiez des bogues supplémentaires, réduisiez fortement la lisibilité et la compréhension du code et optimisiez des procédures qui seront abandonnées, totalement réécrites, ou qui seraient très peu (ou pas) utilisées par la suite. Le danger est également de passer beaucoup de temps à optimiser des parties de code qui consomment très peu de ressources : passer 3 jours pour gagner un facteur 2 dans un sous-programme représentant 0,001% du temps d'exécution n'est pas très productif.

Il ne faut donc se lancer dans l'optimisation que si vous estimez que votre application est trop lente ou ne permet pas de faire tourner vos gros calculs dans un temps raisonnable : si vous êtes pleinement satisfait de votre application, l'investissement n'est peut être pas nécessaire.

N'oubliez pas non plus qu'avant de vous lancer dans cette démarche, vous avez besoin de temps. Optimiser un code de calcul n'est pas une tâche aisée et prend beaucoup de temps (généralement beaucoup plus qu'estimé, surtout si vous n'êtes pas expérimenté). Si vous avez de nombreuses autres contraintes, ne vous lancez pas ! Il vaut mieux obtenir des résultats, même s'il faut patienter un peu, que de commencer à optimiser un code et ne plus avoir le temps de faire vos calculs.

Comment optimiser ? Méthodologie conseillée

Le profilage

Une fois que avez décidé d'optimiser votre application, ne commencez pas à modifier vos sources. Avant de commencer, il est absolument nécessaire de déterminer où l'effort doit être porté : il faut identifier les parties critiques de votre application qui nécessitent des améliorations. La plupart du temps, les sections du code consommant le plus de ressources ne sont pas celles que l'on pense : le profilage permet de vérifier (et souvent d'invalider) vos hypothèses à ce propos.

Le profilage d'une application consiste donc à déterminer les parties qui utilisent le plus de ressources. Il faut bien être attentif à le faire sur un jeu de données caractéristique de ce que vous souhaitez calculer : essayez de vous approcher le plus possible des conditions réelles d'un calcul de production pour obtenir un profil réaliste. Un certain nombre d'outils sont disponibles sur Turing; ils sont décrits dans les pages pointées ci-aprèssuivantes :

L'optimisation

Une fois le profilage effectué et les zones les plus consommatrices mises en évidence, l'optimisation proprement dite pourra commencer : il faudra évidement s'attaquer d'abord aux zones les plus gourmandes en ressources car c'est là que se trouvent potentiellement les plus gros gains. Il ne sert pas à grand chose de travailler sur les parties peu consommatrices, d'autant plus que votre temps est précieux.

L'optimisation peut se faire dans 3 domaines différents. Les liens qui suivent détaillent :

Il est très important de vérifier systématiquement que les modifications apportées ne changent pas les résultats obtenus : il est en effet déjà très facile d'introduire des bogues lorsque vous programmez couramment, mais c'est encore plus vrai dans les phases d'optimisation.

N'oubliez pas non plus de vous assurer qu'il y ait bien une amélioration des performances : il n'est pas rare que les modifications introduites produisent l'effet inverse. Très souvent, on observe des pertes de performances, ou des gains proches de zéro. Cela est généralement dû au fait que vous pouvez involontairement compliquer le travail d'optimisation du compilateur, ou encore à une mauvaise compréhension de l'effet attendu.

Dans le cas où les gains sont faibles, il faut se poser la question de l'intérêt de garder le nouveau code, surtout si la lisibilité s'en ressent : il ne faut pas hésiter à revenir à la solution initiale pour garder une application plus facile à maintenir.

Que fournit l'IDRIS ?

En plus de la documentation sur notre serveur web, nous fournissons plusieurs moyens pour vous aider :

  • des formations sur la Blue Gene/Q (cours Utilisation Turing) sont données. Bien que non axées sur l'optimisation, une description détaillée de l'architecture et de nombreux conseils y sont donnés. Une bonne compréhension du fonctionnement de Turing est indispensable pour pouvoir obtenir des gains significatifs ;
  • pour des besoins ponctuels, le Support Utilisateurs peut répondre à vos questions par email ( ) ou par téléphone au 01.69.35.85.55 ;
  • pour des demandes importantes, un service de Support Avancé peut être fourni.