Metafor

ULiege - Aerospace & Mechanical Engineering

User Tools

Site Tools


commit:2014:09_03

Commit 2014-09-03

C++11

Suite à la mise à jour des machines linux, nous pouvons maintenant utiliser des compilateurs plus récents qui proposent des nouvelles fonctionnalités liées à la norme [http://fr.wikipedia.org/wiki/C%2B%2B11|C++11]]. Ces fonctionnalités sont, pour ainsi dire, toutes liées à une augmentation de la lisibilité du code (simplifications d'écriture) et une amélioration du typage des objets pour rendre la programmation plus sûre (conversions implicites plus difficiles).

Malheureusement, toutes les fonctionnalités C++11 ne sont pas implémentées dans les compilateurs actuels, même les plus récents (voir ici pour une comparaison VS11 et gcc). Pour Metafor, on doit donc se limiter à un sous ensemble de fonctionnalités pour rester portable. En pratique, parmi les compilateurs qu'on utilise, c'est le visual studio qui est le plus à la traîne, donc on va se limiter à ce que propose Microsoft concernant le C++11. Vous allez voir: c'est déjà pas mal du tout!

J'ai trouvé un article assez bien rédigé qui présente cet ensemble de nouveautés. Ci-dessous, je vous résume cet article dans le “cadre Metafor”:

auto

Il est devenu possible, en C++11, lors de la déclaration d'une variable, de ne pas spécifier son type. On utilise dans ce cas le mot-clef auto. Par exemple:

auto i = 0;  // i est un 'int'

Le but est de raccourcir les déclarations dont le type est très long. Petit exemple tiré de Gen4:

std::list<Node *>::iterator itn;
for(itn=line.nodes.begin(); itn!=line.nodes.end(); ++itn)
    (*itn)->container = &side;

devient

for(auto itn=line.nodes.begin(); itn!=line.nodes.end(); ++itn)
    (*itn)->container = &side;

Conclusion: dans Metafor, on pourrait utiliser auto pour déclarer des itérateurs ou des types un peu bizarres, longs à écrire (templates). Evidemment, utiliser auto pour toutes les déclarations est possible mais ce ne sera autorisé que dans Metalub!

nullptr

Le pointeur NULL peut s'écrire maintenant nullptr. C'est un objet qui a son propre type, qui n'est pas un int. Le but est de rendre la conversion NULLint plus difficile.

Conclusion: dans Metafor, tous les pointeurs NULL devraient pouvoir être remplacés par des nullptr. Seule exception: les appels vers des libs qu'on ne contrôle pas (Qt, VTK, BLAS, etc).

''begin''/''end'' non membre

Lors d'une boucle sur un container STL, Stroustrup conseille d'utiliser std::begin() et std::end() dans les arguments du for pour extraire les bornes du container. La boucle de mon exemple précédent deviendrait:

for(auto itn=std::begin(line.nodes); itn!=std::end(line.nodes); ++itn)
    (*itn)->container = &side;

Ca permet de surdéfinir std::begin et std::end pour définir des itérations plus complexes, sans pour autant dériver la classe du container.

Dans Metafor, on pourrait peut être utiliser ceci pour éliminer le code des itérateurs complexes (ElementSet p.expl.) - à regarder de plus près… plus tard.

Fonctions lambda

Comme en python, on peut écrire des fonctions lambda. Ça sert à quoi? L'utilisation la plus courante est d'éviter de créer une classe pour définir un objet-fonction qu'on ne va utiliser qu'une seule fois par la suite. Une application directe est l'utilisation des fonctions TBB pour paralléliser des boucles (le corps de la boucle peut être une fonction lambda, ce qui raccourcit l'écriture).

La boucle de l'exemple précédent devient par exemple:

std::for_each(std::begin(line.nodes), std::end(line.nodes), 
    [&](Node *n) { n->container = &side; });

Dans Metafor, ces fonctions lambda vont permettre de nettoyer la plupart des boucles de type std::for_each qui utilisent aujourd'hui des atrocités du type std::bind2nd(std::mem_fun_ref(…) et les boucles TBB.

