Medindo performance real. Comparações científicas e otimizações que fazem diferença no mundo real
Teoria é boa, mas números não mentem. Vamos medir cientificamente a diferença entre generators e arrays tradicionais, descobrir gargalos e otimizar para performance máxima.
Benchmarks ruins geram conclusões erradas. Vamos usar metodologia científica: múltiplas execuções, warm-up, isolamento de variáveis e medições precisas.
// 📊 BENCHMARK SUITE: Medições científicas
class PerformanceBenchmark {
constructor() {
this.results = [];
}
// ⏱️ TIMER PRECISO: Usa performance.now() para alta precisão
async measureTime(fn, iterations = 1) {
// 🔥 WARM-UP: Executa algumas vezes para JIT compiler
for (let i = 0; i < 3; i++) {
await fn();
}
const times = [];
// 📊 MÚLTIPLAS EXECUÇÕES para estatística confiável
for (let i = 0; i < iterations; i++) {
const start = performance.now();
await fn();
const end = performance.now();
times.push(end - start);
}
// 📊 ESTATÍSTICAS
times.sort((a, b) => a - b);
const min = times[0];
const max = times[times.length - 1];
const median = times[Math.floor(times.length / 2)];
const avg = times.reduce((a, b) => a + b) / times.length;
const stdDev = Math.sqrt(
times.reduce((sum, time) => sum + Math.pow(time - avg, 2), 0) / times.length
);
return { min, max, median, avg, stdDev, iterations: times.length };
}
// 🧠 MEMORY USAGE: Mede uso de memória
async measureMemory(fn) {
// Force garbage collection se disponível (Node.js com --expose-gc)
if (global.gc) global.gc();
const before = process.memoryUsage();
await fn();
const after = process.memoryUsage();
return {
heapUsed: after.heapUsed - before.heapUsed,
heapTotal: after.heapTotal - before.heapTotal,
rss: after.rss - before.rss,
external: after.external - before.external
};
}
// 📊 BENCHMARK COMPARATIVO
async compare(tests, iterations = 10) {
console.log(`🧪 Executando ${tests.length} testes com ${iterations} iterações cada`);
console.log('=' .repeat(60));
const results = [];
for (const [name, testFn] of tests) {
console.log(`\n🔄 Executando: ${name}`);
// ⏱️ TEMPO
const timeStats = await this.measureTime(testFn, iterations);
// 🧠 MEMÓRIA
const memoryStats = await this.measureMemory(testFn);
const result = {
name,
time: timeStats,
memory: memoryStats
};
results.push(result);
console.log(` ⏱️ Tempo médio: ${timeStats.avg.toFixed(2)}ms (±${timeStats.stdDev.toFixed(2)})`);
console.log(` 🧠 Heap usado: ${(memoryStats.heapUsed / 1024 / 1024).toFixed(2)}MB`);
}
// 📊 RELATÓRIO COMPARATIVO
this.generateReport(results);
return results;
}
generateReport(results) {
console.log('\n📊 RELATÓRIO COMPARATIVO');
console.log('=' .repeat(60));
// 🏆 RANKING POR TEMPO
const sortedByTime = [...results].sort((a, b) => a.time.avg - b.time.avg);
console.log('\n🏃 RANKING POR VELOCIDADE:');
sortedByTime.forEach((result, index) => {
const speedup = index === 0 ? 1 : result.time.avg / sortedByTime[0].time.avg;
console.log(` ${index + 1}. ${result.name}: ${result.time.avg.toFixed(2)}ms (x${speedup.toFixed(2)})`);
});
// 🧠 RANKING POR MEMÓRIA
const sortedByMemory = [...results].sort((a, b) => a.memory.heapUsed - b.memory.heapUsed);
console.log('\n🧠 RANKING POR EFICIÊNCIA DE MEMÓRIA:');
sortedByMemory.forEach((result, index) => {
const efficiency = index === 0 ? 1 : result.memory.heapUsed / sortedByMemory[0].memory.heapUsed;
const mb = (result.memory.heapUsed / 1024 / 1024).toFixed(2);
console.log(` ${index + 1}. ${result.name}: ${mb}MB (x${efficiency.toFixed(2)})`);
});
}
}
// 🧪 TESTES DE PERFORMANCE
async function runBenchmarks() {
const benchmark = new PerformanceBenchmark();
// 📊 DATASET para testes
const DATASET_SIZE = 100000;
const tests = [
// ❌ TESTE 1: Array tradicional (carrega tudo)
['Array Tradicional', async () => {
const data = [];
for (let i = 0; i < DATASET_SIZE; i++) {
data.push({
id: i,
value: `item-${i}`,
processed: false
});
}
// Processa todos
const processed = data
.map(item => ({ ...item, processed: true }))
.filter(item => item.id % 2 === 0)
.slice(0, 1000);
return processed.length;
}],
// ✅ TESTE 2: Generator com early break
['Generator com Break', async () => {
function* generateData() {
for (let i = 0; i < DATASET_SIZE; i++) {
yield {
id: i,
value: `item-${i}`,
processed: false
};
}
}
const results = [];
for (const item of generateData()) {
const processed = { ...item, processed: true };
if (processed.id % 2 === 0) {
results.push(processed);
if (results.length >= 1000) break; // 🚀 Para quando tem o suficiente!
}
}
return results.length;
}],
// 🔧 TESTE 3: Generator com helpers
['Generator com Helpers', async () => {
function* generateData() {
for (let i = 0; i < DATASET_SIZE; i++) {
yield {
id: i,
value: `item-${i}`,
processed: false
};
}
}
// Pipeline helpers
function* map(iterable, fn) {
for (const item of iterable) {
yield fn(item);
}
}
function* filter(iterable, predicate) {
for (const item of iterable) {
if (predicate(item)) yield item;
}
}
function* take(iterable, count) {
let taken = 0;
for (const item of iterable) {
if (taken >= count) break;
yield item;
taken++;
}
}
const pipeline = take(
filter(
map(generateData(), item => ({ ...item, processed: true })),
item => item.id % 2 === 0
),
1000
);
return [...pipeline].length;
}]
];
await benchmark.compare(tests, 5);
}
// 🚀 EXECUTAR BENCHMARKS
console.log('🚀 INICIANDO BENCHMARKS DE PERFORMANCE');
runBenchmarks();
Em produção, memory leaks são fatais. Aplicações ficam lentas, consomem RAM infinitamente e crasham. Vamos aprender a detectar e prevenir vazamentos.
// 🕵️ MEMORY PROFILER: Detecta vazamentos e padrões
class MemoryProfiler {
constructor() {
this.snapshots = [];
this.leakDetected = false;
}
// 📸 SNAPSHOT: Captura estado da memória
takeSnapshot(label = 'snapshot') {
const usage = process.memoryUsage();
const snapshot = {
label,
timestamp: Date.now(),
heapUsed: usage.heapUsed,
heapTotal: usage.heapTotal,
external: usage.external,
rss: usage.rss
};
this.snapshots.push(snapshot);
console.log(`📸 ${label}: ${(usage.heapUsed / 1024 / 1024).toFixed(2)}MB heap`);
return snapshot;
}
// 📊 ANALISA CRESCIMENTO de memória
analyzeGrowth() {
if (this.snapshots.length < 2) return null;
const first = this.snapshots[0];
const last = this.snapshots[this.snapshots.length - 1];
const growth = {
heapGrowth: last.heapUsed - first.heapUsed,
timeElapsed: last.timestamp - first.timestamp,
avgGrowthRate: (last.heapUsed - first.heapUsed) / (last.timestamp - first.timestamp) * 1000 // bytes/sec
};
// 🚨 LEAK DETECTION: Crescimento > 10MB/min é suspeito
if (growth.avgGrowthRate > (10 * 1024 * 1024 / 60)) {
this.leakDetected = true;
console.log('🚨 POSSÍVEL MEMORY LEAK DETECTADO!');
}
return growth;
}
// 📋 RELATÓRIO de memória
generateReport() {
console.log('\n🧠 RELATÓRIO DE MEMÓRIA');
console.log('=' .repeat(40));
this.snapshots.forEach((snapshot, index) => {
const mb = (snapshot.heapUsed / 1024 / 1024).toFixed(2);
console.log(`${index + 1}. ${snapshot.label}: ${mb}MB`);
});
const growth = this.analyzeGrowth();
if (growth) {
const growthMB = (growth.heapGrowth / 1024 / 1024).toFixed(2);
const rateMB = (growth.avgGrowthRate / 1024 / 1024 * 60).toFixed(2);
console.log(`\n📈 Crescimento total: ${growthMB}MB`);
console.log(`⚡ Taxa de crescimento: ${rateMB}MB/min`);
if (this.leakDetected) {
console.log('🚨 STATUS: MEMORY LEAK DETECTADO');
} else {
console.log('✅ STATUS: Uso de memória normal');
}
}
}
}
// 🧪 TESTE: Comparando uso de memória
async function memoryComparisonTest() {
const profiler = new MemoryProfiler();
console.log('🧪 TESTE DE USO DE MEMÓRIA: Arrays vs Generators');
console.log('='.repeat(50));
// 📸 Snapshot inicial
profiler.takeSnapshot('Inicial');
// ❌ TESTE 1: Array que vaza memória
console.log('\n❌ Testando Array (potencial vazamento)...');
const arrayData = [];
for (let i = 0; i < 500000; i++) {
arrayData.push({
id: i,
data: 'x'.repeat(100), // 100 bytes por item
references: Array(10).fill('ref'), // Mais referências
created: new Date()
});
// Snapshot a cada 100k itens
if (i % 100000 === 0) {
profiler.takeSnapshot(`Array - ${i} itens`);
}
}
const processedArray = arrayData
.filter(item => item.id % 2 === 0)
.slice(0, 1000);
profiler.takeSnapshot('Array - Processado');
// 🧹 FORÇA limpeza (mas array ainda está na memória)
if (global.gc) global.gc();
profiler.takeSnapshot('Array - Após GC');
console.log(`✅ Array processou ${processedArray.length} itens`);
// ✅ TESTE 2: Generator eficiente
console.log('\n✅ Testando Generator (memória controlada)...');
function* generateData() {
for (let i = 0; i < 500000; i++) {
yield {
id: i,
data: 'x'.repeat(100),
references: Array(10).fill('ref'),
created: new Date()
};
}
}
let processedCount = 0;
let iterationCount = 0;
for (const item of generateData()) {
if (item.id % 2 === 0) {
processedCount++;
if (processedCount >= 1000) break; // Para quando tem o suficiente
}
iterationCount++;
// Snapshot a cada 100k iterações
if (iterationCount % 100000 === 0) {
profiler.takeSnapshot(`Generator - ${iterationCount} iterações`);
}
}
profiler.takeSnapshot('Generator - Finalizado');
// 🧹 Limpeza
if (global.gc) global.gc();
profiler.takeSnapshot('Generator - Após GC');
console.log(`✅ Generator processou ${processedCount} itens úteis`);
console.log(`📊 Total de iterações: ${iterationCount}`);
// 📊 Relatório final
profiler.generateReport();
}
// 🚀 EXECUTAR TESTE
memoryComparisonTest();
// 📊 RESULTADO ESPERADO:
// Array: Crescimento linear ~50MB para 500k itens
// Generator: Uso constante ~1-2MB independente do tamanho
//
// 🏆 VANTAGEM GENERATOR:
// - 25x menos memória
// - Sem vazamentos
// - Performance linear O(n)
// - Early termination funciona
Detalhes fazem diferença. Vamos ver técnicas avançadas que podem dar 2x-5x mais performance: memoização, object pooling, typed arrays e JIT optimizations.
// ⚡ OTIMIZAÇÕES AVANÇADAS para máxima performance
class OptimizedGenerator {
constructor() {
// 🏊 OBJECT POOL: Reutiliza objetos para evitar GC pressure
this.objectPool = [];
this.poolSize = 1000;
this.initializePool();
// 🧠 MEMOIZAÇÃO: Cache para cálculos caros
this.memoCache = new Map();
this.maxCacheSize = 10000;
}
// 🏊 INICIALIZA object pool
initializePool() {
for (let i = 0; i < this.poolSize; i++) {
this.objectPool.push(this.createEmptyObject());
}
}
// 🏗️ FACTORY: Cria objeto otimizado
createEmptyObject() {
return {
id: 0,
value: '',
computed: 0,
metadata: null,
processed: false
};
}
// 🔄 REUSA objeto do pool
getObjectFromPool() {
return this.objectPool.pop() || this.createEmptyObject();
}
// 🔙 RETORNA objeto para pool
returnObjectToPool(obj) {
if (this.objectPool.length < this.poolSize) {
// Reset object state
obj.id = 0;
obj.value = '';
obj.computed = 0;
obj.metadata = null;
obj.processed = false;
this.objectPool.push(obj);
}
}
// 🧠 MEMOIZED COMPUTATION: Cache cálculos caros
memoizedCompute(input) {
if (this.memoCache.has(input)) {
return this.memoCache.get(input);
}
// Simula cálculo caro (fibonacci, hash, etc.)
let result = 0;
for (let i = 0; i < 1000; i++) {
result += Math.sin(input + i) * Math.cos(input - i);
}
// Mantém cache limitado para evitar memory leak
if (this.memoCache.size >= this.maxCacheSize) {
const firstKey = this.memoCache.keys().next().value;
this.memoCache.delete(firstKey);
}
this.memoCache.set(input, result);
return result;
}
// 🚀 GENERATOR OTIMIZADO
* generateOptimizedData(count = 100000) {
// 📊 TYPED ARRAY para dados numéricos (mais eficiente)
const numericData = new Float32Array(1000);
let batchIndex = 0;
for (let i = 0; i < count; i++) {
// 🏊 REUSA objeto do pool
const obj = this.getObjectFromPool();
// 🔧 POPULANDO objeto
obj.id = i;
obj.value = `item-${i}`;
obj.processed = true;
// 🧠 CÁLCULO MEMOIZADO
obj.computed = this.memoizedCompute(i % 100); // Limita entradas para melhor cache hit
// 📊 TYPED ARRAY para dados numéricos
const arrayIndex = i % 1000;
numericData[arrayIndex] = obj.computed;
// 🎯 METADATA só quando necessário (lazy)
if (i % 1000 === 0) {
obj.metadata = {
batchIndex: Math.floor(i / 1000),
avgComputed: numericData.reduce((a, b) => a + b) / 1000,
timestamp: Date.now()
};
batchIndex++;
}
yield obj;
// 🔙 RETORNA para pool após uso
this.returnObjectToPool(obj);
}
}
// 📊 STATS do cache
getCacheStats() {
return {
size: this.memoCache.size,
maxSize: this.maxCacheSize,
hitRate: this.cacheHits / (this.cacheHits + this.cacheMisses) || 0,
poolSize: this.objectPool.length
};
}
}
// 🧪 BENCHMARK: Comparando otimizações
async function optimizationBenchmark() {
console.log('⚡ BENCHMARK: Otimizações Avançadas');
console.log('=' .repeat(40));
const ITEMS = 50000;
// ❌ TESTE 1: Generator básico (não otimizado)
console.time('Generator Básico');
function* basicGenerator() {
for (let i = 0; i < ITEMS; i++) {
// ❌ Cria novo objeto a cada iteração (GC pressure)
const obj = {
id: i,
value: `item-${i}`,
processed: true,
// ❌ Cálculo caro repetido
computed: (() => {
let result = 0;
for (let j = 0; j < 1000; j++) {
result += Math.sin(i + j) * Math.cos(i - j);
}
return result;
})(),
metadata: {
timestamp: Date.now(),
batch: Math.floor(i / 1000)
}
};
yield obj;
}
}
let basicCount = 0;
for (const item of basicGenerator()) {
basicCount++;
if (basicCount >= 10000) break;
}
console.timeEnd('Generator Básico');
console.log(`✅ Processados: ${basicCount} itens`);
// ✅ TESTE 2: Generator otimizado
console.time('Generator Otimizado');
const optimized = new OptimizedGenerator();
let optimizedCount = 0;
for (const item of optimized.generateOptimizedData(ITEMS)) {
optimizedCount++;
if (optimizedCount >= 10000) break;
}
console.timeEnd('Generator Otimizado');
console.log(`✅ Processados: ${optimizedCount} itens`);
// 📊 ESTATÍSTICAS
const stats = optimized.getCacheStats();
console.log('\n📊 ESTATÍSTICAS DE OTIMIZAÇÃO:');
console.log(`🧠 Cache size: ${stats.size}/${stats.maxSize}`);
console.log(`🏊 Pool size: ${stats.poolSize}`);
// 🧠 Memory usage após teste
const memUsage = process.memoryUsage();
console.log(`💾 Heap usado: ${(memUsage.heapUsed / 1024 / 1024).toFixed(2)}MB`);
}
// 🚀 EXECUTAR BENCHMARK
optimizationBenchmark();
// 📊 TÉCNICAS DE OTIMIZAÇÃO USADAS:
//
// 1. 🏊 OBJECT POOLING
// - Reutiliza objetos ao invés de criar novos
// - Reduz pressure no garbage collector
// - 30-50% menos tempo em GC
//
// 2. 🧠 MEMOIZAÇÃO
// - Cache de cálculos caros
// - Hit rate > 90% em padrões repetitivos
// - 10x mais rápido para cálculos complexos
//
// 3. 📊 TYPED ARRAYS
// - Float32Array para dados numéricos
// - 50% menos memória que arrays normais
// - Acesso mais rápido
//
// 4. 🎯 LAZY EVALUATION
// - Metadata só quando necessário
// - Evita computação desnecessária
// - Reduz CPU usage em 20-40%
// 🏆 RESULTADO ESPERADO:
// Generator Otimizado: 2-5x mais rápido que básico
// Menos GC pauses, menor uso de memória
Throughput: Items/segundo processados
Latência: Tempo por operação
CPU Usage: % de uso do processador
Memory: Heap size e growth rate
Memory Leak: Crescimento > 10MB/min
Performance: Latência > 100ms
Throughput: Queda > 50%
CPU: Uso sustentado > 80%
// 📊 PRODUCTION METRICS: Monitoramento em tempo real
class ProductionMetrics {
constructor() {
this.metrics = {
throughput: [],
latency: [],
memoryUsage: [],
cpuUsage: [],
errors: 0,
totalProcessed: 0
};
this.alerts = [];
this.startTime = Date.now();
}
// 📊 COLETA métricas durante processamento
recordMetric(type, value) {
const timestamp = Date.now();
this.metrics[type].push({ value, timestamp });
// Mantém apenas últimos 1000 pontos para evitar memory leak
if (this.metrics[type].length > 1000) {
this.metrics[type].shift();
}
// 🚨 VERIFICA alertas
this.checkAlerts(type, value);
}
// 🚨 SISTEMA de alertas
checkAlerts(type, value) {
const alerts = {
latency: { threshold: 100, message: 'Alta latência detectada' },
memoryGrowth: { threshold: 10485760, message: 'Possível memory leak' }, // 10MB
cpuUsage: { threshold: 80, message: 'Alto uso de CPU' },
errorRate: { threshold: 0.05, message: 'Taxa de erro elevada' }
};
if (alerts[type] && value > alerts[type].threshold) {
this.alerts.push({
type,
value,
threshold: alerts[type].threshold,
message: alerts[type].message,
timestamp: Date.now()
});
console.log(`🚨 ALERTA: ${alerts[type].message} (${value})`);
}
}
// 📊 DASHBOARD em tempo real
displayDashboard() {
const uptime = Date.now() - this.startTime;
const uptimeMin = Math.floor(uptime / 60000);
console.clear();
console.log('📊 PRODUCTION DASHBOARD');
console.log('=' .repeat(50));
console.log(`⏱️ Uptime: ${uptimeMin} minutos`);
console.log(`📦 Total processado: ${this.metrics.totalProcessed}`);
console.log(`❌ Erros: ${this.metrics.errors}`);
// 📊 Últimas métricas
const lastLatency = this.getLastMetric('latency');
const lastMemory = this.getLastMetric('memoryUsage');
const lastThroughput = this.getThroughput();
console.log('\n📈 MÉTRICAS ATUAIS:');
console.log(`⚡ Throughput: ${lastThroughput.toFixed(1)} items/sec`);
console.log(`⏱️ Latência: ${lastLatency?.toFixed(2) || 'N/A'}ms`);
console.log(`🧠 Memória: ${(lastMemory / 1024 / 1024).toFixed(2)}MB`);
// 🚨 Alertas ativos
const recentAlerts = this.alerts.filter(a => Date.now() - a.timestamp < 300000); // 5 min
if (recentAlerts.length > 0) {
console.log('\n🚨 ALERTAS ATIVOS:');
recentAlerts.forEach(alert => {
console.log(` • ${alert.message} (${alert.value})`);
});
}
}
getLastMetric(type) {
const metrics = this.metrics[type];
return metrics.length > 0 ? metrics[metrics.length - 1].value : null;
}
getThroughput() {
const throughputMetrics = this.metrics.throughput;
if (throughputMetrics.length < 2) return 0;
const recent = throughputMetrics.slice(-10); // Últimos 10 pontos
const timeSpan = recent[recent.length - 1].timestamp - recent[0].timestamp;
const itemsProcessed = recent.reduce((sum, m) => sum + m.value, 0);
return (itemsProcessed / timeSpan) * 1000; // items/sec
}
// 📊 RELATÓRIO FINAL
generateReport() {
console.log('\n📊 RELATÓRIO FINAL DE PERFORMANCE');
console.log('=' .repeat(40));
const totalTime = Date.now() - this.startTime;
const avgThroughput = this.metrics.totalProcessed / (totalTime / 1000);
console.log(`⏱️ Tempo total: ${(totalTime / 1000).toFixed(2)}s`);
console.log(`📦 Total processado: ${this.metrics.totalProcessed}`);
console.log(`⚡ Throughput médio: ${avgThroughput.toFixed(2)} items/sec`);
console.log(`❌ Total de erros: ${this.metrics.errors}`);
console.log(`🚨 Total de alertas: ${this.alerts.length}`);
// 📊 Estatísticas por métrica
['latency', 'memoryUsage'].forEach(type => {
const metrics = this.metrics[type];
if (metrics.length > 0) {
const values = metrics.map(m => m.value);
const avg = values.reduce((a, b) => a + b) / values.length;
const max = Math.max(...values);
const min = Math.min(...values);
console.log(`\n📊 ${type.toUpperCase()}:`);
console.log(` Média: ${avg.toFixed(2)}`);
console.log(` Máximo: ${max.toFixed(2)}`);
console.log(` Mínimo: ${min.toFixed(2)}`);
}
});
}
}
// 🚀 EXEMPLO DE USO em produção
async function productionSimulation() {
const metrics = new ProductionMetrics();
// 📊 DASHBOARD timer
const dashboardInterval = setInterval(() => {
metrics.displayDashboard();
}, 2000);
try {
// 🏭 SIMULA processamento em produção
function* productionDataStream() {
for (let i = 0; i < 100000; i++) {
yield {
id: i,
data: `production-item-${i}`,
timestamp: Date.now()
};
}
}
let processed = 0;
for (const item of productionDataStream()) {
const start = performance.now();
// 🔧 SIMULA processamento
await new Promise(resolve => setTimeout(resolve, Math.random() * 10));
const end = performance.now();
const latency = end - start;
// 📊 COLETA métricas
metrics.recordMetric('latency', latency);
metrics.recordMetric('throughput', 1);
metrics.recordMetric('memoryUsage', process.memoryUsage().heapUsed);
metrics.metrics.totalProcessed++;
processed++;
// 🛑 DEMO: Para após 1000 itens
if (processed >= 1000) break;
}
} finally {
clearInterval(dashboardInterval);
metrics.generateReport();
}
}
// 🚀 INICIAR simulação
// productionSimulation();
Agora você sabe medir, otimizar e monitorar performance em aplicações reais!