Table of Contents

Commit 2016/01/19

Optimisation FVTM (5 et normalement dernier)

Suite à mes commits d'optimisation précédents, le transfert de données peut se faire dans un temps raisonnable. Par temps raisonnable, je veux dire que la durée du remaillage/transfert/rééquilibrage est à présent du même ordre de grandeur (pour rester modeste) que l'intégration temporelle, en 2D mais aussi en 3D !

Exemple 2D

Voici deux petits exemples pour vous donner une idée. Si je commence par lancer le test backwardExtrusionRemeshing, qui est 2D, pour différents maillages, j'ai comme évolution :

Nb éléments Nb remaillages Temps total Temps intégration Temps transferts Temps par transfert
80 10 13.2s 8.5s 1.56s 0.16s
224 12 21.1s 12.0s 4.4s 0.37s
440 13 36s 21s 8.7s 0.67s
896 15 75s 44s 20s 1.33s
1672 21 185s 105s 51s 2.43s
3584 23 485s 283s 131s 5.70s
6864 25 1217s 744s 304s 12.2s
8800 28 1885s 1157s 460s 16.4s

Grâce au graphe, on remarque avec grand plaisir que la courbe d'intégration temporelle est au dessus de celle du transfert de données. Comme quoi, tout peut arriver.

J'ai aussi l'impression que la durée d'une étape de transfert varie linéairement avec le nombre d'éléments, ce qui est plutôt pas mal. Il faudrait vérifier pour des maillages plus gros…

La durée globale de transfert de données augmente, elle, plus que proportionnellement, mais c'est parce que plus on raffine le maillage, plus vite le critère de distorsion de mailles est vérifié et plus souvent on remaille.

Exemple 3D

A 3D maintenant :

Nb éléments Nb remaillages Temps total Temps intégration Temps transferts Temps par transfert
448 1 28s 15s 7.9s 7.9s
720 1 44s 26s 11.3s 11.3s
880 1 53s 34s 14.2s 14.2s
1792 1 133s 90s 32.6s 32.6s
2720 1 197s 141s 48s 48s
4968 2 788s 593s 161s 80s

Les observations sont similaires que dans le cas bidimensionnel. On voit bien ici le saut dans les courbes lorsqu'on passe de un à deux remaillages.

Perspectives

Continuer à optimiser commence à devenir difficile, car contrairement à la situation initiale, on n'est plus en présence d'une routine nettement plus longue que toutes les autres (LocalTransferMethod::executeTransfer()), mais plutôt à un niveau où le temps de calcul est relativement bien réparti entre différentes étapes du transfert de données. Par exemple, le transfert d'un maillage de 3600 éléments tétra prend 2m40 : 1m15 d'initialisation (toDofSet), 25s de couplage et 1m de transfert.

Initialement, cette routine qui était vraiment très longue était parallélisée. Pour y voir clair, je l'avais remise en séquentiel, mais maintenant que je ne souhaite plus optimiser le code je l'ai remise en parallèle.

Le “speedup” est nettement moins bon qu'avant. En effet, vu que cette routine occupait au départ au moins 95% du temps total du transfert, la paralléliser et lancer la simulation sur 4 coeurs réduisait le temps de transfert d'un facteur 4 ou presque. Maintenant que cette routine n'est plus l'étape lente, réaliser le transfert avec 4 coeurs ne réduit même pas sa durée d'un facteur 2. Par exemple, pour backwardExtrusion avec 2720 éléments tétra, un transfert de données se fait en 52 secondes sur un thread et en 31 secondes sur quatre, ce qui donc un “speedup” d'à peine 1.67 au lieu de 4.

Il serait intéressant de voir si d'autres routines ne pourraient pas être parallélisées, mais bon…

Résolution du problème de welding en parallèle

Nous avions remarqué il y a déjà de nombreux mois, que les cas-tests apps.welding se lançaient en parallèle même si on leur imposait explicitement de tourner sur un seul thread. Cela posait des problèmes lorsqu'on lançait la batterie, car même en –low le PC répondait très lentement pendant que ces tests-là tournaient.

Je pense avoir finalement trouvé l'origine du problème, qui apparaît en fait quand on utilise l'ALE conjointement avec le remaillage (typiquement, welding).

La première intégration temporelle et le premier transfert de données se font bien sur un seul thread. Seulement, lors du premier restart, avant de recommencer l'intégration temporelle on execute un loadFac, pour recharger la configuration remaillée. Ce loadFac va notamment appeler la fonction AleMethod::initialize(), qui est parallélisée. Comme à ce stade on est sorti de la première intégration temporelle mais pas encore rentré dans le second, il n'y a pas de tbb::task_scheduler_init init(nbt) actif. Visiblement, lorsqu'on passe par une boucle parallélisée sans scheduler_init, cette boucle se fait sur le nombre maximal de threads que le PC peut supporter. Une fois ces threads lancés, toutes les prochaines opérations parallèles se font sur tous ces threads, indépendamment des prochains scheduler_init.

Rajouter un tbb::task_scheduler_init init(nbt) au niveau de l'initialisation de l'ALE résout le problème, on va pouvoir lancer des batteries et continuer à travailler en même temps…

Je trouve personnellement très, très dommage que, lorsqu'il a codé l'ALE, Romain n'ait pas pensé qu'elle pourrait entrer en conflit avec le remaillage… (Le fait que le remaillage a été codé bien après l'ALE n'excuse évidemment rien :-P)

Divers

A la demande de Luc, renaming des “Chung Hulbert” en “Alpha Generalized”.

J'ai aussi modifié les sorties des routines de transfert de données et ma procédure de remaillage, pour essayer que ce qui est affiché à l'écran lorsqu'on lance un test soit plus compréhensible. Des avis sont les bienvenus…

J'ai aussi réorganisé les timers, également du transfert de données et ma procédure de remaillage.

Fichiers ajoutés/supprimés

A : 
R : 

Tests ajoutés/supprimés

A : 
R : 

Pierre Joris 2016/01/19