Table of Contents

Commit 2015-08-12

Portage MinGW

J'ai modifié le code pour pouvoir le compiler avec MinGW (Minimalist GNU for Windows), c'est-à-dire le compilateur gcc (4.8.1) sous Windows. Ce compilateur se distingue de “gcc sous Cygwin” par le fait que c'est un vrai compilateur POUR Windows. Les bibliothèques utilisables sont donc similaires à celles du Visual Studio (avec ce compilateur, on peut par exemple faire un #include <Windows.h> et appeler directement l'API Windows, contrairement à gcc sous Cygwin). MinGW crée des exécutables similaires à Visual Studio (.exe, .dll) qui ne dépendent pas d'une couche d'émulation de type Cygwin. On peut même s'amuser à mélanger l'output des 2 compilateurs; ce que je n'ai pas fait ici.

Quels sont les buts?

Ci-dessous, je liste les différents problèmes que j'ai dû résoudre pour ce portage.

Compilation des libs

Le système utilisé est “Windows XP SP3”, pour être sûr d'être sur un OS bien vieux, mais tout de même patché au maximum.

Le compilateur est MinGW. J'ai installé cette version (et pas celle-ci qui est plus récente et disponible en 64bits aussi) car elle possède un installeur très bien foutu qui ressemble à synaptic. Cette version est également fournie avec MSYS qui est un ensemble d'outils GNU compilés pour Windows qui permettent d'avoir éventuellement des Makefiles de type Unix.

C'est un peu déroutant au début: avec MinGW, on peut utiliser soit des “MinGW Makefiles” ou des “MSYS Makefiles” (ce sont les 2 choix que propose CMake).

En pratique, il faut malheureusement jongler avec les 2 systèmes, du moins quand on veut compiler les libs. En effet, une lib peut utiliser des “MinGW Makefiles” alors que la suivante utilisera plutôt des “MSYS Makefiles”. Il faut donc que MinGW et MSYS soient tous les 2 dans le PATH… sauf que mingw32-make ne fonctionne pas si sh.exe de MSYS est dans le PATH. Au lieu de jongler avec des changements de PATH, j'ai renommé sh.exe en sh.exe_ quand j'en avais pas besoin. Ca permet d'avoir accès aux programmes de MSYS tels que bison et flex tout en utilisant mingw32-make.

Cette subtilité comprise, j'ai essayé de ne compiler que ce qui était vraiment nécessaire pour Metafor en partant de l'hypothèse que je ne vais pas me casser la tête à faire une version Debug, du moins pour le moment.

Voici un résumé de la procédure pour obtenir les libs:

Pour la batterie, il ne me manque que triangle, tetgen et numpy. Mais comme je ne compte pas passer de batterie avec cette version 32 bits, je ferai ça plus tard.

Petite remarque: les fichiers .lib n'existent pas avec MinGW. Les libs statiques sont des .a et les DLLs sont des fichiers .dll qu'on peut référencer directement dans la commande de link (au lieu du traditionnel .lib).

Remarques générales

Export des singletons

Metafor définit une belle quantité de singletons qui sont, pour la plupart, des listes d'identificateurs (qui font office d'enums améliorées). C'est dans ces singletons qu'on trouve par exemple tous les codes qui définissent les propriétés des matériaux (POISSON_RATIO p. expl), ou des éléments, ou encore les divers codes de la visu, etc.

Ces listes sont instanciées dans certaines DLLs et sont utilisées un peu partout dans le code. Sous Linux, toutes les classes/fonctions instanciées dans une DLL sont automatiquement visibles par tout le code externe qui utilise cette DLL. Sous Windows, il faut explicitement “exporter” ces classes et fonction avec un __declspec(dllexport). C'est le rôle des macros MTGLOBAL_API, MTKERNEL_API, etc.

Je ne le savais pas mais lorsque les classes compilées sont des instantiations de templates, le Visual Studio fait pas mal de choses implicitement (par exemple, la classe dont dérive une classe exportée est automatiquement exportée) et, de plus, le Visual permet de modifier un template après son instanciation.

Le gcc n'est pas aussi tolérant. Autrement dit, il faut exporter explicitement toutes les classes de base des classes exportées (c'était pas fait).

