Cuánta memoria se necesita en 2024 para ejecutar 1 millón de tareas concurrentes
(hez2010.github.io)- Es un benchmark que compara el uso de memoria de 1 a 1 millón de tareas concurrentes con base en los lenguajes y runtimes más recientes a finales de 2024, y se indica consultar una página separada de Take 2 para ver los resultados más recientes
- Todas las pruebas se ajustaron a la misma estructura: cada tarea espera 10 segundos y luego se espera a que todas terminen; se comparan las características de memoria de corrutinas, tareas asíncronas, goroutines e hilos virtuales, más que de múltiples hilos
- Los comparados son Rust
tokioyasync_std, C# y NativeAOT, NodeJS, Pythonasyncio, goroutine de Go, virtual thread de Java, y la native image de GraalVM para Java; todo el código está publicado en GitHub - A medida que aumentó la cantidad de tareas, la magnitud del aumento de memoria varió mucho entre runtimes, y con 1 millón de tareas C# mostró el menor uso de memoria, mientras Rust también mantuvo resultados eficientes
- El .NET más reciente mostró una gran mejora y NativeAOT compitió con Rust, pero las goroutines de Go usaron más de 13 veces la memoria del ganador y más del doble que Java con 1 millón de tareas
Método del benchmark y materiales públicos
- Este es el resultado de repetir la comparación del consumo de memoria en programación asíncrona de 2023 usando las versiones más recientes de cada lenguaje a finales de 2024
- En la parte superior se indica que, para ver los resultados más recientes, hay que consultar How Much Memory Do You Need in 2024 to Run 1 Million Concurrent Tasks? - Take 2
- El programa de prueba crea
Ntareas concurrentes recibidas como argumento de línea de comandos, cada una espera durante 10 segundos, y el programa termina cuando todas finalizan - El foco de la comparación no está en múltiples hilos, sino en modelos de concurrencia basados en corrutinas
- Todo el código del benchmark está publicado en async-runtimes-benchmarks-2024
Lenguajes y runtimes comparados
- Rust se compara usando dos runtimes asíncronos:
tokioyasync_std- Ambos son runtimes asíncronos ampliamente usados en Rust
- C# tiene soporte directo para
async/awaity ejecuta las tareas conTask.DelayyTask.WhenAll- También se compara NativeAOT, disponible desde .NET 7
- NativeAOT compila directamente el código administrado a un binario final para poder ejecutarlo sin VM
- NodeJS envuelve
setTimeoutconutil.promisifyy luego espera conPromise.all - Python usa
asyncio.sleepyasyncio.gather - Go usa goroutine como componente de concurrencia y, en lugar de await individual, espera a que todas las tareas terminen con
WaitGroup - Java usa virtual thread, disponible desde JDK 21
- También se compara la native image de GraalVM
- La native image de GraalVM se incluye como un concepto similar a .NET NativeAOT
Entorno de prueba
- Hardware: 13th Gen Intel Core i7-13700K
- Sistema operativo: Debian GNU/Linux 12(bookworm)
- Rust: 1.82.0
- .NET: 9.0.100
- Go: 1.23.3
- Java: openjdk 23.0.1 build 23.0.1+11-39
- Java (GraalVM): java 23.0.1 build 23.0.1+11-jvmci-b01
- NodeJS: v23.2.0
- Python: 3.13.0
- Siempre que fue posible, todos los programas se ejecutaron en release mode
- Como el entorno de prueba no tenía
libicu, el soporte de internacionalización y globalización quedó desactivado
Cambios de memoria al aumentar la cantidad de tareas
-
Huella mínima: 1 tarea
- Para ver cuánta memoria requiere el runtime por sí mismo, primero se ejecutó solo 1 tarea
- Rust, C# NativeAOT y Go se compilaron como binarios nativos estáticos y usaron muy poca memoria, con resultados similares entre sí
- La native image de Java GraalVM también dio buenos resultados, aunque usó un poco más de memoria que los otros casos compilados estáticamente
- Los programas que se ejecutan sobre plataformas administradas o intérpretes consumieron más memoria
- En este tramo, Go mostró la huella más pequeña
- Java GraalVM usó mucha más memoria que Java sobre OpenJDK, aunque esto podría ajustarse con configuración
-
10 mil tareas
- Los dos benchmarks de Rust no aumentaron mucho su uso de memoria frente a la huella mínima incluso con 10 mil tareas, y mantuvieron un consumo muy bajo
- C# NativeAOT también usó solo alrededor de 10 MB de memoria y quedó muy cerca de Rust
- El uso de memoria de Go aumentó bastante en este tramo
- Las virtual threads de Java en GraalVM parecen más ligeras que las goroutines de Go
- Go y la native image de Java GraalVM se compilaron como binarios nativos estáticos, pero aun así usaron más RAM que C#, que corre sobre una VM
-
100 mil tareas
- Cuando la cantidad de tareas aumentó a 100 mil, el consumo de memoria de todos los lenguajes empezó a crecer con fuerza
- Rust y C# siguieron mostrando buenos resultados en este tramo
- C# NativeAOT usó menos RAM que Rust y quedó por delante de todos los lenguajes
- El programa de Go ya iba por detrás no solo de Rust, sino también de Java, C# y NodeJS
- Como excepción, Java ejecutado en GraalVM no se incluye entre los que superaron a Go
-
1 millón de tareas
- Con 1 millón de tareas, C# quedó claramente por delante de todos los demás lenguajes
- Rust, como era de esperarse, mantuvo buenos resultados en eficiencia de memoria
- La brecha entre Go y los otros runtimes se hizo aún mayor
- Go usó más de 13 veces la memoria del resultado ganador
- Incluso frente a Java, Go usó más del doble de memoria, lo que contradice la percepción común de que la JVM consume mucha memoria y Go es liviano
Observaciones finales
- Cuando la cantidad de tareas concurrentes es muy grande, pueden usar una cantidad considerable de memoria incluso si cada tarea no hace cálculos complejos
- Los trade-offs varían según el runtime del lenguaje
- Con pocas tareas puede ser ligero y eficiente
- Pero al escalar a cientos de miles de tareas, el aumento de memoria puede volverse grande
- Con los compiladores y runtimes más recientes, .NET mostró una gran mejora
- .NET NativeAOT obtuvo resultados competitivos frente a Rust
- La native image de GraalVM para Java también mostró buena eficiencia de memoria
- Las goroutines de Go siguieron mostrando resultados ineficientes en consumo de recursos
Aún no hay comentarios.