Partager via


Déboguer une utilisation élevée du processeur dans .NET Core

Cet article s’applique au ✔️ Kit de développement logiciel (SDK) .NET Core 3.1 et versions ultérieures

Dans ce tutoriel, vous allez apprendre à déboguer un scénario d’utilisation excessive du processeur. À l’aide de l’exemple fourni ASP.NET référentiel de code source d’application web Core , vous pouvez provoquer un blocage intentionnellement. Le point de terminaison cesse de répondre et d’expérimenter l’accumulation de threads. Vous découvrirez comment utiliser différents outils pour diagnostiquer ce scénario avec plusieurs éléments clés de données de diagnostic.

Dans ce didacticiel, vous allez :

  • Examiner l’utilisation élevée du processeur
  • Déterminer l’utilisation du processeur avec dotnet-counters
  • Utiliser dotnet-trace pour la génération de trace
  • Performances de profil dans PerfView
  • Diagnostiquer et résoudre une utilisation excessive du processeur

Prerequisites

Le tutoriel utilise :

Compteurs d’UC

Avant d’essayer ce tutoriel, installez la dernière version de dotnet-counters :

dotnet tool install --global dotnet-counters

Si votre application exécute une version de .NET antérieure à .NET 9, l’interface utilisateur de sortie des compteurs dotnet est légèrement différente ; pour plus d’informations, consultez dotnet-counters .

Avant de tenter de collecter des données de diagnostic, vous devez observer une condition processeur élevée. Exécutez l’exemple d’application à l’aide de la commande suivante à partir du répertoire racine du projet.

dotnet run

Pour vérifier l’utilisation actuelle du processeur, utilisez la commande de l’outil dotnet-counters :

dotnet-counters monitor -n DiagnosticScenarios --showDeltas

La sortie doit être similaire à ce qui suit :

Press p to pause, r to resume, q to quit.
    Status: Running

Name                                                            Current Value      Last Delta
[System.Runtime]
    dotnet.assembly.count ({assembly})                               111               0
    dotnet.gc.collections ({collection})
        gc.heap.generation
        ------------------
        gen0                                                           8               0
        gen1                                                           1               0
        gen2                                                           0               0
    dotnet.gc.heap.total_allocated (By)                        4,042,656          24,512
    dotnet.gc.last_collection.heap.fragmentation.size (By)
        gc.heap.generation
        ------------------
        gen0                                                     801,728               0
        gen1                                                       6,048               0
        gen2                                                           0               0
        loh                                                            0               0
        poh                                                            0               0
    dotnet.gc.last_collection.heap.size (By)
        gc.heap.generation
        ------------------
        gen0                                                     811,512               0
        gen1                                                     562,024               0
        gen2                                                   1,095,056               0
        loh                                                       98,384               0
        poh                                                       24,528               0
    dotnet.gc.last_collection.memory.committed_size (By)       5,623,808               0
    dotnet.gc.pause.time (s)                                           0.019           0
    dotnet.jit.compilation.time (s)                                    0.582           0
    dotnet.jit.compiled_il.size (By)                             138,895               0
    dotnet.jit.compiled_methods ({method})                         1,470               0
    dotnet.monitor.lock_contentions ({contention})                     4               0
    dotnet.process.cpu.count ({cpu})                                  22               0
    dotnet.process.cpu.time (s)
        cpu.mode
        --------
        system                                                         0.109           0
        user                                                           0.453           0
    dotnet.process.memory.working_set (By)                    65,515,520               0
    dotnet.thread_pool.queue.length ({work_item})                      0               0
    dotnet.thread_pool.thread.count ({thread})                         0               0
    dotnet.thread_pool.work_item.count ({work_item})                   6               0
    dotnet.timer.count ({timer})                                       0               0

En mettant l’accent sur les Last Delta valeurs de , celles-ci nous indiquent le nombre de dotnet.process.cpu.timesecondes pendant la période d’actualisation (actuellement définie sur la valeur par défaut de 1 s) que l’UC a été active. Avec l’application web en cours d’exécution, immédiatement après le démarrage, l’UC n’est pas consommée du tout et ces deltas sont tous les deux 0. Accédez à l’itinéraire api/diagscenario/highcpu avec 60000 comme paramètre d’itinéraire :

https://localhost:5001/api/diagscenario/highcpu/60000

À présent, réexécutez la commande dotnet-counters .

dotnet-counters monitor -n DiagnosticScenarios --showDeltas

Vous devriez voir une augmentation de l’utilisation du processeur, comme indiqué ci-dessous (en fonction de l’ordinateur hôte, attendez-vous à une utilisation variable de l’UC) :

Press p to pause, r to resume, q to quit.
    Status: Running

