Framework .NET – C# – Retours d’expérience pour de meilleures performances

J’aimerais faire des retours d’expérience concernant certaines techniques de développement avec le Framework .NET (Core) et le langage C#. Certaines d’entre elles peuvent être très utiles dans de nombreuses situations, mais trop les utiliser peuvent pénaliser les performances dans certains contextes :

Les finaliseurs

Le tas (espace de stockage mémoire des variables de type référence) est composé de trois générations. Les classes ayant un finaliseur permettront de créer des objets qui ne seront pas stockés dans la première génération qui offre les meilleures performances en temps d’accès. De plus, lorsque le Garbage Collector s’exécute pour collecter la mémoire des objets qui ne sont plus utilisés par l’application, tous les threads de l’application sont gelés. Ce temps de « gel » peut être aggravé par l’utilisation de finaliseurs, consommateurs en temps, car ils sont appelés par le Garbage Collector. Il est alors préférable d’utiliser le Dispose pattern avec les interfaces IDisposable / IAsyncDisposable.

La levée d’exceptions

Elle est utile pour interrompre un traitement afin d’avertir l’utilisateur qu’une erreur a été rencontrée. Mais attention, elle ne doit pas être utilisée pour permettre à deux objets de s’envoyer des messages en interrompant un traitement car elle est coûteuse en temps d’exécution. Pour cela, il est préférable d’utiliser les évènements et les délégués. De même, éviter les instructions du type int.Parse(…) dans un bloc Try … Catch … et préférez l’utilisation de int.TryParse(…).

La réflexion

Très puissante, elle permet d’introspecter les assemblies, d’obtenir dynamiquement une instance d’une classe à partir de son nom complet (nom et espace de nom), … Mais attention en abuser diminue aussi les performances d’exécution des applications. En faisant des tests comparatifs de blocs d’instructions utilisant la réflexion par rapport à d’autres faisant la même chose sans l’utiliser, les blocs l’utilisant étaient environ 1000 fois plus lents (ceci est lié au fait que des liaisons tardives d’objets sont utilisées au lieu de liaisons anticipées). Si vous devez l’utiliser dans des traitements itératifs, dans la mesure du possible, encapsuler-là dans un délégué puis utiliser ce délégué.

Les instructions dynamiques

Aussi appelé typage dynamique (introduit dans la version 4 du langage C# avec le mot-clé dynamic), elles consistent à déclarer des objets dans des variables dont le compilateur n’effectue aucun contrôle, ces objets étant résolu de manière tardive (autrement dit lors de l’exécution et non lors de la compilation). Leur utilisation pénalise les performances des applications. Ces instructions sont à utilisées principalement avec les langages dynamiques comme IronPython ou IronRuby), la réflexion ou encore la programmation COM. Ne pas confondre avec le mot-clé var permettant de mettre en œuvre l’inférence de type qui repose sur les liaisons anticipées.

La synchronisation multi-threads

Lors de la pose de verrous sur des objets, provoquant la mise en pause de threads en attendant la libération de ces objets, les performances de l’application peuvent se trouver impactées.

Eviter les fuites mémoires

Eviter les fuites mémoires pendant l’écriture d’algorithme n’est pas toujours évident (d’où l’intérêt de la phase de conception précédant les développements). Car attention, elles provoquent l’augmentation de la mémoire allouée par le système à l’application et pénalise lourdement les performances. Je vous recommande alors d’utiliser des logiciels spécialisés dans la détection de fuites mémoire tels que ANTS Memory Profiler (de l’éditeur Redgate) afin de les identifier.

Étiquettes : ,

About: James RAVAILLE

Travaillant avec la plateforme Microsoft .NET depuis 2002, j'alterne les missions de formation et d'ingénierie avec cette plateforme. J'écris ce blog pour transmettre mes connaissances à tout développeur, qu'il soit débutant ou expérimenté.