Home > Blog > Desenvolvimento Web
Desenvolvimento Web
JavaScript
Programação

Performance Hooks no Node.js

Atualizado em: 8 de agosto de 2026

Rack de servidores processando fluxos de dados no Node.js

Otimizar uma aplicação sem medir pode transformar uma suspeita em uma alteração inútil. O módulo Performance Hooks no Node.js, disponível em node:perf_hooks, fornece relógios de alta resolução, marcas, medidas, observadores, histogramas e métricas do event loop para investigar o comportamento real do processo.

Essas APIs ajudam a responder perguntas práticas: quanto tempo uma consulta levou, qual etapa de inicialização é mais lenta, quanto o event loop está atrasado e se uma nova versão aumentou o custo de uma função. As medições podem ser enviadas para logs, métricas ou ferramentas de observabilidade.

Neste guia, você aprenderá a usar performance.now(), mark(), measure(), PerformanceObserver, timerify, monitoramento do event loop, histogramas e padrões seguros para instrumentar produção.

O que é node:perf_hooks?

O módulo implementa conceitos semelhantes à Performance API dos navegadores, adaptados ao ambiente Node.js. Ele oferece um relógio monotônico, independente de mudanças no horário do sistema, além de uma timeline de entradas de desempenho.

A documentação oficial de Performance Hooks lista as classes, tipos de entrada e métricas disponíveis. Para entender a base do runtime, consulte Event Loop no Node.js e o que é Node.js.

Medindo com performance.now()

performance.now() retorna milissegundos com precisão fracionária desde uma origem monotônica:

const { performance } = require('node:perf_hooks');

const start = performance.now();

performTask();

const duration = performance.now() - start;
console.log(`Duração: ${duration.toFixed(2)} ms`);

Para duração, ele é preferível a Date.now(), porque ajustes de relógio, sincronização de horário ou mudança manual não fazem o valor retroceder.

Medindo código assíncrono

const start = performance.now();

await fetchData();

const duration = performance.now() - start;

A medição inclui o tempo de espera da operação externa. Isso é útil para observar a experiência completa, mas não separa rede, fila, servidor remoto e processamento local. Combine métricas e traces quando precisar dessa decomposição.

Criando marcas

performance.mark() registra pontos nomeados na timeline:

performance.mark('load-start');

await loadConfiguration();

performance.mark('load-end');

Marcas podem incluir detalhes:

performance.mark('query-start', {
  detail: { operation: 'list-users' }
});

Não coloque senhas, tokens, consultas completas ou dados pessoais em detalhes que possam ser exportados.

Criando medidas

performance.measure(
  'configuration-load',
  'load-start',
  'load-end'
);

A medida recebe duração calculada entre as marcas. Você pode recuperá-la:

const entries = performance.getEntriesByName(
  'configuration-load'
);

console.log(entries[0].duration);

Limpando a timeline

Entradas permanecem na timeline até serem removidas:

performance.clearMarks('load-start');
performance.clearMarks('load-end');
performance.clearMeasures('configuration-load');

Uma aplicação que cria marcas por requisição e nunca limpa pode acumular memória. Prefira um observador que processe entradas e libere o histórico.

PerformanceObserver

O observador recebe entradas conforme são criadas:

const {
  performance,
  PerformanceObserver
} = require('node:perf_hooks');

const observer = new PerformanceObserver(list => {
  for (const entry of list.getEntries()) {
    console.log({
      name: entry.name,
      type: entry.entryType,
      duration: entry.duration
    });
  }
});

observer.observe({ entryTypes: ['measure'] });

Desconecte observadores quando não forem mais necessários:

observer.disconnect();

Instrumentando funções com timerify()

performance.timerify() cria um wrapper que produz entradas do tipo function:

const calculate = performance.timerify(function calculate(data) {
  return data.reduce((total, value) => total + value, 0);
});

calculate([1, 2, 3]);

Configure um observador:

const observer = new PerformanceObserver(list => {
  for (const entry of list.getEntries()) {
    console.log(entry.name, entry.duration);
  }
});

observer.observe({ entryTypes: ['function'] });

Instrumentar toda função gera overhead e volume excessivo. Aplique apenas em caminhos críticos ou durante investigações controladas.

Histogramas de duração

Um histograma preserva a distribuição, permitindo observar percentis:

const { createHistogram } = require('node:perf_hooks');

const histogram = createHistogram();

histogram.record(1200000);
histogram.record(5000000);

console.log({
  p50: histogram.percentile(50),
  p99: histogram.percentile(99),
  max: histogram.max
});

Verifique a unidade esperada pela API. Misturar nanossegundos e milissegundos produz métricas sem significado.

monitorEventLoopDelay()

O atraso do event loop indica quanto a thread principal demorou além do esperado:

const { monitorEventLoopDelay } = require('node:perf_hooks');

const delay = monitorEventLoopDelay({ resolution: 20 });
delay.enable();

