paint-brush
Async vs Sync Benchmark (.NET): la diferencia entre métodos asincrónicos y sincrónicospor@artemmikulich
1,892 lecturas
1,892 lecturas

Async vs Sync Benchmark (.NET): la diferencia entre métodos asincrónicos y sincrónicos

por Artem Mikulich5m2024/09/21
Read on Terminal Reader

Demasiado Largo; Para Leer

Este artículo muestra la diferencia entre los métodos asincrónicos y sincrónicos en la práctica. Dos instancias independientes de Locust se ejecutan en dos máquinas. La cantidad de usuarios crece de manera uniforme hasta alcanzar la cantidad objetivo (*Número de usuarios*). La velocidad de crecimiento está controlada por un parámetro *Tasa de generación* (cantidad de usuarios únicos que se unen por segundo)
featured image - Async vs Sync Benchmark (.NET): la diferencia entre métodos asincrónicos y sincrónicos
Artem Mikulich HackerNoon profile picture

Una de mis preguntas favoritas en las entrevistas es “¿Qué te dicen palabras como async y await ?” porque abre la oportunidad de tener una conversación interesante con el entrevistado… O no, porque se habla de este tema. En mi opinión, es sumamente importante entender por qué usamos esta técnica.


Siento que muchos desarrolladores prefieren confiar en la declaración “es la mejor práctica” y usar métodos asincrónicos ciegamente.


Este artículo muestra la diferencia entre métodos asincrónicos y sincrónicos en la práctica.

Herramientas

  • Aplicación .NET Web API (objetivo de prueba)


  • 2 bases de datos SQL de Azure


  • 2 Azure App Service en Windows (aloja la aplicación)


  • Azure App Insights (para recopilar métricas)


  • marco locust (para simular la carga del usuario).

Configuración

Esquema del experimento

Ejecutaré un benchmark de la siguiente manera. Dos instancias de Locust independientes se ejecutan en dos máquinas. Las instancias de Locust simulan un usuario que hace lo siguiente:

  • El usuario del host Locust 1 llega al punto final sincrónico del App Service 1, obtiene la respuesta y permanece inactivo durante 0,5 a 1 segundo (el retraso exacto es aleatorio). Se repite hasta el final del experimento.


  • El usuario del host Locust 2 se comporta exactamente de la misma manera, con una sola diferencia: accede al punto final asincrónico de App Service 2.


En segundo plano, cada App Service se conecta a su propia base de datos y ejecuta una consulta SELECT que tarda cinco segundos y devuelve algunas filas de datos. Consulta el código del controlador a continuación para obtener referencias. Usaré Dapper para realizar una llamada a la base de datos. Me gustaría llamar tu atención sobre el hecho de que el punto final asincrónico también llama a la base de datos de forma asincrónica ( QueryAsync<T> ).


Código de servicios de aplicaciones


Vale la pena agregar que implemento el mismo código en ambos servicios de aplicación.


Durante la prueba, la cantidad de usuarios crece de manera uniforme hasta alcanzar la cantidad objetivo ( Número de usuarios ). La velocidad de crecimiento se controla mediante un parámetro de tasa de generación (cantidad de usuarios únicos que se unen por segundo): cuanto mayor sea la cantidad, más rápido se agregarán los usuarios. La tasa de generación se establece en 10 usuarios/s para todos los experimentos.


Todos los experimentos están limitados a 15 minutos.


Puede encontrar detalles de configuración de la máquina en la sección Detalles técnicos del artículo.

Métrica

  • solicitudes por minuto : muestra la cantidad de solicitudes que la aplicación realmente procesó y devolvió un código de estado.
  • recuento de subprocesos : muestra la cantidad de subprocesos que consume el servicio de la aplicación.
  • tiempo de respuesta medio, ms


Las líneas rojas se refieren al punto final asincrónico y las líneas azules, al sincrónico, respectivamente.


Eso es todo en cuanto a la teoría. Empecemos.

Experimento n.° 1

  • Número de usuarios : 75 (por servicio)


Podemos ver que ambos puntos finales funcionan de manera similar: manejan alrededor de 750 solicitudes por minuto con un tiempo de respuesta medio de 5200 ms.


Experimento n.° 1. Solicitudes por minuto


