Compartir a través de


Aislar un problema de rendimiento (C#, Visual Basic, F#)

En este artículo, aprenderá a usar herramientas de generación de perfiles para investigar problemas de rendimiento y aislar áreas de problemas.

En lugar de proporcionar instrucciones paso a paso, la intención aquí es mostrar cómo usar las herramientas de generación de perfiles de forma eficaz y cómo interpretar los datos. Al igual que la herramienta Uso de CPU, la herramienta Contadores de .NET también es un buen punto de partida para una investigación de rendimiento. Una vez que identifique datos interesantes, puede usar otras herramientas de generación de perfiles para investigar más en detalle. Para comparar herramientas, consulte ¿Qué herramienta debo usar?

Acerca de la aplicación de ejemplo

Las capturas de pantalla que se muestran en este artículo se basan en una aplicación de ASP.NET que ejecuta consultas en una base de datos simulada. El ejemplo se basa en la Muestra de Diagnóstico.

Inicio de una investigación

  • Inicie la investigación mediante el análisis de las métricas del contador de .NET al recopilar datos de rendimiento.
  • A continuación, para obtener información adicional para ayudar a aislar problemas, considere la posibilidad de llevar un seguimiento mediante una de las otras herramientas de generación de perfiles, como la herramienta Uso de CPU, la herramienta Instrumentación y otras.

La recopilación de datos requiere los pasos siguientes (no se muestran en este artículo):

  • Establezca la aplicación en una versión de lanzamiento.
  • Seleccione la herramienta Contadores de .NET en el Generador de perfiles de rendimiento (Alt+F2) (Los pasos posteriores implican la herramienta Instrumentación).
  • En el Generador de perfiles de rendimiento, inicie la aplicación.

Comprobar contadores de rendimiento

Al ejecutar la aplicación, vea los contadores en la herramienta Contadores de .NET. En el caso de las investigaciones iniciales, se incluyen algunas métricas clave que se deben tener en cuenta:

  • CPU Usage. Vea este contador para ver si se produce un problema de rendimiento con un uso elevado o bajo de CPU. Esto puede ser una pista con respecto a los tipos específicos de problemas de rendimiento. Por ejemplo:
    • Con un uso elevado de la CPU, use la herramienta Uso de CPU para identificar las áreas en las que puede optimizar el código. Para ver un tutorial sobre esto, consulte Guía para principiantes para optimizar el código.
    • Con un uso bajo de CPU, use la herramienta Instrumentación para identificar recuentos de llamadas y tiempo medio de función en función del tiempo del reloj. Esto puede ayudarle a identificar problemas como la contención o el colapso del grupo de subprocesos.
  • Allocation Rate. Para una aplicación web que atiende solicitudes, la velocidad debe ser bastante estable.
  • GC Heap Size. Observe este contador para ver si el uso de memoria está creciendo continuamente y potencialmente filtrando. Si parece elevado, puede usar una de las herramientas de uso de memoria.
  • Threadpool Thread Count. Para una aplicación web que atiende solicitudes, vea este contador para ver si el recuento de subprocesos se mantiene estable o aumenta a una velocidad estable.

Este es un ejemplo que muestra que CPU Usage es bajo, mientras que ThreadPool Thread Count es relativamente alto.

Captura de pantalla de los contadores que se muestran en la herramienta Contadores de .NET.

Un número de subprocesos creciente constantemente con un uso bajo de CPU puede ser un indicador del colapso del grupo de subprocesos. El grupo de subprocesos se ve obligado a seguir hilando nuevos subprocesos. La inanición del grupo de subprocesos se produce cuando el grupo no tiene subprocesos disponibles para procesar nuevos elementos de trabajo. A menudo, esto hace que las aplicaciones respondan lentamente.

Si existe un bajo uso de CPU y un recuento de subprocesos relativamente alto, y según la teoría de un posible caso de colapso del grupo de subprocesos, cambie al uso de la herramienta Instrumentación.

Investigar los recuentos de llamadas y los datos de tiempo

Echemos un vistazo a un seguimiento de la herramienta Instrumentación para ver si podemos intentar obtener más información sobre lo que sucede con los subprocesos.

Cuando se carguen los datos de diagnóstico, compruebe primero la página inicial del informe .diagsession que muestra la información principal y la ruta de acceso activa. En el seguimiento de Instrumentación, la ruta de acceso activa muestra la ruta de acceso al código con tiempos de función más largos en la aplicación. Estas secciones pueden proporcionar sugerencias que le ayudarán a identificar rápidamente los problemas de rendimiento que puede mejorar.

En el seguimiento recopilado, use el vínculo Abrir detalles en el informe y, a continuación, seleccione Gráfico de flama.

Captura de pantalla del Gráfico de flama en la herramienta Instrumentación.

La visualización del Gráfico de flama nos muestra que la función QueryCustomerDB es responsable de una parte significativa del tiempo de ejecución de la aplicación.

Haga clic con el botón derecho en la función QueryCustomerDB y elija Ver en árbol de llamadas.

Captura de pantalla del árbol de llamadas en la herramienta Instrumentación.

En la vista Árbol de llamadas, verá que la ruta de acceso activa (icono de llama) incluye la función QueryCustomerDB, lo que apunta a un posible problema de rendimiento.

En relación con el tiempo invertido en otras funciones, los valores Propio y Promedio propio de la función QueryCustomerDB son muy altos. A diferencia del Total y el Promedio total, los valores Propio excluyen el tiempo invertido en otras funciones, por lo que este es un buen lugar para buscar el cuello de botella de rendimiento.

Sugerencia

Si los valores Propio eran relativamente bajos en lugar de altos, probablemente querrá examinar las consultas reales invocadas por la función QueryCustomerDB.

Haga doble clic en la función QueryCustomerDB para mostrar el código fuente de la función.

public ActionResult<string> QueryCustomerDB()
{

    Task dbTask = QueryCustomerFromDbAsync("Dana");
    return "success:tasksleepwait";
}

Mediante una pequeña investigación, descubrimos que este código llama a una API asincrónica sin usar await. Este es el patrón de código sync-over-async, que es una causa común de colapso del grupo de subprocesos y puede bloquear los subprocesos.

Para resolverlo, use await.

public async Task<ActionResult<string>> TaskAsyncWait()
{
    Customer c = await PretendQueryCustomerFromDbAsync("Dana");
    return "success:taskasyncwait";
}

Si detecta problemas de rendimiento relacionados con las consultas de base de datos, puede usar la herramienta Base de datos para investigar si determinadas llamadas son más lentas. Estos datos pueden indicar una oportunidad para optimizar las consultas. Para ver un tutorial que muestra cómo utilizar la herramienta Base de datos para investigar un problema de rendimiento, consulte la Guía para principiantes para optimizar el código. La herramienta Base de datos admite .NET Core con ADO.NET o Entity Framework Core.

Para obtener visualizaciones en Visual Studio de un comportamiento de subproceso individual, puede usar la ventana Pilas paralelas durante la depuración. En esta ventana se muestran subprocesos individuales junto con información sobre los subprocesos que están esperando, subprocesos en espera y bloqueos.

Para obtener información adicional sobre el colapso del grupo de subprocesos, consulte Detección del colapso del grupo de subprocesos.

Pasos siguientes

Los siguientes artículos y entradas de blog proporcionan más información para ayudarle a aprender a usar las herramientas de rendimiento de Visual Studio de forma eficaz.