Sdílet prostřednictvím


Ladění vysokého využití procesoru v .NET Core

Tento článek se vztahuje na: ✔️ .NET Core 3.1 SDK a novější verze

V tomto kurzu se dozvíte, jak ladit scénář nadměrného využití procesoru. Pomocí poskytnutého příkladu ASP.NET úložiště zdrojového kódu webové aplikace Core můžete záměrně způsobit zablokování. Koncový bod přestane reagovat a dojde k nahromadění vláken. Dozvíte se, jak můžete pomocí různých nástrojů diagnostikovat tento scénář několika klíčovými částmi diagnostických dat.

V tomto kurzu:

  • Zkoumání vysokého využití procesoru
  • Určení využití procesoru pomocí čítačů dotnet
  • Použití trasování dotnet-trace pro generování trasování
  • Výkon profilu v nástroji PerfView
  • Diagnostika a řešení nadměrného využití procesoru

Požadavky

Kurz používá:

Čítače procesoru

Před pokusem o tento kurz nainstalujte nejnovější verzi čítačů dotnet-counters:

dotnet tool install --global dotnet-counters

Pokud vaše aplikace používá verzi .NET starší než .NET 9, bude výstupní uživatelské rozhraní čítačů dotnet-counter vypadat trochu jinak; Podrobnosti najdete v čítačích dotnet. .

Než se pokusíte shromáždit diagnostická data, musíte sledovat vysokou podmínku procesoru. Spusťte ukázkovou aplikaci pomocí následujícího příkazu z kořenového adresáře projektu.

dotnet run

Pokud chcete zkontrolovat aktuální využití procesoru, použijte příkaz nástroje dotnet-counters :

dotnet-counters monitor -n DiagnosticScenarios --showDeltas

Výstup by měl vypadat přibližně takto:

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

Když se zaměříte na Last Delta hodnoty dotnet.process.cpu.time, tyto nám říkají, kolik sekund v období aktualizace (aktuálně je nastaveno na výchozí hodnotu 1 s), procesor byl aktivní. Když je webová aplikace spuštěná hned po spuštění, procesor se vůbec nespotřebovává a tyto rozdíly jsou oba 0. Přejděte na trasu api/diagscenario/highcpu jako 60000 parametr trasy:

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

Teď spusťte příkaz dotnet-counters znovu.

dotnet-counters monitor -n DiagnosticScenarios --showDeltas

Měli byste vidět zvýšení využití procesoru, jak je znázorněno níže (v závislosti na hostitelském počítači očekáváte různé využití procesoru):

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

Po celou dobu trvání požadavku se využití procesoru přesune kolem zvýšené hodnoty.

Návod

Pokud chcete vizualizovat ještě vyšší využití procesoru, můžete tento koncový bod uplatnit na několika kartách prohlížeče současně.

V tomto okamžiku můžete bezpečně říci, že procesor běží vyšší, než očekáváte. Identifikace účinků problému je klíčem k nalezení příčiny. K nalezení příčiny problému použijeme kromě diagnostických nástrojů také účinek vysoké spotřeby procesoru.

Analýza vysokého využití procesoru pomocí Profileru

Při analýze aplikace s vysokým využitím procesoru potřebujete diagnostický nástroj, který vám poskytne přehled o tom, co kód dělá. Obvyklou volbou je profiler a existují různé možnosti profileru, ze které si můžete vybrat. dotnet-trace lze použít ve všech operačních systémech, ale jeho omezení předsudků bezpečných bodů a volání pouze spravovaných volání vedou k obecnějším informacím v porovnání s profilerem podporujícím jádro, jako je "perf" pro Linux nebo ETW pro Windows. Pokud vaše šetření výkonu zahrnuje jenom spravovaný kód, bude obecně dotnet-trace dostačující.

Nástroj perf lze použít ke generování profilů aplikací .NET Core. Tento nástroj si ukážeme, i když se dá použít i dotnet-trace. Ukončete předchozí instanci ukázkového cíle ladění.

Nastavte proměnnou DOTNET_PerfMapEnabled prostředí tak, aby aplikace .NET vytvořila map soubor v /tmp adresáři. Tento map soubor se používá perf k mapování adres procesoru na funkce generované JIT podle názvu. Další informace najdete v tématu Export map výkonu a výpisů paměti jit.

Spusťte ukázkový cíl ladění ve stejné relaci terminálu.

export DOTNET_PerfMapEnabled=1
dotnet run

Znovu si procvičte koncový bod rozhraní API s vysokým využitím procesoru (https://localhost:5001/api/diagscenario/highcpu/60000). Během 1minutového požadavku spusťte perf příkaz s ID procesu:

sudo perf record -p 2266 -g

Příkaz perf spustí proces shromažďování výkonu. Nechte ho běžet asi 20 až 30 sekund a pak stisknutím ctrl+C ukončete proces shromažďování. K zobrazení výstupu trasování můžete použít stejný perf příkaz.

sudo perf report -f

Graf plamene můžete také vygenerovat pomocí následujících příkazů:

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

Tento příkaz vygeneruje flamegraph.svg , že můžete zobrazit v prohlížeči a prošetřit problém s výkonem:

Obrázek SVG grafu plamene

Analýza dat s vysokým využitím procesoru pomocí sady Visual Studio

Všechny soubory *.nettrace je možné analyzovat v sadě Visual Studio. Pokud chcete analyzovat soubor Linux *.nettrace v sadě Visual Studio, přeneste soubor *.nettrace kromě dalších potřebných dokumentů na počítač s Windows a pak otevřete soubor *.nettrace v sadě Visual Studio. Další informace najdete v tématu Analýza dat využití procesoru.

Viz také

Další kroky