Felsöka hög CPU-användning i .NET Core

Den här artikeln gäller för: ✔️ .NET Core 3.1 SDK och senare versioner

I den här självstudien får du lära dig hur du felsöker ett scenario med överdriven CPU-användning. Med hjälp av det angivna exemplet ASP.NET Källkodslagringsplats för Core-webbappen kan du avsiktligt orsaka ett dödläge. Slutpunkten slutar svara och får trådansamling. Du får lära dig hur du kan använda olika verktyg för att diagnostisera det här scenariot med flera viktiga delar av diagnostikdata.

I den här handledningen kommer du att:

Förutsättningar

Självstudien använder:

CPU-räknare

Innan du provar den här självstudien installerar du den senaste versionen av dotnet-counters:

dotnet tool install --global dotnet-counters

Om din app kör en version av .NET som är äldre än .NET 9 ser utdatagränssnittet för dotnet-counters något annorlunda ut. se dotnet-counters för mer information.

Innan du försöker samla in diagnostikdata måste du observera ett högt CPU-tillstånd. Kör exempelprogrammet med hjälp av följande kommando från projektrotkatalogen.

dotnet run

Om du vill kontrollera den aktuella CPU-användningen använder du verktyget dotnet-counters :

dotnet-counters monitor -n DiagnosticScenarios --showDeltas

Utdata bör likna följande:

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

Om du fokuserar på värdena Last Deltaför visar dessa hur många sekunder inom uppdateringsperioden (för närvarande inställt på dotnet.process.cpu.time standardvärdet 1 s) processorn har varit aktiv. När webbappen körs, direkt efter start, förbrukas inte processorn alls och dessa deltan är båda 0. Navigera till api/diagscenario/highcpu vägen med 60000 som vägparameter:

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

Kör nu kommandot dotnet-counters igen.

dotnet-counters monitor -n DiagnosticScenarios --showDeltas

Du bör se en ökning av CPU-användningen enligt nedan (beroende på värddatorn kan du förvänta dig varierande CPU-användning):

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

Under hela begärans varaktighet hovra cpu-användningen runt det ökade värdet.

Tips/Råd

Om du vill visualisera en ännu högre CPU-användning kan du använda den här slutpunkten på flera webbläsarflikar samtidigt.

I det här läget kan du på ett säkert sätt säga att processorn körs högre än förväntat. Att identifiera effekterna av ett problem är nyckeln till att hitta orsaken. Vi använder effekten av hög CPU-förbrukning utöver diagnostikverktyg för att hitta orsaken till problemet.

Analysera hög CPU med Profiler

När du analyserar en app med hög CPU-användning behöver du ett diagnostikverktyg som kan ge insikter om vad koden gör. Det vanliga valet är en profilerare, och det finns olika profilerare alternativ att välja mellan. dotnet-trace kan användas på alla operativsystem, men dess begränsningar av safe-point bias och managed-only callstacks resulterar i mer allmän information jämfört med en kernelmedveten profilerare som "perf" för Linux eller ETW för Windows. Om din prestandaundersökning endast omfattar hanterad kod räcker det vanligtvis dotnet-trace .

Verktyget perf kan användas för att generera .NET Core-appprofiler. Vi kommer att demonstrera det här verktyget, även om dotnet-trace också kan användas. Avsluta den tidigare instansen av exempelfelsökningsmålet.

DOTNET_PerfMapEnabled Ange miljövariabeln så att .NET-appen skapar en map fil i /tmp katalogen. Den här map filen används av perf för att mappa CPU-adresser till JIT-genererade funktioner efter namn. Mer information finns i Exportera perf-kartor och jit-dumpar.

Kör exempelfelsökningsmålet i samma terminalsession.

export DOTNET_PerfMapEnabled=1
dotnet run

Träna den höga CPU API-slutpunkten (https://localhost:5001/api/diagscenario/highcpu/60000) igen. När den körs inom en minuts begäran kör perf du kommandot med ditt process-ID:

sudo perf record -p 2266 -g

Kommandot perf startar processen för prestandainsamling. Låt den köras i cirka 20–30 sekunder och tryck sedan på Ctrl+C för att avsluta insamlingsprocessen. Du kan använda samma perf kommando för att se utdata från spårningen.

sudo perf report -f

Du kan också generera en flame-graph med hjälp av följande kommandon:

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

Det här kommandot genererar en flamegraph.svg som du kan visa i webbläsaren för att undersöka prestandaproblemet:

SvG-bild för flamdiagram

Analysera hög CPU-data med Visual Studio

Alla *.nettrace-filer kan analyseras i Visual Studio. Om du vill analysera en Linux *.nettrace-fil i Visual Studio överför du filen *.nettrace, förutom de andra nödvändiga dokumenten, till en Windows-dator och öppnar sedan filen *.nettrace i Visual Studio. Mer information finns i Analysera CPU-användningsdata.

Se även

Nästa steg