Perfilamento de código JavaScript: encontrando gargalos para iniciantes

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

Perfilamento de código JavaScript: encontrando gargalos

\n\n

Quando seu projeto Node.js começa a ficar lento, o primeiro pensamento é "preciso reescrever tudo em Rust". Mas não se apresse! Na maioria das vezes, o problema está em algumas linhas de código que funcionam de forma ineficiente. Neste artigo, vamos explorar como encontrar esses "gargalos" usando ferramentas simples de perfilamento. Você aprenderá como medir o desempenho, construir um flamegraph e entender para onde vai a CPU.

\n\n

Por que perfilar o código?

\n\n

Imagine que seu servidor processa 100 requisições por segundo, mas precisa processar 500. Você pode otimizar tudo, mas isso levaria semanas. Ou você pode encontrar uma única função que consome 80% do tempo e corrigi-la em uma hora. O perfilamento é exatamente o que mostra onde o tempo está sendo gasto.

\n\n

Vamos considerar os principais cenários em que o perfilamento é necessário:

\n
    \n
  • Alta latência nas respostas da API
  • \n
  • Vazamentos de memória (memory leaks)
  • \n
  • Alta carga na CPU sem motivo aparente
  • \n
  • Processamento lento de grandes conjuntos de dados
  • \n
\n\n

Ferramenta 1: Profiler integrado do Node.js (--prof)

\n\n

A maneira mais simples é executar o Node.js com a flag --prof. Ele coleta estatísticas de execução e as salva em um arquivo isolate-*.log.

\n\n

Vamos criar um script de teste que contém um gargalo:

\n\n
// slow.js\nfunction heavyComputation() {\n    let sum = 0;\n    for (let i = 0; i < 1e7; i++) {\n        sum += Math.sqrt(i) * Math.sin(i);\n    }\n    return sum;\n}\n\nfunction fastFunction() {\n    return 42;\n}\n\nconsole.time('total');\nheavyComputation();\nfastFunction();\nconsole.timeEnd('total');\n
\n\n

Vamos iniciar o perfilamento:

\n
node --prof slow.js
\n\n

Após a execução, um arquivo isolate-0x...-v8.log será gerado. Para lê-lo, use o utilitário node --prof-process:

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

Abra o profile.txt e veja a tabela com as porcentagens de tempo:

\n
[Summary]:\n   ticks  total  nonlib   name\n   785    78.5%  85.3%   JavaScript\n   215    21.5%  14.7%   C++\n   ...\n[Bottom up (heavy) profile]:\n  ticks parent  name\n   500   50.0%   LazyCompile: *heavyComputation slow.js:2:17\n   100   10.0%   Builtin: MathSqrt\n    50    5.0%   Builtin: MathSin\n
\n\n

Vemos que 50% do tempo é gasto na função heavyComputation. Agora sabemos o que otimizar primeiro.

\n\n

Ferramenta 2: Chrome DevTools (inspetor)

\n\n

Para uma análise mais visual, vamos conectar o Chrome DevTools. Execute o Node.js com a flag --inspect:

\n
node --inspect slow.js
\n\n

Abra o Chrome e vá para chrome://inspect. Clique em "Open dedicated DevTools for Node". Aba "Profiler" → "Start" → execute o script → "Stop".

\n\n

Você verá um flamegraph — um gráfico onde a largura do bloco mostra o tempo de execução. Quanto mais largo o bloco, mais "quente" é a função. Nosso heavyComputation será um enorme retângulo.

\n\n

Dica: Use o filtro "Focus selected" na função para ver suas chamadas internas. Por exemplo, você notará que Math.sqrt é chamado 10 milhões de vezes — talvez valha a pena pré-calcular as raízes.

\n\n

Ferramenta 3: perf_hooks — medições precisas no código

\n\n

Às vezes, é necessário medir um trecho específico do código sem executar ferramentas externas. Para isso, use o módulo integrado perf_hooks.

\n\n

Exemplo de medição do tempo de execução de uma função:

\n\n
const { performance } = require('perf_hooks');\n\nfunction processData(items) {\n    let result = [];\n    for (let item of items) {\n        // Simulação de processamento complexo\n        for (let j = 0; j < 1000; j++) {\n            result.push(item * j);\n        }\n                

Blogs

Book Recommendations