Bagikan melalui


Debug penggunaan CPU tinggi di .NET Core

Artikel ini berlaku untuk: ✔️ .NET Core 3.1 SDK dan versi yang lebih baru

Dalam tutorial ini, Anda akan mempelajari cara men-debug skenario penggunaan CPU yang berlebihan. Dengan menggunakan contoh yang disediakan ASP.NET repositori kode sumber aplikasi web Core , Anda dapat menyebabkan kebuntuan dengan sengaja. Titik akhir akan berhenti merespons dan mengalami akumulasi utas. Anda akan mempelajari bagaimana Anda dapat menggunakan berbagai alat untuk mendiagnosis skenario ini dengan beberapa bagian utama data diagnostik.

Dalam tutorial ini, Anda akan:

  • Menyelidiki penggunaan CPU yang tinggi
  • Menentukan penggunaan CPU dengan penghitung dotnet
  • Menggunakan dotnet-trace untuk pembuatan jejak
  • Performa profil di PerfView
  • Mendiagnosis dan memecahkan penggunaan CPU yang berlebihan

Prasyarat

Tutorial ini menggunakan:

Penghitung CPU

Sebelum mencoba tutorial ini, silakan instal versi terbaru penghitung dotnet:

dotnet tool install --global dotnet-counters

Jika aplikasi Anda menjalankan versi .NET yang lebih lama dari .NET 9, antarmuka pengguna output penghitung dotnet akan terlihat sedikit berbeda; lihat penghitung dotnet untuk detailnya.

Sebelum mencoba mengumpulkan data diagnostik, Anda perlu mengamati kondisi CPU yang tinggi. Jalankan aplikasi sampel menggunakan perintah berikut dari direktori akar proyek.

dotnet run

Untuk memeriksa penggunaan CPU saat ini, gunakan perintah alat penghitung dotnet :

dotnet-counters monitor -n DiagnosticScenarios --showDeltas

Outputnya harus mirip dengan yang berikut ini:

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

Berfokus pada Last Delta nilai dotnet.process.cpu.time, ini memberi tahu kami berapa detik dalam periode refresh (saat ini diatur ke default 1 d) CPU telah aktif. Dengan aplikasi web berjalan, segera setelah startup, CPU tidak digunakan sama sekali dan delta ini keduanya 0. Navigasikan ke api/diagscenario/highcpu rute dengan 60000 sebagai parameter rute:

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

Sekarang, jalankan kembali perintah dotnet-counters .

dotnet-counters monitor -n DiagnosticScenarios --showDeltas

Anda akan melihat peningkatan penggunaan CPU seperti yang ditunjukkan di bawah ini (tergantung pada komputer host, mengharapkan penggunaan CPU yang bervariasi):

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

Selama durasi permintaan, penggunaan CPU akan melayang di sekitar nilai yang ditingkatkan.

Petunjuk / Saran

Untuk memvisualisasikan penggunaan CPU yang lebih tinggi, Anda dapat menjalankan titik akhir ini di beberapa tab browser secara bersamaan.

Pada titik ini, Anda dapat dengan aman mengatakan CPU berjalan lebih tinggi dari yang Anda harapkan. Mengidentifikasi efek masalah adalah kunci untuk menemukan penyebabnya. Kami akan menggunakan efek konsumsi CPU yang tinggi selain alat diagnostik untuk menemukan penyebab masalah.

Menganalisis CPU Tinggi dengan Profiler

Saat menganalisis aplikasi dengan penggunaan CPU yang tinggi, Anda memerlukan alat diagnostik yang dapat memberikan wawasan tentang apa yang dilakukan kode. Pilihan yang biasa adalah profiler, dan ada berbagai opsi profiler untuk dipilih. dotnet-trace dapat digunakan pada semua sistem operasi, namun, keterbatasan bias titik aman dan tumpukan panggilan khusus terkelola menghasilkan informasi yang lebih umum dibandingkan dengan profiler yang sadar kernel seperti 'perf' untuk Linux atau ETW untuk Windows. Jika penyelidikan performa Anda hanya melibatkan kode terkelola, umumnya dotnet-trace akan cukup.

Alat ini perf dapat digunakan untuk menghasilkan profil aplikasi .NET Core. Kami akan menunjukkan alat ini, meskipun dotnet-trace juga dapat digunakan. Keluar dari instans sebelumnya dari target debug sampel.

Atur DOTNET_PerfMapEnabled variabel lingkungan untuk menyebabkan aplikasi .NET membuat map file di /tmp direktori. File ini map digunakan oleh perf untuk memetakan alamat CPU ke fungsi yang dihasilkan JIT berdasarkan nama. Untuk informasi selengkapnya, lihat Mengekspor peta perf dan jit dump.

Jalankan target debug sampel dalam sesi terminal yang sama.

export DOTNET_PerfMapEnabled=1
dotnet run

Jalankan titik akhir API CPU tinggi (https://localhost:5001/api/diagscenario/highcpu/60000) lagi. Saat berjalan dalam permintaan 1 menit, jalankan perf perintah dengan ID proses Anda:

sudo perf record -p 2266 -g

Perintah perf memulai proses pengumpulan performa. Biarkan berjalan selama sekitar 20-30 detik, lalu tekan Ctrl+C untuk keluar dari proses pengumpulan. Anda dapat menggunakan perintah yang sama perf untuk melihat output pelacakan.

sudo perf report -f

Anda juga dapat menghasilkan grafik api dengan menggunakan perintah berikut:

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

Perintah ini menghasilkan flamegraph.svg yang dapat Anda lihat di browser untuk menyelidiki masalah performa:

Gambar SVG grafik api

Menganalisis Data CPU Tinggi dengan Visual Studio

Semua file *.nettrace dapat dianalisis di Visual Studio. Untuk menganalisis file Linux *.nettrace di Visual Studio, transfer file *.nettrace, selain dokumen lain yang diperlukan, ke komputer Windows, lalu buka file *.nettrace di Visual Studio. Untuk informasi selengkapnya, lihat Menganalisis Data Penggunaan CPU.

Lihat juga

Langkah selanjutnya