.net native : améliorez la performance de vos applications universelles

Vous avez profité des vacances pour vous reposer et couper toute communication avec le monde extérieur ? Je ne peux que vous féliciter ! Je me permets donc de vous annoncer que Windows 10 est désormais disponible depuis le 29 juillet. Une bonne nouvelle ne venant jamais seule, les outils de développement, ainsi que le Windows Dev Center ont également fait peau neuve avec leur lot de nouveautés.Windows-10-Ninja-Cat-TRex

Comme nous sommes tous frais et reposés en cette belle rentrée (meuh si) ; je vous propose d’étudier plus en détails les nouveautés de la plateforme universelle (UWP) disponible avec Windows 10. Comme il faut bien commencer par un sujet, je débute aujourd’hui par un sujet lié aux performances de vos applications C# et VB : .NET Native !

.NET Native

Alors un peu de contexte pour démystifier le sujet : historiquement, quand vous compilez du code qui repose sur le Framework .NET (C# ou VB dans le cadre du Windows Store), Visual Studio génère pour vous un package en code MSIL (Microsoft Intermediate Language). Ce code intermédiaire est ensuite lui même recompilé en code natif lors de l’exécution de l’application sur le poste cible (on utilise le terme JIT alias Just-In-Time compilation pour définir cette opération).

Cette gestion a ses avantages (il permet notamment de de fournir le même package indépendamment de l’architecture cible : x86, x64 ou ARM)  mais a pour désagrément d’impacter les performances (principalement au démarrage de l’application).

Alors quoi de neuf pour UWP ? Et bien toutes les applications universelles pour Windows 10 - écrites en C# et VB - sont désormais compilées en code natif (via .NET Native) avant de transférer le package sur le poste cible. Plus besoin de compilation JIT pour convertir le code MSIL vers du natif ; le gain en performance est donc significatif.

  • Jusqu’à 60% pour un démarrage à froid
  • Jusqu’à 40% pour un démarrage à chaud

Les dépendances au code .NET sont également injectés en statique au code de l’application – .Net native en profite pour supprimer les types/fonctions qui ne seront pas utilisés par votre code, l’empreinte mémoire est du coup plus faible, on peut donc espérer gagner jusqu'à 30 % de la consommation mémoire habituelle. Toutes ses valeurs restent théoriques mais pour résumer simplement : vous aller forcément gagner en performance !

Si vous avez toujours des doutes, vous pouvez tester par vous même votre gain de performance : Mesure de l'amélioration du démarrage avec .NET Native.

Le passage du code IL vers le code machine passe par les rouages d’une série d’outils, vous trouverez donc souvent la terminologie .NET Native tool chain dans les différentes documentations. Si le sujet vous passionne et que vous avez l’envie de soulever le capot,  l’article The .NET Native Tool-Chain est un bon point de départ.

Bon, ca rocks sérieusement du poney (David Catuhe 2015©) mais à quel moment se déclenche cette optimisation ? Attention, moment émotion :

Debug versus Release

Visual Studio s’adapte à ce changement de paradigme. En mode debug, la compilation .NET Native est désactivée par défaut. Visual Studio génère donc du code MSIL et mon package dépend toujours des librairies du Framework .NET. On ne gagne pas en performance mais msbuild compile votre solution rapidement (et ce point à son importance). Il est toutefois possible d’activer .NET Native au besoin dans les options de compilation.

netnative

Pour le mode release c’est une autre histoire, .NET Native compile votre projet en natif (cette fois-ci par défaut). Visual Studio prend donc son temps pour faire les choses bien et optimiser aux petits oignions votre code ! Vous pourrez profiter de ce moment délicieux pour faire une ou deux pause cafés bien méritées (bref, Sit back and Relax). A noter que AnyCPU est une option qui n’a plus lieu d’être car la compilation cible une architecture spécifique (du moins pour les applications, il reste toujours possible de l’utiliser si vous développez une librairie).

Le déploiement sur le Windows Store

Votre application est finalisée, voici venu le moment tant espéré de la publication.

packages

L’assistant vous propose de sélectionner les architectures que vous souhaitez supporter, Visual Studio s’occupe du reste. La pause café terminée, vous obtenez le résultat suivant:

appxupload

- ou, si vous activez l’option bundle -

appxuploadbundle

Pourquoi un répertoire Test !? Et bien ce répertoire contient l’application optimisé avec .NET Native, il sert principalement (comme son nom l’indique) pour effectuer des tests en local - ou - si vous souhaitez déployer votre application en mode sideloading (sans passer par le Windows Store).

Le fichier .appxupload contient quand à lui votre application à destination du Windows Dev Center (ou les différents fichiers .appxupload spécifiques à chaque architecture si vous n’êtes pas en mode bundle).

La compilation made in cloud

Petit point déstabilisant au premier abord, le fichier .appxupload n’est pas optimisé avec la .NET Native tool chain, (à cet instant, vous devez ne plus rien comprendre à ce que je viens de vous raconter précédemment, don’t panic).

.appxupload contient simplement du code MSIL ainsi que la version des outils utilisés par la tool chain pour compiler votre application (pour les curieux, cette liste est référencée dans le fichier AppxManifest.xml , disponible dans les répertoires de compilation).

image

L’étape suivant se déroule in the cloud, le Windows Store prend le relais et va se charger de recompiler l’application en code natif lorsque vous soumettez votre package. L’intérêt ne saute pas aux yeux tout de suite mais l’initiative permet au store une  petite liberté : il va recompiler votre application (de manière transparente pour vous) si des optimisations ou correctifs sont effectués dans les outils de la .NET Native tool chain. Ce petit détail implique deux choses pour vous.

  1. Vous n’avez désormais plus accès au numéro de révision de l’application (les trois premiers chiffres du numéro de version vous appartiennent toujours, c’est simplement le dernier qui est préempté par le store – il sera incrémenté à chaque recompilation made in cloud).
  2. Comme l’application n’est pas compilée localement, les symboles de debug (*.pdb) sont générés également pour vous. Vous pouvez les télécharger depuis le Windows Dev Center dans la rubrique packages actuels.

symbols

J’espère que ces quelques lignes vous seront bénéfiques, n’hésitez pas à me contacter - via ce blog ou twitter (@dupuyjs) - pour tout commentaire ou question.