• Aucun résultat trouvé

2.2 Problèmes numériques et Simulations scientifiques

2.2.5 Programmation et parallélisation

x

Figure 2.8 – Fonctions de base Φj de type “tente”.

2.2.5 Programmation et parallélisation

Après avoir traité un ensemble de généralités pour comprendre les simulations scien-tifiques et leur problématiques, et après avoir brièvement expliqué les trois méthodes les plus connues de résolutions numériques, nous allons aborder leur programmation sé-quentielle, puis leur programmation parallèle. Dans cette thèse l’utilisation du terme simulation scientifique fera référence à des simulations dont le phénomène réel est mo-délisé par des EDP, et dont les méthodes de résolution sont basées sur des maillages. Nous nous limitons, de plus, et dans l’état actuel du travail, à l’obtention de schémas numériques explicites stables.

2.2.5.1 Programmation séquentielle et optimisations

La programmation d’une simulation modélisée par des EDP, dont la méthode de résolution est basée sur des maillages et sur des schémas numériques explicites, a toujours la même forme. Le pseudo-algorithme de la programmation séquentielle d’une simulation est présenté dans l’algorithme 1.

La programmation séquentielle d’une simulation possède donc deux grandes diffi-cultés. La première est la programmation d’une structure de données permettant de représenter le maillage et sa connectivité. Afin que le programme soit suffisamment per-formant, cette structure de données doit permettre un accès rapide aux éléments du

Algorithme 1 : Algorithme séquentiel d’une simulation basée sur un maillage. Création du maillage µ

Création des quantités à simuler appliquées à µ Initialisation des quantités et des paramètres Définition du pas de temps : ∆t

Définition du temps maximal : tmax tant que t < tmax faire

pour chaque élément de la bordure physique : b faire Calcul des conditions limites de la simulation fin

pour chaque x ∈ Eµ faire

Obtention de σ(x, t − 1) et σ(y, t − 1), y ∈ N (x) Calcul de σ(x, t) en suivant le schéma explicite fin

t = t + ∆t fin

maillage et également à leur voisinage, afin de calculer les stencils. La deuxième difficulté est ensuite d’effectuer l’ensemble des calculs numériques de la simulation de façon effi-cace. Cette efficacité dépend en grande partie de l’efficacité de la structure de données, mais elle dépend également de la programmation et du conditionnement des calculs nu-mériques. Afin de rendre un calcul performant, il est tout d’abord important de réfléchir à l’utilisation de la mémoire cache de la machine.

Une bonne utilisation de cette mémoire cache réside dans le fait de favoriser la lecture d’éléments contigus en mémoire, et donc déjà chargés en cache, ainsi que de favoriser la réutilisation de données récemment calculées, elles aussi présentes en cache. Pour agir sur cette optimisation il est important de convenablement écrire les boucles sur les éléments du maillage, de réfléchir à la façon dont doivent être organisés les éléments de la structure de données et à l’organisation du calcul en lui-même.

La deuxième façon de rendre un calcul performant, qui peut paraître évidente, est de réduire au maximum son nombre d’opérations arithmétiques, en particulier les multi-plications et divisions, plus coûteuses en temps d’exécution. La factorisation de boucles est une façon simple de réduire le nombre d’opérations dans un calcul, mais le calcul en lui-même peut être organisé différemment afin de réduire le nombre d’opérations. Pre-nons un exemple illustré par Jedrzejewski [73]. Imaginons que dans un programme nous soyons amenés à évaluer un polynôme P (x) = ax4+ bx3+ cx2+ dx + e en un point x. Si l’on calcule au préalable les variables suivantes

α = (b − a)/2a β = (dαc/a2) + α2(α + 1) γ = (c/a) − α(α + 1) − β δ = e − aβγ

2.2. Problèmes numériques et Simulations scientifiques 33

et que ces variables sont accessibles dans le reste du programme, alors l’évaluation du polynôme P en x ne coûte plus que trois multiplications :

P (x) = [(x + α)x + β][(x + α)x + (x + γ)] + δ.

L’optimisation des codes séquentiels est une première étape pour obtenir des simula-tions performantes. Toutefois, même si les optimisasimula-tions augmentent la performance des codes séquentiels, il est très souvent nécessaire, dans le domaine du calcul scientifique, de produire des programmes parallèles.

2.2.5.2 Parallélisation

Le domaine de la simulation scientifique est largement à l’origine des besoins en performances et en programmation parallèle. Ces besoins permanents de performances sont principalement dus à trois phénomènes :

1. Tout d’abord, plus les simulations sont complexes et précises, plus le nombre de calculs numériques à effectuer est grand. Le temps de calcul d’une simulation est d’autant plus long qu’il y a de calculs à effectuer.