Name                                                            Current Value      Last Delta
[System.Runtime]
    dotnet.assembly.count ({assembly})                               111               0
    dotnet.gc.collections ({collection})
        gc.heap.generation
        ------------------
        gen0                                                           8               0
        gen1                                                           1               0
        gen2                                                           0               0
    dotnet.gc.heap.total_allocated (By)                        4,042,656          24,512
    dotnet.gc.last_collection.heap.fragmentation.size (By)
        gc.heap.generation
        ------------------
        gen0                                                     801,728               0
        gen1                                                       6,048               0
        gen2                                                           0               0
        loh                                                            0               0
        poh                                                            0               0
    dotnet.gc.last_collection.heap.size (By)
        gc.heap.generation
        ------------------
        gen0                                                     811,512               0
        gen1                                                     562,024               0
        gen2                                                   1,095,056               0
        loh                                                       98,384               0
        poh                                                       24,528               0
    dotnet.gc.last_collection.memory.committed_size (By)       5,623,808               0
    dotnet.gc.pause.time (s)                                           0.019           0
    dotnet.jit.compilation.time (s)                                    0.582           0
    dotnet.jit.compiled_il.size (By)                             138,895               0
    dotnet.jit.compiled_methods ({method})                         1,470               0
    dotnet.monitor.lock_contentions ({contention})                     4               0
    dotnet.process.cpu.count ({cpu})                                  22               0
    dotnet.process.cpu.time (s)
        cpu.mode
        --------
        system                                                         0.344           0.013
        user                                                          14.203           0.963
    dotnet.process.memory.working_set (By)                    65,515,520               0
    dotnet.thread_pool.queue.length ({work_item})                      0               0
    dotnet.thread_pool.thread.count ({thread})                         0               0
    dotnet.thread_pool.work_item.count ({work_item})                   6               0
    dotnet.timer.count ({timer})                                       0               0

Pendant toute la durée de la requête, l’utilisation du processeur pointe autour de la valeur accrue.

Conseil / Astuce

Pour visualiser une utilisation encore plus élevée du processeur, vous pouvez exercer ce point de terminaison dans plusieurs onglets de navigateur simultanément.

À ce stade, vous pouvez dire en toute sécurité que le processeur est en cours d’exécution plus élevé que prévu. Identifier les effets d’un problème est essentiel pour trouver la cause. Nous allons utiliser l’effet d’une consommation élevée du processeur en plus des outils de diagnostic pour trouver la cause du problème.

Analyser le processeur élevé avec Profiler

Lors de l’analyse d’une application avec une utilisation élevée du processeur, vous avez besoin d’un outil de diagnostic qui peut fournir des insights sur ce que fait le code. Le choix habituel est un profileur, et il existe différentes options de profileur à choisir. dotnet-trace peut être utilisé sur tous les systèmes d’exploitation, cependant, ses limitations de biais de point sûr et de piles d’appels managées entraînent des informations plus générales par rapport à un profileur prenant en charge le noyau comme « perf » pour Linux ou ETW pour Windows. Si votre enquête sur les performances implique uniquement du code managé, cela suffit généralement dotnet-trace .

L’outil perf peut être utilisé pour générer des profils d’application .NET Core. Nous allons illustrer cet outil, bien que dotnet-trace puisse également être utilisé. Quittez l’instance précédente de l’exemple de cible de débogage.

Définissez la DOTNET_PerfMapEnabled variable d’environnement pour que l’application .NET crée un map fichier dans le /tmp répertoire. Ce map fichier est utilisé pour perf mapper les adresses UC aux fonctions générées par JIT par nom. Pour plus d’informations, consultez Exporter les cartes perf et les vidages jit.

Exécutez l’exemple de cible de débogage dans la même session de terminal.

export DOTNET_PerfMapEnabled=1
dotnet run

Effectuez à nouveau l’exercice du point de terminaison d’API processeur élevé (https://localhost:5001/api/diagscenario/highcpu/60000). Pendant qu’elle s’exécute dans la requête de 1 minute, exécutez la commande avec votre perf ID de processus :

sudo perf record -p 2266 -g

La perf commande démarre le processus de collecte des performances. Laissez-le s’exécuter pendant environ 20 à 30 secondes, puis appuyez sur Ctrl+C pour quitter le processus de collecte. Vous pouvez utiliser la même perf commande pour afficher la sortie de la trace.

sudo perf report -f

Vous pouvez également générer un graphe de flamme à l’aide des commandes suivantes :

git clone --depth=1 https://github.com/BrendanGregg/FlameGraph
sudo perf script | FlameGraph/stackcollapse-perf.pl | FlameGraph/flamegraph.pl > flamegraph.svg

Cette commande génère une flamegraph.svg vue que vous pouvez afficher dans le navigateur pour examiner le problème de performances :

Image SVG de graphe de flamme

Analyse de données processeur élevées avec Visual Studio

Tous les fichiers *.nettrace peuvent être analysés dans Visual Studio. Pour analyser un fichier *.nettrace Linux dans Visual Studio, transférez le fichier *.nettrace, en plus des autres documents nécessaires, vers une machine Windows, puis ouvrez le fichier *.nettrace dans Visual Studio. Pour plus d’informations, consultez Analyser les données d’utilisation du processeur.

Voir aussi

Étapes suivantes