Blog ENI : Toute la veille numérique !
🎃 Jusqu'à -30% sur les livres en ligne, vidéos et e-formations.
Code : GHOST30.
Cliquez ici !
Accès illimité 24h/24 à tous nos livres & vidéos ! 
Découvrez la Bibliothèque Numérique ENI. Cliquez ici
  1. Livres et vidéos
  2. Ecrire du code .NET performant
  3. Principes du profilage
Extrait - Ecrire du code .NET performant Profilage, benchmarking et bonnes pratiques (2e édition)
Extraits du livre
Ecrire du code .NET performant Profilage, benchmarking et bonnes pratiques (2e édition) Revenir à la page d'achat du livre

Principes du profilage

Une activité strictement régulée

Lorsqu’on entreprend d’améliorer les performances d’une application, il est essentiel de passer par plusieurs étapes. On y trouvera notamment les éléments suivants :

  • Le profilage : il s’agit, grâce à un outillage spécialisé, de trouver tous les points de contention simples dans du code et de les résoudre. C’est cette étape que nous allons détailler principalement dans ce livre, car elle est souvent sous-estimée alors qu’elle permet presque toujours de résoudre les problèmes de lenteur d’une solution logicielle. À ce niveau d’analyse, toutes les améliorations se cumulent et il est extrêmement rare qu’une modification de code pour la performance supprime les effets de la correction précédente.

  • Le tuning : cette deuxième étape correspond au moment où le profilage ne montre plus de goulets d’étranglement. L’application est donc considérée comme correcte du point de vue du code. Toutefois, la performance peut ne pas être suffisante pour les usages, et il faut jouer sur la configuration finement pour améliorer le temps de déroulement des scénarios. Il s’agit de réglages dépendant fortement du contexte, et qui du coup ont de nombreuses interférences les uns avec les autres. Une amélioration de l’occupation mémoire pourra se solder par plus de CPU consommé, ou une modification permettant d’exécuter un scénario plus rapidement fera peut-être ralentir un autre scénario. Le tuning de performance est donc un art très difficile, et c’est là l’origine de la réputation de domaine complexe que celui des améliorations de performance, alors que le stade de profilage montre un rendement excellent par rapport à la difficulté de réalisation. Il faut toujours conserver à l’esprit que les performances brutes n’ont que peu d’intérêt. Ce qui prime avant tout est d’avoir une expérience utilisateur qui soit acceptable pour son travail au quotidien avec le logiciel développé. Si, en parallèle de l’expérience utilisateur...

Stabilité de la plate-forme

1. Pourquoi cette règle ?

Il est essentiel de bien préciser sur quelle plate-forme nous travaillons, et ce, de manière détaillée : version du logiciel ciblé, type de base de données, système d’exploitation, type de machine, capacités matérielles, etc.

Les raisons sont avant tout de nature quantitative. Pour comparer des analyses dans le temps, et en particulier savoir si un logiciel s’améliore ou voit ses performances se dégrader au fur et à mesure des versions, il faut pouvoir comparer sur une référence stable.

Mais c’est surtout à l’intérieur d’une campagne d’optimisation de la performance que cette stabilité est nécessaire. Si un banc de test doit évoluer lors du passage à une nouvelle version, il est tout à fait possible de tester celle-ci sur l’ancien banc de test en même temps, et ainsi établir des correspondances de performances entre les anciens temps et les nouveaux.

En revanche, à partir du moment où des mesures ont été lancées sur une version du logiciel, il faut continuer les itérations d’optimisation sur la même plate-forme.

2. Comment appliquer cette règle ?

L’idéal pour assurer la prise en compte correcte de cette contrainte est de disposer d’une usine logicielle permettant de créer un livrable et de déployer un environnement de test en continu. De nos jours, les systèmes DevOps sont au cœur de la production logicielle, ce qui rend l’application de cette règle plus facile qu’auparavant.

Pour mettre cela en pratique, une branche de code source doit être créée spécialement pour les optimisations de performance. Ceci présente deux avantages :

  • Le premier assez évident est que le reste de l’équipe peut continuer à coder des évolutions sur le logiciel, tout en restant sur un code source stable. Ceci s’intègre parfaitement dans les différents flows proposés aujourd’hui avec Git.

  • Le second est qu’en fin de campagne d’optimisation, la comparaison avec les branches de travail et de production sera facilitée. Cela permet d’intégrer facilement...

Neutralité de l’environnement

1. Le principe

La neutralité de l’environnement est la seconde règle de base lors de l’optimisation logicielle. Elle peut paraître proche de la nécessité de stabilité de la plate-forme, mais il n’en est rien. La contrainte précédente concernait le code à optimiser, tandis que celle-ci concerne l’environnement (ou plutôt les environnements, comme cela a été précisé plus haut) qui va servir de support aux tests d’optimisation.

Il est important que ces environnements soient multiples. Par exemple, si notre application supporte plusieurs moteurs de bases de données et que nous utilisons un générateur de requêtes SQL sur lequel nous souhaitons augmenter les performances, il faut bien prendre garde à tester nos améliorations sur tous les moteurs de base de données avec lesquels nous sommes compatibles. Il est en effet tout à fait possible qu’une modification du code SQL permette une meilleure performance sur un moteur et conduise, à l’inverse, à une baisse de performance sur un autre.

