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 naučíte, jak odstraňovat chyby ve scénáři 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í dotnet-counters
  • Použijte 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

V kurzu se používá:

Čítače procesoru

Před pokusem o tento tutoriál nainstalujte nejnovější verzi 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é údaje, musíte sledovat vysoké využití 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ěříme na Last Delta hodnoty dotnet.process.cpu.time, ty nám ukazují, kolik sekund během období aktualizace (které je aktuálně nastaveno na výchozí hodnotu 1 s) byl procesor činný. Jakmile je webová aplikace spuštěna, ihned po spuštění se procesor vůbec nevyužívá a tyto změny jsou obě 0. Přejděte na trasu api/diagscenario/highcpu s 60000 jako parametrem 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 pracuje na vyšší frekvenci, 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 použijte profiler k pochopení toho, co kód dělá. dotnet-trace collect funguje ve všech operačních systémech, ale závislost na bezpečných bodech a pouze spravované zásobníky volání ji omezují na obecnější informace než profiler s povědomím o jádru, jako je ETW pro Windows nebo perf pro Linux. V závislosti na verzi operačního systému a platformy .NET můžou být k dispozici vylepšené možnosti profilace – podrobné pokyny najdete na kartách specifických pro konkrétní platformu.

Použijte dotnet-trace collect-linux (.NET 10+)

V .NET 10 a novějších je dotnet-trace collect-linux doporučenou metodou profilování na Linuxu. Kombinuje EventPipe s perf_events na úrovni operačního systému a vytváří jednotné trasování, které zahrnuje spravované i nativní zásobníky volání, a to vše bez nutnosti restartování procesu. To vyžaduje kořenová oprávnění a linuxové jádro 6.4+ s CONFIG_USER_EVENTS=y. Úplné požadavky najdete v požadavcích na collect-linux .

Ujistěte se, že je ukázkový cíl ladění nakonfigurován na cíl .NET 10 nebo novější, potom ho spusťte a znovu využijte koncový bod s vysokým využitím CPU (https://localhost:5001/api/diagscenario/highcpu/60000). Když běží během 1minutového požadavku, spusťte dotnet-trace collect-linux zachytávání trasování na celém počítači:

sudo dotnet-trace collect-linux

Nechte ji běžet asi 20 až 30 sekund a pak stisknutím kombinace kláves Ctrl+C nebo Enter kolekci zastavte. Výsledkem je .nettrace soubor, který zahrnuje jak spravované, tak nativní zásobníky volání.

Otevřete .nettrace s PerfView a použijte zobrazení Zásobníky procesoru k identifikaci metod, které spotřebovávají nejvíce času procesoru.

Informace o řešení symbolů nativního běhového prostředí v trasování viz téma Získání symbolů pro nativní běhové rámce.

Použijte perf

Nástroj perf lze také použít ke generování profilů aplikací .NET Core. Ukončete předchozí případ ukázkového ladícího cíle.

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ý objekt ladění ve stejném sezení 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, který můžete zobrazit v prohlížeči a analyzovat problém s výkonem.

Obrázek SVG grafu plamenů

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

Všechny soubory s příponou *.nettrace lze analyzovat ve 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