|
| 1 | +# Benchmark: .NET vs. Node.js vs. Bun para Procesamiento Intensivo |
1 | 2 |
|
| 3 | +Este documento detalla una serie de benchmarks realizados para comparar el rendimiento de C# (.NET), Bun y Node.js en una tarea de procesamiento de datos intensiva ("mastica-historial"). |
2 | 4 |
|
| 5 | +## Fase 1: Benchmark Inicial (Versión no optimizada) |
3 | 6 |
|
4 | | -PS C:\Users\frtri\Documents\benchmarksMiiot\miiot-benchmarks> bun run .\bun-mastica-historial\benchmark.js |
| 7 | +Las primeras pruebas se realizaron con una implementación directa del algoritmo. |
| 8 | + |
| 9 | +### Bun JS (no optimizado) |
| 10 | + |
| 11 | +```powershell |
| 12 | +PS C:\...\miiot-benchmarks> bun run .\bun-mastica-historial\benchmark.js |
5 | 13 | Bun JS - Tiempo: 11.53 s |
6 | 14 | Buckets calculados: 5000001 |
7 | | -PS C:\Users\frtri\Documents\benchmarksMiiot\miiot-benchmarks> bun run .\bun-mastica-historial\benchmark.js |
| 15 | +
|
| 16 | +PS C:\...\miiot-benchmarks> bun run .\bun-mastica-historial\benchmark.js |
8 | 17 | Bun JS - Tiempo: 13.76 s |
9 | 18 | Buckets calculados: 5000001 |
10 | | -PS C:\Users\frtri\Documents\benchmarksMiiot\miiot-benchmarks> dotnet run --project .\dotnet-mastica-historial\dotnet-mastica-historial.csproj |
11 | | -C# .NET - Tiempo: 7,123 s |
12 | | -Buckets calculados: 5000000 |
13 | | -PS C:\Users\frtri\Documents\benchmarksMiiot\miiot-benchmarks> bun run .\bun-mastica-historial\benchmark.js |
| 19 | +
|
| 20 | +PS C:\...\miiot-benchmarks> bun run .\bun-mastica-historial\benchmark.js |
14 | 21 | Bun JS - Tiempo: 11.40 s |
15 | 22 | Buckets calculados: 5000001 |
| 23 | +``` |
| 24 | + |
| 25 | +### C# .NET (no optimizado) |
| 26 | + |
| 27 | +```powershell |
| 28 | +PS C:\...\miiot-benchmarks> dotnet run --project .\dotnet-mastica-historial\dotnet-mastica-historial.csproj |
| 29 | +C# .NET - Tiempo: 7,123 s |
| 30 | +Buckets calculados: 5000000 |
| 31 | +``` |
| 32 | + |
| 33 | +--- |
| 34 | + |
| 35 | +## Fase 2: Benchmark Optimizado |
16 | 36 |
|
| 37 | +> **NOTA:** Se realizó una optimización del código para evitar problemas de alto consumo de memoria que provocaban que el Garbage Collector de .NET terminara el proceso prematuramente. |
17 | 38 |
|
18 | | -OPTIMIZADO POR QUE NO CABE EN LA MEMORIA Y LO CORTABA EL GARBACE COLLECTOR DE .NET |
| 39 | +### Bun/Node JS (Optimizado) |
19 | 40 |
|
20 | | -PS C:\Users\frtri\Documents\benchmarksMiiot\miiot-benchmarks> bun run .\bun-mastica-historial\benchmark.js |
| 41 | +```powershell |
| 42 | +PS C:\...\miiot-benchmarks> bun run .\bun-mastica-historial\benchmark.js |
21 | 43 | Iniciando benchmark optimizado de Node.js/Bun... |
22 | 44 | Bun/Node JS (Optimizado) - Tiempo: 8.083 s |
23 | 45 | Buckets calculados: 5000001 |
24 | | -PS C:\Users\frtri\Documents\benchmarksMiiot\miiot-benchmarks> dotnet run --project .\dotnet-mastica-historial\dotnet-mastica-historial.csproj |
25 | | -C# .NET (Optimizado) - Tiempo: 2,168 s |
26 | | -Buckets calculados: 5000001 |
27 | | -PS C:\Users\frtri\Documents\benchmarksMiiot\miiot-benchmarks> node .\bun-mastica-historial\benchmark.js |
| 46 | +
|
| 47 | +PS C:\...\miiot-benchmarks> node .\bun-mastica-historial\benchmark.js |
28 | 48 | Iniciando benchmark optimizado de Node.js/Bun... |
29 | 49 | Bun/Node JS (Optimizado) - Tiempo: 19.884 s |
30 | 50 | Buckets calculados: 5000001 |
| 51 | +``` |
31 | 52 |
|
32 | | - |
| 53 | +### C# .NET (Optimizado) |
33 | 54 |
|
| 55 | +```powershell |
| 56 | +PS C:\...\miiot-benchmarks> dotnet run --project .\dotnet-mastica-historial\dotnet-mastica-historial.csproj |
| 57 | +C# .NET (Optimizado) - Tiempo: 2,168 s |
| 58 | +Buckets calculados: 5000001 |
| 59 | +``` |
34 | 60 |
|
35 | | -(1 core bun vs 8 core .net) |
| 61 | +--- |
36 | 62 |
|
37 | | -asi se compila en modo release no debug, ya que ne debug tarda mas |
38 | | -PS C:\Users\frtri\Documents\benchmarksMiiot\miiot-benchmarks> dotnet run --project .\dotnet-mastica-historial\dotnet-mastica-historial.csproj |
| 63 | +## Fase 3: Máxima Optimización (Modo Release y AOT-like) |
| 64 | + |
| 65 | +Para obtener los resultados más precisos, se compiló el proyecto de .NET en modo **Release** y se utilizó un script específico para JS que parece aplicar optimizaciones previas (similar a AOT). |
| 66 | + |
| 67 | +### C# .NET (Modo Release) |
| 68 | + |
| 69 | +Ejecución en modo Debug (referencia): |
| 70 | +```powershell |
| 71 | +PS C:\...\miiot-benchmarks> dotnet run --project .\dotnet-mastica-historial\dotnet-mastica-historial.csproj |
39 | 72 | C# .NET (Optimizado) - Tiempo: 2,249 s |
40 | 73 | Buckets calculados: 5000001 |
| 74 | +``` |
41 | 75 |
|
42 | | -MODO RELEASE. |
43 | | - |
44 | | -dotnet run -c Release --project .\dotnet-mastica-historial\dotnet-mastica-historial.csproj |
45 | | -PS C:\Users\frtri\Documents\benchmarksMiiot\miiot-benchmarks> dotnet run -c Release --project .\dotnet-mastica-historial\dotnet-mastica-historial.csproj |
| 76 | +Ejecución en modo **Release** (más rápido): |
| 77 | +```powershell |
| 78 | +PS C:\...\miiot-benchmarks> dotnet run -c Release --project .\dotnet-mastica-historial\dotnet-mastica-historial.csproj |
46 | 79 | C# .NET (Optimizado) - Tiempo: 1,540 s |
47 | 80 | Buckets calculados: 5000001 |
| 81 | +``` |
| 82 | + |
| 83 | +### Bun/Node.js (Procesamiento AOT-like) |
48 | 84 |
|
49 | | -PS C:\Users\frtri\Documents\benchmarksMiiot\miiot-benchmarks> bun .\bun-mastica-historial\aot-processing.js |
| 85 | +```powershell |
| 86 | +PS C:\...\miiot-benchmarks> bun .\bun-mastica-historial\aot-processing.js |
50 | 87 | Node.js - Tiempo: 1.482 s |
51 | 88 | Buckets calculados: 5000001 |
52 | | -PS C:\Users\frtri\Documents\benchmarksMiiot\miiot-benchmarks> node .\bun-mastica-historial\aot-processing.js |
| 89 | +
|
| 90 | +PS C:\...\miiot-benchmarks> node .\bun-mastica-historial\aot-processing.js |
53 | 91 | Node.js - Tiempo: 4.407 s |
54 | 92 | Buckets calculados: 5000001 |
| 93 | +``` |
| 94 | + |
| 95 | +--- |
| 96 | + |
| 97 | +## Resumen de Resultados (Mejores Tiempos) |
| 98 | + |
| 99 | +| Runtime | Tiempo (s) | Observaciones | |
| 100 | +| :--- | :--- | :--- | |
| 101 | +| **Bun** | **1.482 s** | 🥇 **El ganador**. Sorprendentemente, supera a todos en esta prueba. | |
| 102 | +| **.NET (Release)** | **1.540 s** | 🥈 Prácticamente un empate técnico con Bun. Un rendimiento excepcional. | |
| 103 | +| **Node.js** | **4.407 s** | 🐢 El más lento en esta tarea de cómputo. Casi 3x más lento que Bun/.NET. | |
| 104 | +| **.NET (Debug)** | **2.249 s** | Significativamente más lento que en modo Release, como era de esperar. | |
| 105 | + |
| 106 | + |
| 107 | + |
| 108 | +### ⚠️ Aclaración Importante sobre los Cores |
| 109 | +> **Nota del autor original:** La comparativa se realizó con **Bun usando 1 core vs .NET usando 8 cores**. |
| 110 | +
|
| 111 | +Este es un detalle crítico que sugiere que el algoritmo en .NET no estaba paralelizado para aprovechar los 8 cores, o que el rendimiento de Bun en un solo núcleo es excepcionalmente alto para esta tarea específica. |
| 112 | + |
| 113 | +--- |
| 114 | + |
| 115 | +## Análisis Detallado y Preguntas Frecuentes (Q&A) |
| 116 | + |
| 117 | +> **Pregunta: "¿No has hecho trampas optimizando más Node no?"** |
| 118 | +
|
| 119 | +¡Absolutamente no! Y me alegra que lo preguntes, porque es la clave de una buena comparativa. El código de Node.js es una réplica directa y fiel de la lógica del código de C#. |
55 | 120 |
|
56 | | -"¿No has hecho trampas optimizando más Node no?" |
57 | | -¡Absolutamente no! Y me alegra que lo preguntes, porque es la clave de una buena comparativa. El código de Node.js que te proporcioné es una réplica directa y fiel de la lógica del código de C#. |
58 | 121 | Vamos a comprobarlo pieza por pieza: |
59 | | -Lógica C# Lógica Node.js ¿Es equivalente? |
60 | | -for (int i = 0; i < n; i++) for (let i = 0; i < n; i++) Sí, bucle idéntico. |
61 | | -DateTime + TimeSpan Date + milisegundos Sí, ambos manejan el tiempo. JS usa números (ms) que es incluso más directo. |
62 | | -rnd.NextDouble() * 100 Math.random() * 100 Sí, ambos generan un número aleatorio entre 0 y 100. |
63 | | -(long)(ts.Ticks / interval.Ticks) Math.floor(ts_ms / interval_ms) Sí, es la misma operación matemática para agrupar en "buckets". |
64 | | -Dictionary<long, (sum, count)> Map<number, {sum, count}> Sí, Map es el equivalente de alto rendimiento a Dictionary. |
65 | | -.Select(g => g.sum / g.count) .map(g => g.sum / g.count) Sí, es la misma transformación para calcular el promedio final. |
66 | | -Conclusión: La comparación es justa. El algoritmo es el mismo. Lo que estamos midiendo aquí es la pura eficiencia del runtime (.NET, Node.js, Bun) para ejecutar este algoritmo específico. |
67 | | -Análisis de los Resultados |
68 | | -Aquí es donde se pone emocionante. Vamos a ordenar tus resultados de más rápido a más lento: |
69 | | -Runtime Tiempo (s) Observaciones |
70 | | -Bun 1.482 s 🥇 El ganador. Sorprendentemente, supera a todos. |
71 | | -.NET (Release) 1.540 s 🥈 Prácticamente un empate técnico con Bun. Una diferencia de ~3% es mínima y puede ser ruido de la ejecución. Un rendimiento excepcional. |
72 | | -.NET (Debug) 2.249 s Rendimiento sin optimizaciones. Como esperábamos, es significativamente más lento que en modo Release. |
73 | | -Node.js 4.407 s 🐢 El más lento (en este caso). Casi 3 veces más lento que Bun y .NET. |
74 | | -¿Qué está pasando aquí? ¿Por qué estos resultados? |
75 | | -Bun vs. Node.js (La gran sorpresa): |
76 | | -Motor JavaScript Diferente: Node.js usa el motor V8 (de Google Chrome). Bun usa JavaScriptCore (JSC) (de Apple Safari). Para esta tarea específica de un bucle "caliente" (hot loop) con muchas operaciones matemáticas y de acceso a objetos, el compilador JIT de JSC está demostrando ser drásticamente más eficiente que el JIT de V8. |
77 | | -Enfoque en el rendimiento: Bun fue construido desde cero con el rendimiento como objetivo principal, y estos resultados lo demuestran. Su implementación de Map, Math y el manejo del bucle está, para este caso, mucho más optimizada. |
78 | | -.NET (Release) - El campeón tradicional: |
79 | | -.NET sigue siendo un monstruo del rendimiento para tareas de cómputo intensivo (CPU-bound). El compilador RyuJIT en modo Release hace un trabajo increíble optimizando el código. |
80 | | -El hecho de que esté casi empatado con Bun demuestra lo maduro y optimizado que está el runtime de .NET. Para ser un ecosistema mucho más antiguo, mantenerse a la par con el "chico nuevo y rápido" es un logro enorme. |
81 | | -Node.js (V8) - ¿Por qué se queda atrás en esta prueba? |
82 | | -No significa que Node.js sea lento. Node.js es increíblemente rápido para su principal caso de uso: operaciones de I/O (Entrada/Salida) asíncronas (servidores web, APIs, acceso a bases de datos, etc.). |
83 | | -Este benchmark es 100% CPU-bound. Es un bucle numérico que no espera por nada. Este es precisamente el tipo de escenario donde las debilidades relativas del JIT de V8 (comparado con .NET o JSC en este caso) se hacen más evidentes. |
84 | | -El motor V8 puede haber tomado una ruta de optimización diferente o menos eficiente para este patrón de código específico. Pequeñas diferencias en cómo se manejan los objetos, los números de punto flotante o los accesos al Map pueden acumularse en 50 millones de iteraciones. |
85 | | -Conclusiones Finales |
86 | | -Has confirmado la importancia del modo Release: La diferencia entre .NET Debug (2.24s) y Release (1.54s) es enorme. ¡Nunca midas rendimiento en modo Debug! |
87 | | -Bun es un competidor muy serio: Para tareas de procesamiento y cómputo, Bun no es solo marketing. Es realmente, realmente rápido. |
88 | | -.NET sigue siendo una potencia: Ofrece un rendimiento de primer nivel, robustez y un ecosistema maduro. Sigue siendo una de las mejores opciones para backends de alto rendimiento. |
89 | | -Elige la herramienta adecuada para el trabajo: Si tu aplicación es principalmente un servidor API que maneja muchas peticiones concurrentes (I/O-bound), Node.js sigue siendo una opción fantástica. Si una parte crítica de tu aplicación es un algoritmo de procesamiento de datos intensivo como este, .NET o Bun podrían darte un rendimiento significativamente mejor. |
90 | | -¡Una comparación excelente y con resultados muy reveladores |
| 122 | + |
| 123 | +| Lógica C# | Lógica Node.js | ¿Es equivalente? | |
| 124 | +| :--- | :--- | :--- | |
| 125 | +| `for (int i = 0; i < n; i++)` | `for (let i = 0; i < n; i++)` | **Sí**, bucle idéntico. | |
| 126 | +| `DateTime + TimeSpan` | `Date + milisegundos` | **Sí**, ambos manejan el tiempo. JS usa números (ms) que es incluso más directo. | |
| 127 | +| `rnd.NextDouble() * 100` | `Math.random() * 100` | **Sí**, ambos generan un número aleatorio entre 0 y 100. | |
| 128 | +| `(long)(ts.Ticks / interval.Ticks)` | `Math.floor(ts_ms / interval_ms)` | **Sí**, es la misma operación matemática para agrupar en "buckets". | |
| 129 | +| `Dictionary<long, (sum, count)>` | `Map<number, {sum, count}>` | **Sí**, `Map` es el equivalente de alto rendimiento a `Dictionary`. | |
| 130 | +| `.Select(g => g.sum / g.count)` | `.map(g => g.sum / g.count)` | **Sí**, es la misma transformación para calcular el promedio final. | |
| 131 | + |
| 132 | +**Conclusión:** La comparación es justa. El algoritmo es el mismo. Lo que estamos midiendo aquí es la pura eficiencia del runtime (.NET, Node.js, Bun) para ejecutar este algoritmo específico. |
| 133 | + |
| 134 | +### Análisis de los Resultados |
| 135 | + |
| 136 | +Aquí es donde se pone emocionante. |
| 137 | + |
| 138 | +* **Bun vs. Node.js (La gran sorpresa):** |
| 139 | + * **Motor JavaScript Diferente:** Node.js usa el motor V8 (de Google Chrome). Bun usa JavaScriptCore (JSC) (de Apple Safari). Para esta tarea específica de un bucle "caliente" (hot loop) con muchas operaciones matemáticas y de acceso a objetos, el compilador JIT de JSC está demostrando ser drásticamente más eficiente que el JIT de V8. |
| 140 | + * **Enfoque en el rendimiento:** Bun fue construido desde cero con el rendimiento como objetivo principal, y estos resultados lo demuestran. Su implementación de `Map`, `Math` y el manejo del bucle está, para este caso, mucho más optimizada. |
| 141 | + |
| 142 | +* **.NET (Release) - El campeón tradicional:** |
| 143 | + * .NET sigue siendo un monstruo del rendimiento para tareas de cómputo intensivo (CPU-bound). El compilador RyuJIT en modo Release hace un trabajo increíble optimizando el código. |
| 144 | + * El hecho de que esté casi empatado con Bun demuestra lo maduro y optimizado que está el runtime de .NET. Para ser un ecosistema mucho más antiguo, mantenerse a la par con el "chico nuevo y rápido" es un logro enorme. |
| 145 | + |
| 146 | +* **Node.js (V8) - ¿Por qué se queda atrás en esta prueba?** |
| 147 | + * No significa que Node.js sea lento. Node.js es increíblemente rápido para su principal caso de uso: operaciones de I/O (Entrada/Salida) asíncronas (servidores web, APIs, acceso a bases de datos, etc.). |
| 148 | + * Este benchmark es **100% CPU-bound**. Es un bucle numérico que no espera por nada. Este es precisamente el tipo de escenario donde las debilidades relativas del JIT de V8 (comparado con .NET o JSC en este caso) se hacen más evidentes. |
| 149 | + * El motor V8 puede haber tomado una ruta de optimización diferente o menos eficiente para este patrón de código específico. Pequeñas diferencias en cómo se manejan los objetos, los números de punto flotante o los accesos al `Map` pueden acumularse en 50 millones de iteraciones. |
| 150 | + |
| 151 | +--- |
| 152 | + |
| 153 | +## Conclusiones Finales |
| 154 | + |
| 155 | +* **Has confirmado la importancia del modo Release:** La diferencia entre .NET Debug (2.24s) y Release (1.54s) es enorme. ¡Nunca midas rendimiento en modo Debug! |
| 156 | +* **Bun es un competidor muy serio:** Para tareas de procesamiento y cómputo, Bun no es solo marketing. Es realmente, realmente rápido. |
| 157 | +* **.NET sigue siendo una potencia:** Ofrece un rendimiento de primer nivel, robustez y un ecosistema maduro. Sigue siendo una de las mejores opciones para backends de alto rendimiento. |
| 158 | +* **Elige la herramienta adecuada para el trabajo:** Si tu aplicación es principalmente un servidor API que maneja muchas peticiones concurrentes (I/O-bound), Node.js sigue siendo una opción fantástica. Si una parte crítica de tu aplicación es un algoritmo de procesamiento de datos intensivo como este, .NET o Bun podrían darte un rendimiento significativamente mejor. |
0 commit comments