Cependant, chaque environnement pris séparément doit être le plus neutre possible, c’est-à-dire qu’il ne doit pas impacter les résultats obtenus de manière arbitraire ou variable dans le temps. Expliquons ce point plus précisément car il est impossible que l’environnement n’impacte pas les performances. Le CPU utilisé, la mémoire disponible et même le système d’exploitation utilisé modifient les performances de notre application. Il est essentiel que cet impact soit stable dans le temps. Si notre application utilise IIS (Internet Information Server, le serveur web de Microsoft), il est logique qu’elle dispose de moins de ressources lorsque celui-ci traite des requêtes, mais cela fait tout simplement partie du système mesuré, et surtout ces ressources sont utilisées de manière à peu près déterministe sur notre scénario de test.

En poussant le raisonnement à l’absurde, nous pourrions tout à fait mener des scénarios de test de performance (on parle de "tirs") sur une machine où le CPU disponible les 30 premières...

Objectifs fixés avant l’analyse

Des cinq règles du profilage d’application, la règle des objectifs fixés avant l’analyse est celle qui peut paraître la moins indispensable. Après tout, nous pouvons lancer une campagne d’amélioration de performance sans chiffrer les objectifs, et en nous disant tout simplement que nous souhaitons faire le mieux possible dans un temps donné. Nous fixons le temps à consacrer à cette campagne au lieu de fixer le pourcentage de performance à gagner sur un scénario donné. Même dans le cas où nous souhaitons prendre tout le temps qu’il faut pour améliorer une application, il faudra bien sortir une version avant que notre client n’aille voir un autre fournisseur. Peu importe le sens dans lequel nous prenons le problème, nous retombons sur des limites.

Comme sur n’importe quel développement informatique, nous pouvons agir sur trois paramètres :

  • La quantité d’optimisation à apporter : quels sont nos buts en termes de temps passé et sur quels scénarios précis d’utilisation de notre application ? Atteindre moins de trois secondes entre le lancement du processus et l’affichage de la fenêtre principale ? Lancer un traitement particulier sur une instance métier en moins d’une seconde ? Exécuter un traitement multiple (une « moulinette ») permettant de traiter un grand nombre d’éléments en deux, voire trois fois moins de temps ?...

Améliorations mesurables

1. Pourquoi cette règle ?

Qui dit objectifs chiffrés dit moyens pour les mesurer. Cette règle est certainement la plus simple des cinq, en tout cas à expliquer : lors d’une campagne d’optimisation des performances d’une application, il faut se mettre d’accord à l’avance sur ce qui va être mesuré, et comment.

Le « quoi » correspond aux scénarios : quelles sont les interactions utilisateurs les plus longues ? Quels sont les traitements de masse qui désespèrent nos clients ? C’est souvent le département de tests/qualité logicielle qui identifiera pour nous ces scénarios. Parfois, ce sera directement notre client, et il est évident dans ces cas qu’une amélioration s’impose d’autant plus vite.

2. Comment appliquer cette règle ?

Le « comment » est à notre discrétion : nous pouvons choisir d’ajouter des compteurs logiciels de temps passé entre un clic sur un bouton et l’apparition du résultat à l’écran, ou demander à un testeur de se munir d’un chronomètre et de réaliser quelques tests sur notre application.

Cette deuxième méthode ne doit pas être sous-estimée : elle est évidemment moins...

Granularité descendante

1. Commençons par une parabole

Le lecteur a peut-être déjà entendu cette parabole de la vie consistant à la comparer au remplissage d’une jarre avec des rochers, des cailloux, du sable : si nous commençons par remplir une jarre avec du sable et des cailloux (le moins important dans la vie), nous n’aurons plus la possibilité de faire entrer ensuite les gros rochers (l’essentiel). Tandis que si nous remplissons la jarre avec des roches, nous pouvons toujours faire couler quelques cailloux entre les rochers. Et de la même manière, le sable pourra ensuite combler les interstices.

Il en va de même pour le profilage : si le développeur commence par améliorer les fonctions les moins utilisées, il se rendra compte qu’en améliorant les principaux points de contention du logiciel, il annihile le travail réalisé auparavant. À l’inverse, s’il optimise son code en partant des plus gros points de contention (en bref des goulets d’étranglement), il fait apparaître au fur et à mesure de plus petits problèmes. Surtout lorsqu’il corrige ces petits problèmes, il n’impacte généralement pas les corrections plus importantes qu’il a réalisées en amont.

2. Un exemple

Prenons un exemple : la fenêtre principale d’une application met trop de temps à apparaître. Une analyse par un profileur montre qu’une fonction de chargement de librairie est trop lente (disons deux secondes) et nous décidons de l’optimiser. Après modification du code, nous avons gagné une seconde. Ce n’est pas suffisant et nous décidons de pousser plus loin l’optimisation, en chargeant les librairies pendant que l’utilisateur se connecte. Or, des mesures indiquent que celui-ci met trois secondes en moyenne à taper son mot de passe. Nous avons désormais ce temps (trois secondes) devant nous pour charger notre librairie, et la réduction de temps de chargement que nous avions opérée auparavant n’est alors plus utile.

Cela ne veut pas dire que le travail est à mettre à la poubelle : peut-être qu’un jour nous chargerons davantage de librairies pendant la phase d’authentification...