Ir para o conteúdo

Cross Runtime Benchmark Library — Node.js Edition

Version Node.js License

Biblioteca de Benchmarking de Alta Precisão para aplicações Node.js

Solução profissional para medição de performance com rigor estatístico, isolamento metodológico e reprodutibilidade científica.


1. Visão Geral

A Cross Runtime Benchmark Library é uma solução completa para benchmarking de aplicações Node.js, projetada para produzir resultados estatisticamente confiáveis e reproduzíveis.

Características Principais

Característica Descrição
Warmup Adaptativo Detecção automática de estabilidade via CV% (Coeficiente de Variação)
Isolamento por Processo Forking via child_process.fork() para eliminação de estado acumulado
Timer de Alta Precisão process.hrtime.bigint() com resolução de nanosegundos
Prevenção de DCE Blackhole pattern para evitar Dead Code Elimination pelo V8
Detecção de Outliers Métodos IQR, MAD e Z-Score para filtragem estatística
Intervalos de Confiança Cálculo de IC 95% via distribuição t-Student
GC Forçado Integração com --expose-gc para limpeza controlada de heap
Saída Padronizada JSON compatível com Java e C# para análise cross-platform

Público-Alvo

  • Equipes de engenharia de performance
  • Desenvolvedores de bibliotecas críticas
  • Pesquisadores em otimização de software
  • Times de QA com foco em regressão de performance

2. Arquitetura

Arquitetura

Componentes Principais

Componente Módulo Responsabilidade
BenchmarkRunner core Orquestração do pipeline de execução
ProcessBenchmarkRunner core Execução com isolamento por processo
WarmupManager warmup Gerenciamento de aquecimento adaptativo
StatisticalAnalysis statistics Cálculos estatísticos descritivos
AdvancedStatistics statistics Testes de significância e comparação
HardwareCountersCollector metrics Coleta de contadores (fallback V8)
JsonReporter reporters Exportação em formato JSON
Blackhole utils Prevenção de Dead Code Elimination

3. Fluxo de Execução

Pipeline de Execução


4. Instalação

npm

npm install lib-cross-runtime-bench-node

yarn

yarn add lib-cross-runtime-bench-node

Build Local

git clone https://github.com/eduardocvalente/lib-cross-runtime-bench-node-ifg.git
cd lib-cross-runtime-bench-node
npm install

5. Exemplos de Uso

5.1 Benchmark Simples

const { Benchmark } = require('lib-cross-runtime-bench-node');

async function main() {
    // Benchmark com configuração padrão
    const result = await Benchmark.run('My Function', () => {
        let sum = 0;
        for (let i = 0; i < 1000; i++) {
            sum += i;
        }
        return sum;
    });

    console.log(`Mean: ${(result.statistics.mean / 1_000_000).toFixed(3)} ms (CV: ${(result.statistics.coefficientOfVariation * 100).toFixed(2)}%)`);
}

main();

5.2 Benchmark com Configuração Personalizada

const { Benchmark, BenchmarkConfig } = require('lib-cross-runtime-bench-node');

async function main() {
    // Configuração de alta precisão
    const config = BenchmarkConfig.builder()
        .withIterations(200)
        .withWarmupIterations(50)
        .withForceGC(true)
        .withAdaptiveWarmup(true)
        .withWarmupStabilityThreshold(0.03)  // 3% CV
        .withMaxWarmupIterations(500)
        .withOutlierDetection('MAD')
        .withOutlierThreshold(3.5)
        .build();

    const result = await Benchmark.run('Configured Test', () => {
        return performExpensiveOperation();
    }, config);

    // Exportar para JSON
    const { JsonReporter } = require('lib-cross-runtime-bench-node');
    await JsonReporter.report(result, 'benchmark_result.json');
}

main();

5.3 Benchmark com Forking (Isolamento por Processo)

const { Benchmark, BenchmarkConfig } = require('lib-cross-runtime-bench-node');

