.NET sur Linux : les premiers tests de Mono

 

Go-Mono est de loin l’initiative qui aura suscité le plus de passion de la part de la communauté Linux cette année. A l’origine de ce projet, un provocateur avéré et connu de tous pour ses positions très tranchées : Miguel de Icaza. Miguel fait partie de ces personnages mystérieux et inclassable dont la motivation principale est de créer des projets originaux avec l’aide des meilleurs développeurs du monde libre. Go-Mono fait partie de ceux là avec pour objectif, porter la nouvelle architecture .NET de Microsoft sur Linux.

Si les implications politiques qui en découlent sont considérables, la difficulté technique n’en est pas moins des plus ardue. Imaginez porter le travail effectué par des centaines d’ingénieurs de chez Microsoft sous la plate-forme Linux en l’espace d’un ou deux ans. D’autant plus que la taille de l’éditeur de Redmond est sans aucune mesure avec celle du projet Mono (28 personnes dont 6 permanents).  Imaginez enfin que ce projet fournisse une implémentation de l’environnement d’exécution (CLR) proche ou supérieure à celle de Microsoft. C’est l’un des vœux les plus chers de Miguel. Si aux yeux de la communauté Linux, il est le traître de la cause Linux, aux yeux de Microsoft il représente potentiellement un danger certain : celui d’attirer les développeurs Windows vers la plate-forme concurrente.

Qu’en est-il aujourd’hui de toutes ces interrogations ? Les tout premiers tests tendent-ils à affirmer ou infirmer une éventuelle supériorité des implémentations de Mono ? Le chemin est-il encore long pour arriver à un environnement stable et performant ?

Nous allons tâcher de répondre à toutes ces questions à travers une présentation de l’architecture Mono, mais aussi une série de tests effectués sur les premières implémentations (builds) du produit. 

Installation et configuration de Mono sur Linux

Pour installer Mono, vous disposez de plusieurs choix.

·        Vous êtes sous Windows (2000 ou NT). Après avoir récupéré la distribution sur le site http://www.go-mono.org, il vous suffit d’installer les outils Cygnus disponibles à l’adresse suivante : www.cygwin.com. Ces outils sont le fruit du portage de l’ensemble des commandes Unix sur Windows. Reste ensuite à installer les diverses librairies externes requises par Mono pour fonctionner (pkgconfig, glibc, …). Pour finir, soit vous préférez recompiler entièrement les sources de Mono, auquel cas il faudra vous armer de patience, soit vous téléchargez directement les binaires requis par l’outil et l’installation devient plus aisée.

·        Vous êtes sous Linux (c’est notre cas). Le plus simple et le plus rapide consiste à suivre les instructions du site http://mono.baselabs.org. Les différentes étapes d’installation y sont détaillées de manière très pédagogique. En suivant à la lettre ces directives, l’opération nous a pris une petite dizaine de minutes. A noter que le packaging RPM nous a été d’un grand secours car il intégrait déjà les bibliothèques dépendantes requises par Mono (glib, …).

Nous avons installé le package complet sur une distribution Mandrake 8.0 noyau 2.4.8. L’écran suivant nous liste l’ensemble des fichiers présents dans l’archive Mono. Vous remarquerez que les Assemblies systèmes ne sont pas toutes implémentées (taille du fichier égale à zéro). Nous expliquerons par la suite pourquoi.

Mono installe aussi un certain nombre de fichiers .h représentant les entrées/sorties et les méta-données.

L’environnement d’exécution (Runtime) 

Le Runtime Mono est basé sur un moteur JIT (Just In Time) chargé de compiler à la volée les instructions CIL. Mono intègre aussi un interpréteur de code conçu pour être aisément porté vers d’autres plate-formes : Mint. Le Runtime comprend en plus du chargeur de classes (ClassLoader), un ramasse miettes et un système de gestion de Threads. Vous trouverez donc deux outils lié au Runtime et quelques utilitaires :

·        Mono (compilateur Jut-In-Time)

·        Mint (Un interpréteur de code)

·        Monodis (désassembleur de code CIL)

·        Monograph (générateur de graphes d’appels)

Les algorithmes utilisés par le ramasse miettes sont basés sur Intel ORP (Open Runtime Plateform). Quant au compilateur Just In Time, il utilise une interface de génération dynamique de code. Pour plus d'informations, consultez  : http://www.research.microsoft.com/~drh/pubs/interface.pdf .