Range-based for (boucle "à la python")

Après vous avoir expliqué comment utiliser proprement et élégamment des itérateurs, je vous explique maintenant qu'il est possible, en C++11, de simplement écrire des boucles comme on le fait en python. L'utilisation des itérateurs est alors implicite.

Le code de ma boucle-exemple devient ridiculement simple et lisible par un débutant:

for(auto n : line.nodes)
    n->container = &side;

J'espère que cette dernière manière d'écrire les boucles en C++ vous aura convaincu que l'utilisation de la nouvelle norme apporte quelque chose. Si on veut résumer en une seule phrase l'intérêt, on peut dire que les nouvelles fonctionnalités du C++ qui ont été retenues ici permettent d'utiliser la STL de manière simple et claire, ce qui n'était pas le cas avec l'ancienne norme où les itérateurs apparaissaient explicitement dans le code.

Autres

Je n'ai pas encore testé d'autres fonctionnalités, mais je vais le faire. Voici ce qui me semble utile à première vue parmi ce que propose le VS11 (le C++11 est bien plus que ça!):

  1. Les énums fortement typées: il s'agit d'enums qui ne peuvent pas être converties en int. De plus, les valeurs sont définies sous la forme nom_de_l_enum.VALEUR au lieu de valeur. Bref, comme si on mettait l'énum dans une classe ou un namespace. Je dois vérifier que SWIG les digère bien avant de les utiliser en masse dans le code.
  2. Les constructeurs move: ce sont des nouveaux constructeurs qui permettent par exemple de retourner des objets locaux sans effectuer une copie. Ca permet aussi de gérer efficacement les objets temporaires (on parle de “rvalues”), c'est-à-dire des objets auxquels on ne peut rien assigner et qui sont sur le point d'être détruits. L'idée est de définir un constructeur par copie qui repose sur l'hypothèse que l'objet à copier va être détruit juste après… on peut donc par exemple lui voler la mémoire qu'il a alloué. Application évidente à Metafor: ne refonte des objets Matrix où on pourrait éliminer la gestion pourrie de la mémoire liée aux objets temporaires. C'est un exercice que j'aimerais faire.
  3. Les static_assert: des assertions version C++. J'ai l'impression que c'est surtout utile pour vérifier des types lors de l'instanciation de templates… Pourquoi pas…
  4. Les smart_pointer (unique_ptr<>, shared_ptr<>, etc): des “vrais” pointeurs intelligents qui permettraient peut-être de se passer à terme des compteurs de référence home-made de Metafor.

Quand on passera au VS12 (Visual Studio 2013), on pourra également utiliser:

  1. les arguments de templates “par défaut”: ca permet de définir simplement des arguments par défaut dans un but, à nouveau, de simplifier l'écriture.
  2. les initialiseurs par listes: imaginez initialiser un std::vector<int> par = {1, 2, 3, 4}, comme vous le feriez avec un int[4]… c'est beau et ce sera possible.

Gen4

  • Modification de tous les tests gen4.tests pour permettre leur exécution directe (par double-clic sous Windows, en tapant directement leur nom en ligne de commande, comme des scripts, sous linux). La difficulté est de retrouver automatiquement les binaires Metafor à utiliser. J'ai donc créé un petit module qui va rechercher l'endroit où Metafor a été compilé et l'ajoute au PYTHONPATH avant de continuer l'exécution du script. C'est pas infaillible, mais quand ça marche, cela permet de lancer tous ces tests beaucoup plus simplement qu'avant. Et si on voulait rendre cette recherche plus subtile, il suffit d'améliorer ce module additionnel.
  • Utilisation du C++11: je me suis amusé à transformer les boucles, changer le nom du pointeur NULL, supprimer des objets-fonctions, etc. pour me faire la main avec la nouvelle norme. Gen4 est la partie idéale du code pour faire ça puisque j'ai utilisé énormément la STL pour coder le mailleur.
  • Gen4App: correction d'un plantage sous linux lié à un bug. Qt nécessite que la variable argc passée à la QApplication reste allouée tout au long du programme. Malheureusement, je transmettais une référence vers un objet de la pile. C'est corrigé.