Il faut aussi ne pas se tromper dans l'ordre de définition des fonctions membres des templates. Et surtout, il faut qu'aucune modification du template ne suive une instanciation ou utilisation d'un template particulier. En fait, cette deuxième condition est actuellement impossible à faire dans Metafor à cause de la classe IDListTemplate qui est définie comme dérivant d'un singleton d'elle-même (ne me demandez pas pourquoi j'ai fait ça - ça me semble absurde - mais ça marche). J'ai tout de même trouvé un moyen de contourner ce problème d'exportation en exportant explicitement la variable privée instance du singleton. Malheureusement, à ce moment, c'est le Visual Studio qui ne comprend plus rien. J'ai donc dû (temporairement jusqu'à refonte de IDListTemplate, c'est-à-dire définitivement) exporter la variable privée uniquement dans le cas de l'utilisation du compilateur MinGW (avec des #ifdef un peu pourris).

Nurbs++

Nurbs++, c'est la lib qui gère les NURBS dans Metafor. Un bien mauvais choix car elle n'est plus maintenue depuis un bon bout de temps et elle n'évolue donc pas. C'est pour cela qu'on l'a commitée dans le code source de Metafor pour garder une trace de nos modifs par rapport à la version originale.

Cette lib est basée sur des templates qui ne sont pas vraiment bien écrits et, sans surprise, j'ai rencontré le même genre de problèmes que ceux présentés ci-dessus, mis à part que cette fois les routines problématiques ne sont pas de moi et que c'est donc 10x plus difficile de comprendre ce qui est fait. Malgré tous mes efforts, je n'ai pas réussi à linker correctement Nurbs++ avec Metafor. D'après mes recherches sur Google, une solution “propre” serait possible avec le C++11 et le mot clef “export” qui n'est que partiellement supporté dans les compilateurs actuels. J'ai donc décidé de supprimer la DLL Nurbs++ et d'utiliser les templates comme on utiliserait la STL (par un simple #include du .h), sans les précompiler. Ca ne change rien à l'exécution du code. Il faut donc que toute l'implémentation se fasse dans les .h. C'est ce qui était fait à l'origine.

Cette (grosse) modif devrait permettre dans un avenir proche la compilation de Nurbs++ (et donc de Metafor puisque c'était ça qui bloquait) avec clang pour réobtenir une version MacOSX.

Math Kernel Library

J'ai profité de ce portage pour tenter une compilation de Metafor sans MKL qui coûte un pont sous Windows. On obtient donc ainsi une version compilable sans licence Intel sur n'importe quelle machine (au prix de la perte du DSS). cette modif est d'autant plus importante qu'Intel est en train de changer sa politique de licences. Il y a encore moins d'un an, il était possible de télécharger gratuitement une version “non-commerciale” de MKL pour développer sous Linux. Ce n'est plus le cas! Actuellement, seules les univs amécicaines peuvent obtenir (après justification) une licence d'un an renouvelable (après nouvelle justification). C'est donc le bon moment de permettre aux développeurs de Metafor de se passer de MKL si nécessaire.

En remplacement, j'ai choisi OpenBLAS qui est une implémentation optimisée des BLAS qui a un certain succès. OpenBLAS comprend également LAPACK.

La difficulté n'a pas vraiment été de linker avec OpenBLAS mais plutôt de trouver tous les endroits où on avait oublié de mettre des METAFOR_USE_MKL. N'oubliez donc pas de mettre ce genre de vérification autour de vos futurs appels à MKL.

Pour LAPACK, j'ai vu qu'il y avait des appels MKL dans le module d'analyse fréquentielle. Ce sera bien, à terme, d'utiliser l'interface C de LAPACK (LAPACKE) au lieu de l'interface Fortran, comme on l'a fait pour BLAS en utilisant la couche cblas. Ca permettrait d'avoir des appels plus clairs et de linker avec +/- n'importe quelle lib LAPACK sans se tracasser des conventions uppercase/lowercase, underscore ou non, etc.

Perfs?

Quelles sont les perfs de cette nouvelle version? m'a demandé Luc.

Voilà ce que ça donne sur le cas test du tube.

Pour cette première série de tests, je compare “tube” en séquentiel avec le solveur skyline sur 3 machines:

La version ubuntu est la plus rapide (67s), talonnée par la virtualbox win7-visual (71s) et loin derrière, on retrouve la nouvelle version 32 bits (102s). Petit détail amusant: le skyline est plus rapide dans la virtualbox win7 que sous ubuntu. Par contre toutes les autres routines sont plus efficaces sous ubuntu.

En ce qui concerne la nouvelle version MinGW, le code est plus lent dans toutes les phases de résolution et principalement dans l'assemblage de la matrice de raideur (buildK). Les accès mémoire ne seraient donc pas terribles? Le test consomme pourtant uniquement quelques centaines de Mo (200Mo alloué et une virtual size totale de 600Mo). J'ai également vérifié qu'il n'y avait qu'un seul thread utilisé. Je n'ai pas l'impression que le système swappe lors du calcul. Est ce donc un problème de Win XP?

Il faudra donc tester cette version sur un système récent (win7 x64 p expl). Il faudra également faire des tests de comparaison MKL/OpenBLAS sur la version win7-x64.

Dans un second temps, je me suis demandé si le solveur MUMPS était utilisable avec MinGW puisqu'on n'a plus le DSS avec cette version de Metafor, tout en sachant bien que les perfs du code compilé ainsi sont globalement mauvaises (du moins dans ma virtualbox XP). Je me suis alors rendu compte que l'utilisation du solveur MUMPS augmentait le temps CPU! (+5s!)

La série de tests suivante consiste à tester les différents solveurs sur la même machine. Par simplicité, j'ai choisi ma virtualbox win7-x64. Voilà ce que ça donne. C'est très instructif:

Il y a visiblement un (gros) problème… On voit bien que la plupart des phases qui n'impliquent pas le solveur fournissent un temps CPU +/- identique (contact, Fint, Fext, etc), Les temps varient donc uniquement dans buildK (l'assemblage) et solveK (la résolution). Logique…