async function main() {
    const config = BenchmarkConfig.builder()
        .withProcessIsolation(true)
        .withForksCount(5)
        .withIterations(50)
        .build();

    const result = await Benchmark.run('Isolated Test', () => {
        // Cada fork executa em processo Node.js separado
        // Estado V8, heap, GC completamente isolados
        return myFunction();
    }, config);

    console.log(`Result from ${result.forks} forks: ${(result.statistics.mean / 1e6).toFixed(3)} ms`);
}

main();

5.4 Comparação de Algoritmos

const { Benchmark, ConsoleReporter } = require('lib-cross-runtime-bench-node');

async function main() {
    const comparison = await Benchmark.compare([
        { name: 'Algorithm A', fn: () => algorithmA(data) },
        { name: 'Algorithm B', fn: () => algorithmB(data) },
        { name: 'Algorithm C', fn: () => algorithmC(data) },
    ]);

    ConsoleReporter.reportComparison(comparison);
    // Output: "Algorithm B is 15.3% faster (p < 0.001, Cohen's d = 1.2 [Large])"
}

main();

5.5 Prevenção de DCE com Blackhole

const { Benchmark, Blackhole } = require('lib-cross-runtime-bench-node');

async function main() {
    const blackhole = new Blackhole();

    await Benchmark.run('Blackhole Test', () => {
        // Sem Blackhole: V8 TurboFan pode eliminar código
        const result = expensiveComputation();

        // Com Blackhole: força execução do código
        blackhole.consume(result);
    });
}

main();

5.6 Exportação de Múltiplos Resultados

const { Benchmark, JsonReporter } = require('lib-cross-runtime-bench-node');

async function main() {
    const result1 = await Benchmark.run('Test A', fnA);
    const result2 = await Benchmark.run('Test B', fnB);
    const result3 = await Benchmark.run('Test C', fnC);

    await JsonReporter.reportMultiple([result1, result2, result3], 'output/results.json');
}

main();

6. Configuração

6.1 Presets Disponíveis

Preset Warmup Medição Forks Outliers Uso
DEFAULT 20 iter 100 iter 1 IQR 90% dos casos
FAST 5 iter 20 iter 1 None Dev/debug rápido
HIGH_PRECISION Adaptativo 500 iter 3 MAD Decisões críticas

6.2 Parâmetros de Configuração

Parâmetro Tipo Default Descrição
iterations number 100 Iterações de medição
warmupIterations number 20 Iterações de warmup fixo
forceGC boolean true Forçar GC antes de medir (--expose-gc)
adaptiveWarmup boolean false Usar warmup adaptativo baseado em CV%
warmupStabilityThreshold number 0.05 CV% alvo para estabilização (5%)
maxWarmupIterations number 100 Máximo de iterações de warmup
outlierDetection string 'IQR' Método: IQR, MAD, ZScore, Grubbs, None
outlierThreshold number 1.5 Threshold para detecção de outliers
processIsolation boolean false Executar em processos separados
forksCount number 1 Número de forks

7. Técnicas de Isolamento

7.1 Warmup Adaptativo

Algoritmo:
1. Executar função
2. A cada 5 iterações: calcular CV% das últimas N medições
3. Se CV% < target por 3 verificações consecutivas → parar
4. Senão: continuar até maxWarmupIterations

Por que é importante: - V8 TurboFan compilation ocorre nas primeiras execuções (10-100x mais lento) - Caches de CPU (L1/L2/L3) precisam ser aquecidos - Inline caches (ICs) do V8 precisam de histórico de tipos

7.2 Forking por Processo

Ver Fase 1 em imagens/pipeline.png para o diagrama de isolamento por processo.

Benefícios: - Heap V8 limpo (sem fragmentação acumulada) - JIT TurboFan fresco (sem perfis de tipos enviesados) - GC state resetado - Variação reduzida em 30-50%

7.3 GC Forçado

// Requer execução com: node --expose-gc script.js
if (typeof global.gc === 'function') {
    global.gc();
}

Benefícios: - Elimina objetos órfãos do warmup - Heap em estado conhecido antes de medir - Reduz interferência de GC durante medição

7.4 Prevenção de DCE (Blackhole)

