GNU/Linux >> Tutoriales Linux >  >> Linux

¿Por qué este ciclo de retardo comienza a ejecutarse más rápido después de varias iteraciones sin dormir?

Después de 26 iteraciones, Linux acelera la CPU hasta la velocidad de reloj máxima ya que su proceso usa su segmento de tiempo completo un par de veces seguidas.

Si verificó con los contadores de rendimiento en lugar del tiempo del reloj de pared, vería que los ciclos de reloj del núcleo por bucle de retardo se mantuvieron constantes, lo que confirma que es solo un efecto de DVFS (que todas las CPU modernas usan para funcionar con más energía). frecuencia y voltaje eficientes la mayor parte del tiempo).

Si probó en un Skylake con soporte de kernel para el nuevo modo de administración de energía (donde el hardware toma el control total de la velocidad del reloj), la aceleración ocurriría mucho más rápido.

Si lo deja funcionando por un tiempo en una CPU Intel con Turbo, probablemente verá que el tiempo por iteración aumenta nuevamente una vez que los límites térmicos requieren que la velocidad del reloj se reduzca a la frecuencia máxima sostenida. (Consulte ¿Por qué mi CPU no puede mantener el rendimiento máximo en HPC para obtener más información sobre cómo Turbo permite que la CPU funcione más rápido de lo que puede soportar cargas de trabajo de alta potencia).

Presentamos un usleep evita que el regulador de frecuencia de la CPU de Linux aumente la velocidad del reloj, porque el proceso no genera el 100 % de la carga, incluso con la frecuencia mínima. (Es decir, la heurística del kernel decide que la CPU se ejecuta lo suficientemente rápido para la carga de trabajo que se ejecuta en ella).

comentarios sobre otras teorías :

re:la teoría de David de que un contexto potencial cambia de usleep podría contaminar los cachés:no es una mala idea en general, pero no ayuda a explicar este código.

La contaminación de caché/TLB no es importante para este experimento . Básicamente, no hay nada dentro de la ventana de tiempo que toque la memoria que no sea el final de la pila. La mayor parte del tiempo se pasa en un pequeño bucle (1 línea de caché de instrucciones) que solo toca un int de memoria de pila. Cualquier posible contaminación del caché durante usleep es una pequeña fracción del tiempo para este código (¡el código real será diferente)!

Con más detalle para x86:

La llamada a clock() en sí mismo podría fallar en la caché, pero una falla en la caché de búsqueda de código retrasa la medición del tiempo de inicio, en lugar de ser parte de lo que se mide. La segunda llamada a clock() casi nunca se retrasará, porque aún debería estar caliente en caché.

El run la función puede estar en una línea de caché diferente de main (ya que gcc marca main como "frío", por lo que se optimiza menos y se coloca con otras funciones/datos fríos). Podemos esperar uno o dos errores de caché de instrucciones. Probablemente todavía estén en la misma página de 4k, así que main habrá activado la falla potencial de TLB antes de ingresar a la región cronometrada del programa.

gcc -O0 compilará el código del OP a algo como esto (Godbolt Compiler explorer):manteniendo el contador de bucle en la memoria en la pila.

El bucle vacío mantiene el contador de bucles en la memoria de la pila, por lo que en una CPU Intel x86 típica, el bucle se ejecuta en una iteración por ~6 ciclos en la CPU IvyBridge del OP, gracias a la latencia de reenvío de almacenamiento que forma parte de add con un destino de memoria (lectura-modificación-escritura). 100k iterations * 6 cycles/iteration es de 600 000 ciclos, lo que domina la contribución de un par de errores de caché como máximo (~200 ciclos cada uno para errores de recuperación de código que impiden que se emitan más instrucciones hasta que se resuelvan).

La ejecución fuera de orden y el reenvío de almacenamiento deberían ocultar principalmente la pérdida potencial de caché al acceder a la pila (como parte del call instrucciones).

Incluso si el contador de bucles se mantuviera en un registro, 100k ciclos es mucho.


Una llamada al usleep puede o no resultar en un cambio de contexto. Si lo hace, llevará más tiempo que si no lo hace.


Linux
  1. ¿Por qué Plymouth comienza tan tarde?

  2. ¿Por qué Tomcat funciona con el puerto 8080 pero no con el 80?

  3. ¿Por qué este código falla con la aleatorización de direcciones activada?

  4. ¿Por qué esta expresión regular no funciona en Linux?

  5. ¿Cómo ejecutar un comando en segundo plano con un retraso?

Linux:¿cómo funciona el promedio de carga con las CPU modernas?

¿Por qué el medidor Htop muestra>90 % mientras que la tabla es 0,0 %?

¿Por qué ejecutar un comando de shell de Linux con '&'?

¿Por qué rm manual dice que podemos ejecutarlo sin ningún argumento, cuando esto no es cierto?

¿Por qué un enrutador de hardware funciona mejor que un enrutador Linux con mejores especificaciones (RAM y CPU)?

¿Por qué me falta /var/run/sshd después de cada arranque?