TBB

  • L'option METAFOR_USE_TBBLOOPS n'existe plus! Pour rappel, cette option permettait d'activer l'assemblage des vecteurs et des matrices structuraux en parallèle. Le code est maintenant compilé pour permettre l'utilisation des boucles parallèles via une commande python. Par défaut, la version parallèle n'est pas activée puisque certaines combinaisons d'éléments/algorithmes ne le supportent pas. Pour activer (à vos risques et périls) l'assemblage parallèle, utilisez:
StrVectorBase.useTBB() # assemblage des vecteurs en parallèle
StrMatrixBase.useTBB() # assemblage des matrices en parallèle
  • Le test apps.qs.tube utilise cette fonctionnalité. J'ai changé le maillage pour avoir un test un peu plus gros.
  • Les “infos EAS” (nombre d'éléments actifs, nombre d'itérations moyen, etc.) sont maintenant calculées, même lorsque l'assemblage parallèle est actif. Ça posait problème précédemment puisqu'il s'agit d'un compteur qui doit être incrémenté simultanément par tous les éléments en cours de calcul. J'utilise maintenant un tbb::atomic<int> qui semble donner de bons résultats. Cet objet atomic permet de se passer une lock et d'un mutex. Une autre application que j'ai testée dans un autre code et qui sera transposée à Metafor prochainement sera l'initialisation à la volée de variables statiques, dans des boucles parallèles.

Parallélisation ALE

  • Le démarrage des threads TBB se fait maintenant au niveau de l'intégrateur Metafor et non dans chaque routine qui utilise TBB. c'est ce qui est conseillé dans le manuel. On verra si c'est bénéfique si quelqu'un fait des tests parallèles un peu poussées.
  • J'ai reparallélisé les boucles ALE qui avaient été parallélisées avec OpenMP (autrement dit, uniquement le convecteur et le rezoner eulérien). J'utilise maintenant TBB. Grâce aux fonctions lambda, c'est relativement lisible. Côté performances, j'ai fait quelques tests sur ale.testConv2D et c'est pas terrible. Ça fonctionne, il y a un gain, mais ce n'est vraiment pas fameux pour l'instant. J'ai quelques pistes en tête pour améliorer la manière de faire actuelle, qui ne correspond pas exactement à ce que je faisais en OpenMP.

Post Matlab

  • J'ai modifié l'appel aux opérations de postpro Matlab dans le cas “linux” pour qu'il n'ouvre plus de fenêtre (très pénible lorsqu'on lance une batterie sous linux). J'utilise l'option -nodisplay qui lance Matlab en ligne de commande dans une console. Les cas-tests de Yves, Philippe et Luc ont été modifiés. Faudra d'ailleurs essayer d'uniformiser cette interface qui est copiée/collée un peu partout.

Divers

  • Suppression de quelques warnings repérés sous Eclipse.
  • Amélioration du message d'erreur lorsqu'on essaye (généralement involontairement) de mélanger des versions de TBB incompatibles.

Linuxades

Pour rappel, je travaille maintenant sous Linux. Le but étant de faire du parallèle beaucoup plus facilement que sous Windows et bénéficier des packages de libs bien ficelés fournis avec le système. Installer le système et compiler Metafor à partir de rien prend environ 3 heures avec Linux (comptez 2 jours pour Windows, si Luc ne vous passe pas ses libs). Metafor sous Linux est aussi environ 10% plus rapide que sous Windows.

Revers de la médaille… c'est linux: pas de Corel, pas de Photoshop, ni d'Office… Néanmoins, je crois maintenant avoir trouvé un substitut/une alternative +/- valable pour tous les programmes couramment utilisés. Dans le pire des cas, j'utilise une virtualbox (d'autres installent des programmes windows avec Wine… j'ai évité jusqu'à présent).

Bref, si vous vouliez migrer un jour du côté fuck bilou de la force, la route est déjà balisée. Je peux vous donner des tuyaux.

Romain BOMAN 2014/09/03 09:34

commit/2014/09_03.txt · Last modified: 2016/03/30 15:23 (external edit)