Certainement la session la plus technique, et la plus intéressante, de mon programme Tech Days. En particulier, elle a comblé les quelques manques d’une autre session qui était censée être axée performance mais n’avait finalement que peu parlé de ce sujet (impression par ailleurs confirmé par un de mes contacts à Red Gate).
Nicolas Antoine est Senior Support Manager chez Microsoft, spécialisé dans le support des développeurs .NET. Bref, c’est lui que vous aurez au téléphone si le problème sur lequel vous êtes est VRAIMENT épineux…
Astuce 1 : gestion de l’état de l’application
L’application doit passer en mode running en 5 secondes maxi, sinon l’OS la supprime. Il faut gérer aussi le mode suspended, ainsi que le resuming, de façon à ne pas perdre de la donnée ou des choix utilisateurs lorsque l’OS suspend l’exécution d’une application en arrière plan ou la réactive.
Dans les applications natives, un programme tourne toujours, ce qui bouffe la batterie. Dans Windows 8, on ne peut plus se permettre ça, vu qu’on doit tourner sur des tablettes et des périphériques légers (au passage, il doit y avoir encore du ménage à faire dans les agents, qui eux tournent sans arrêt, pour que la batterie d’une Surface 8 Pro tienne aussi longtemps que celle d’un iPad, de puissance équivalente et pourtant avec une autonomie presque doublée).
Oui, j’ai dit du bien d’un produit Apple. Un début de maladie, certainement…
Evidemment, il faut tenir compte de ce mode de fonctionnement en sauvegardant les valeurs au bon endroit. Pour cela, la slide ci-dessous résume bien le choix :
Techniquement, jeter un œil sur e.suspendignoperation.getdeferral sur l’évènement onsuspending, auquel on s’abonne au lancement de l’application.
Bonne pratique de faire un save dès la modification du modèle de données (mode incrémental), plutôt que d’attendre qu’on soit appelé sur la mise en veille pour le faire, surtout qu’on a peu de temps pour réaliser cette opération avant que l’OS ne se fâche.
Astuce 2 : utiliser l’asynchronisme
async et await sont bien expliqués, pour une fois. Peut-être même un peu trop en détail, car expliquer qu’il y a une machine à état, et montrer IAsyncStateMachine pour bien comprendre, est une bonne idée, mais passer cinq minutes à montrer la progression dans une instance de machine à état n’était pas nécessaire à mon avis (ni à celui de mon voisin Eric, qui prenait des notes deux fois plus vite que moi avec sa Surface RT, alors que je galérais sur ce rogntudju de clavier virtuel Android).
Ce coup-ci, nous avons eu la bonne explication sur ce à quoi il fallait faire attention sur l’utilisation de l’asynchronisme. Voici le mauvais usage d’await :
Dans le code ci-dessus, on lance chacune des commandes en mode asynchrone, mais on attend à chaque fois la fin de cette dernière pour passer à l’itération suivante, et il n’y a donc aucune parallélisation.
A l’inverse, dans le code ci-dessous, on lance bien les cinq tâches en parallèle, et on attend plus bas que tout soit fini :
Le conférencier précise bien qu’on pourrait faire un Task.WaitAll
Astuce 3 : chargement incrémental
ISupportIncrementalLoading est mise en place. La vue utilise automatiquement le support de cette interface si elle la détecte sur une collection qu’on lui binde.
Voir le blog de David Vernier pour un helper sur ObservableCollection.
Astuce 4 : placer les ressources en cache
Rien de spécial à dire…
Astuce 5 : les deux threads dans XAML
Une des astuces les plus riches d’enseignements. L’affichage de XAML utilise deux threads : celui pour l’UI et le thread Compositor.
Encore une fois, il y a découplage : il faut déléguer au maximum au compositor pour que l’UI continue son rôle, a savoir accepter le plus vite possible des inputs de l’utilisateur.
EnableFrameRateCounter permet de voir si les animations sont bien traitées hors UI : il vaut mieux que le première chiffre soit plus haut que le suivant (composition plutôt que UI). EnableDependentAnimation est utilisé pour rendre le développeur conscient du problème : les animations doivent être portées par des objets de transformation, et non par les objets affichés. La propriété doit activée pour que la seconde façon, moins performance, fonctionne, ce qui permet d’être sûr que le développeur est au courant que c’est potentiellement un problème.
Astuce 6 : optimisation XAML
Limiter la profondeur de scène. Limiter l’overdraw par utilisation correcte des templates.
Utiliser DebugSettings.IsOverdrawHeatMapEnabled pour voir si on reste bien en dessous des 3,7 couches de pixels seulement avant les premiers problèmes de perf. Pour cela, ne pas décomposer trop les contrôles en autres contrôles. C’est bien pour la maintenance, mais au final, ça pourrit l’application.
Astuce 7 : virtualiser l’UI
La virtualisation de l’UI permet de ne charger que les contrôles qui sont effectivement utiles à un moment donné à l’affichage. Il est possible d’économiser ainsi les contrôles, en affichant, même pour une liste énorme, que ceux qui sont à l’écran, plus quelques-uns des deux côtés pour ne pas avoir à attendre en cas de scroll.
Sur des contrôles à taille variable, le système ne peut pas le faire pour nous. C’est le cas sur le ScrollViewer, par exemple, et il est donc important de borner le contenu.
Astuce 8 : mettre les pages en cache
NavigationCacheMode à mettre en Enabled pour que Windows remette la page telle qu’elle était quand on y revient, au lieu de tout recharger. C’est tout bête, mais ce n’est pas le comportement par défaut.
Il faut aussi rajouter un “if e.NavigationMode != NavigationMode.Back” à l’endroit qui chargeait le modèle, de façon qu’on fasse bien le premier chargement, mais qu’il ne soit plus réalisé aux navigations suivantes, sauf demande explicite de rafraichissement de la part de l’utilisateur. Le top est de rafraichir quand même, mais seulement avec les nouvelles données depuis le dernier chargement, histoire d’avoir le meilleur des deux modes.
Frame.CacheSize pour limiter ou augmenter le nombre de pages (10 par défaut). Sur ce coup-là, j’avoue ne pas avoir compris l’intérêt de le baisser : on ne va pas gagner de mémoire, vu que s’il n’y a que trois pages en cache, les sept slots restants seront vides…
Astuce 9: utiliser Performance Analyser dans VS 2012
Pour moi qui me targue de bien connaitre les problèmes de performance, j’aurais tendance à dire que ce n’est pas une astuce mais une bonne pratique de base : ne chercher à améliorer les performances que sur un bout de code qui nous a effectivement été désigné par le profileur comme celui qui posait le problème de performance.
Mais je suis d’accord que ça ne fait pas de mal de le rappeler. Le présentateur rappelle d’activer les symboles de debug dans les options pour que le profileur prenne en compte les fonctions .NET.
Astuce 10 : utiliser Windows Performance Analyzer
En complément de la précédente astuce. xperf pour la ligne de commande. L’analyseur de performance permet de voir le démarrage, le XAML chargé, etc., bref toutes les choses qu’il est difficile, voire impossible, de faire avec le profileur de Visual Studio.
Et au pire, il est toujours possible de se rabattre sur le débogueur en ligne de commande et SOS. Mais là, c’est pour ceux qui n’ont pas froid aux yeux… ou plus rien à perdre .
Au final, excellente session, très enrichissante. Je connais pas trop mal le sujet de la performance, mais pas beaucoup spécifiquement à WinRT et Windows 8, et j’ai appris plein de choses. Bref, à voir quand les webcasts sortiront, si vous n’y étiez pas…