Perfilamento de código JavaScript: encontrando gargalos na prática

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Introdução: por que o perfilamento é um must-have para todo desenvolvedor

Você já escreveu um código que aparentemente funciona, mas na produção começa a "travar"? Ou talvez você tenha iniciado um servidor Node.js e, depois de alguns minutos, ele "consumia" toda a memória? Na maioria das vezes, o problema não está na linguagem, mas nos gargalos — trechos de código que são executados de forma não otimizada.

O perfilamento (profiling) é o processo de coleta de métricas sobre a execução do programa: tempo de execução das funções, consumo de memória, frequência de chamadas. Sem ele, qualquer otimização é um chute no escuro. Neste artigo, vamos explorar como perfilar código JavaScript no ambiente Node.js, encontrar "gargalos" e eliminá-los.

Você aprenderá sobre as ferramentas integradas do V8, Chrome DevTools e técnicas práticas que ajudarão a tornar seu código mais rápido.



1. Perfilamento integrado no Node.js: a flag --prof

A maneira mais simples de começar a perfilar é usar a flag --prof ao iniciar a aplicação Node.js. Ela ativa a coleta de estatísticas do V8, que são então convertidas em um relatório legível.



Como funciona

Execute sua aplicação com a flag:

node --prof app.js

Após a conclusão da execução, um arquivo chamado isolate-0x...-v8.log será criado na pasta. Estes são os dados brutos. Para obter o relatório, use o utilitário node --prof-process:

node --prof-process isolate-0x...-v8.log > profile.txt


Exemplo: encontrando uma função lenta

Suponha que temos o seguinte código:

function slowSort(arr) {    return arr.sort((a, b) => a - b);}

function heavyComputation() { let sum = 0; for (let i = 0; i < 1e7; i++) { sum += Math.sqrt(i); } return sum;}

// Execuçãoconst data = Array.from({ length: 10000 }, () => Math.random());slowSort(data);heavyComputation();

Após o perfilamento, no relatório você verá algo como:

[JavaScript]:   ticks  total  nonlib   name   120    60.0%  70.0%   LazyCompile: *heavyComputation app.js:5    40    20.0%  25.0%   LazyCompile: *slowSort app.js:1

O número ticks mostra a quantidade de "ticks" (amostras) que ocorreram em cada função. Quanto mais ticks, mais tempo a função levou para executar. No nosso caso, heavyComputation é o principal candidato para otimização.



2. Chrome DevTools: perfilador visual para Node.js

Se o relatório textual parecer inconveniente, use o Chrome DevTools. Esta é uma interface gráfica que mostra um flamegraph (gráfico de chamas) — uma visualização da pilha de chamadas ao longo do tempo.



Conexão ao processo Node.js

Execute a aplicação com a flag --inspect:

node --inspect app.js

Abra o Chrome, na barra de endereços digite chrome://inspect. Clique em "Open dedicated DevTools for Node". Você será levado a uma interface onde pode capturar perfis.



Análise prática

Vamos considerar um exemplo com vazamento de memória:

const cache = [];

function addToCache(item) { cache.push(item);}

function processData() { for (let i = 0; i < 100000; i++) { addToCache({ id: i, data: new Array(1000).fill('x') }); }}

processData();

Grave um perfil através da aba "Memory" (faça um snapshot do heap). Você verá que o array cache ocupa centenas de megabytes. O flamegraph na aba "Performance" mostrará que a função addToCache é chamada 100.000 vezes — este é o gargalo.

Solução: limitar o tamanho do cache ou usar uma estrutura com remoção automática de elementos antigos (por exemplo, um Map com limite).



3. Perfilamento com o módulo perf_hooks

Para medição pontual do tempo de execução de trechos específicos de código, use o módulo integrado perf_hooks. É um análogo do

Blogs

Book Recommendations