Skip to content

Commit 2f2f829

Browse files
committed
me
1 parent c0a1747 commit 2f2f829

File tree

2 files changed

+122
-8
lines changed

2 files changed

+122
-8
lines changed
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
// aot-processing.js
2+
3+
// Importamos performance de perf_hooks para la medición más precisa
4+
const { performance } = require('perf_hooks');
5+
6+
function processLogsOnTheFly(n, intervalMilliseconds) {
7+
// Map es el equivalente de alto rendimiento a Dictionary<,> en C#
8+
const grouped = new Map();
9+
10+
// Establecemos una fecha de inicio, igual que en el ejemplo de C#
11+
const start = new Date();
12+
start.setDate(start.getDate() - 7);
13+
const startTime = start.getTime(); // Obtenemos el tiempo en ms para cálculos más rápidos
14+
15+
// El bucle principal que genera y procesa los datos "al vuelo"
16+
for (let i = 0; i < n; i++) {
17+
// 1. Genera los datos para esta iteración
18+
// Sumamos i minutos (en milisegundos) a la fecha de inicio
19+
const timestamp_ms = startTime + (i * 60 * 1000);
20+
const value = Math.random() * 100;
21+
22+
// 2. Lógica de procesamiento idéntica a la de C#
23+
// Se calcula el "bucket" truncando la división del tiempo por el intervalo.
24+
// Math.floor es el equivalente al casting a (long) para la división entera.
25+
const bucket = Math.floor(timestamp_ms / intervalMilliseconds) * intervalMilliseconds;
26+
27+
// 3. Agrupación eficiente
28+
// Usamos Map.get() que es muy rápido.
29+
const current = grouped.get(bucket);
30+
31+
if (!current) {
32+
// Si el bucket no existe, lo creamos
33+
grouped.set(bucket, { sum: value, count: 1 });
34+
} else {
35+
// Si ya existe, actualizamos los valores
36+
current.sum += value;
37+
current.count += 1;
38+
// No es necesario volver a hacer grouped.set() porque el objeto se modifica por referencia,
39+
// pero lo dejamos para claridad si se usaran tipos primitivos.
40+
// grouped.set(bucket, current);
41+
}
42+
}
43+
44+
// 4. Cálculo final de los promedios
45+
// Array.from(grouped.values()) convierte el iterador de valores del Map en un array.
46+
// Luego usamos .map() para calcular el promedio de cada grupo.
47+
return Array.from(grouped.values()).map(g => g.sum / g.count);
48+
}
49+
50+
function main() {
51+
const n = 50000000;
52+
const intervalMinutes = 10;
53+
const intervalMilliseconds = intervalMinutes * 60 * 1000;
54+
55+
const startTime = performance.now();
56+
57+
const averages = processLogsOnTheFly(n, intervalMilliseconds);
58+
59+
const endTime = performance.now();
60+
const durationSeconds = (endTime - startTime) / 1000;
61+
62+
console.log(`Node.js - Tiempo: ${durationSeconds.toFixed(3)} s`);
63+
console.log(`Buckets calculados: ${averages.length}`);
64+
}
65+
66+
main();

readme.md

Lines changed: 56 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,3 @@
1-
C:\Users\frtri\Documents\benchmarksMiiot\miiot-benchmarks> bun run .\bun-mastica-historial\benchmark.js
2-
Bun JS - Tiempo: 570.95 ms
3-
Buckets calculados: 500001
4-
5-
PS C:\Users\frtri\Documents\benchmarksMiiot\miiot-benchmarks> dotnet run --project .\dotnet-mastica-historial\dotnet-mastica-historial.csproj
6-
C# .NET - Tiempo: 389 ms
7-
Buckets calculados: 500001
81

92

103

@@ -39,4 +32,59 @@ Buckets calculados: 5000001
3932
![Resultados del benchmark](benchmark-result.png)
4033

4134

42-
(1 core bun vs 8 core .net)
35+
(1 core bun vs 8 core .net)
36+
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
39+
C# .NET (Optimizado) - Tiempo: 2,249 s
40+
Buckets calculados: 5000001
41+
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
46+
C# .NET (Optimizado) - Tiempo: 1,540 s
47+
Buckets calculados: 5000001
48+
49+
PS C:\Users\frtri\Documents\benchmarksMiiot\miiot-benchmarks> bun .\bun-mastica-historial\aot-processing.js
50+
Node.js - Tiempo: 1.482 s
51+
Buckets calculados: 5000001
52+
PS C:\Users\frtri\Documents\benchmarksMiiot\miiot-benchmarks> node .\bun-mastica-historial\aot-processing.js
53+
Node.js - Tiempo: 4.407 s
54+
Buckets calculados: 5000001
55+
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+
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

0 commit comments

Comments
 (0)