performance x86 x86-64 cpu-architecture rdtsc

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 la IA32_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.