Compartir a través de


Caso práctico: Aislamiento de un problema de rendimiento (C#, Visual Basic, F#)

Utilice las herramientas de generación de perfiles para investigar problemas de rendimiento y aislar áreas problemáticas. En este caso práctico se usa una aplicación de ejemplo con problemas de rendimiento para demostrar cómo usar herramientas de generación de perfiles para mejorar la eficacia. Si desea comparar las herramientas de generación de perfiles, consulte ¿Qué herramienta debo elegir?

En este caso práctico se tratan estos temas:

  • Cómo usar las herramientas de generación de perfiles de Visual Studio para analizar el rendimiento de las aplicaciones.
  • Cómo interpretar los datos facilitados por estas herramientas para identificar cuellos de botella de rendimiento.
  • Cómo aplicar estrategias prácticas para optimizar el código, centrarse en contadores de .NET, recuentos de llamadas y datos de temporización.

Siga estos pasos y aplique estas técnicas a sus propias aplicaciones para que sean más eficaces y rentables.

Caso práctico de aislamiento de un problema de rendimiento

La aplicación de ejemplo de este caso práctico es una aplicación ASP.NET que ejecuta consultas en una base de datos simulada. El ejemplo se basa en la Muestra de Diagnóstico.

El problema de rendimiento principal con la aplicación de ejemplo se encuentra en patrones de codificación ineficaces. La aplicación tiene un cuello de botella en el rendimiento que afecta significativamente a su eficiencia. El problema presenta los siguientes síntomas:

  • Uso bajo de CPU: la aplicación muestra un uso bajo de CPU, lo que indica que la CPU no es el cuello de botella.

  • Recuento elevado de subprocesos de ThreadPool: el recuento de subprocesos es relativamente alto y aumenta constantemente, lo que sugiere el colapso del grupo de subprocesos.

  • Respuesta lenta de la aplicación: la aplicación responde lentamente debido a la falta de subprocesos disponibles para procesar nuevos elementos de trabajo.

El caso práctico tiene como objetivo dar solución a estos problemas mediante el uso de las herramientas de generación de perfiles de Visual Studio para analizar el rendimiento de la aplicación. Al comprender dónde y cómo se puede mejorar el rendimiento de la aplicación, los desarrolladores pueden implementar optimizaciones para que el código sea más rápido y eficaz. El objetivo final es mejorar el rendimiento general de la aplicación, lo que permite que se ejecute de forma más eficaz y rentable.

Desafío

La solución a los problemas de rendimiento de la aplicación .NET de ejemplo presenta varias dificultades. Estas cuestiones se derivan de la complejidad al diagnosticar cuellos de botella de rendimiento. Los principales desafíos para solucionar los problemas descritos son los siguientes:

  • Diagnóstico de cuellos de botella de rendimiento: una de las principales dificultades consiste en identificar con precisión las causas principales de los problemas de rendimiento. El bajo uso de CPU combinado con un rendimiento lento puede tener varios factores de contribución. Los desarrolladores deben usar herramientas de generación de perfiles de forma eficaz para diagnosticar estos problemas y, para ello, es necesario conocer cómo funcionan estas herramientas y cómo interpretar sus resultados.

  • Limitaciones en los conocimientos y recursos: los equipos pueden verse limitados por restricciones relacionadas con los conocimientos, la experiencia y los recursos. Para la generación de perfiles y la optimización de una aplicación, se deben reunir una serie de aptitudes y experiencia específicas y no todos los equipos pueden tener acceso inmediato a estos recursos.

Para solucionar estas cuestiones, se necesita aplicar un método estratégico que combine el uso eficaz de herramientas de generación de perfiles, conocimientos técnicos y planificación y programas de pruebas exhaustivos. El caso práctico tiene como objetivo guiar a los desarrolladores a través de este proceso, aportándoles estrategias e información para superar estos desafíos y mejorar el rendimiento de la aplicación.

Estrategia

A continuación, se presenta una visión general del enfoque en este caso práctico:

  • Comenzamos la investigación observando las métricas de contador de .NET mientras recopilamos datos de rendimiento. Al igual que la herramienta Uso de CPU, la herramienta Contadores de .NET de Visual Studio también es un buen punto de partida para una investigación de rendimiento.
  • Después, para obtener información adicional que permita aislar problemas o mejorar el rendimiento, plantéese recopilar una tarea de seguimiento mediante una de las otras herramientas de generación de perfiles. Por ejemplo, eche un vistazo a los recuentos de llamadas y los datos de temporización mediante la herramienta Instrumentación.

Para la recopilación de datos, se deben realizar las siguientes tareas:

  • Configurar la aplicación en una compilación de versión.
  • 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 y recopile un seguimiento.

Comprobar contadores de rendimiento

Al ejecutar la aplicación, observamos 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, utilice la herramienta Uso de CPU para identificar las áreas en las que podemos optimizar el código. Para ver un tutorial sobre esto, consulte Caso práctico: Guía para principiantes sobre la optimización del 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 ayudar a identificar problemas como la contención o la escasez 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 alto, use 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.

Después de recopilar un seguimiento con la herramienta Instrumentación y cargarlo en Visual Studio, primero comprobamos la página inicial del informe .diagsession, donde se muestran los datos resumidos. En el seguimiento recopilado, usamos el vínculo Abrir detalles en el informe y, a continuación, seleccionamos Gráfico de llama.

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

El gráfico de llama nos muestra que la función QueryCustomerDB (que se muestra en amarillo) 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.

La ruta de acceso al código con el uso de CPU más elevado en la aplicación se denomina ruta de acceso activa. El icono de llama de la ruta de acceso activa (Captura de pantalla que muestra el icono de ruta activa.) puede ayudar a identificar rápidamente los problemas de rendimiento que podrían mejorarse.

En la vista Árbol de llamadas, puede ver que la ruta de acceso activa 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()
{
    Customer c = QueryCustomerFromDbAsync("Dana").Result;
    return "success:taskwait";
}

Investigamos un poco. De manera alternativa, podemos ahorrar tiempo y dejar que Copilot haga la investigación por nosotros.

Si usamos Copilot, seleccione Preguntar a Copilot en el menú contextual y escriba la siguiente pregunta:

Can you identify a performance issue in the QueryCustomerDB method?

Sugerencia

Puede usar comandos de barra diagonal como /optimize para ayudar a formular preguntas apropiadas para Copilot.

Copilot nos indica 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. En este ejemplo, Copilot ofrece la siguiente sugerencia de código junto con la explicación.

public async Task<ActionResult<string>> QueryCustomerDB()
{
    Customer c = await QueryCustomerFromDbAsync("Dana");
    return "success:taskwait";
}

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 en el que se muestra cómo usar la herramienta Base de datos para investigar un problema de rendimiento, consulte Caso práctico: Guía para principiantes sobre la optimización de 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.