다음을 통해 공유


.NET Core에서 높은 CPU 사용량 디버그

이 문서는 .NET Core 3.1 SDK 이상 버전에 적용됩니다 ✔️.

이 자습서에서는 과도한 CPU 사용 시나리오를 디버그하는 방법을 알아봅니다. 제공된 예제 ASP.NET Core 웹앱 소스 코드 리포지토리를 사용하여 의도적으로 교착 상태를 일으킬 수 있습니다. 엔드포인트는 응답을 중지하고 스레드 누적을 경험합니다. 다양한 도구를 사용하여 진단 데이터의 몇 가지 주요 부분을 사용하여 이 시나리오를 진단하는 방법을 알아봅니다.

이 자습서에서는 다음을 수행합니다.

  • 높은 CPU 사용량 조사
  • dotnet-counters를 사용하여 CPU 사용량 확인
  • 추적 생성에 dotnet-trace 사용
  • PerfView의 프로필 성능
  • 과도한 CPU 사용량 진단 및 해결

필수 조건

이 자습서에서는 다음을 사용합니다.

CPU 카운터

이 자습서를 시도하기 전에 최신 버전의 dotnet-counters를 설치하세요.

dotnet tool install --global dotnet-counters

앱이 .NET 9보다 오래된 .NET 버전을 실행하는 경우 dotnet-counters의 출력 UI는 약간 다르게 표시됩니다. 자세한 내용은 dotnet-counters를 참조하세요 .

진단 데이터를 수집하기 전에 높은 CPU 상태를 관찰해야 합니다. 프로젝트 루트 디렉터리에서 다음 명령을 사용하여 샘플 애플리케이션 을 실행합니다.

dotnet run

현재 CPU 사용량을 확인하려면 dotnet-counters 도구 명령을 사용합니다.

dotnet-counters monitor -n DiagnosticScenarios --showDeltas

출력은 다음과 유사해야 합니다.

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

Last Deltadotnet.process.cpu.time 초점을 맞추면 새로 고침 기간(현재 기본값인 1초로 설정됨) 내에 CPU가 활성화된 시간(초)이 표시됩니다. 웹앱이 실행되면 시작 직후 CPU가 전혀 소비되지 않으며 이러한 델타는 둘 다 0됩니다. api/diagscenario/highcpu 경로 매개 변수로 경로 60000 로 이동합니다.

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

이제 dotnet-counters 명령을 다시 실행합니다.

dotnet-counters monitor -n DiagnosticScenarios --showDeltas

아래와 같이 CPU 사용량이 증가해야 합니다(호스트 머신에 따라 다양한 CPU 사용량이 예상됨).

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

요청 기간 동안 CPU 사용량은 증가된 값을 가리킵니다.

팁 (조언)

더 높은 CPU 사용량을 시각화하려면 동시에 여러 브라우저 탭에서 이 엔드포인트를 연습할 수 있습니다.

이 시점에서 CPU가 예상보다 높게 실행되고 있다고 안전하게 말할 수 있습니다. 문제의 영향을 식별하는 것이 원인을 찾는 데 중요합니다. 문제의 원인을 찾기 위해 진단 도구 외에도 높은 CPU 사용량의 효과를 사용합니다.

프로파일러를 사용하여 높은 CPU 분석

CPU 사용량이 높은 앱을 분석할 때 코드가 수행하는 작업을 파악할 수 있는 진단 도구가 필요합니다. 일반적인 선택은 프로파일러이며 선택할 수 있는 프로파일러 옵션이 서로 다릅니다. dotnet-trace 는 모든 운영 체제에서 사용할 수 있지만 안전 지점 바이어스 및 관리 전용 호출 스택의 제한으로 인해 Linux용 'perf' 또는 Windows용 ETW와 같은 커널 인식 프로파일러에 비해 더 일반적인 정보가 생성됩니다. 성능 조사에 관리 코드만 포함된 경우 일반적으로 dotnet-trace 충분합니다.

perf 도구를 사용하여 .NET Core 앱 프로필을 생성할 수 있습니다. dotnet 추적도 사용할 수 있지만 이 도구를 보여 줍니다. 샘플 디버그 대상의 이전 인스턴스를 종료합니다.

.NET 앱이 DOTNET_PerfMapEnabled 디렉터리에 파일을 만들도록 환경 변수를 map/tmp 설정합니다. 이 map 파일은 이름으로 JIT 생성 함수에 CPU 주소를 매핑하는 데 사용됩니다 perf . 자세한 내용은 성능 맵 및 jit 덤프 내보내기를 참조하세요.

동일한 터미널 세션에서 샘플 디버그 대상 을 실행합니다.

export DOTNET_PerfMapEnabled=1
dotnet run

높은 CPU API 엔드포인트()를https://localhost:5001/api/diagscenario/highcpu/60000 다시 실행합니다. 1분 요청 내에서 실행되는 동안 프로세스 ID를 사용하여 perf 명령을 실행합니다.

sudo perf record -p 2266 -g

perf 명령은 성능 수집 프로세스를 시작합니다. 약 20-30초 동안 실행한 다음 Ctrl+C 를 눌러 컬렉션 프로세스를 종료합니다. 동일한 perf 명령을 사용하여 추적의 출력을 볼 수 있습니다.

sudo perf report -f

다음 명령을 사용하여 화염 그래프 를 생성할 수도 있습니다.

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

이 명령은 성능 문제를 조사하기 위해 브라우저에서 볼 수 있는 항목을 생성 flamegraph.svg 합니다.

화염 그래프 SVG 이미지

Visual Studio를 사용하여 높은 CPU 데이터 분석

모든 *.nettrace 파일은 Visual Studio에서 분석할 수 있습니다. Visual Studio에서 Linux *.nettrace 파일을 분석하려면 필요한 다른 문서 외에도 *.nettrace 파일을 Windows 컴퓨터로 전송한 다음 Visual Studio에서 *.nettrace 파일을 엽니다. 자세한 내용은 CPU 사용량 현황 데이터 분석을 참조하세요.

참고하십시오

다음 단계