Partager via


Étude de cas : Isolez un problème de performance (C#, Visual Basic, F#)

Utilisez les outils de profilage pour étudier les problèmes de performances et isoler les zones problématiques. Cette étude de cas utilise un exemple d'application présentant des problèmes de performance pour montrer comment utiliser les outils de profilage afin d'améliorer l'efficacité. Si vous souhaitez comparer les outils de profilage, consultez la section Quel outil choisir ?

Cette étude de cas couvre les rubriques suivantes :

  • Comment utiliser les outils de profilage de Visual Studio pour analyser les performances des applications.
  • Comment interpréter les données fournies par ces outils pour identifier les goulots d'étranglement en matière de performances.
  • Comment appliquer des stratégies pratiques pour optimiser le code, en se concentrant sur les compteurs .NET, le nombre d'appels et les données de synchronisation.

Suivez la procédure et appliquez ensuite ces techniques à vos propres applications pour les rendre plus efficaces et plus rentables.

Isolation d'un problème de performance - étude de cas

L'exemple d'application de cette étude de cas est une application ASP.NET qui exécute des requêtes sur une base de données simulée. L’exemple est basé sur l’Exemple Diagnostics.

Le principal problème de performance de cette application réside dans des schémas de codage inefficaces. L'application présente un goulot d'étranglement au niveau des performances qui a un impact significatif sur son efficacité. Le problème présente les symptômes suivants :

  • Faible utilisation du processeur : L'application présente une faible utilisation du processeur, ce qui indique que le processeur n'est pas le goulot d'étranglement.

  • Nombre de threads élevé dans le ThreadPool : Le nombre de threads est relativement élevé et augmente régulièrement, ce qui suggère une famine du pool de threads.

  • Réponse lente de l'application : L'application répond lentement en raison du manque de threads disponibles pour traiter les nouveaux éléments de travail.

L'étude de cas vise à résoudre ces problèmes en utilisant les outils de profilage de Visual Studio pour analyser les performances de l'application. En comprenant où et comment les performances de l'application peuvent être améliorées, les développeurs peuvent mettre en œuvre des optimisations pour rendre le code plus rapide et plus efficace. L'objectif final est d'améliorer les performances globales de l'application, ce qui la rend plus efficace et plus rentable.

Défi

La résolution des problèmes de performance dans l'exemple d'application .NET présente plusieurs défis. Ces défis découlent de la complexité du diagnostic des goulets d'étranglement des performances. Les principaux défis à relever pour résoudre les problèmes décrits sont les suivants :

  • Diagnostiquer les goulets d'étranglement des performances : L'un des principaux défis consiste à identifier avec précision les causes profondes des problèmes de performance. Une faible utilisation du processeur combinée à une lenteur des performances peut avoir de multiples facteurs contributifs. Les développeurs doivent utiliser efficacement les outils de profilage pour diagnostiquer ces problèmes, ce qui nécessite une certaine compréhension du fonctionnement de ces outils et de l'interprétation de leurs résultats.

  • Contraintes liées aux connaissances et aux ressources : Les équipes peuvent être confrontées à des contraintes liées aux connaissances, à l'expertise et aux ressources. Le profilage et l'optimisation d'une application requièrent des compétences et une expérience spécifiques, et toutes les équipes n'ont pas forcément un accès immédiat à ces ressources.

Pour relever ces défis, il faut adopter une approche stratégique qui combine l'utilisation efficace des outils de profilage, les connaissances techniques, ainsi qu'une planification et des tests minutieux. Cette étude de cas vise à guider les développeurs tout au long de ce processus, en leur fournissant des stratégies et des aperçus pour surmonter ces défis et améliorer les performances de l'application.

Stratégie

Voici une vue d'ensemble de l'approche adoptée dans cette étude de cas :

  • Nous commençons l'enquête en observant les mesures des compteurs .NET tout en collectant des données sur les performances. Tout comme l'outil Utilisation du processeur, l'outil Compteurs .NET de Visual Studio est également un bon point de départ pour une enquête sur les performances.
  • Ensuite, pour obtenir des aperçus supplémentaires permettant d'isoler les problèmes ou d'améliorer les performances, envisagez de collecter une trace à l'aide de l'un des autres outils de profilage. Par exemple, examinez le nombre d'appels et les données temporelles à l'aide de l'outil Instrumentation.

La collecte des données nécessite les tâches suivantes :

  • Paramétrer l'application sur une build de mise en production.
  • Sélectionnez l’outil Compteurs .NET dans le Profileur de performances (Alt+F2). (Les étapes ultérieures impliquent l’outil Instrumentation.)
  • À partir du Profileur de performances, démarrez l’application et collectez une trace.

Vérifier les compteurs de performances

Pendant l'exécution de l'application, nous observons les compteurs dans l'outil .NET Counters. Pour les examens initiaux, quelques indicateurs clés à surveiller comprennent :

  • CPU Usage. Regardez ce compteur pour déterminer si un problème de performances se produit avec une utilisation élevée ou faible de l’UC. Il peut s’agir d’un indice de types spécifiques de problèmes de performances. Par exemple :
    • En cas d'utilisation élevée du processeur, utilisez l'outil Utilisation du processeur pour identifier les zones où nous pourrions optimiser le code. Pour un tutoriel sur ce sujet, consultez l'étude de cas : Guide du débutant pour l'optimisation du code.
    • Avec une faible utilisation de l’UC, utilisez l’outil Instrumentation pour identifier les nombres d’appels et le temps de fonction moyen en fonction de l’heure d’horloge murale. Cela peut aider à identifier des problèmes tels que la contention ou le threading pool starvation.
  • Allocation Rate. Pour une application web qui sert des requêtes, le taux doit être assez stable.
  • GC Heap Size. Regardez ce compteur pour voir si l’utilisation de la mémoire augmente continuellement et fuite potentiellement. Si la valeur semble élevée, utilisez l'un des outils d'utilisation de la mémoire.
  • Threadpool Thread Count. Pour une application web qui sert des requêtes, regardez ce compteur pour voir si le nombre de threads est stable ou augmente à un rythme régulier.

Voici un exemple montrant comment la valeur CPU Usage est faible, tandis que le valeur ThreadPool Thread Count est relativement élevée.

Capture d’écran des compteurs affichés dans l’outil Compteurs .NET.

Une augmentation constante du nombre de threads avec une faible utilisation de l’UC peut être un indicateur de privation du pool de threads. Le pool de threads est forcé de continuer à faire tourner de nouveaux threads. La privation de pool de threads se produit lorsque le pool n’a pas de threads disponibles pour traiter de nouveaux éléments de travail et entraîne souvent lenteur de réponse des applications.

En fonction de l’utilisation faible de l’UC et du nombre de threads relativement élevés, et en travaillant sur la théorie d’un cas possible de privation de pool de threads, basculez vers l’utilisation de l’outil Instrumentation.

Examiner les nombres d’appels et les données temporelles

Examinons une trace de l’outil Instrumentation pour voir si nous pouvons essayer d’en savoir plus sur ce qui se passe au niveau des threads.

Après avoir collecté une trace avec l'outil d'instrumentation et l'avoir chargée dans Visual Studio, nous vérifions d'abord la page de rapport .diagsession initiale qui présente des données résumées. Dans la trace collectée, nous utilisons le lien Ouvrir les détails dans le rapport, puis nous sélectionnons Flame Graph.

Capture d’écran du graphique en flamme dans l’outil Instrumentation.

La visualisation du Flame Graph nous montre que la fonction QueryCustomerDB (en jaune) est responsable d'une part importante du temps d'exécution de l'application.

Cliquez avec le bouton droit sur la fonction QueryCustomerDB et choisissez Affichage dans l’arborescence des appels.

Capture d’écran de l’arborescence des appels dans l’outil Instrumentation.

Le chemin de code qui utilise le plus de processeur dans l'application est appelé chemin chaud. L'icône de flamme du chemin chaud (Capture d'écran montrant l'icône de chemin chaud.) peut aider à identifier rapidement les problèmes de performance susceptibles d'amélioration.

Dans l'arborescence des appels, vous pouvez voir que le chemin chaud inclut la fonction QueryCustomerDB, ce qui indique un problème potentiel de performance.

Par rapport au temps passé dans d’autres fonctions, les valeurs Self et Avg Self pour la fonction QueryCustomerDB sont très élevées. Contrairement à Total et Avg Total, les valeurs Self excluent le temps passé dans d’autres fonctions. Il s’agit donc d’un bon endroit pour rechercher le goulot d’étranglement de performances.

Conseil

Si les valeurs Self étaient relativement basses au lieu d’être élevées, vous aurez probablement intérêt à examiner les requêtes réelles appelées par la fonction QueryCustomerDB.

Double-cliquez sur la fonction QueryCustomerDB pour afficher le code source de la fonction.

public ActionResult<string> QueryCustomerDB()
{
    Customer c = QueryCustomerFromDbAsync("Dana").Result;
    return "success:taskwait";
}

Nous effectuons quelques recherches. Alternativement, nous pouvons gagner du temps et laisser Copilot faire la recherche pour nous.

Si nous utilisons Copilot, nous sélectionnons Demander à Copilot dans le menu contextuel et tapons la question suivante :

Can you identify a performance issue in the QueryCustomerDB method?

Conseil

Vous pouvez utiliser des commandes slash comme /optimiser pour aider à formuler de bonnes questions pour Copilot.

Copilot nous indique que ce code appelle une API asynchrone sans utiliser await. Il s’agit du modèle de code sync-over-async, est une cause de privation de pool de threads et êut bloquer les threads.

Pour résoudre ce problème, utilisez await. Dans cet exemple, Copilot propose la suggestion de code suivante, accompagnée de l’explication.

public async Task<ActionResult<string>> QueryCustomerDB()
{
    Customer c = await QueryCustomerFromDbAsync("Dana");
    return "success:taskwait";
}

Si vous constatez des problèmes de performances liés aux requêtes de base de données, vous pouvez utiliser l’outil Base de données pour déterminer si certains appels sont plus lents. Ces données peuvent indiquer une opportunité d’optimisation des requêtes. Pour un didacticiel montrant comment utiliser l'outil Base de données pour étudier un problème de performances, consultez l'étude de cas : Guide du débutant pour l'optimisation du code. L’outil Base de données prend en charge .NET Core avec ADO.NET ou Entity Framework Core.

Pour obtenir des visualisations dans Visual Studio d’un comportement de thread individuel, vous pouvez utiliser la fenêtre Piles parallèles lors du débogage. Cette fenêtre affiche les threads individuels, ainsi que des informations sur les threads qui attendent, les threads qu’ils attendent et les blocages.

Pour plus d’informations sur la privation de pool de threads, consultez Détection de la privation de pool de threads.

Étapes suivantes

Les articles et billets de blog suivants fournissent plus d’informations pour vous aider à apprendre à utiliser efficacement les outils de performances Visual Studio.