// Problema: V8 TurboFan pode eliminar código "morto"
const result = compute(); // Pode ser eliminado!

// Solução: Blackhole
blackhole.consume(result); // Força execução via globalThis sink

8. Métricas Coletadas

8.1 Timing

Métrica Unidade Descrição
mean nanosegundos Média aritmética
median nanosegundos Valor central (resistente a outliers)
stdDev nanosegundos Desvio padrão
min nanosegundos Valor mínimo
max nanosegundos Valor máximo

8.2 Memória (V8)

Métrica Unidade Descrição
heapUsed bytes Heap ocupado (process.memoryUsage().heapUsed)
heapTotal bytes Heap total alocado
rss bytes Resident Set Size total do processo
external bytes Memória de objetos nativos referenciados pelo V8

8.3 CPU

Métrica Unidade Descrição
userMs milissegundos Tempo em user mode (process.cpuUsage().user)
systemMs milissegundos Tempo em kernel mode (process.cpuUsage().system)
totalMs milissegundos Tempo total de CPU

8.4 Sistema

Métrica Descrição
nodeVersion Versão do Node.js (process.version)
platform Sistema operacional (os.platform())
cpuCount Número de CPUs lógicas (os.cpus().length)
totalMemory Memória total do sistema (os.totalmem())
loadAvg Carga média do sistema (os.loadavg())

9. Análise Estatística

9.1 Estatísticas Descritivas

Estatística Fórmula Interpretação
Mean (μ) Σx / n Centro da distribuição
Median valor central Resistente a outliers
StdDev (σ) √(Σ(x-μ)²/(n-1)) Dispersão
CV% (σ/μ) × 100 Confiabilidade relativa
IQR Q3 - Q1 Dispersão robusta

9.2 Intervalo de Confiança 95%

IC = μ ± (t × SEM)

onde:
  SEM = σ / √n          (Erro Padrão da Média)
  t = valor t-Student   (depende de n-1 graus de liberdade)

9.3 Detecção de Outliers

Método Fórmula Uso
IQR Outlier se x < Q1-1.5×IQR ou x > Q3+1.5×IQR Padrão, robusto
MAD Outlier se |x - median| > 3 × MAD Distribuições enviesadas
Z-Score Outlier se |ModZ| > 3.5 Distribuições normais
Grubbs Teste estatístico formal Detecção rigorosa

9.4 Teste de Significância (Welch t-test)

const { AdvancedStatistics } = require('lib-cross-runtime-bench-node');

const comparison = AdvancedStatistics.compareBenchmarks(
    resultA.measurements, resultB.measurements,
    'Algorithm A', 'Algorithm B'
);

// Interpretação:
// p < 0.05: Diferença significativa (95% confiança)
// p < 0.01: Diferença muito significativa (99% confiança)
// Cohen's d: Tamanho do efeito (0.2=small, 0.5=medium, 0.8=large)
console.log(comparison.summary);

10. Formato de Saída

10.1 JSON Schema

{
  "export_date": "2026-02-17T12:34:56Z",
  "benchmark_name": "sort_algorithm_benchmark",
  "metrics": {
    "timing": {
      "mean_ns": 12345678.9,
      "median_ns": 12200000,
      "stddev_ns": 500000,
      "min_ns": 11500000,
      "max_ns": 13500000,
      "sample_count": 100,
      "cv_percent": 4.05
    },
    "throughput": {
      "ops_per_second": 81.0
    },
    "memory": {
      "heap_bytes": 33554432,
      "total_bytes": 301989888
    },
    "cpu": {
      "user_time_ms": 120.5,
      "system_time_ms": 5.2,
      "cpu_usage_percent": 12.34
    },
    "hardware_counters": {
      "cycles": 0,
      "instructions": 0,
      "instructions_per_cycle": 0,
      "is_available": false
    }
  },
  "environment": {
    "os": "Windows 11",
    "runtime": "Node.js v20.11.0",
    "cpu_model": "Intel Core i7-9700K",
    "processor_count": 8
  }
}

10.2 Compatibilidade Cross-Platform