El gráfico más fascinante de este experimento es una tendencia de subprocesos. Se pueden ver números significativamente más altos para el punto final sincrónico (un gráfico azul): ¡más de 100 subprocesos!


Experimento n.° 1. Número de hilos


Sin embargo, esto es lo esperado y coincide con la teoría: cuando llega una solicitud y la aplicación realiza una llamada a la base de datos, el hilo se bloquea porque tiene que esperar a que se complete un viaje de ida y vuelta. Por lo tanto, cuando llega otra solicitud, la aplicación tiene que generar un nuevo hilo para manejarla.


El gráfico rojo (el recuento de subprocesos del punto final asincrónico) demuestra un comportamiento diferente. Cuando llega una solicitud y la aplicación realiza una llamada a la base de datos, el subproceso vuelve a un grupo de subprocesos en lugar de bloquearse. Por lo tanto, cuando llega otra solicitud, este subproceso libre se reutiliza. A pesar de que las solicitudes entrantes aumentan, la aplicación no requiere ningún subproceso nuevo, por lo que su recuento sigue siendo el mismo.


Vale la pena mencionar la tercera métrica: el tiempo de respuesta medio . Ambos puntos finales mostraron el mismo resultado: 5200 ms. Por lo tanto, no hay diferencia en términos de rendimiento.


Experimento n.° 1. Resumen


Ahora es el momento de levantar las estacas.

Experimento #2

  • Número de usuarios : 150


Duplicamos la carga. El punto final asincrónico maneja esta tarea con éxito: su tasa de solicitudes por minuto ronda las 1500. El hermano sincrónico finalmente alcanzó una cantidad comparable de 1410. Pero si observa el gráfico a continuación, ¡verá que tardó 10 minutos!


El motivo es que el punto final sincrónico reacciona a la llegada de un nuevo usuario creando otro hilo, pero los usuarios se agregan al sistema (solo para recordarle que la tasa de generación es de 10 usuarios/s) más rápido de lo que el servidor web puede adaptarse. Es por eso que puso en cola tantas solicitudes desde el principio.


Experimento n.° 2. Solicitudes por minuto


Como era de esperar, la métrica de recuento de subprocesos sigue rondando los 34 para el punto final asincrónico, mientras que aumentó de 102 a 155 para el sincrónico. El tiempo de respuesta medio se degradó de manera similar a la tasa de solicitudes por minuto : el tiempo de respuesta sincrónico era mucho mayor al comienzo del experimento. Si hubiera mantenido la prueba durante 24 horas, los números medios se habrían vuelto iguales.


Experimento n.° 2. Resumen


Experimento n.° 3

  • Número de usuarios : 200


El tercer experimento tiene como objetivo comprobar las tendencias reveladas durante el segundo: podemos ver una mayor degradación del punto final sincrónico.


Experimento n.° 3. Resumen


Conclusión

El uso de operaciones asincrónicas en lugar de sincrónicas no mejora directamente el rendimiento ni la experiencia del usuario. En primer lugar, mejora la estabilidad y la previsibilidad bajo presión. En otras palabras, aumenta el umbral de carga para que el sistema pueda procesar más antes de degradarse.

Apéndice 1. Detalles técnicos

  • Azure App Service: B1, 100 ACU , 1,75 Gb de memoria, equivalente de cómputo de la serie A.
  • Base de datos SQL de Azure: Estándar S4: 200 DTU, 500 Mb de almacenamiento.
  • Configuración de conexión SQL: Tamaño máximo de grupo = 200.

Apéndice n.° 2. Notas

Para lograr el resultado de prueba más limpio, debería haber ejecutado pruebas desde 2 máquinas virtuales ubicadas en la misma red donde se encuentran los servicios de aplicaciones de destino.


Sin embargo, supuse que un retraso en la red afectaría a ambas aplicaciones de una manera más o menos similar. Por lo tanto, no puede poner en peligro el objetivo principal: comparar cómo se comportan los métodos asincrónicos y sincrónicos.

Apéndice n.° 3. Experimento adicional

¿Qué hice para forzar al punto final sincrónico a funcionar casi como asincrónico y trazar el gráfico a continuación (las condiciones del experimento son las mismas que en el tercero: 200 usuarios)?


Experimento adicional. Solicitudes por minuto