Medición del rendimiento de Javascript

Medir el tiempo que lleva ejecutar una función es una buena forma de demostrar que una implementación de un mecanismo es más productiva que otra. Esto le permite asegurarse de que el rendimiento de la función no se haya visto afectado después de algunos cambios realizados en el código. También ayuda a buscar cuellos de botella en el rendimiento de las aplicaciones.

Si un proyecto web tiene un alto rendimiento, esto contribuye a su percepción positiva por parte de los usuarios. Y si a los usuarios les gusta trabajar con el recurso, tienen la propiedad de regresar. Por ejemplo, en esteEl estudio mostró que el 88% de los clientes en línea tienen menos probabilidades de volver a los recursos que encuentran con cualquier inconveniente. Estos inconvenientes pueden deberse a problemas de rendimiento.

Es por eso que las herramientas para ayudar a encontrar cuellos de botella en el rendimiento y medir los resultados de las mejoras realizadas al código son importantes en el desarrollo web. Dichas herramientas son especialmente relevantes en el desarrollo de JavaScript. Es importante saber que cada línea de código JavaScript puede potencialmente bloquear el DOM, ya que JavaScript es un lenguaje de subproceso único. En este artículo hablaré sobre cómo medir el rendimiento de las funciones y qué hacer con los resultados de la medición.





Si cree que algunos cálculos son demasiado pesados ​​para realizarlos en el hilo principal, entonces puede decidir trasladarlos a un trabajador de servicio o trabajador web.

Método Performance.now ()


La interfaz de rendimiento da acceso a un valor de tipo DOMHighResTimeStamp a través de un método performance.now(). Este método devuelve una marca de tiempo que indica el tiempo en milisegundos que ha transcurrido desde que el documento comenzó a existir. Además, la precisión de este indicador es de aproximadamente 5 microsegundos (fracciones de milisegundo).

Para medir el rendimiento de un fragmento de código utilizando el método performance.now(), debe realizar dos mediciones de tiempo, guardar los resultados de estas mediciones en variables y luego restar los resultados de la primera de los resultados de la segunda medición:

const t0 = performance.now();
for (let i = 0; i < array.length; i++) 
{
  // - 
}
const t1 = performance.now();
console.log(t1 - t0, 'milliseconds');

En Chrome, después de ejecutar este código, puede obtener algo como esto:

0.6350000001020817 "milliseconds"

En Firefox, así:

1 milliseconds

Como puede ver, los resultados de medición obtenidos en diferentes navegadores son muy diferentes. El hecho es que en Firefox 60, se reduce la precisión de los resultados devueltos por la API de rendimiento. Hablaremos más sobre esto.

La interfaz de rendimiento tiene muchas más capacidades que simplemente devolver una determinada marca de tiempo. Estos incluyen la medición de diversos aspectos del rendimiento representado por dichas extensiones de este interfaz que el rendimiento de la línea de tiempo de la API , Navigation Timing , tiempo de usuario , sincronización de recursos . Aquí está el material para obtener más información sobre estas API.

En nuestro caso, estamos hablando de medir el rendimiento de las funciones, por lo que tenemos suficientes oportunidades que el método brindaperformance.now().

Date.now () y performance.now ()


Aquí puede pensar que puede usar el método para medir el rendimiento Date.now(). De hecho, esto es posible, pero este enfoque tiene inconvenientes.

El método Date.now()devuelve el tiempo en milisegundos transcurrido desde la era de Unix (1970-01-01T00: 00: 00Z) y depende del reloj del sistema. Esto significa no solo que este método no es tan preciso como performance.now(), sino que, por el contrario performance.now(), devuelve valores que, bajo ciertas condiciones, pueden basarse en indicadores de reloj incorrectos. Esto es lo que dice Rich Gentlekor, un programador relacionado con el motor WebKit: “Quizás los programadores tengan menos probabilidades de pensar que las lecturas regresaron al accederDatebasado en el tiempo del sistema, es absolutamente imposible llamarlo ideal para monitorear aplicaciones reales. La mayoría de los sistemas tienen un demonio que sincroniza regularmente el tiempo. Ajustar el reloj del sistema por unos pocos milisegundos cada 15-20 minutos es algo común. Con tal frecuencia, la configuración del reloj alrededor del 1% de las mediciones de intervalos de 10 segundos será inexacta "

