Partager via


Guide du débutant pour optimiser le code et réduire les coûts de calcul (C#, Visual Basic, C++, F#)

Réduire le temps de calcul signifie réduire les coûts, donc l’optimisation de votre code peut faire des économies. Dans cet article, nous montrons comment vous pouvez utiliser différents outils de profilage pour vous aider à accomplir cette tâche.

Au lieu de fournir des instructions pas à pas, notre intention est de vous montrer comment utiliser efficacement les outils de profilage et comment interpréter les données. L’outil Utilisation du processeur peut vous aider à capturer et à visualiser où les ressources de calcul sont utilisées dans votre application. Les affichages Utilisation de l’UC tels que l’arborescence des appels et le graphique en flamme fournissent une visualisation graphique agréable de l’endroit où le temps est passé dans votre application. En outre, les insights automatiques peuvent afficher des optimisations précises qui peuvent avoir un impact important. D’autres outils de profilage peuvent également vous aider à isoler les problèmes. Pour comparer les outils, consultez Quel outil choisir ?

Démarrer une investigation

  • Commencez votre enquête en effectuant une trace de l’utilisation de l’UC. L’outil Utilisation du processeur est souvent utile pour commencer les investigations de performances et pour optimiser le code afin de réduire les coûts.
  • Ensuite, si vous souhaitez obtenir des informations supplémentaires pour isoler les problèmes ou améliorer le niveau de performance, envisagez de collecter une trace à l’aide de l’un des autres outils de profilage. Par exemple :
    • Examinez l’utilisation de la mémoire. Pour .NET, essayez d’abord l’outil Allocation d’objets .NET. Pour .NET ou C++, vous pouvez consulter l’outil Utilisation de la mémoire.
    • Si votre application utilise l’E/S de fichier, utilisez l’outil D/S fichier.
    • Si vous utilisez ADO.NET ou Entity Framework, vous pouvez essayer l’outil Base de données pour examiner les requêtes SQL, l’heure précise des requêtes, etc.

Exemple de collections de données

Les exemples de captures d’écran présentés dans cet article sont basés sur une application .NET qui exécute des requêtes sur une base de données de blogs et de billets de blog associés. Vous allez d’abord examiner une trace d’utilisation du processeur pour rechercher les opportunités d’optimisation du code et de réduction des coûts de calcul. Après avoir une idée générale de ce qui se passe, vous examinerez également les traces d’autres outils de profilage pour vous aider à isoler les problèmes.

La collecte de données nécessite les étapes suivantes (non affichées ici) :

  • Définir votre application sur une build de mise en production
  • Sélectionnez l’outil Utilisation du processeur dans le Profileur de performances (Alt+F2). (Les étapes ultérieures impliquent quelques-uns des autres outils.)
  • À partir du Profileur de performances, démarrez l’application et collectez une trace.

Inspecter les zones d’utilisation élevée du processeur

Commencez par collecter une trace avec l’outil Utilisation du processeur. Lorsque les données de diagnostic se chargent, tout d’abord vérifiez la page de rapport .diagsession initiale qui affiche Insights supérieurs et le chemin chaud. Le chemin chaud affiche le chemin du code avec l’utilisation la plus élevée du processeur dans votre application. Ces sections peuvent fournir des conseils pour vous aider à identifier rapidement les problèmes de performances que vous pouvez améliorer.

Vous pouvez également afficher le chemin chaud dans l’affichage Arborescence des appels. Pour ouvrir cet affichage, utilisez le lien Ouvrir les détails dans le rapport, puis sélectionnez Arborescence des appels.

Dans cet affichage, vous voyez à nouveau le chemin chaud, qui montre une utilisation élevée du processeur pour la GetBlogTitleX méthode dans l’application, en utilisant environ 60 % de l’utilisation du processeur de l’application. Toutefois, la valeur de l’auto-processeur pour GetBlogTitleX est faible, seulement environ .10%. Contrairement au processeur total, la valeur de l’auto-processeur exclut le temps passé dans d’autres fonctions. Nous savons qu’il faut regarder plus loin dans l’Arborescence des appels pour trouver le goulot d’étranglement réel.

Capture d’écran de l’arborescence des appels dans l’outil Utilisation du processeur.

GetBlogTitleX effectue des appels externes à deux DLL LINQ, qui utilisent la plupart du temps processeur, comme le montrent les valeurs très élevées de l’auto-processeur. Il s’agit du premier indice que vous pouvez rechercher une requête LINQ en tant que zone à optimiser.

Capture d’écran de l’arborescence des appels dans l’outil d’utilisation de l’UC avec l’UC en surbrillance.

Pour obtenir une arborescence d’appels visualisées et un autre affichage des données, basculez vers la vue Graphique de type flamme (sélectionnez dans la même liste que l’arborescence des appels). Là encore, il semble que la méthode GetBlogTitleX soit responsable d’une grande partie de l’utilisation du processeur de l’application (illustré en jaune). Les appels externes aux DLL LINQ s’affichent sous la boîte GetBlogTitleX et ils utilisent tout le temps processeur pour la méthode.

Capture d’écran de l’affichage Graphique de type flamme dans l’outil Utilisation du processeur.

Collecter des données supplémentaires

Souvent, d’autres outils peuvent fournir des informations supplémentaires pour contribuer à l’analyse et isoler le problème. Pour cet exemple, nous adoptons l’approche suivante :

  • Tout d’abord, nous allons examiner l’utilisation de la mémoire. Il peut y avoir une corrélation entre une utilisation élevée de l’UC et une utilisation élevée de la mémoire. Il peut donc être utile d’examiner les deux pour isoler le problème.
  • Comme nous avons identifié les DLL LINQ, nous allons également examiner l’outil Base de données.

Vérifier l’utilisation de la mémoire

Pour voir ce qui se passe avec l’application en termes d’utilisation de la mémoire, collectez une trace à l’aide de l’outil d’allocation d’objets .NET. (Pour C++, utilisez l’outil d’utilisation de la mémoire). L’affichage Arborescence des appels dans la trace de mémoire affiche le chemin chaud et vous aide à identifier une zone d’utilisation élevée de la mémoire. Pas de surprise à ce stade, la méthode GetBlogTitleX semble générer beaucoup d’objets ! Plus de 900 000 allocations d’objets, en fait.

Capture d’écran de l’arborescence des appels dans l’outil d’allocation d’objets .NET.

La plupart des objets créés sont des chaînes, des tableaux d’objets et des Int32. Vous pourrez peut-être voir comment ces types sont générés en examinant le code source.

Vérifier la requête dans l’outil Base de données

Vous pouvez sélectionner plusieurs fois l’outil Base de données ainsi que l’utilisation de l’UC. Une fois que vous avez collecté une trace, sélectionnez l’onglet Requêtes dans la page diagnostics. Dans l’onglet Requêtes de la trace de base de données, vous pouvez voir que la première ligne affiche la requête la plus longue, 2 446 ms. La colonne Enregistrements indique le nombre d’enregistrements que la requête lit. Nous pouvons utiliser ces informations pour une comparaison ultérieure.

Capture d’écran des requêtes de base de données dans l’outil Base de données.

En examinant l’instruction SELECT générée par LINQ dans la colonne Requête, vous identifiez la première ligne comme la requête associée à la méthode GetBlogTitleX. Pour afficher la chaîne de requête complète, développez la largeur de colonne si nécessaire. La chaîne de requête complète est la suivante :

SELECT "b"."Url", "b"."BlogId", "p"."PostId", "p"."Author", "p"."BlogId", "p"."Content", "p"."Date", "p"."MetaData", "p"."Title"
FROM "Blogs" AS "b" LEFT JOIN "Posts" AS "p" ON "b"."BlogId" = "p"."BlogId" ORDER BY "b"."BlogId"

Notez que vous récupérez ici beaucoup de valeurs de colonne, peut-être plus que nécessaire. Examinons le code source.

Optimiser le code

Il est temps d’examiner le code source GetBlogTitleX. Dans l’outil Base de données, cliquez avec le bouton droit sur la requête et choisissez Accéder au fichier source. Dans le code source de GetBlogTitleX, nous trouvons le code suivant qui utilise LINQ pour lire la base de données.

foreach (var blog in db.Blogs.Select(b => new { b.Url, b.Posts }).ToList())
  {
    foreach (var post in blog.Posts)
    {
      if (post.Author == "Fred Smith")
      {
        Console.WriteLine($"Post: {post.Title}");
      }
  }
}

Ce code utilise des boucles foreach pour rechercher la base de données tous les blogs avec « Fred Smith » comme auteur. En l’examinant, vous pouvez voir que de nombreux objets sont générés en mémoire : un nouveau tableau d’objets pour chaque blog de la base de données, des chaînes associées pour chaque URL et des valeurs pour les propriétés contenues dans les billets, comme l’ID de blog.

Vous effectuez quelques recherches et trouvez des suggestions courantes sur la façon d’optimiser les requêtes LINQ et de créer ce code.

foreach (var x in db.Posts.Where(p => p.Author.Contains("Fred Smith")).Select(b => b.Title).ToList())
{
  Console.WriteLine("Post: " + x);
}

Dans ce code, vous avez apporté plusieurs modifications pour optimiser la requête :

  • Vous avez ajouté la clause Where et supprimé l’une des boucles foreach.
  • Vous avez projeté uniquement la propriété Titre dans l’instruction Select, ce qui est tout ce dont vous avez besoin dans cet exemple.

Ensuite, retestez à l’aide des outils de profilage.

Vérification des résultats

Après avoir mis à jour le code, réexécutez l’outil Utilisation du processeur pour collecter une trace. L’affichage Arborescence des appels montre que GetBlogTitleX ne s’exécute que 1 754 ms, en utilisant 37 % du processeur total de l’application, une amélioration significative par rapport à 59 %.

Capture d’écran de l’utilisation améliorée du processeur dans l’arborescence des appels de l’outil Utilisation du processeur.

Basculez vers l’affichage Graphique de type flamme pour afficher une autre visualisation de l’amélioration. Dans cet affichage, GetBlogTitleX utilise également une plus petite partie du processeur.

Capture d’écran de l’utilisation améliorée du processeur dans l’affichage Graphique de type flamme de l’outil Utilisation du processeur.

Vérifiez les résultats dans la trace de l’outil de base de données et seuls deux enregistrements sont lus à l’aide de cette requête, au lieu de 100 000 ! En outre, la requête est beaucoup simplifiée et élimine la jointure LEFT inutile qui a été générée précédemment.

Capture d’écran de temps de requête plus rapide dans l’outil Base de données.

Ensuite, vérifiez à nouveau les résultats dans l’outil d’allocation d’objets .NET et vérifiez que GetBlogTitleX n’est responsable que de 56 000 allocations d’objets, soit une réduction de près de 95 % par exemple de 900 000 !

Capture d’écran des allocations de mémoire réduites dans l’outil d’allocation d’objets .NET.

Itérer

Plusieurs optimisations peuvent être nécessaires et vous pouvez continuer à itérer avec les modifications du code pour voir quelles modifications améliorent le niveau de performance et réduisent votre coût de calcul.

Étapes suivantes

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