LAB 004
Cuánto duerme de verdad un sleep(1ms)
Pides un milisegundo. Nadie te promete un milisegundo. 400 muestras por lengua y objetivo, en la misma máquina, sin carga.
Lo medido
Linux x86-64, kernel con CONFIG_HZ moderno, máquina en reposo. C con nanosleep, Python 3.12 con time.sleep, Node 22 con await setTimeout. Todos los tiempos en milisegundos.
| objetivo | C · med | Python · med | Node · med | Node · máx |
| 0,1 ms | 0,165 | 0,175 | 1,138 | 1,758 |
| 1 ms | 1,091 | 1,086 | 1,132 | 5,878 |
| 5 ms | 5,105 | 5,098 | 5,132 | 5,669 |
| 16 ms | 16,121 | 16,115 | 16,177 | 22,569 |
Tres cosas que se leen ahí
Uno. Nadie duerme menos de lo que pide, y todos duermen algo más. Hay un peaje fijo de entre 60 y 120 microsegundos: salir a la llamada al sistema, ceder la CPU, que el planificador te devuelva. Ese peaje no baja optimizando tu código, porque no es tuyo.
Dos. C y Python son indistinguibles. Es contraintuitivo y no lo es: los dos acaban en el mismo nanosleep, y el intérprete de Python cuesta muchísimo menos que un cambio de contexto. Cuando el cuello de botella es el kernel, el lenguaje deja de importar.
Tres, y es el bueno. Node pide 0,1 ms y duerme 1,14. Un factor de once. setTimeout tiene un suelo de 1 ms por especificación: cualquier valor menor se redondea hacia arriba. Si estás escribiendo un bucle de simulación, un planificador o cualquier cosa que crea que puede iterar a 0,1 ms en Node, no está iterando a 0,1 ms. Está iterando a 1,14 y acumulando el error en silencio.
Y fíjate en los máximos de Node: 5,9 ms cuando pidió 1, y 22,6 cuando pidió 16. Eso es el recolector de basura pasando por encima. La mediana es honesta; la cola, no.
Las distribuciones
La mediana esconde la forma. Pulsa para cambiar de objetivo.
■ python ■ node
Tu navegador
Los números de arriba son de una máquina. Este es el tuyo, medido ahora mismo — setTimeout(0) y setTimeout(1), 200 muestras cada uno.
sin medir
- setTimeout(0) med
- —
- setTimeout(1) med
- —
- p95 de (1)
- —
- peor caso
- —
Si tu setTimeout(0) sale por encima de 4 ms, tu navegador está aplicando el clamping de temporizadores anidados: a partir del quinto encadenado, la especificación permite subir el suelo a 4 ms. Es la razón por la que los bucles basados en setTimeout(0) se arrastran.
Qué hacer con esto
Nada, si tu tolerancia es de decenas de milisegundos. Y si no lo es: para animación, requestAnimationFrame. Para audio y cualquier cosa con ritmo, el reloj de AudioContext y un planificador con antelación — está en el 001. Para tiempo real de verdad, espera activa y aceptar que quemas un núcleo.
La regla que queda: un sleep es un mínimo, nunca una cita.