Depuración del uso elevado de CPU en .NET Core

Este artículo se aplica a: ✔️ SDK de .NET Core 3.1 y versiones posteriores

En este tutorial, aprenderá a depurar un escenario de uso excesivo de CPU. Con el repositorio de código fuente de ejemplo proporcionado, Aplicación web de ASP.NET Core, puede provocar un interbloqueo de forma intencionada. El punto de conexión dejará de responder y experimentará una acumulación de subprocesos. Aprenderá a usar diversas herramientas para diagnosticar este escenario con varios elementos clave de datos de diagnóstico.

En este tutorial va a:

  • Investigar el uso elevado de CPU
  • Determinar el uso de CPU con dotnet-counters
  • Usar dotnet-trace para la generación de seguimiento
  • Realizar perfiles de rendimiento en PerfView
  • Diagnosticar y resolver el uso excesivo de CPU

Requisitos previos

En el tutorial se usa:

Contadores de CPU

Antes de intentar recopilar datos de diagnóstico, debe observar una condición de CPU alta. Ejecute la aplicación de ejemplo mediante el siguiente comando desde el directorio raíz del proyecto.

dotnet run

Para encontrar el identificador de proceso, use el comando siguiente:

dotnet-trace ps

Anote el identificador de proceso de la salida del comando. El identificador de proceso era 22884, pero el suyo será diferente. Para comprobar el uso de CPU actual, use el comando de la herramienta dotnet-counters:

dotnet-counters monitor --refresh-interval 1 -p 22884

refresh-interval es el número de segundos entre los valores de CPU de sondeo de contador. La salida debe ser similar a la siguiente:

Press p to pause, r to resume, q to quit.
    Status: Running

[System.Runtime]
    % Time in GC since last GC (%)                         0
    Allocation Rate / 1 sec (B)                            0
    CPU Usage (%)                                          0
    Exception Count / 1 sec                                0
    GC Heap Size (MB)                                      4
    Gen 0 GC Count / 60 sec                                0
    Gen 0 Size (B)                                         0
    Gen 1 GC Count / 60 sec                                0
    Gen 1 Size (B)                                         0
    Gen 2 GC Count / 60 sec                                0
    Gen 2 Size (B)                                         0
    LOH Size (B)                                           0
    Monitor Lock Contention Count / 1 sec                  0
    Number of Active Timers                                1
    Number of Assemblies Loaded                          140
    ThreadPool Completed Work Item Count / 1 sec           3
    ThreadPool Queue Length                                0
    ThreadPool Thread Count                                7
    Working Set (MB)                                      63

Con la aplicación web en ejecución, inmediatamente después del inicio, no se consume CPU en absoluto y se muestra 0%. Vaya a la ruta api/diagscenario/highcpu con 60000 como parámetro de ruta:

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

Ahora, vuelva a ejecutar el comando dotnet-counters. Si solo está interesado en supervisar el contador cpu-usage, agregue "--counters System.Runtime[cpu-usage]" al comando anterior. No estamos seguros de si se consume la CPU, por lo que supervisaremos la misma lista de contadores que ha aparecido antes para comprobar que los valores de contador están en el intervalo esperado para nuestra aplicación.

dotnet-counters monitor -p 22884 --refresh-interval 1

Debería ver un aumento en el uso de la CPU, como se muestra a continuación (según el equipo host, espere un uso variable de la CPU):

Press p to pause, r to resume, q to quit.
    Status: Running

[System.Runtime]
    % Time in GC since last GC (%)                         0
    Allocation Rate / 1 sec (B)                            0
    CPU Usage (%)                                         25
    Exception Count / 1 sec                                0
    GC Heap Size (MB)                                      4
    Gen 0 GC Count / 60 sec                                0
    Gen 0 Size (B)                                         0
    Gen 1 GC Count / 60 sec                                0
    Gen 1 Size (B)                                         0
    Gen 2 GC Count / 60 sec                                0
    Gen 2 Size (B)                                         0
    LOH Size (B)                                           0
    Monitor Lock Contention Count / 1 sec                  0
    Number of Active Timers                                1
    Number of Assemblies Loaded                          140
    ThreadPool Completed Work Item Count / 1 sec           3
    ThreadPool Queue Length                                0
    ThreadPool Thread Count                                7
    Working Set (MB)                                      63

