Skip to main content

Inspection e Autocannon | CPUProfile | Performance | Node.js

  • Inspection e Autocannon | CPUProfile | Performance | Node.js

Erick Wendel - Vídeo: 🔗 Clique aqui


Neste artigo, vamos aprender como:

  1. Instrumentar o Node.js Inspector diretamente no código
  2. Gerar um arquivo .cpuprofile
  3. Simular carga com Autocannon
  4. Analisar o resultado no Discovery.js CPU Profile Viewer

Essa abordagem é ideal para:

  • Profiling automatizado
  • Execução em CI
  • Ambientes onde o Chrome DevTools não é viável
  • Reprodutibilidade de resultados

1. Visão geral da estratégia

O fluxo será:

Aplicação Node.js
├─ Inicia Inspector Session
├─ Ativa CPU Profiler
├─ Executa aplicação sob carga (Autocannon)
├─ Para o Profiler
├─ Salva arquivo .cpuprofile
└─ Analisa no Discovery.js

2. Exemplo de aplicação Node.js

2.1 Estrutura do projeto

project/
├─ index.js
└─ package.json

3. Código com inspector.Session (index.js)

Este exemplo:

  • Cria um servidor HTTP
  • Inicia automaticamente o CPU Profiler
  • Para o profiler após um tempo
  • Salva o arquivo .cpuprofile
import http from 'node:http'
import inspector from 'node:inspector'
import fs from 'node:fs'

// -----------------------------
// CPU PROFILER SETUP
// -----------------------------
const session = new inspector.Session()
session.connect()

function post(method, params = {}) {
return new Promise((resolve, reject) => {
session.post(method, params, (err, result) => {
if (err) reject(err)
else resolve(result)
})
})
}

async function startProfiling() {
await post('Profiler.enable')
await post('Profiler.start')
console.log('CPU profiling started')
}

async function stopProfiling() {
const { profile } = await post('Profiler.stop')

fs.writeFileSync(
'cpu-profile.cpuprofile',
JSON.stringify(profile)
)

console.log('CPU profiling finished → cpu-profile.cpuprofile')
session.disconnect()
}

// -----------------------------
// APPLICATION CODE
// -----------------------------
function heavyComputation(iterations) {
let total = 0
for (let i = 0; i < iterations; i++) {
total += Math.sqrt(i * Math.random())
}
return total
}

const server = http.createServer((req, res) => {
if (req.url === '/compute') {
const result = heavyComputation(5_000_000)

res.writeHead(200, { 'Content-Type': 'application/json' })
res.end(JSON.stringify({ result }))
return
}

res.writeHead(404)
res.end()
})

server.listen(3000, async () => {
console.log('Server running on http://localhost:3000')

// inicia profiling automaticamente ao subir o servidor
await startProfiling()

// para o profiling após 30 segundos
setTimeout(async () => {
await stopProfiling()
server.close()
}, 30_000)
})

4. Gerando carga com Autocannon

Em outro terminal, execute:

npx autocannon -c 50 -d 25 http://localhost:3000/compute

Recomendações:

  • O tempo do Autocannon deve estar contido dentro do tempo do profiling
  • Evite iniciar Autocannon antes do Profiler.start

5. Arquivo gerado

Após a execução, você verá:

cpu-profile.cpuprofile

Esse arquivo é compatível com:

  • Chrome DevTools
  • VS Code
  • Discovery.js CPU Profiler

6. Analisando no Discovery.js

  1. Abra: 👉 https://discoveryjs.github.io/cpupro/

  2. Arraste o arquivo:

    cpu-profile.cpuprofile
  3. Explore:

    • Flame graph interativo
    • Self time vs Total time
    • Call tree
    • Hot paths

7. O que analisar no Discovery.js

Pontos importantes:

  • Funções com maior self time
  • Call stacks profundas
  • Funções chamadas excessivamente
  • Código síncrono bloqueando o event loop

No exemplo, você verá algo como:

heavyComputation
└─ Math.sqrt

Confirmando o gargalo de CPU.


8. Boas práticas com inspector.Session

Controle explícito

  • Inicie e pare o profiler em momentos bem definidos
  • Evite perfis muito longos (10–60s é ideal)

Produção

  • Nunca deixe profiling ativo permanentemente
  • Use flags de ambiente:
if (process.env.ENABLE_PROFILING === 'true') {
await startProfiling()
}