Perfilado de código JavaScript: encontrando cuellos de botella en la práctica

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Introducción: por qué el perfilado es imprescindible para cada desarrollador

¿Alguna vez ha escrito código que parece funcionar, pero en producción comienza a "ralentizarse"? ¿O quizás ha ejecutado un servidor Node.js y en unos minutos "consumía" toda la memoria? La mayoría de las veces el problema no está en el lenguaje, sino en los cuellos de botella: secciones de código que se ejecutan de manera no óptima.

El perfilado (profiling) es el proceso de recopilar métricas sobre el funcionamiento del programa: tiempo de ejecución de funciones, consumo de memoria, frecuencia de llamadas. Sin él, cualquier optimización es como adivinar el futuro. En este artículo analizaremos cómo perfilar código JavaScript en el entorno Node.js, encontrar los "puntos críticos" y eliminarlos.

Aprenderá sobre las herramientas integradas de V8, Chrome DevTools y técnicas prácticas que ayudarán a que su código sea más rápido.



1. Perfilado integrado en Node.js: el flag --prof

La forma más sencilla de comenzar a perfilar es usar el flag --prof al ejecutar una aplicación Node.js. Este activa la recopilación de estadísticas de V8, que luego se convierten en un informe legible.



Cómo funciona

Ejecute su aplicación con el flag:

node --prof app.js

Después de que la aplicación termine, en la carpeta aparecerá un archivo isolate-0x...-v8.log. Estos son datos sin procesar. Para obtener el informe, use la utilidad node --prof-process:

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


Ejemplo: encontrando una función lenta

Supongamos que tenemos este 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;}

// Ejecuciónconst data = Array.from({ length: 10000 }, () => Math.random());slowSort(data);heavyComputation();

Después del perfilado, en el informe 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

El número ticks muestra la cantidad de "ticks" (muestras) que correspondieron a cada función. Cuantos más ticks, más tiempo se ejecutó la función. En nuestro caso, heavyComputation es el principal candidato para optimización.



2. Chrome DevTools: perfilador visual para Node.js

Si el informe de texto le parece incómodo, use Chrome DevTools. Es una interfaz gráfica que muestra un flamegraph (gráfico de llamas): una visualización de la pila de llamadas a lo largo del tiempo.



Conexión al proceso Node.js

Ejecute la aplicación con el flag --inspect:

node --inspect app.js

Abra Chrome, en la barra de direcciones escriba chrome://inspect. Haga clic en "Open dedicated DevTools for Node". Accederá a una interfaz donde puede tomar perfiles.



Análisis práctico

Consideremos un ejemplo con fuga de memoria:

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();

Grabe un perfil a través de la pestaña "Memory" (tome una instantánea del heap). Verá que el array cache ocupa cientos de megabytes. El flamegraph en la pestaña "Performance" mostrará que la función addToCache se llama 100 000 veces: ese es el cuello de botella.

Solución: limitar el tamaño de la caché o usar una estructura con eliminación automática de elementos antiguos (por ejemplo, un Map con límite).



3. Perfilado con el módulo perf_hooks

Para medir con precisión el tiempo de ejecución de secciones específicas de código, use el módulo integrado perf_hooks. Es un análogo de

Blogs

Book Recommendations