Durante toda la solicitud, el uso de la CPU rondará en torno al porcentaje aumentado.

Sugerencia

Para visualizar un uso de CPU incluso mayor, puede ejecutar este punto de conexión simultáneamente en varias pestañas del explorador.

En este momento, puede afirmar con seguridad que el uso de CPU es mayor del esperado. Identificar los efectos de un problema es clave para encontrar la causa. Usaremos el efecto del alto consumo de CPU, además de las herramientas de diagnóstico, para encontrar la causa del problema.

Análisis de CPU elevada con Profiler

Al analizar una aplicación con un uso elevado de la CPU, necesita una herramienta de diagnóstico que pueda proporcionar información sobre lo que hace el código. Lo habitual es optar por un generador de perfiles. Existen diferentes opciones de generador de perfiles entre las que elegir. dotnet-trace se puede usar en todos los sistemas operativos, sin embargo, sus limitaciones de sesgo de punto seguro y de pilas de llamadas que son solo administradas dan lugar a una información más general, en comparación con un generador de perfiles compatible con kernels, como "perf" para Linux o ETW para Windows. Si la investigación de rendimiento solo implica código administrado, por lo general dotnet-trace será suficiente.

La herramienta perf se puede usar para generar perfiles de aplicaciones .NET Core. Vamos a mostrar esta herramienta, aunque también se podría usar dotnet-trace. Salga de la instancia anterior del destino de depuración de ejemplo.

Establezca la variable de entorno DOTNET_PerfMapEnabled para que la aplicación .NET cree un archivo map en el directorio /tmp. perf usa este archivo map para asignar, según el nombre, las direcciones de CPU a las funciones generadas con JIT. Para obtener más información, consulte Exportación de mapas de rendimiento y volcados de JIT.

Nota:

.NET 6 estandariza en el prefijo DOTNET_ en lugar de en COMPlus_ para las variables de entorno que configuran el comportamiento en tiempo de ejecución de .NET. Sin embargo, el prefijo COMPlus_ seguirá funcionando. Si usa una versión anterior del runtime de .NET, debe seguir usando el prefijo COMPlus_ para las variables de entorno.

Ejecute el destino de depuración de ejemplo en la misma sesión de terminal.

export DOTNET_PerfMapEnabled=1
dotnet run

Ejecute de nuevo el punto de conexión de la API de uso elevado de CPU (https://localhost:5001/api/diagscenario/highcpu/60000). Mientras se ejecuta en la solicitud de 1 minuto, ejecute el comando perf con el identificador de proceso:

sudo perf record -p 2266 -g

El comando perf inicia el proceso de recopilación de rendimiento. Deje que se ejecute durante unos 20 o 30 segundos y, luego, presione Ctrl+C para salir del proceso de recopilación. Puede usar el mismo comando perf para ver la salida del seguimiento.

sudo perf report -f

También puede generar un gráfico de llamas mediante los comandos siguientes:

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

Este comando genera un archivo flamegraph.svg que puede ver en el explorador para investigar el problema de rendimiento:

Flame graph SVG image

Análisis de datos de CPU elevada con Visual Studio

Todos los archivos *.nettrace se pueden analizar en Visual Studio. Para analizar un archivo *.nettrace de Linux en Visual Studio, transfiera el archivo *.nettrace, además del resto de documentos necesarios, a una máquina Windows, y después abra el archivo *.nettrace en Visual Studio. Para más información, consulte Análisis de datos de uso de la CPU.

Consulte también

Pasos siguientes