Les modèles d’entrées/sorties (I/O) et de gestion des threads présenté par Microsoft à l’ECMA s’appuient sur les API Win32. L’équipe de Mono a donc dû créer une abstraction de ce mécanisme afin de faciliter son portage vers d’autres plate-formes.

Enfin, les fonctions d’appels vers du code natif telles que pinvoke() ont été portées de manière à unifier l’accès aux bibliothèques Unix et aux appels Systèmes natifs. Pour l’heure, Mono se destine essentiellement aux architectures x86 concernant la compilation Just In Time. Même si la documentation précise la démarche à suivre concernant le portage du compilateur vers d’autres plate-formes, il n’existe aujourd’hui aucune autre implémentation tierce (Sparc, PPC, ..).

Le Compilateur Mono 

La machine virtuelle est la brique essentielle d’une plateforme. Lorsque la machine virtuelle et le compilateur sont implémentés, vous accédez à l’autonomie presque totale appelé aussi « Self Hosting ». Le travail restant à fournir se réduit au portage du code natif. Les équipes de Ximian l’ont bien compris et ils se sont attachés, dans un premier temps, à implémenter un compilateur C#  et un environnement d’exécution sur le principe de la CLI.  Examinons de plus près le compilateur.

Le compilateur utilisé par Mono s’appelle MCS (mcs.exe) et est entièrement écrit en C# sur la base d’un analyseur grammatical maison (un portage de Yacc en C#). Ceci afin de faciliter le portage du compilateur vers d’autres plate-formes. La figure suivante nous illustre les différentes options proposées par l’outil :

 Il est d’ores et déjà possible d’effectuer certaines optimisations de codes et la résolution d’assemblies externes est prévue. Théoriquement, à ce stade, il doit être possible de compiler un source qui sera exécuté ensuite par la CLR du Framework Windows. Avant de réaliser ces tests, attardons nous sur les particularités du portage des API du Framework par Mono.

Les portage des API Windows

La difficulté principale d’un projet tel que Mono consiste à trouver des solutions de remplacement aux innombrables appels de code natif effectués dans les classes du Framework. Ainsi, les WinForms sont basés sur l’API GDI+ développée en C++ qui s’appuie sur des routines de bas niveau. Les classes ADO.NET, quant à elles, font aussi référence à du code natif : les « unmanaged  providers ».  Quant au Namespace System.Enterprise.* permettant de développer des composants COM+, il possède la palme avec toute une infrastructure basée sur du code non managé. Comment dans ces conditions, effectuer un portage fidèle, qui plus est lorsque le code source n’est pas disponible ?. Les équipes de Mono ont donc dû réaliser des adaptations « maison » afin de reproduire le comportement de ces API natives sans trop briser le modèle original du Framework et en gardant la compatibilité des API. 

Tout d’abord, attardons nous sur le portage de l’environnement graphique, avez-vous déjà jeté un œil sur la pile d’exécution d’une application WinForm en cas d’erreur ? Cette pile nous donne un certain nombre d’informations quant à la nature de l’erreur, mais aussi le détail et le type de la fonction ayant généré l’erreur. Ainsi nous pouvons nous apercevoir que  l’API WinForms repose sur un système de fenêtrage entièrement natif illustré par les messages suivants :

System.Windows.Forms.Control.OnClick(EventArgs e)
   at System.Windows.Forms.Button.OnClick(EventArgs e)
   at System.Windows.Forms.Button.OnMouseUp(MouseEventArgs mevent)
   at System.Windows.Forms.Control.WmMouseUp(Message& m, MouseButtons button, Int32 clicks)
   at System.Windows.Forms.Control.WndProc(Message& m)
   at System.Windows.Forms.ButtonBase.WndProc(Message& m)
   at System.Windows.Forms.Button.WndProc(Message& m)
   at System.Windows.Forms.ControlNativeWindow.OnMessage(Message& m)
   at System.Windows.Forms.ControlNativeWindow.WndProc(Message& m)
   at System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr)
   at System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG& msg)


Mono a donc dû trouver une solution afin de remplacer l’ensemble des classes WinForms constituées de code natif et proposer une alternative qui soit portable. GTK+ a donc été choisi pour diverses raisons. Tout d’abord, c’est un Framework graphique existant sur la plupart des plate-formes, dont Windows. De plus, Gnome, le célèbre environnement graphique de Linux s’appuie sur ces mêmes API GTK, et enfin, Ximian, qui a énormément contribué à son développement, dispose ainsi d’une occasion de promouvoir cette API. Pour avoir un aperçu du Look and Feel que pourrait avoir vos futures applications .NET sous Linux, n’hésitez pas à jeter un oeil sur le site officiel de GTK : http://www.gtk.org. A noter qu'aujourd'hui, Mono ne propose aucune version fonctionnelle de GTK+.