setInterval(() => {
  console.log({
    p50Ms: delay.percentile(50) / 1e6,
    p99Ms: delay.percentile(99) / 1e6,
    maxMs: delay.max / 1e6
  });

  delay.reset();
}, 10000);

Um valor alto pode ser causado por cálculo síncrono, garbage collection, serialização, expressões regulares custosas ou excesso de callbacks. Correlacione com CPU e tráfego.

Event loop utilization

A utilização do event loop mede tempo ativo e ocioso:

const first = performance.eventLoopUtilization();

setTimeout(() => {
  const result = performance.eventLoopUtilization(first);
  console.log(result.utilization);
}, 5000);

Uma utilização próxima de 1 por longos períodos pode indicar saturação. Entretanto, uma aplicação com trabalho útil intenso também pode apresentar valor alto. Interprete junto com latência e throughput.

PerformanceResourceTiming

Algumas APIs podem produzir entradas de recurso. Elas permitem observar fases de operações compatíveis, semelhante ao que ocorre no navegador. A disponibilidade depende do recurso e da versão do Node.js.

Não baseie toda observabilidade em um tipo de entrada experimental. Mantenha métricas próprias para operações de negócio importantes.

Medindo inicialização

Marcas ajudam a identificar dependências lentas:

performance.mark('bootstrap-start');

await connectDatabase();
performance.mark('database-ready');

await startServer();
performance.mark('server-ready');

performance.measure(
  'database-connect',
  'bootstrap-start',
  'database-ready'
);

performance.measure(
  'server-start',
  'database-ready',
  'server-ready'
);

Esse diagnóstico é valioso em ambientes com autoscaling, nos quais inicializações lentas atrasam readiness e substituição de instâncias.

Medindo endpoints

Um middleware pode registrar duração total:

app.use((req, res, next) => {
  const start = performance.now();

  res.on('finish', () => {
    const duration = performance.now() - start;

    metrics.observeHttpDuration({
      method: req.method,
      route: req.route?.path || 'unknown',
      status: res.statusCode,
      duration
    });
  });

  next();
});

Use uma rota normalizada, não a URL completa, para evitar cardinalidade alta. Identificadores de usuário e parâmetros dinâmicos não devem virar labels.

Média não é suficiente

Uma média de 50 ms pode esconder algumas requisições de vários segundos. Observe p50, p90, p95, p99 e máximo. Também acompanhe quantidade de amostras, porque um percentil calculado com poucos eventos pode ser instável.

Overhead da instrumentação

Toda medição consome CPU e memória. Logs por chamada podem ser mais caros que a função medida. Use amostragem, agregação e exportação em lote. Evite transformar um diagnóstico em novo gargalo.

Performance Hooks e OpenTelemetry

Performance Hooks oferece medições locais e de baixo nível. OpenTelemetry organiza traces e métricas distribuídas entre serviços. As duas abordagens são complementares. Use performance.now() para trechos específicos e spans para acompanhar uma operação entre APIs, filas e bancos.

Veja o guia de OpenTelemetry no Node.js para instrumentação distribuída.

Testes de desempenho

Execute benchmarks com aquecimento, várias iterações e dados representativos. O compilador JIT, o garbage collector e caches afetam os resultados. Não conclua que uma abordagem é mais rápida com uma única execução.

Separe microbenchmark de teste de carga. Uma função rápida isolada pode não melhorar a latência do sistema quando o gargalo real é banco, rede ou contenção.

Erros comuns

  • Usar Date.now para duração crítica: o relógio pode sofrer ajustes.
  • Não limpar marcas: a timeline cresce continuamente.
  • Instrumentar tudo: overhead e volume escondem o sinal.
  • Observar apenas média: caudas lentas ficam invisíveis.
  • Usar URL completa como label: a cardinalidade explode.
  • Misturar unidades: números se tornam incorretos.
  • Otimizar microbenchmark isolado: o gargalo do sistema pode ser outro.

Boas práticas para produção

  • Use relógio monotônico para durações.
  • Meça operações relevantes para o usuário.
  • Observe percentis e quantidade de amostras.
  • Limpe entradas ou processe com observadores.
  • Aplique amostragem em alto volume.
  • Evite dados sensíveis e labels dinâmicas.
  • Monitore event loop delay e utilization.
  • Correlacione métricas com CPU, memória e tráfego.
  • Compare versões com a mesma carga.
  • Defina objetivos de latência antes de otimizar.

Conclusão

O módulo Performance Hooks no Node.js transforma percepções em medições. Relógios monotônicos, marcas, medidas, observadores e histogramas permitem identificar trechos lentos sem depender apenas de logs genéricos.

Uma instrumentação eficiente deve ser seletiva, agregada e conectada a objetivos reais. Ao acompanhar percentis, atraso do event loop e utilização, você encontra gargalos com mais confiança e evita otimizações que não melhoram a experiência do sistema.

Os 10 Melhores Cursos de Programação de 2026

Descubra os melhores cursos de programação. Aprenda a escolher o curso ideal para iniciar ou avançar na carreira de desenvolvedor

POSTS RELACIONADOS

Ver todos

Seta para a direita