2. La discrétisation du temps, ou autrement dit la “durée” simulée, peut également être à l’origine d’un temps de calcul important. En effet, si l’on simule une longue durée avec un pas de temps petit, le nombre d’itérations en temps à effectuer devient important. De plus, l’ensemble des calculs numériques doit être effectué pour chaque itération de temps supplémentaire.

3. Enfin, comme nous l’avons vu précédemment, plus la discrétisation du domaine en espace est fine, plus la solution est précise, et plus le nombre de calculs à effectuer est important.

Étant donné que plus un calcul est précis, plus les résultats sont intéressants pour la communauté, et que la précision d’un calcul peut être augmenté presque sans limite (avec une taille du domaine importante, une discrétisation du domaine très précise, et un temps simulés de plus en plus longs), le besoin en parallélisme est lui aussi sans limite pour le calcul scientifique. Il devient d’ailleurs limitant pour les scientifiques, en terme de résultats, de ne pas produire des codes parallèles. De nombreux travaux décrivent la parallélisation de problèmes et de simulations numériques.

Une première façon d’appréhender la parallélisation de simulations scientifiques, est de chercher, pour chaque sous-problème numérique posé (factorisation LU, méthode de Jacobi, interpolation etc.), une parallélisation idéale et optimisée du problème. De nom-breux travaux détaillent la parallélisation précise de problèmes liés aux simulations et aux calculs scientifiques. Dans ces solutions il n’est pas rare de voir apparaître de nom-breuses optimisations et parfois le mélange de différents modèles de parallélisation dans différentes parties du code, de façon à obtenir un programme encore plus performant. Ces codes sont souvent le résultat de plusieurs mois à plusieurs années de travail, et nécessitent parfois des collaborations ou des ressources humaines importantes. Ce type de paralléli-sation est très efficace mais nécessite une longue réflexion et une bonne connaissance des

architectures et des paradigmes parallèles. Tous les modèles et paradigmes de program-mation parallèle peuvent être appliqués aux simulations numériques, des modèles induits par l’architecture mémoire jusqu’à la programmation d’architectures hybrides telles que les GPGPU, ou encore l’introduction d’instructions vectorielles dans les programmes. Toutefois, dans la plupart des cas, lorsque les ressources humaines, les collaborations ou le temps manquent, les scientifiques apprennent et utilisent un modèle de programmation parallèle facile à appréhender et à mettre en œuvre. Le livre de Bisseling [17] explique et illustre, par exemple, l’utilisation du modèle BSP et son implémentation en MPI pour paralléliser trois problèmes très importants du calcul scientifique : la décomposition LU, qui permet de décomposer une matrice comme un produit d’une matrice triangulaire inférieure L et d’une matrice triangulaire supérieure U , permettant de simplifier un sys-tème d’équations linéaires ; la transformée de Fourier, très utilisée en traitement du signal notamment ; et la multiplication d’une matrice creuse par un vecteur, qui est une opéra-tion très fréquente dans les calculs scientifiques. Dans ce cas, le modèle BSP est utilisé afin de résoudre des problèmes fréquents des mathématiques numériques, et une fois ces problèmes parallélisés, ils peuvent être utilisés dans des codes plus larges.

Une approche plus générale et plus facile de la parallélisation des simulations numé-riques est l’utilisation du modèle SPMD (Simple Program, Multiple Data). En effet, étant donné qu’une simulation scientifique est basée sur un maillage, il paraît plus simple de dé-couper ce maillage en sous-parties, et que chacune soit gérée par un processeur différent. Dans ce cas, on procède à une décomposition de domaine (notion qui sera précisément décrite plus loin) et l’ensemble de la simulation est effectué par chaque processeur sur sa partie du domaine. Cependant, puisque les schémas numériques d’une simulation scien-tifique font intervenir un voisinage N (x) pour les calculs de type stencil, il est nécessaire, dans ce type de programmes, de faire intervenir des communications entre les processeurs afin d’échanger les informations aux bords de leur sous-domaine. On définit un partition-nement du maillage µ en p parties µ0, µ1, . . . , µp−1, tel que Eµi ⊂ Eµ, Eµi 6= ∅ pour tout i, et Eµi∩ Eµj = ∅ pour i 6= j. Le programme séquentiel de l’algorithme 1 devient alors l’algorithme 2. Le parallélisme de données est donc appliqué, dans ce cas, aux simulations numériques, et ce type de parallélisation offre des accélérations proches de l’idéal. Dans cette thèse, nous nous intéressons à la parallélisation SPMD de simulations numériques, pour des architectures à mémoire distribuée et hybride de type grappe, et nous cherchons à rendre ce type de parallélisation implicite.