O schema JSON é 100% compatível com as implementações Java e C#, permitindo: - Comparação de resultados entre runtimes - Análise unificada em ferramentas de BI - Integração com pipelines CI/CD multi-linguagem


11. Integração CI/CD

11.1 GitHub Actions

name: Performance Regression

on: [push, pull_request]

jobs:
  benchmark:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'

      - name: Install dependencies
        run: npm install

      - name: Run Benchmarks
        run: node --expose-gc examples/SimpleExample.js

      - name: Check Regression
        run: |
          # Comparar com baseline
          python scripts/check_regression.py \
            --baseline baseline.json \
            --current result.json \
            --threshold 5

11.2 Armazenamento de Baselines

const { Benchmark, JsonReporter, AdvancedStatistics } = require('lib-cross-runtime-bench-node');
const fs = require('fs');

// Salvar baseline
const result = await Benchmark.run('My Benchmark', myFn);
await JsonReporter.report(result, 'baselines/v2.0.0_baseline.json');

// Comparar com baseline
const baseline = JSON.parse(fs.readFileSync('baselines/v2.0.0_baseline.json'));
const comparison = AdvancedStatistics.compareBenchmarks(
    baseline.measurements, result.measurements
);

if (comparison.statisticalTest.isSignificant && comparison.percentChange > 5.0) {
    throw new Error(`Regression detected: ${comparison.summary}`);
}

11.3 Execução Recomendada

# Habilitar GC manual para métricas precisas
node --expose-gc examples/SimpleExample.js

# Exemplos disponíveis
node --expose-gc examples/SimpleExample.js       # Uso básico
node --expose-gc examples/ConfiguredExample.js   # Configuração avançada
node --expose-gc examples/IsolatedExample.js     # Isolamento de processo
node --expose-gc examples/CompareExample.js      # Comparação de algoritmos
node --expose-gc examples/SortingBenchmark.js    # Benchmark de ordenação

12. Referências

Literatura Acadêmica

  1. Georges, A., Buytaert, D., & Eeckhout, L. (2007). Statistically Rigorous Java Performance Evaluation. OOPSLA.
  2. Mytkowicz, T., et al. (2009). Producing Wrong Data Without Doing Anything Obviously Wrong!. ASPLOS.
  3. Kalibera, T., & Jones, R. (2013). Rigorous Benchmarking in Reasonable Time. ISMM.

Bibliotecas de Referência

Recursos Adicionais


Estrutura do Projeto

src/
├── Benchmark.js                     # API pública principal
├── index.js                         # Exportações do módulo
├── config/
│   └── BenchmarkConfig.js           # Configuração com builder pattern
├── core/
│   ├── BenchmarkRunner.js           # Engine principal de execução
│   └── ProcessBenchmarkRunner.js    # Execução com child_process.fork()
├── warmup/
│   └── WarmupManager.js             # Warmup adaptativo (CV%)
├── statistics/
│   ├── StatisticalAnalysis.js       # Estatísticas descritivas + outliers
│   └── AdvancedStatistics.js        # Welch t-test, Cohen's d
├── metrics/
│   ├── CpuMetricsCollector.js       # process.cpuUsage()
│   ├── MemoryMetricsCollector.js    # process.memoryUsage() + V8
│   ├── SystemMetricsCollector.js    # os.cpus(), os.totalmem()
│   └── HardwareCountersCollector.js # Fallback gracioso
├── utils/
│   ├── HighPrecisionTimer.js        # process.hrtime.bigint()
│   └── Blackhole.js                 # Prevenção de DCE
├── reporters/
│   ├── JsonReporter.js              # Exportação JSON compatível
│   └── ConsoleReporter.js           # Saída formatada no terminal
└── Models/
    └── BenchmarkResult.js           # Modelo de resultado

Compatibilidade

  • Node.js: >= 22.0.0
  • Flags recomendadas: --expose-gc para GC forçado
  • Formato: JSON schema compatível com Java e C#

Licença

MIT License — Copyright (c) 2026 Eduardo Costa Valente


Cross Runtime Benchmark Library — Medição de performance com rigor científico.