performance - Ciclos perdidos en Intel? Una inconsistencia entre rdtsc y CPU_CLK_UNHALTED.REF_TSC
x86 x86-64 (1)
TL; DR
La discrepancia que observa entre
RDTSC
y
REFTSC
se debe a las transiciones del estado P de TurboBoost.
Durante estas transiciones, la mayor parte del núcleo, incluido el contador de rendimiento de función fija
REF_TSC
, se detiene durante aproximadamente 20000-21000 ciclos (8.5us), pero
rdtsc
continúa a su frecuencia invariable.
rdtsc
está probablemente en un dominio de potencia y reloj aislado porque es muy importante y por su comportamiento documentado similar al de un reloj de
rdtsc
.
La discrepancia
RDTSC-REFTSC
La discrepancia se manifiesta como una tendencia de
RDTSC
a contar en exceso a
REFTSC
.
Cuanto más se ejecute el programa, más positiva
RDTSC-REFTSC
la diferencia que
RDTSC-REFTSC
tiende a ser.
En tramos muy largos, puede subir hasta 1% -2% o incluso más.
Por supuesto, usted ya ha observado que el exceso de conteo desaparece cuando TurboBoost está desactivado, lo que se puede hacer de la siguiente manera cuando se usa
intel_pstate
:
echo 1 > /sys/devices/system/cpu/intel_pstate/no_turbo
Pero eso no nos dice con certeza que TurboBoost tiene la culpa de la discrepancia; Podría ser que los estados P superiores habilitados por TurboBoost consuman el espacio libre disponible, causando estrangulamiento térmico y paradas.
Posible estrangulamiento?
TurboBoost es una solución de escalado dinámico de frecuencia y voltaje para aprovechar de manera oportunista el espacio libre en la envolvente operativa (térmica o eléctrica). Cuando sea posible, TurboBoost aumentará la frecuencia del núcleo y el voltaje del procesador más allá de su valor nominal, mejorando así el rendimiento a expensas de un mayor consumo de energía.
El mayor consumo de energía, por supuesto, aumenta la temperatura central y el consumo de energía. Eventualmente, se alcanzará algún tipo de límite, y TurboBoost tendrá que reducir el rendimiento.
TM1 estrangulamiento térmico?
Comencé investigando si los circuitos de control térmico (TCC) para el monitor térmico 1 (TM1) o 2 (TM2) estaban causando estrangulamiento térmico.
TM1 reduce el consumo de energía al insertar ciclos de cronómetro TM, y estas son una de las condiciones documentadas que conducen a la suspensión de
REFTSC
.
TM2, por otro lado, no bloquea el reloj;
Solo escala la frecuencia.
libpfc
para permitirme leer MSR seleccionados, específicamente los
IA32_PACKAGE_THERM_STATUS
e IA32_THERM_STATUS.
Ambos contienen un estado de solo lectura y un indicador de registro de hardware de lectura y escritura para varias condiciones térmicas:
(El registro
IA32_PACKAGE_THERM_STATUS
es sustancialmente el mismo)
Si bien algunos de estos bits se configuraron ocasionalmente (¡especialmente al bloquear las salidas de aire de la computadora portátil!), No parecían correlacionarse con el
RDTSC
excesivo de RDTSC, lo que ocurriría de manera confiable independientemente del estado térmico.
Ciclos de servicio de hardware? C-Residencia del Estado?
Al excavar en otro lugar del SDM para hardware similar a un cronómetro, me topé con HDC (Hardware Duty Cycle), un mecanismo por el cual el sistema operativo puede solicitar manualmente a la CPU que opere solo una proporción fija del tiempo; El hardware HDC implementa esto ejecutando el procesador durante 1-15 ciclos de reloj por período de 16 relojes, y forzándolo a ralentí durante los restantes 15-1 ciclos de reloj de ese período.
HDC ofrece registros muy útiles, en particular los MSR:
-
IA32_THREAD_STALL
: Cuenta el número de ciclos detenidos debido a laIA32_THREAD_STALL
forzada en este procesador lógico. -
MSR_CORE_HDC_RESIDENCY
: Igual que el anterior pero para el procesador físico, cuenta los ciclos cuando uno o más procesadores lógicos de este núcleo están inactivos. -
MSR_PKG_HDC_SHALLOW_RESIDENCY
: Cuenta los ciclos que el paquete estaba en estado C2 y que al menos un procesador lógico estaba en ralentí forzado. -
MSR_PKG_HDC_DEEP_RESIDENCY
: Cuenta los ciclos que el paquete estaba en un estado C más profundo (que es precisamente configurable) y que al menos un procesador lógico estaba inactivo a la fuerza.
Para más detalles, consulte Intel SDM Volumen 3, Capítulo 14, §14.5.1 Interfaz de programación de ciclo de trabajo de hardware .
Pero mi CPU i7-4700MQ 2.4 GHz no es compatible con HDC, y eso fue todo para HDC.
Otras fuentes de estrangulamiento?
Excavando un poco más en el Intel SDM encontré un MSR
muy, muy
jugoso:
MSR_CORE_PERF_LIMIT_REASONS
.
Este registro informa una gran cantidad de estado muy útil y bits de registro fijos:
690H MSR_CORE_PERF_LIMIT_REASONS - Paquete - Indicador de recorte de frecuencia en núcleos de procesador
- Bit
0
: Estado de PROCHOT- Bit
1
: estado térmico- Bit
4
: Estado del controlador de gráficos . Cuando se establece, la frecuencia se reduce por debajo de la solicitud del sistema operativo debido a la anulación del controlador de gráficos del procesador.- Bit
5
: Estado de control de frecuencia basado en la utilización autónoma . Cuando se establece, la frecuencia se reduce por debajo de la solicitud del sistema operativo porque el procesador ha detectado que la utilización es baja.- Bit
6
: Estado de alerta térmica del regulador de voltaje . Cuando se establece, la frecuencia se reduce por debajo de la solicitud del sistema operativo debido a una alerta térmica del regulador de voltaje.- Bit
8
: Estado del punto de diseño eléctrico . Cuando se establece, la frecuencia se reduce por debajo de la solicitud del sistema operativo debido a restricciones de puntos de diseño eléctrico (por ejemplo, consumo máximo de corriente eléctrica).- Bit
9
: Estado de limitación de energía del núcleo . Cuando se establece, la frecuencia se reduce por debajo de la solicitud del sistema operativo debido a la limitación de potencia a nivel de dominio.- Bit
10
: estado de PL1 de límite de potencia de nivel de paquete . Cuando se establece, la frecuencia se reduce por debajo de la solicitud del sistema operativo debido a la limitación de potencia a nivel de paquete PL1.- Bit
11
: estado de PL2 con límite de potencia a nivel de paquete . Cuando se establece, la frecuencia se reduce por debajo de la solicitud del sistema operativo debido a la limitación de potencia a nivel de paquete PL2.- Bit
12
: estado de límite máximo de turbo . Cuando se establece, la frecuencia se reduce por debajo de la solicitud del sistema operativo debido a los límites de turbo de varios núcleos.- Bit
13
: Estado de atenuación de transición turbo . Cuando se establece, la frecuencia se reduce por debajo de la solicitud del sistema operativo debido a la atenuación de transición Turbo. Esto evita la degradación del rendimiento debido a los frecuentes cambios en la relación de operación.- Bit
16
: Registro PROCHOT- Bit
17
: registro térmico- Bit
20
: Registro del controlador de gráficos- Bit
21
: Registro de control de frecuencia basado en la utilización autónoma- Bit
22
: Registro de alertas térmicas del regulador de voltaje- Bit
24
: Registro de puntos de diseño eléctrico- Bit
25
: registro de limitación de energía del núcleo- Bit
26
: Registro PL1 de limitación de potencia a nivel de paquete- Bit
27
: Registro PL2 de limitación de potencia a nivel de paquete- Bit
28
: Registro de límite máximo de turbo- Bit
29
: Registro de atenuación de transición turbo
pfc.ko
ahora admite este MSR, y una
demo
imprime cuál de estos bits de registro está activo.
El controlador
pfc.ko
borra los bits adhesivos en cada lectura.
Repasé sus experimentos mientras imprimo los bits, y mi CPU informa bajo una carga muy pesada (los 4 núcleos / 8 hilos activos) varios factores limitantes, incluidos el punto de diseño eléctrico y la limitación de potencia del núcleo . Los bits Package-Level PL2 y Max Turbo Limit siempre están configurados en mi CPU por razones desconocidas para mí. También vi en ocasiones Atenuación de Transición Turbo .
Si bien ninguno de estos bits se correlacionó exactamente con la presencia de la discrepancia
RDTSC-REFTSC
, el último bit me dio que pensar.
La mera
existencia
de la
Atenuación de Transición Turbo
implica que el cambio de los Estados P tiene un costo sustancial suficiente que debe limitarse con algún mecanismo de histéresis.
Cuando no pude encontrar un MSR que contara estas transiciones, decidí hacer la siguiente mejor
RDTSC-REFTSC
la magnitud del
RDTSC-REFTSC
REFTSC para caracterizar las implicaciones de rendimiento de una transición TurboBoost.
Experimentar
La configuración del experimento es la siguiente.
En mi CPU i7-4700MQ, velocidad nominal 2.4GHz y velocidad Turbo máxima 3.4 GHz, desconectaré todos los núcleos excepto 0 (el procesador de arranque) y 3 (un núcleo de víctima conveniente no numerado 0 y no un hermano lógico de 0).
Luego le pediremos al controlador
intel_pstate
que nos proporcione un rendimiento de paquete de no menos del 98% y no más del 100%;
Esto limita al procesador a oscilar entre el segundo estado P más alto y el más alto (3.3 GHz y 3.4 GHz).
Hago esto de la siguiente manera:
echo 0 > /sys/devices/system/cpu/cpu1/online
echo 0 > /sys/devices/system/cpu/cpu2/online
echo 0 > /sys/devices/system/cpu/cpu4/online
echo 0 > /sys/devices/system/cpu/cpu5/online
echo 0 > /sys/devices/system/cpu/cpu6/online
echo 0 > /sys/devices/system/cpu/cpu7/online
echo 98 > /sys/devices/system/cpu/intel_pstate/min_perf_pct
echo 100 > /sys/devices/system/cpu/intel_pstate/max_perf_pct
Ejecuté la aplicación de demo para 10000 muestras en
1000, 1500, 2500, 4000, 6300,
10000, 15000, 25000, 40000, 63000,
100000, 150000, 250000, 400000, 630000,
1000000, 1500000, 2500000, 4000000, 6300000,
10000000, 15000000, 25000000, 40000000, 63000000
nanosegundos por
add_calibration()
ejecutado a la frecuencia nominal de la CPU (multiplique los números anteriores por 2.4 para obtener el argumento real de
add_calibration()
).
Resultados
Esto produce registros que se ven así (caso de 250000 nanos):
CPU 0, measured CLK_REF_TSC MHz : 2392.56
CPU 0, measured rdtsc MHz : 2392.46
CPU 0, measured add MHz : 3286.30
CPU 0, measured XREF_CLK time (s) : 0.00018200
CPU 0, measured delta time (s) : 0.00018258
CPU 0, measured tsc_delta time (s) : 0.00018200
CPU 0, ratio ref_tsc :ref_xclk : 24.00131868
CPU 0, ratio ref_core:ref_xclk : 33.00071429
CPU 0, ratio rdtsc :ref_xclk : 24.00032967
CPU 0, core CLK cycles in OS : 0
CPU 0, User-OS transitions : 0
CPU 0, rdtsc-reftsc overcount : -18
CPU 0, MSR_IA32_PACKAGE_THERM_STATUS : 000000008819080a
CPU 0, MSR_IA32_PACKAGE_THERM_INTERRUPT: 0000000000000003
CPU 0, MSR_CORE_PERF_LIMIT_REASONS : 0000000018001000
PROCHOT
Thermal
Graphics Driver
Autonomous Utilization-Based Frequency Control
Voltage Regulator Thermal Alert
Electrical Design Point (e.g. Current)
Core Power Limiting
Package-Level PL1 Power Limiting
* Package-Level PL2 Power Limiting
* Max Turbo Limit (Multi-Core Turbo)
Turbo Transition Attenuation
CPU 0, measured CLK_REF_TSC MHz : 2392.63
CPU 0, measured rdtsc MHz : 2392.62
CPU 0, measured add MHz : 3288.03
CPU 0, measured XREF_CLK time (s) : 0.00018192
CPU 0, measured delta time (s) : 0.00018248
CPU 0, measured tsc_delta time (s) : 0.00018192
CPU 0, ratio ref_tsc :ref_xclk : 24.00000000
CPU 0, ratio ref_core:ref_xclk : 32.99983509
CPU 0, ratio rdtsc :ref_xclk : 23.99989006
CPU 0, core CLK cycles in OS : 0
CPU 0, User-OS transitions : 0
CPU 0, rdtsc-reftsc overcount : -2
CPU 0, MSR_IA32_PACKAGE_THERM_STATUS : 000000008819080a
CPU 0, MSR_IA32_PACKAGE_THERM_INTERRUPT: 0000000000000003
CPU 0, MSR_CORE_PERF_LIMIT_REASONS : 0000000018001000
PROCHOT
Thermal
Graphics Driver
Autonomous Utilization-Based Frequency Control
Voltage Regulator Thermal Alert
Electrical Design Point (e.g. Current)
Core Power Limiting
Package-Level PL1 Power Limiting
* Package-Level PL2 Power Limiting
* Max Turbo Limit (Multi-Core Turbo)
Turbo Transition Attenuation
CPU 0, measured CLK_REF_TSC MHz : 2284.69
CPU 0, measured rdtsc MHz : 2392.63
CPU 0, measured add MHz : 3151.99
CPU 0, measured XREF_CLK time (s) : 0.00018121
CPU 0, measured delta time (s) : 0.00019036
CPU 0, measured tsc_delta time (s) : 0.00018977
CPU 0, ratio ref_tsc :ref_xclk : 24.00000000
CPU 0, ratio ref_core:ref_xclk : 33.38540919
CPU 0, ratio rdtsc :ref_xclk : 25.13393301
CPU 0, core CLK cycles in OS : 0
CPU 0, User-OS transitions : 0
CPU 0, rdtsc-reftsc overcount : 20548
CPU 0, MSR_IA32_PACKAGE_THERM_STATUS : 000000008819080a
CPU 0, MSR_IA32_PACKAGE_THERM_INTERRUPT: 0000000000000003
CPU 0, MSR_CORE_PERF_LIMIT_REASONS : 0000000018000000
PROCHOT
Thermal
Graphics Driver
Autonomous Utilization-Based Frequency Control
Voltage Regulator Thermal Alert
Electrical Design Point (e.g. Current)
Core Power Limiting
Package-Level PL1 Power Limiting
* Package-Level PL2 Power Limiting
* Max Turbo Limit (Multi-Core Turbo)
Turbo Transition Attenuation
CPU 0, measured CLK_REF_TSC MHz : 2392.46
CPU 0, measured rdtsc MHz : 2392.45
CPU 0, measured add MHz : 3287.80
CPU 0, measured XREF_CLK time (s) : 0.00018192
CPU 0, measured delta time (s) : 0.00018249
CPU 0, measured tsc_delta time (s) : 0.00018192
CPU 0, ratio ref_tsc :ref_xclk : 24.00000000
CPU 0, ratio ref_core:ref_xclk : 32.99978012
CPU 0, ratio rdtsc :ref_xclk : 23.99989006
CPU 0, core CLK cycles in OS : 0
CPU 0, User-OS transitions : 0
CPU 0, rdtsc-reftsc overcount : -2
CPU 0, MSR_IA32_PACKAGE_THERM_STATUS : 000000008819080a
CPU 0, MSR_IA32_PACKAGE_THERM_INTERRUPT: 0000000000000003
CPU 0, MSR_CORE_PERF_LIMIT_REASONS : 0000000018001000
PROCHOT
Thermal
Graphics Driver
Autonomous Utilization-Based Frequency Control
Voltage Regulator Thermal Alert
Electrical Design Point (e.g. Current)
Core Power Limiting
Package-Level PL1 Power Limiting
* Package-Level PL2 Power Limiting
* Max Turbo Limit (Multi-Core Turbo)
Turbo Transition Attenuation
Hice varias observaciones sobre los registros, pero una se destacó:
Para nanos <~ 250000, hay un desbordamiento despreciable de RDTSC. Para nanos> ~ 250000, se puede observar de manera confiable el conteo excesivo de ciclos de reloj de poco más de 20000 ciclos de reloj. Pero no se deben a las transiciones del sistema operativo del usuario.
Aquí hay una trama visual:
Puntos azules saturados: 0 desviaciones estándar (cerca de la media)
Puntos rojos saturados: +3 desviaciones estándar (por encima de la media)
Puntos Verdes Saturados: -3 desviaciones estándar (debajo de la media)
Hay una marcada diferencia antes, durante y después de aproximadamente 250000 nanosegundos de disminución sostenida.
Nanos <250000
Antes del umbral, los registros CSV se ven así:
24.00,33.00,24.00,-14,0,0
24.00,33.00,24.00,-20,0,0
24.00,33.00,24.00,-4,3639,1
24.00,33.00,24.00,-20,0,0
24.00,33.00,24.00,10,0,0
24.00,33.00,24.00,10,0,0
24.00,33.00,24.00,-14,0,0
24.00,33.00,24.00,-14,0,0
24.00,33.00,24.00,10,0,0
24.00,33.00,24.00,-44,0,0
24.00,33.00,24.00,10,0,0
24.00,33.00,24.00,-14,0,0
24.00,33.00,24.00,-20,0,0
24.00,33.00,24.00,10,0,0
24.00,33.00,24.00,10,0,0
24.00,33.00,24.00,10,0,0
24.00,33.00,24.00,10,0,0
24.00,33.00,24.00,-20,0,0
24.00,33.00,24.00,10,0,0
24.00,33.00,24.00,10,0,0
24.00,33.00,24.00,10,0,0
24.00,33.00,24.00,10,0,0
24.00,33.00,24.00,12,0,0
24.00,33.00,24.00,10,0,0
24.00,33.00,24.00,10,0,0
24.00,33.00,24.00,10,0,0
24.00,33.00,24.00,10,0,0
24.00,33.00,24.00,10,0,0
24.00,33.00,24.00,10,0,0
24.00,33.00,24.00,-20,0,0
24.00,33.00,24.00,32,3171,1
24.00,33.00,24.00,-20,0,0
24.00,33.00,24.00,10,0,0
Indicando una relación TurboBoost perfectamente estable a 33x, un
RDTSC
que cuenta en sincronía con
REFTSC
a 24x la tasa de
REF_XCLK
(100 MHz), exceso de conteo insignificante, típicamente 0 ciclos gastados en el núcleo y, por lo tanto, 0 transiciones en el núcleo.
Las interrupciones del kernel requieren aproximadamente 3000 ciclos de referencia para el servicio.
Nanos == 250000
En el umbral crítico, el registro contiene grupos de excesos de 20000 ciclos, y los excesos se correlacionan muy bien con valores multiplicadores estimados no enteros entre 33x y 34x:
24.00,33.00,24.00,-2,0,0
24.00,33.00,24.00,-2,0,0
24.00,33.00,24.00,2,0,0
24.00,33.00,24.00,22,0,0
24.00,33.00,24.00,-2,0,0
24.00,33.00,24.00,-2,0,0
24.00,33.00,24.00,-2,0,0
24.00,33.05,25.11,20396,0,0
24.00,33.38,25.12,20212,0,0
24.00,33.39,25.12,20308,0,0
24.00,33.42,25.12,20296,0,0
24.00,33.43,25.11,20158,0,0
24.00,33.43,25.11,20178,0,0
24.00,33.00,24.00,-4,0,0
24.00,33.00,24.00,20,3920,1
24.00,33.00,24.00,-2,0,0
24.00,33.00,24.00,-4,0,0
24.00,33.44,25.13,20396,0,0
24.00,33.46,25.11,20156,0,0
24.00,33.46,25.12,20268,0,0
24.00,33.41,25.12,20322,0,0
24.00,33.40,25.11,20216,0,0
24.00,33.46,25.12,20168,0,0
24.00,33.00,24.00,-2,0,0
24.00,33.00,24.00,-2,0,0
24.00,33.00,24.00,-2,0,0
24.00,33.00,24.00,22,0,0
Nanos> 250000
El TurboBoost de 3.3 GHz a 3.4 GHz ahora ocurre de manera confiable. A medida que aumentan los nanos, los registros se llenan con múltiplos aproximadamente enteros de cuantos de 20000 ciclos. Eventualmente, hay tantos nanos que las interrupciones del programador de Linux se convierten en elementos permanentes, pero la preferencia se detecta fácilmente con los contadores de rendimiento, y su efecto no es en absoluto similar a las paradas de TurboBoost.
24.00,33.75,24.45,20166,0,0
24.00,33.78,24.45,20302,0,0
24.00,33.78,24.45,20202,0,0
24.00,33.68,24.91,41082,0,0
24.00,33.31,24.90,40998,0,0
24.00,33.70,25.30,58986,3668,1
24.00,33.74,24.42,18798,0,0
24.00,33.74,24.45,20172,0,0
24.00,33.77,24.45,20156,0,0
24.00,33.78,24.45,20258,0,0
24.00,33.78,24.45,20240,0,0
24.00,33.77,24.42,18826,0,0
24.00,33.75,24.45,20372,0,0
24.00,33.76,24.42,18798,4081,1
24.00,33.74,24.41,18460,0,0
24.00,33.75,24.45,20234,0,0
24.00,33.77,24.45,20284,0,0
24.00,33.78,24.45,20150,0,0
24.00,33.78,24.45,20314,0,0
24.00,33.78,24.42,18766,0,0
24.00,33.71,25.36,61608,0,0
24.00,33.76,24.45,20336,0,0
24.00,33.78,24.45,20234,0,0
24.00,33.78,24.45,20210,0,0
24.00,33.78,24.45,20210,0,0
24.00,33.00,24.00,-10,0,0
24.00,33.00,24.00,4,0,0
24.00,33.00,24.00,18,0,0
24.00,33.00,24.00,2,4132,1
24.00,33.00,24.00,44,0,0
Conclusiones
La maquinaria TurboBoost es responsable de la discrepancia en
RDTSC-REFTSC
.
Esta discrepancia se puede usar para determinar que una transición de estado TurboBoost de 3.3 GHz a 3.4 GHz toma aproximadamente 20500 ciclos de reloj de referencia (8.5us), y se activa a más tardar aproximadamente 250000 ns (250us; 600000 ciclos de reloj de referencia) después de ingresar en
add_reference()
, cuando el procesador decide que la carga de trabajo es lo suficientemente intensa como para merecer una escala de voltaje de frecuencia.
Trabajo futuro
Se necesita hacer más investigación para determinar cómo el costo de transición varía con la frecuencia y si el hardware que selecciona el estado de energía se puede ajustar. De particular interés para mí son las "Unidades de atenuación turbo", pistas que he visto en los confines de la web. ¿Quizás el hardware Turbo tiene una ventana de tiempo configurable? Actualmente, la relación entre el tiempo dedicado a decidir y el tiempo dedicado a la transición es de 30: 1 (600us: 20us). ¿Se puede sintonizar?
En las CPU recientes (al menos en la última década más o menos), Intel ha ofrecido tres contadores de rendimiento de hardware de función fija, además de varios contadores de rendimiento configurables. Los tres contadores fijos son:
INST_RETIRED.ANY
CPU_CLK_UNHALTED.THREAD
CPU_CLK_UNHALTED.REF_TSC
El primero cuenta las instrucciones retiradas, el segundo número de ciclos reales y el último es lo que nos interesa. La descripción para el Volumen 3 del manual de Intel Software Developers es:
Este evento cuenta el número de ciclos de referencia a la velocidad de TSC cuando el núcleo no está en un estado de detención y no en un estado de cronómetro TM. El núcleo ingresa al estado de detención cuando ejecuta la instrucción HLT o la instrucción MWAIT. Este evento no se ve afectado por los cambios de frecuencia del núcleo (p. Ej., Estados P), pero cuenta con la misma frecuencia que el contador de marca de tiempo. Este evento puede aproximarse al tiempo transcurrido mientras el núcleo no estaba en estado de detención y no en estado de parada de TM.
Por lo tanto, para un bucle vinculado a la CPU, espero que este valor sea el mismo que el valor TSC de ejecución libre leído desde
rdstc
, ya que deberían divergir solo para instrucciones de ciclos detenidos o cuál es el "estado del cronómetro TM".
Pruebo esto con el siguiente bucle (toda la demostración independiente está disponible en github ):
for (int i = 0; i < 100; i++) {
PFC_CNT cnt[7] = {};
int64_t start = nanos();
PFCSTART(cnt);
int64_t tsc =__rdtsc();
busy_loop(CALIBRATION_LOOPS);
PFCEND(cnt);
int64_t tsc_delta = __rdtsc() - tsc;
int64_t nanos_delta = nanos() - start;
printf(CPU_W "d" REF_W ".2f" TSC_W ".2f" MHZ_W ".2f" RAT_W ".6f/n",
sched_getcpu(),
1000.0 * cnt[PFC_FIXEDCNT_CPU_CLK_REF_TSC] / nanos_delta,
1000.0 * tsc_delta / nanos_delta,
1000.0 * CALIBRATION_LOOPS / nanos_delta,
1.0 * cnt[PFC_FIXEDCNT_CPU_CLK_REF_TSC]/tsc_delta);
}
Lo único importante en la región cronometrada es
busy_loop(CALIBRATION_LOOPS);
que es simplemente un ciclo cerrado de tiendas volátiles, que
compilado
por
gcc
y
clang
ejecuta en un ciclo por iteración en hardware reciente:
void busy_loop(uint64_t iters) {
volatile int sink;
do {
sink = 0;
} while (--iters > 0);
(void)sink;
}
Los comandos
PFCSTART
y
PFCEND
leen el contador
libpfc
usando
libpfc
.
El
__rdtsc()
es un intrínseco que lee el TSC a través de la instrucción
rdtsc
.
Finalmente, medimos en tiempo real con
nanos()
que es simplemente:
int64_t nanos() {
auto t = std::chrono::high_resolution_clock::now();
return std::chrono::time_point_cast<std::chrono::nanoseconds>(t).time_since_epoch().count();
}
Sí, no
cpuid
un
cpuid
, y las cosas no se entrelazan de manera exacta, pero el ciclo de calibración es un segundo completo, por lo que estos problemas de escala de nanosegundos simplemente se diluyen a más o menos nada.
Con TurboBoost habilitado, estos son los primeros resultados de una ejecución típica en mi CPU i7-6700HQ Skylake:
CPU# REF_TSC rdtsc Eff Mhz Ratio
0 2392.05 2591.76 2981.30 0.922946
0 2381.74 2591.79 3032.86 0.918955
0 2399.12 2591.79 3032.50 0.925660
0 2385.04 2591.79 3010.58 0.920230
0 2378.39 2591.79 3010.21 0.917663
0 2355.84 2591.77 2928.96 0.908970
0 2364.99 2591.79 2942.32 0.912492
0 2339.64 2591.77 2935.36 0.902720
0 2366.43 2591.79 3022.08 0.913049
0 2401.93 2591.79 3023.52 0.926747
0 2452.87 2591.78 3070.91 0.946400
0 2350.06 2591.79 2961.93 0.906733
0 2340.44 2591.79 2897.58 0.903020
0 2403.22 2591.79 2944.77 0.927246
0 2394.10 2591.79 3059.58 0.923723
0 2359.69 2591.78 2957.79 0.910449
0 2353.33 2591.79 2916.39 0.907992
0 2339.58 2591.79 2951.62 0.902690
0 2395.82 2591.79 3017.59 0.924389
0 2353.47 2591.79 2937.82 0.908047
Aquí,
REF_TSC
es el contador de rendimiento fijo de TSC como se describió anteriormente, y
rdtsc
es el resultado de la instrucción
rdtsc
.
Eff Mhz
es la frecuencia de CPU real calculada efectiva durante el intervalo y se muestra principalmente por curiosidad y como una confirmación rápida de cuánto está
REF_TSC
turbo. La
Ratio
es la relación de las columnas
REF_TSC
y
rdtsc
.
Esperaría que esto sea muy cercano a 1, pero en la práctica vemos que oscila entre 0,90 y 0,92 con mucha variación (lo he visto tan bajo como 0,8 en otras carreras).
Gráficamente se parece a esto 2 :
La llamada
rdstc
está devolviendo resultados casi
exactos
1
, mientras que el contador PMU TSC está por todas partes, a veces casi tan bajo como 2300 MHz.
Sin embargo, si apago el turbo , los resultados son mucho más consistentes:
CPU# REF_TSC rdtsc Eff Mhz Ratio
0 2592.26 2592.25 2588.30 1.000000
0 2592.26 2592.26 2591.11 1.000000
0 2592.26 2592.26 2590.40 1.000000
0 2592.25 2592.25 2590.43 1.000000
0 2592.26 2592.26 2590.75 1.000000
0 2592.26 2592.26 2590.05 1.000000
0 2592.25 2592.25 2590.04 1.000000
0 2592.24 2592.24 2590.86 1.000000
0 2592.25 2592.25 2590.35 1.000000
0 2592.25 2592.25 2591.32 1.000000
0 2592.25 2592.25 2590.63 1.000000
0 2592.25 2592.25 2590.87 1.000000
0 2592.25 2592.25 2590.77 1.000000
0 2592.25 2592.25 2590.64 1.000000
0 2592.24 2592.24 2590.30 1.000000
0 2592.23 2592.23 2589.64 1.000000
0 2592.23 2592.23 2590.83 1.000000
0 2592.23 2592.23 2590.49 1.000000
0 2592.23 2592.23 2590.78 1.000000
0 2592.23 2592.23 2590.84 1.000000
0 2592.22 2592.22 2588.80 1.000000
Básicamente, la relación es 1.000000 a 6 decimales .
Gráficamente (con la escala del eje Y obligada a ser la misma que la gráfica anterior):
Ahora el código solo está ejecutando un bucle
hlt
, y no debe haber instrucciones
hlt
o
mwait
, ciertamente nada que implique una variación de más del 10%.
No puedo decir
con certeza
cuáles son los "ciclos de cronómetro TM", pero apuesto a que son "ciclos de cronómetro de gestión térmica", un truco utilizado para acelerar temporalmente la CPU cuando alcanza su temperatura máxima.
Sin embargo, miré las lecturas integradas del termistor, y nunca vi que la CPU se rompiera 60C, muy por debajo del 90C-100C, donde entra en juego la gestión térmica (creo).
¿Alguna idea de lo que podría ser? ¿Existen "ciclos de detención" implícitos para la transición entre diferentes frecuencias turbo? Esto definitivamente sucede ya que la caja no es silenciosa y, por lo tanto, la frecuencia turbo sube y baja a medida que otros núcleos comienzan y dejan de funcionar en el fondo (la frecuencia turbo máxima depende directamente de la cantidad de núcleos activos: en mi caja es 3.5, 3.3, 3.2, 3.1 GHz para 1, 2, 3 o 4 núcleos activos, respectivamente).
1
De hecho, durante un tiempo realmente obtuve resultados
exactos
con dos decimales:
2591.97 MHz
- iteración tras iteración.
Entonces algo cambió y no estoy exactamente seguro de qué, y hay una pequeña variación de aproximadamente 0.1% en los resultados de
rdstc
.
Una posibilidad es el ajuste gradual del reloj, realizado por el subsistema de temporización de Linux para alinear el tiempo derivado del cristal local con el tiempo determinado por
ntpd
.
Tal vez, es solo una deriva de cristal: el último gráfico anterior muestra un aumento constante en el período medido de
rdtsc
cada segundo.
2 Los gráficos no corresponden a las mismas ejecuciones que los valores que se muestran en el texto porque no voy a actualizar los gráficos cada vez que cambie el formato de salida de texto. Sin embargo, el comportamiento cualitativo es esencialmente el mismo en cada carrera.