Le deuxième namespace prêtant à discussion est System.Data. Celui-ci représente sans nul doute une lourde tâche pour Mono car il est constitué d’une multitude de classes en tout genre allant de l’accès aux bases de données relationnelles à l’utilisation de providers non managés.  Les développeurs auront fort à faire dans ce domaine et il semblerait qu’ils s’orientent d’ores et déjà vers GnomeDB. Le choix de GnomeDB s’explique aisément. En effet, Gnome DB et ADO.NET se ressemblent sur plusieurs points. Les deux Frameworks utilisent des « Data providers  », sorte d’adaptateurs entre le client et la base. Enfin, les deux modèles de développement : GnomeDB et ADO.NET adoptent la même démarche générale concernant le traitement des enregistrements (DataSet) et l’affichage des DataGrid.

Pour vous en convaincre, référez vous à l’article suivant : http://www.oreillynet.com/pub/a/dotnet/2001/07/09/mono.html.

Quant au codes développés avec ODBC, il restera compatible sous Linux dans la mesure où Mono propose le provider adéquat.

 

 

 

 

 Enfin, le portage de COM+ qui pourrait sembler à première vue comme la tâche la plus délicate se résume à l’intégration d’une API existante. En effet, Mozilla propose une implémentation OpenSource de COM sous le nom de XPCOM. Mono devra donc intégrer ces API en proposant des Wrappers écrits en C. XPCOM contrairement à COM de Microsoft a été revisité par les soins de la communauté Open Source pour être multi-plateforme. Plutôt que d’utiliser MIDL pour la conception des interfaces distribuées, XPCOM se base sur XPIDL, langage compatible avec Corba. L’objectif d’XPCOM est de fournir une implémentation robuste en supprimant du Framework la partie OLE responsable aux yeux de certains de l’instabilité de COM.

C’est un point très important qu’il faudra prendre en compte, Mono a pris ces distances avec les outils intégrés de Windows et fournit une alternative « maison » totalement Open-Source de bout en bout. C’est aussi, quelque part, une manière de se détacher des contraintes de licencing imposées par l’éditeur de Redmond concernant COM+.

C’est pourquoi, il faudra, à l'avenir, se garder de qualifier Mono de clone Linux de .NET. Nous avons vu que les deux implémentations seront vraisemblablement très différentes.

Les premiers tests de portabilité

Une fois Mono installé, difficile de résister à l’envie de compiler un source C# pour tester la portabilité entre les deux environnements Windows et Linux. Avant  cela, nous avons jugé utile de mettre en place un scénario de tests très précis résumé dans le schéma suivant. L’objectif est de vérifier si les binaires CIL générés par Mono sont portables sous la CLR de Microsoft et inversement.



La première étape consiste à compiler un HelloWorld classique sur Windows avec csc.exe :



Nous effectuons une rapide copie de HelloWindows.exe sous Linux afin de lancer Mint.exe, l’interpréteur CIL de Mono, et vérifier si la portabilité est vraiment respectée…..
Aussitôt dit, aussitôt fait, nous lançons Mint suivi de l’exécutable Windows, et là… avec émerveillement nous apercevons le message HelloWorld s’afficher sous nos yeux ébahis (;-)). Imaginez un instant tout le chemin qu’il a fallu parcourir pour arriver à cette simple exécution de code. La portabilité est au rendez-vous, vous en avez rêvé, Mono l’a fait.





Continuons nos petit tests et attardons nous maintenant sur la procédure inverse. Compiler un programme sous Mono et l’exécuter ensuite avec la CLR Windows. A noter que cela nous permet au passage d’effectuer notre première compilation avec mcs. L’outil est assez austère, il n’est vraisemblablement pas finalisé et un rapide coup d’œil sur la documentation nous indique qu’il est encore en phase béta. Peu importe, la compilation se passe bien et nous nous empressons de copier le fichier sous Windows. 


 