Método Console.time ()


La medición del tiempo usando esta API es extremadamente simple. Suficiente, antes del código cuyo rendimiento necesita evaluar, llame al método console.time(), y después de este código, el método console.timeEnd(). En este caso, uno y los otros métodos deben pasar el mismo argumento de cadena. En una página, se pueden usar hasta 10,000 de estos temporizadores simultáneamente.

La precisión de las mediciones de tiempo realizadas con esta API es la misma que cuando se usa la API de rendimiento, pero la precisión que se logrará en cada situación específica depende del navegador.

console.time('test');
for (let i = 0; i < array.length; i++) {
  // - 
}
console.timeEnd('test');

Después de ejecutar dicho código, el sistema enviará automáticamente información sobre el tiempo transcurrido a la consola.

En Chrome, se verá más o menos así:

test: 0.766845703125ms

En Firefox, así:

test: 2ms - timer ended

De hecho, todo aquí es muy similar a lo que vimos mientras trabajábamos performance.now().

La fuerza del método console.time()es su facilidad de uso. Es decir, estamos hablando del hecho de que su aplicación no requiere la declaración de variables auxiliares y de encontrar la diferencia entre los indicadores registrados en ellas.

Precisión de tiempo reducida


Si, utilizando las herramientas descritas anteriormente, midió el rendimiento de su código en diferentes navegadores, podría prestar atención al hecho de que los resultados de la medición pueden variar.

La razón de esto es que los navegadores están tratando de proteger a los usuarios de ataques basados ​​en el tiempo y de los mecanismos de identificación del navegador ( Browser Fingerprinting ). Si los resultados de la medición del tiempo son demasiado precisos, esto puede dar a los atacantes la oportunidad, por ejemplo, de identificar a los usuarios.

En Firefox 60, como ya se mencionó, la precisión de los resultados de medición de tiempo se reduce . Esto se hace estableciendo el valor de la propiedad privacy.reduceTimerPrecisionen 2 ms.

Algo a tener en cuenta al probar el rendimiento


Ahora tiene herramientas a su disposición para medir el rendimiento de las funciones de JavaScript. Pero antes de comenzar a trabajar, debe considerar algunas de las características de las que hablaremos ahora.

▍ Divide y vencerás


Suponga que, al filtrar algunos datos, prestó atención al funcionamiento lento de la aplicación. Pero no sabes exactamente dónde está el cuello de botella en el rendimiento.

En lugar de especular sobre qué parte del código se ejecuta lentamente, sería mejor averiguarlo utilizando los métodos anteriores.

Para ver la imagen general de lo que está sucediendo, primero debe usar console.time()y console.timeEnd()evaluar el rendimiento del bloque de código, que, presumiblemente, tiene un efecto negativo en el rendimiento. Luego debe observar la velocidad de las partes individuales de este bloque. Si uno de ellos se ve notablemente más lento que los demás, debe prestarle especial atención y cómo analizarlo.

Cuanto menos código haya entre las llamadas a métodos que miden el tiempo, menor será la probabilidad de que se mida algo que no es relevante para la situación del problema.

▍ Tener en cuenta el comportamiento de las funciones con diferentes valores de entrada


En aplicaciones reales, los datos recibidos en la entrada de una función particular pueden ser muy diferentes. Si mide el rendimiento de una función que pasó un conjunto de datos seleccionado al azar, esto no proporcionará ninguna información valiosa que pueda aclarar lo que está sucediendo.

Las funciones al investigar el rendimiento deben llamarse con datos de entrada que se parezcan lo más posible a los reales.

▍ Ejecuta funciones muchas veces


Supongamos que tiene una función que itera sobre una matriz. Ella realiza algunos cálculos utilizando cada elemento de la matriz, y después de eso devuelve una nueva matriz con los resultados de los cálculos. Pensando en optimizar esta función, desea saber qué funciona más rápido en su situación: un bucle forEacho un bucle regular for.

Aquí hay dos opciones para esta función:

function testForEach(x) {
  console.time('test-forEach');
  const res = [];
  x.forEach((value, index) => {
    res.push(value / 1.2 * 0.1);
  });

  console.timeEnd('test-forEach')
  return res;
}

function testFor(x) {
  console.time('test-for');
  const res = [];
  for (let i = 0; i < x.length; i ++) {
    res.push(x[i] / 1.2 * 0.1);
  }

  console.timeEnd('test-for')
  return res;
}

Prueba las funciones:

const x = new Array(100000).fill(Math.random());
testForEach(x);
testFor(x);

Después de ejecutar el código, obtenemos los siguientes resultados:

test-forEach: 27ms - timer ended
test-for: 3ms - timer ended

El ciclo parecía forEachser mucho más lento que el ciclo for. Después de todo, ¿los resultados de la prueba indican exactamente esto?

De hecho, después de una sola prueba, es demasiado pronto para sacar tales conclusiones. Intentemos llamar a las funciones dos veces:

testForEach(x);
testForEach(x);
testFor(x);
testFor(x);

Obtenemos lo siguiente:

test-forEach: 13ms - timer ended
test-forEach: 2ms - timer ended
test-for: 1ms - timer ended
test-for: 3ms - timer ended

Resulta que la función en la que se usa forEach, llamada segunda vez, es tan rápida como aquella en la que se usa for. Pero, dado el hecho de que la primera forEachfunción llama, la función funciona mucho más lentamente, puede que no valga la pena usarla.

Performance Rendimiento de prueba en diferentes navegadores


Las pruebas anteriores se realizaron en Firefox. Pero, ¿y si los ejecutas en Chrome? Los resultados serán completamente diferentes:

test-forEach: 6.156005859375ms
test-forEach: 8.01416015625ms
test-for: 4.371337890625ms
test-for: 4.31298828125ms

El hecho es que los navegadores Chrome y Firefox se basan en diferentes motores de JavaScript que implementan diferentes optimizaciones de rendimiento. Es muy útil saber acerca de estas diferencias.

En este caso, Firefox tiene una mejor optimización forEachcon una entrada similar. Y el ciclo fores más rápido que forEachen Chrome y Firefox. Como resultado, probablemente sea mejor detenerse en la variante de la función c for.

Este es un buen ejemplo, que demuestra la importancia de medir el rendimiento en diferentes navegadores. Si evalúa el rendimiento de algún código solo en Chrome, puede llegar a la conclusión de que el ciclo forEach, en comparación con el ciclo for, no es tan malo.

▍ Aplicar límites artificiales a los recursos del sistema


Los valores obtenidos en nuestros experimentos no parecen particularmente grandes. Pero tenga en cuenta que las computadoras que se utilizan para el desarrollo suelen ser mucho más rápidas que, por ejemplo, el teléfono móvil promedio que usan para navegar por la web.

Para ponerse en el lugar de un usuario que no tiene el dispositivo más rápido, use las capacidades del navegador para limitar artificialmente los recursos del sistema. Por ejemplo, para reducir el rendimiento del procesador.

Con este enfoque, 10 o 50 milisegundos pueden convertirse fácilmente en 500.

▍ Medir el rendimiento relativo


Las mediciones de rendimiento generalmente dependen no solo del hardware, sino también de la carga actual del procesador y de la carga de trabajo del hilo principal de la aplicación JavaScript. Por lo tanto, trate de confiar en los indicadores relativos que caracterizan el cambio en el rendimiento, ya que los indicadores absolutos obtenidos al analizar el mismo fragmento de código en diferentes momentos pueden variar mucho.

Resumen


En este artículo, analizamos algunas API de JavaScript diseñadas para medir el rendimiento. Hablamos sobre cómo usarlos para analizar código real. Creo que para realizar algunas mediciones simples, es más fácil de usar console.time().

Tengo la sensación de que muchos desarrolladores front-end no prestan suficiente atención a la medición del rendimiento de sus proyectos. Y deben monitorear constantemente los indicadores relevantes, ya que la productividad afecta el éxito y la rentabilidad de los proyectos.

¡Queridos lectores! Si monitorea constantemente el desempeño de sus proyectos, díganos cómo lo hace.


All Articles