El throttling de CPU: qué hace el multiplicador por dentro
Cómo se implementa la ralentización, por qué no afecta a todo por igual, los preajustes calibrados, y cómo elegir el factor que corresponde a tus usuarios.
El desplegable de ralentización de CPU es el ajuste que más cambia lo que ves en un perfil y el que menos gente entiende. Un multiplicador de cuatro no significa que todo tarde cuatro veces más: significa que el hilo se detiene periódicamente de una forma concreta, y esa forma afecta a unas actividades mucho más que a otras. Saber qué hace exactamente el mecanismo es lo que permite interpretar bien los números que produce.
- Explicar el mecanismo por el que se implementa la ralentización.
- Enumerar qué actividades se ven afectadas y cuáles no.
- Elegir el factor adecuado midiendo en lugar de suponiendo.
- Usar los preajustes calibrados y saber qué calibran.
El mecanismo
La ralentización se aplica insertando pausas: el motor detiene periódicamente la ejecución de forma que, en promedio, el trabajo avanza a la fracción indicada de su velocidad normal. Con un factor de cuatro, aproximadamente tres de cada cuatro unidades de tiempo el hilo está parado.
Ese mecanismo tiene tres consecuencias importantes.
Es una ralentización del hilo, no del procesador. No cambia la frecuencia, no cambia la caché, no cambia el ancho de banda de memoria. Un dispositivo real lento lo es por todas esas razones a la vez, y su perfil de lentitud no es uniforme: es especialmente malo en cargas que dependen de memoria y de caché, que es exactamente lo que hace la manipulación del documento.
Afecta a lo que se ejecuta en ese hilo y no a lo demás. La red no se ralentiza. Los hilos de rasterización tampoco, ni el compositor, ni la GPU. Los workers son otro asunto: dependiendo de la implementación, pueden no verse afectados.
Es aproximadamente uniforme entre tipos de trabajo, mientras que un dispositivo real no lo es. En un teléfono, la decodificación de imágenes, la disposición y la compilación de JavaScript se degradan en proporciones distintas.
La consecuencia práctica de que la red no se ralentice con la CPU es que la combinación por defecto es irreal: red rápida con CPU lenta no corresponde a ningún dispositivo. Un móvil de gama baja tiene además una conexión peor. Ralentizar solo la CPU produce un perfil donde los recursos llegan instantáneamente y el procesamiento es lento, que no es el escenario de nadie.
Qué se ralentiza y qué no
| Actividad | ¿Se ralentiza? |
|---|---|
| Ejecución de JavaScript | Sí |
| Cálculo de estilo y disposición | Sí |
| Construcción de la lista de pintado | Sí |
| Análisis del HTML | Sí |
| Descarga de recursos | No |
| Rasterización | No, o parcialmente |
| Composición y presentación | No |
| Decodificación de imágenes | No, ocurre en otro hilo |
| Trabajo en un worker | Depende |
Esa tabla explica un fenómeno que confunde: con la ralentización activada, la proporción entre categorías en el resumen del perfil cambia. El trabajo de script crece respecto al de pintado, porque uno se ralentiza y el otro no. Comparar el reparto de colores de un perfil ralentizado con el de uno sin ralentizar es comparar dos cosas distintas.
Elegir el factor
La forma habitual de elegir el factor es coger el número que ofrece el desplegable, y no corresponde a nada en particular. La forma correcta es medir.
Paso uno: mide tu máquina con una carga de trabajo representativa.
Paso dos: mide el dispositivo objetivo con la misma carga, por depuración remota.
Paso tres: el cociente es tu factor.
Las versiones recientes de las herramientas incluyen preajustes calibrados que hacen algo parecido automáticamente: miden la capacidad de tu máquina y calculan el factor que la equipara a un dispositivo de gama media o de gama baja. Eso es una mejora sustancial sobre un número fijo, porque el mismo factor en dos máquinas distintas produce dos velocidades distintas, y ese es el motivo principal de que dos personas del mismo equipo obtengan perfiles incomparables.
// Comprueba empiricamente que factor esta aplicado ahora mismo
(() => {
const cargaDeTrabajo = () => {
let x = 0;
for (let i = 0; i < 2e6; i++) x += Math.sqrt(i) % 7;
return x;
};
cargaDeTrabajo(); // calentamiento
const medidas = [];
for (let i = 0; i < 5; i++) {
const t0 = performance.now();
cargaDeTrabajo();
medidas.push(performance.now() - t0);
}
medidas.sort((a, b) => a - b);
const mediana = medidas[2];
const REFERENCIA = Number(localStorage.getItem('__referencia_cpu') || 0);
console.log('Mediana de esta ejecucion:', Math.round(mediana), 'ms');
if (!REFERENCIA) {
localStorage.setItem('__referencia_cpu', String(mediana));
console.log('Guardada como referencia sin ralentizacion.');
console.log('Activa ahora la ralentizacion y vuelve a ejecutar este fragmento.');
} else {
console.log('Referencia guardada:', Math.round(REFERENCIA), 'ms');
console.log('Factor efectivo aplicado:', (mediana / REFERENCIA).toFixed(2) + 'x');
console.log('Para reiniciar: localStorage.removeItem("__referencia_cpu")');
}
})();
Ese fragmento sirve para dos cosas. La primera es comprobar que la ralentización está realmente aplicada, porque es sorprendentemente fácil dejarla puesta de una sesión anterior o creer que está y que no lo esté. La segunda es verificar que el factor efectivo se corresponde con el nominal, cosa que no siempre ocurre exactamente.
Cómo usarlo bien
Cuatro reglas que evitan las conclusiones erróneas más comunes.
Anota siempre el factor junto al resultado. Un perfil sin esa anotación no se puede comparar con ningún otro. Es la información que más veces falta cuando alguien comparte un hallazgo.
Ralentiza red y CPU juntas. Ninguna combinación de red rápida con CPU lenta corresponde a un dispositivo real.
Úsalo también en desarrollo, no solo al perfilar. Trabajar con una ralentización moderada activada durante el desarrollo normal hace que los problemas se noten en el momento de introducirlos. Es incómodo y es la práctica que más regresiones previene.
No lo uses para medir mejoras absolutas. Los tiempos con ralentización no son los tiempos de ningún dispositivo real; son una aproximación. Sirven para comparar dos versiones entre sí bajo las mismas condiciones, no para afirmar cuánto tardará algo en un teléfono concreto.
Hay una tendencia estructural detrás de todo este nivel que conviene tener presente porque explica por qué el problema empeora con el tiempo en lugar de mejorar. El equipo con el que se desarrolla se renueva mucho más rápido que el dispositivo con el que se consume. Un desarrollador cambia de portátil cada dos o tres años y suele recibir uno de gama alta, porque compilar es caro y el coste se justifica solo. Un usuario cambia de teléfono cuando se le rompe, y compra en la franja de precio que puede permitirse. El resultado es que la brecha entre el aparato en el que se escribe el código y el aparato en el que se ejecuta no ha dejado de ensancharse, y hoy es de un orden de magnitud largo en muchos mercados. Eso tiene una consecuencia que la mayoría de los equipos no ha interiorizado: el rendimiento de una aplicación web se degrada con el tiempo aunque nadie toque el código, no porque el código empeore sino porque el equipo de desarrollo se aleja de sus usuarios y deja de percibir el coste de lo que añade. Cada dependencia nueva, cada capa de abstracción, cada componente más rico, se evalúa en una máquina donde su coste es imperceptible. Las tres contramedidas que funcionan son todas de proceso y no de herramienta. La primera: ralentización activada por defecto en el entorno de desarrollo, no como algo que se enciende para perfilar. Si el desarrollo diario ocurre a la velocidad de un dispositivo de gama media, el coste de cada adición se percibe en el momento de añadirla. La segunda: recalibrar el factor cada vez que alguien cambia de máquina, porque el número que era correcto con el portátil anterior es insuficiente con el nuevo. Y la tercera, la más incómoda y la más eficaz: que todo el equipo, no solo quien se ocupa del rendimiento, use el producto en un dispositivo real de gama baja de vez en cuando. No como tarea de verificación sino como uso normal, con la intención de hacer algo. Media hora al mes cambia el criterio de un equipo entero sobre qué es aceptable, y ese criterio es lo que de verdad determina el rendimiento de un producto a largo plazo, mucho más que cualquier optimización concreta.