Si on regarde la résolution (solve K), le DSS est incontestablement le plus rapide, mais MUMPS n'est pas si mauvais que ça (5% plus lent que DSS à peine!) et MUMPS est largement devant le skyline!

Par contre, l'assemblage MUMPS (build K) pose un gros problème! Il y a donc du boulot pour Lilia: il faut clairement optimiser cette partie du code avant toute chose, au lieu d'essayer d'optimiser la phase de résolution qui elle semble OK (du moins en séquentiel).

samcef.py

Je me suis rendu compte que la licence actuelle de SAMCEF ne donne plus accès à GHS3D. Ca a peut être été dit, mais j'avais oublié.

Conséquence: le fichier contactTetra.fdb, que j'avais commité au départ pour pouvoir lancer la batterie avec la version russe de Samcef qui ne possède pas non plus GHS3D, est donc maintenant impossible à régénérer.

Le problème c'est que la manière dont j'avais écrit samcef.py pour gérer les .fdb commités (à coté du .dat correspondant) était mal écrite. En effet, si par hasard le .dat est plus récent que le .fdb, BACON est relancé pour régénégerle .fdb. C'est très ennuyeux parce qu'en pratique, même lors d'un simple checkout, le .dat peut être plus vieux que le .fdb simplement parce que le svn checkout de Metafor a écrit le .fdb avant le .dat (c'est systématique avec ma virtualbox). Donc on subit une rebaconnade et paf, ca plante.

J'ai donc modifié samcef.py pour ne jamais rebaconner quand un .fdb est copié à côté du .dat. Pour rebaconner, il faudrait supprimer le .fdb.

Remarque: il n'y a aucune raison de copier un .fdb à côté du .dat en dehors du cadre de la batterie. De plus, le système reste intelligent: la rebaconnerie n'est jamais relancée si le .fdb du workspace est plus ancien que le .dat. Donc en pratique, le système reste flexible. Si on veut encore plus de flexibilité, il faudra utiliser des checksums MD5 comme on l'a fait pour les maillages gen4.

Remarques finales

La version MinGW a du mal à démarrer dans ma virtualbox (il faut compter entre 5 à 10 secondes pour voir apparaitre le splash screen de Metafor. Même en -nogui c'est lent au démarrage.

Ci-dessous, un aperçu d'un run du “tube” avec le Process Explorer:

On voit qu'il y a un gros travail de la part du système pour démarrer le code (la phase rouge au niveau du CPU). Est-ce dû au 32bits? à XP? à la virtualbox? Il faut que je tire ça au clair.

Pour info, voici le même graphe du même test avec MUMPS:

On voit bien que Lilia a ajouté une belle chevelure au graphe de CPU, ainsi qu'un tapis rouge tout le long du calcul. A vue de nez (je n'ai pas ouvert les routines), il y a trop d'allocations/désallocations lors du calcul.

Romain BOMAN 2015/08/12