Rien

- Ajout du destructeur de UnionLock (ça existait pas et ça fait des new partout!)
- Suppression (par macro) de la pool utilisée pour l'allocation des ddl (classe Dof). Cette pool est impossible à désallouer. De plus son utilité est à démontrer... (surtout face à l'efficacité redoutable des get_properties)
- destructeurs de Partition, BWOptimisationMethod.
- ajout d'un protected au lieu de private dans DofSet pour la dérivation de MetaDofSet (qui vire les Dofs)
- Suppression des memory leaks de Metafor et appel complet des destructeurs avant la sortie du programme. J'ai donc créé et appelé les destructeurs des classes: ce travail m'a permis de définir avec précision quels sont les relations d'appartenances entre les différents objets. En effet, vu qu'il semble plus ou moins utopique (au point de vue purement informatique mais aussi du point de vue consommation mémoire) d'utiliser des compteurs de références dans Metafor, il est indispensable de définir clairement, pour un objet donné, les objets devant être détruits dans son destructeur. Ceci pour qu'à la fin du programme, tout soit correctement nettoyé.
Parmi les destructeurs écrits, les plus importants sont VolumeMetaElement (tous les GPStates sont correctement virés), ObjectiveFunctionSet, les classes d'intégration, Domain (était partiellement écrit), Geometry, AleMethod, AuxiliaryMeshBuilder.
- Supprimer les memory leaks (il en restait très peu) est primordial pour pouvoir lancer des longs calculs sans exploser son PC après plusieurs jours de calculs.
- Supprimer tous les objets avant de quitter le programme semble peut-être moins important au premier coup d'oeil (le système se charge de récupérer la mémoire tout seul comme dirait Igor). Ca me paraît cependant tout aussi important pour plusieurs raisons:
- la première est de définir qui est responsable de quoi. Par exemple, j'ai choisi que le Domain est responsable de la plupart de ses composants. Il demande à la géométrie et la topologie de se détruire. Idem pour les chargements, interactions, etc. La géométrie détruit les ensembles géométriques et les composants de ses ensembles. On voit ici, dans ce dernier cas, que les ensembles ne détruisent pas leurs composants. Ca permet d'utiliser les ensembles comme conteneurs d'objets et comme conteneurs de références d'objets. Certains choix que j'ai fait pourrait être discutés. Par exemple le Domain supprime les éléments et les noeuds (tous d'un seul coup). Il serait peut-être plus intéressant de laisser ce travail aux interactions qui ce sont chargé de les créer. On pourrait ainsi concevoir de supprimer sélectivement des éléments générés à partir d'une interaction particulière.
- la seconde est de permettre, plus tard, l'appel de ces destructeurs en cours de calcul pour supprimer par exemple des éléments de manière propre ou recréer une nouvelle géométrie ou un nouveau maillage (avec la topologie associée).
- la troisième est d'y voir plus clair dans purify pour détecter d'éventuels nouveaux problèmes.
- La mémoire non désallouée (en général une centaine de Ko si -novtk est utilisé) provient de plusieurs endroits:
- Python et Swig (les variables globales du module Metafor)
- Les variables statiques allouées dynamiquement dans les éléments (Dofmap, ContactmetaElement::GPSTIFF, ...) - il faudrait une fct destroy sur le même niveau que init et build pour résoudre proprement ce problème
- VTK (il faudrait vérifier les destructeurs - celui de ElementSetDrawable n'est pas complet)
- Qt (pas grand chose)
- Le système d'exploitation (des GlobalAlloc non identifiés - en général qq Ko)

- Leaks dans cont2 (kernel32 et python cachés)
- Ajout des fichiers DebugMem.h et DebugMem.cpp: ces fichiers surchargent les opérateurs new et delete et permettent de voir où s'effectuent les allocations. Il est alors possible de les repertorier (ce n'est pas encore fait) et de les analyser. la nouvelle classe MemManager se charge des allocations et affiche des messages à l'écran. Elle pourra éventuellement compter la mémoire allouée pour détecter les memory leaks sans l'aide de purify.
- Ajout des fonctions meminfo() et sizeOf() dans la plupart des classes. La fonction meminfo calcule la quantité de mémoire qui serait libérée par le destructeur de l'objet sans la taille statique de l'objet lui-même. Par exemple, pour Domain, la taille statique de l'objet vaut sizeof(Domain) et sa taille dynamique vaut sizeof(Domain)+domain.meminfo(). J'ai fait aussi une virtuelle fct qui retourne la taille statique sizeof(object) au cas où on ne possède pas le type exact de l'objet (domain.sizeOf()) - c'est utile par exemple quand on veut compter la taille des courbes dans la géométrie et qu'on a des Arcs, des NurbsCurves, etc. Pourquoi ne pas inclure la taille statique dans meminfo? simplement pour des raisons d'implémentation. Au final, j'aimerais qu'en appelant domain.meminfo() on récupère la taille totale du problème (ou au moins une estimation réaliste). J'aimerais aussi avoir une idée de la taille réelle occupée par un point ou toute autre entité géométrique pour pouvoir éventuellement la réduire (et ainsi permettre à mon PC d'avaler de plus gros cas-tests).
- Début de correction des problèmes de mémoire sur les machines Tru64: je me suis rendu compte, lors de mon dernier commit, que l'ALE avait tendance à bouffer beaucoup de mémoire sous Tru64. C'en était au point où un cas test qui consomme 100Mo sous Windows ou Linux ne passait pas sous spirou (allocation de plus de 3.5Go au total).
- J'ai d'abord cru que j'étais victime de fragmentation de mémoire lors de la création de la topologie des maillages auxiliaires ALE. En effet, j'ajoute les entités de chaque maille séquentiellement et non pas regroupées par type. Si bien que Metafor est amené à faire des séquences de new/delete sur des types de tailles différentes... La solution serait dans ce cas de créer des pools et surdéfinir les opérateurs new/delete des entités géométriques de base (points/courbes/faces/etc). Bref un gros boulot pour pas grand chose vu que tout marche très bien à par sous Tru64.
- Après plusieurs jours (!) d'essais non concluants, je me suis dit que ça pouvait venir du malloc Tru64. J'ai donc linké avec GNU malloc mais le problème n'a pas disparu.
- J'ai donc analysé les allocations en détail avec la classe DebugMem et j'ai remarqué que les allocations dans la STL Windows et Tru64 n'étaient pas identiques. Après avoir lu en détail l'allocateur par défaut des conteneurs STL Tru64 (/usr/inslude/cxx/memory), j'ai remarqué que, par défaut, Tru64 alloue un conteneur à une taille par défaut assez gigantesque (1024/sizeof(T) bytes - ce qui donne 128 éléments si T est un bête pointeur codé sur 64bits). Ce 1024 est défini par la macro _RWSTD_CONTAINER_BUFFER_SIZE (bien sûr non documentée). J'ai donc modifié cette valeur pour réduire la mémoire allouée par défaut (j'ai utilisé 32).
Voilà comment l'allocation des conteneurs STL marche (d'après mes essais):
- Si on définit un vecteur/une liste de taille n, le système alloue toujours n elems.
- Si on ne définit pas la taille initiale, le système alloue aucun elem. C'est cependant pas le cas pour un std::vector<std::list> ou chaque liste est initialisee avec 1 "insert" ce qui provoque une allocation gigantesque pour chaque liste. le problème c'est que, pour l'instant, nous utilisons ce type (vecteur de listes) pour les tables de hashage).
- Si on ajoute un elem (std::list::insert, std::vector::push_back), Windows multiplie la capacité par deux ; Tru64 fait pareil. Dans le cas ou la capacité initiale vaut 0, Windows alloue 1 elem et Tru64 alloue _RWSTD_CONTAINER_BUFFER_SIZE elems !
Fichiers ajoutés:
mtGlobal/metaStrStream.cpp
mtGlobal/MetaDebug.*
mtKernel/MetaDofSet.*
Cas-test: (le fameux à 3.5Go)
apps/ale/pstArcelor3d.py