5. Gestion mémoire/temps et optimisation#
Avant de parler d’optimisation, la première question que l’on se pose est comment choisir les paramètres d’exécution dans Astk: mémoire totale et temps pour que le premier lancement du calcul se termine correctement. Ensuite, une fois que le calcul sera passé une première fois, il sera toujours possible d’essayer de l’optimiser en fonction des informations que l’on aura récupérées du 1er calcul.
5.1. Première exécution d’un calcul#
La connaissance de la taille de la mémoire dont on dispose est nécessaire. La réponse dépend bien entendu de la machine d’exécution: serveur centralisé, machine locale…. Sur une machine locale, il est toujours possible d’autoriser la mémoire maximale disponible alors que sur le serveur centralisé, il peut être opportun de ne pas demander trop de mémoire sous peine de devoir attendre que la classe de job associée se libère.
Estimation de la mémoire nécessaire : en quasi-statique, la mémoire totale est généralement fonction de la taille du ou des systèmes linéaires à résoudre. Pour les éléments finis iso-paramétriques, il est assez facile d’estimer la taille globale de ces systèmes. Le nombre de degrés de liberté peut être estimé par la relation: (nombre de nœuds du maillage) x (dimension du problème (2 ou 3))
Cette relation ne prend pas en compte les ddls relatifs aux conditions aux limites dualisées. Mais généralement, cette part est faible. Si les éléments finis ne sont pas iso-paramétriques (formulations mixtes par exemple), cette estimation est plus délicate.
De toute manière, la taille des systèmes linéaires est affichée à chaque résolution dans le fichier .mess: nombre total d’équations ou la matrice est de taille n équations. Il est important de connaître ce nombre (bien qu’à lui seul, il ne soit pas toujours un indicateur totalement fiable, car la mémoire et le temps nécessaire à la résolution d’un système linéaire dépend de bien d’autres paramètres: taille de la largeur de bande, nombre de termes non nuls dans la matrice, renuméroteur… mais cela nous emmènerait trop loin). Donc si on ne sait pas estimer la taille du système linéaire, il est intéressant de lancer une première fois l’étude en mode dégradé : lecture du maillage, affectation des éléments-finis, du matériau et résolution (MECA_STATIQUE ou STAT_NON_LINE) avec le solveur direct MUMPS. Lancer l’étude simplifiée en mode interactif avec suivi interactif, puis arrêter le calcul une fois l’information sur la taille du système connue.
Dans l’idéal [1] , il faut disposer d’environ 30 Go pour faire passer un calcul à un million de degrés de liberté (cas du cube maillé en HEXA8, dont une face est encastrée par dualisation), la mémoire minimale [2] étant 9 Go. Le tableau ci-dessous donne des ordres de grandeur de la mémoire «idéale» (ou optimale) et minimale pour résoudre un système de taille donnée (avec le solveur direct MUMPS).
Nombre de degrés de liberté |
Mémoire minimale |
Mémoire «idéale» |
200000 |
1 Go |
4 Go |
400000 |
2,5 Go |
9 Go |
600000 |
4 Go |
15 Go |
800000 |
6 Go |
23 Go |
1000000 |
9 Go |
31 Go |
Une fois la mémoire nécessaire estimée, on peut lancer le calcul complet avec le solveur direct MUMPS. A la suite de ce 1er calcul, il est très intéressant de regarder:
Dans le fichier .mess la mémoire totale (JEVEUX + python + librairies externes) utilisée par le calcul: cette information est affichée à la fin du fichier: voir MAXIMUM DE MEMOIRE UTILISEE PAR LE PROCESSUS;
Dans le fichier .resu: le temps de résolution (MECA_STATIQUE / STAT_NON_LINE) par rapport au temps total du calcul.
5.2. Optimisation d’un calcul#
L’optimisation d’un calcul sur les aspects temps et mémoire est une opération intéressante mais un peu délicate car il n’existe pas de recette miracle. D’ailleurs il n’est pas toujours facile de faire la distinction entre optimisation de la mémoire et optimisation du temps de calcul, donc dans les paragraphes suivants, on donne des pistes d’optimisation «au sens large» et on renvoie vers les documents adéquats.
Le parallélisme
L’utilisation du parallélisme est certainement la solution la plus facile et la plus sûre pour diminuer le temps de calcul (et parfois aussi la mémoire nécessaire). Le parallélisme consiste à exécuter des opérations sur plusieurs processeurs en même temps. La mise en place du parallélisme n’est efficace que si le temps passé dans STAT_NON_LINE est prépondérant par rapport au temps total du calcul. Pour cela, il suffit de choisir un solveur adéquat (exemple: MUMPS, PETSC), une version MPI de Code_Aster et de spécifier le nombre de processeurs dans Astk (menu Options/mpi_nbcpu). Sur le serveur centralisé aster4, on conseille de commencer par choisir mpi_nbcpu=2, d’observer le gain en temps puis de recommencer avec mpi_nbcpu=4, puis éventuellement mpi_nbcpu=8. Il est possible de choisir mpi_nbcpu=16 mais on risque d’attendre longtemps avant que tous ces processeurs soient disponibles, donc avant que le calcul ne démarre (tout dépend de la charge machine!).
De nombreux conseils sur le parallélisme sont donnés dans le document u2.08.06. Des informations plus fines sur les temps passés dans chaque partie de la résolution sont également affichées, et peuvent servir à mieux paramétrer le calcul (temps, mémoire, nombre de processeurs…). On renvoie pour cela à la documentation u1.03.03.
Le solveur linéaire
Suivant la taille de la matrice, l’utilisation d’un solveur itératif (comme PETSC) à la place d’un solveur direct permettra un gain en temps. À titre d’information, on considère qu’un solveur itératif est plus rapide qu’un solveur direct (par défaut) à partir d’environ 200000 équations en 3d. Mais cela dépend du type de structure [3] . La contre-partie de l’utilisation d’un solveur itératif est une robustesse non garantie. Il est à noter que l’utilisation de PETSC nécessite une version MPI de Code_Aster (même si on n’utilise qu’un seul processeur). A partir d’une certaine taille de problème (quelques millions de ddls), l’utilisation d’un solveur direct devient impossible et il est indispensable d’utiliser un solveur itératif.
En cas d’échec de la résolution par un solveur itératif, les leviers d’actions sont le choix d’un autre préconditionneur (préconditionneur direct simple précision par défaut LDLT_SP, préconditionneur incomplet LDLT_INC, …), le choix d’un autre algorithme itératif (GMRES par défaut, CG, CR, …).
De nombreux conseils sur le choix du solveur sont donnés dans u2.08.03.
La gestion de la base globale
En cas de dépassement de la mémoire limite, on peut jouer sur l’archivage des résultats. L’archivage (mot-clé facteur ARCHIVAGEde STAT_NON_LINE) permet de réduire sensiblement la taille des bases en sélectionnant les instants sauvegardés. Par défaut, tout est archivé (même les instants issus du sous-découpage du pas de temps). Dans la phase de mise en place d’une étude, cela peut s’avérer utile, mais trop couteux en mémoire. L’observation et le suivi de certaines grandeurs peut répondre de manière efficace au besoin d’archivage (voir §3.4 de u2.04.01). Une fois l’étude mise en place, on peut restreindre l’archivage à un nombre limité de pas de temps (instants de post-traitement par exemple).
Pour réduire l’encombrement de la structure de données resultat, il est possible de choisir a posteriori les champs à archiver soit en indiquant les champs à conserver, soit en indiquant les champs à exclure (commande EXTR_RESU). L’extraction peut aussi se faire sur une partie du maillage ou du modèle. La commande DETRUIRE peut aussi être utilisée pour détruire un concept. Suite à ces opérations d’extraction ou de suppression, il faut spécifier RETASSAGE=”OUI” dans la commande FIN afin de récupérer effectivement l’espace disque associée à la base globale.
Pour réduire la taille des fichiers résultat au format MED (.rmed): utiliser IMPR_RESU/RESTREINT.
Dans un objectif d’une poursuite, on peut dans certains cas remplacer la base par l’impression de champs MED. La «poursuite» débutera alors par les commandes DEBUT puis LIRE_RESU.