Perfilamento de código JavaScript: encontrando gargalos no Node.js
A velocidade de uma aplicação é um dos principais fatores de sucesso. Mesmo um código perfeitamente escrito pode começar a "ficar lento" com o tempo devido ao aumento da carga, algoritmos ineficientes ou vazamentos de memória. No mundo do Node.js e JavaScript, o perfilamento ajuda a encontrar a causa da lentidão. Neste artigo, vamos analisar como identificar gargalos usando ferramentas nativas e de terceiros, e fornecer exemplos práticos.
O que é perfilamento e por que é necessário?
Perfilamento (profiling) é o processo de coletar dados sobre como um programa utiliza recursos: tempo de CPU, memória, chamadas de funções. O profiler registra quais funções estão demorando mais e com que frequência são chamadas. Isso permite que o desenvolvedor não adivinhe, mas saiba exatamente qual parte do código precisa de otimização.
Principais objetivos do perfilamento:
- Identificar "pontos críticos" (hotspots) — partes do código que consomem mais tempo.
- Encontrar vazamentos de memória.
- Otimizar algoritmos e estruturas de dados.
- Verificar a eficiência de operações assíncronas.
Profiler nativo do Node.js (V8)
O Node.js vem com um profiler nativo baseado no motor V8. Ele permite coletar a pilha de chamadas e o tempo de execução das funções sem instalar pacotes adicionais.
Execute sua aplicação com a flag --prof:
node --prof app.jsApós a conclusão do programa, um arquivo chamado isolate-0x...-v8.log (ou similar) aparecerá na pasta atual. Para obter um relatório legível, processe-o:
node --prof-process isolate-0x...-v8.log > processed.txtExemplo de saída (simplificada):
Statistical profiling result from isolate-0x... [Shared libraries]: ticks total nonlib name 100 50.0% node::Start
[JavaScript]: ticks total nonlib name 40 20.0% 40.0% LazyCompile: *slowFunction /path/to/file.js:10 20 10.0% 20.0% LazyCompile: *fastFunction /path/to/file.js:5Aqui, ticks é o número de "ticks" (medições) em que a função foi observada. Quanto mais ticks, mais "pesada" é a função. No exemplo, slowFunction ocupa 40% do tempo.
Importante: O profiler nativo fornece uma imagem estatística (amostral), que pode ser imprecisa para funções muito rápidas.
Perfilamento via Chrome DevTools
Para uma análise mais visual, você pode usar o Chrome DevTools. O Node.js suporta um protocolo de depuração especial que permite conectar as ferramentas do navegador.
Execute o Node.js com a flag --inspect:
node --inspect app.jsUm link como chrome-devtools://devtools/bundled/... aparecerá no console. Abra-o no navegador Chrome. Você entrará na interface familiar do DevTools, mas vinculada ao seu processo Node.js.
Na aba Profiler (ou Performance), você pode:
- Gravar um perfil de execução (Start → Stop).
- Visualizar o flame graph (gráfico de chamas), onde a largura da "barra" mostra o tempo de execução da função.
- Analisar as chamadas nos modos Bottom-Up ou Tree.
Exemplo de código para teste:
// app.jsfunction heavyComputation() { let sum = 0; for (let i = 0; i < 1e7; i++) { sum += Math.sqrt(i); } return sum;}
function main() { console.time('heavy'); const result = heavyComputation(); console.timeEnd('heavy'); console.log(result);}
main();Após gravar o perfil no DevTools, você verá que heavyComputation ocupa quase 100% do tempo — esse é o gargalo.