Et là, après avoir cliqué sur l’exécutable HelloMono.exe c’est la grande désillusion …. La CLR rejette notre fichier avec le message suivant « application non valide ». Apparemment, le format binaire pose quelque problème à la CLR Windows. 


 


  Déterminé à trouver une explication, nous nous lançons dans la comparaison des Assemblies. De toute évidence, certaines informations requises par la CLR .NET ne se trouvent pas dans le fichier d’origine généré par Mono. Les deux systèmes proposant des désassembleurs de code, monodis pour Mono et ildasm pour .NET, nous effectuons une comparaison du CIL généré de part et d’autre. Voici le résultat pour HelloMono.il et HelloWindows.il. Vous aurez par vous même noté la différence. Si la génération du CIL est fidèle, il manque coté Mono le bloc de code correspondant aux classes dépendantes, dans notre cas, System.Object. Cela suffit donc à rejeter l’exécutable sous Windows. Dommage… espérons que ce problème mineur sera corrigé dans les prochaines versions.

En résumé, les tests sont très positifs dans l’ensemble même si nous sommes assez frustré de pas avoir pu tester les WinForms avec GTK+ et l’ensemble des services du Framework. Il faut savoir rester patient, l’avenir nous réservera sûrement d’autres bonnes surprises.

Les performances

Nous avons voulu avoir un rapide aperçu des performances de Mono sans pour autant tirer de conclusion abusive avec la version Windows. Mono étant un projet en phase de développement et la RTM de .NET étant finalisée, aucune comparaison n’est crédible.

Mint ne posséde pas de compilateur JIT intégré, il faudra donc relativiser ses performances par rapport à Mono mais aussi et surtout par rapport à la CLR Windows.


 


Les résultats sont assez surprenant. Alors que logiquement Mint prend énormément de temps à exécuter notre bout de code (presque 2 secondes  !), mono s’effondre littéralement avec un core dumped. Pourtant le source de notre programme n’est pas des plus compliqué, une simple boucle contenant une allocation mémoire afin de tester le ramasse miettes. Bizarre, Bizarre, mais passons … 

    public static void Main()

                {

                        string s = null ;

                        DateTime startTime = DateTime.Now;

 

                        for (int j=0;j<100;j++) {

                        // Utilisation du garbage collector

                                for (int i=0;i<100;i++){

                                  s = i.ToString() ;

                                }

                         }

                         TimeSpan elapsed = DateTime.Now - startTime;

                        // Utilisation du type int car la fonction Double.ToString() n’est pas implémentée par Mono L

                         int val = (int) elapsed.TotalMilliseconds ;

                        Console.WriteLine("Time elapsed " + val + " ms");

               }

Quant à l’exécution du même programme sous Windows, il est pratiquement instantané (< 10 ms).

Les tests ont été effectués plusieurs fois afin d’en déduire un temps moyen. Au vue de l’échec des tests du compilateur JIT, nous ne pouvons tirer de conclusion quant aux performances de Mono mais gageons que les prochaines versions seront plus abouties et nous permettront d’y voir un peu plus clair.

Les outils restant à implémenter

Voici une liste non exhaustive des outils restant à implémenter prochainement et prévu au roadmap de Ximian:

  • Un assembleur de code IL en exécutable : ILASM
  • ·        Le portage de l’outil AL.exe de Windows concernant le déploiement d’assembly

    ·        Une intégration plus poussée avec l’IDE Open Source CsharpDevelop

    ·        Un système d’aide (HelpBrowser)

    ·        Un vérificateur de code (vérifier)

    Conclusion

    Il faut bien l’avouer, les premiers tests de Mono sont assez concluant. Si le système commence à atteindre une autonomie presque totale, il reste cependant un énorme travail à accomplir concernant les classes du Framework. Nous sommes qu’au début du projet et l’avenir devrait nous réserver quelques bonnes surprises de la part des développeurs impliqués dans l’aventure. Ils nous ont prouvé par le passé avec Gnôme qu’ils étaient capable de se surpasser et de produire des applications d’exception en un temps record. Rendez-vous donc l’année prochaine.

    Quant aux implications d’ordre stratégiques et politiques d’un tel projet, ils sont évidemment important et de là à penser que les développeurs .NET préfèreront Mono à  l’original, il y a un pas que nous nous garderons de franchir.

    Ressources

    Le site officiel de Mono : http://www.go-mono.com
    Le site personnel de Miguel de Icaza http://primates.helixcode.com/~miguel/
    Que fait Miguel tous les jours ?? Son Journal de bord (décidément, on se demande quand est-ce qu'il trouve du temps pour travailler)

     

    Auteur : Sami Jaber

    Copyright : DotNetGuru Ó 2002