c++ - microprocesador - que es un ciclo de reloj en un procesador
Encontrar la frecuencia del reloj de la CPU(por núcleo, por procesador) (6)
Anteriormente publiqué sobre este tema (junto con un algoritmo básico): here . Que yo sepa, el algoritmo (ver la discusión) es muy preciso. Por ejemplo, Windows 7 informa el reloj de mi CPU como 2.00 GHz, CPU-Z como 1994-1996 MHz y mi algoritmo como 1995025-1995075 kHz.
El algoritmo realiza muchos bucles para hacer esto, lo que hace que la frecuencia de la CPU aumente al máximo (como también lo hará durante los puntos de referencia), por lo que el software de aceleración de velocidad no entrará en juego.
Información adicional here y here .
En cuanto a la velocidad de aceleración, realmente no lo veo como un problema a menos que una aplicación use los valores de velocidad para determinar los tiempos transcurridos y que los tiempos sean extremadamente importantes. Por ejemplo, si una división requiere x ciclos de reloj para completarla, no importa si la CPU está funcionando a 3 GHz o 300 MHz: todavía necesitará x ciclos de reloj y la única diferencia es que completará la división en un décimo del tiempo a @ 3 GHz.
Los programas como CPUz son muy buenos para brindar información detallada sobre el sistema (velocidad del bus, tiempos de memoria, etc.)
Sin embargo, existe una forma programática de calcular la frecuencia por núcleo (y por procesador, en sistemas de múltiples procesadores con múltiples núcleos por CPU) sin tener que lidiar con información específica de la CPU.
Estoy tratando de desarrollar una herramienta anti trampa (para el uso con competiciones de referencia de reloj limitado) que será capaz de registrar el reloj de la CPU durante la ejecución de referencia para todos los núcleos activos en el sistema (en todos los procesadores).
Expandiré mis comentarios aquí. Esto es demasiado grande y profundo para encajar en los comentarios.
Lo que estás tratando de hacer es muy difícil, hasta el punto de ser poco práctico por las siguientes razones:
- No hay una forma portátil de obtener la frecuencia del procesador.
rdtsc
NO siempre da la frecuencia correcta debido a efectos tales como SpeedStep y Turbo Boost. - Todos los métodos conocidos para medir la frecuencia requieren una medición precisa del tiempo. Sin embargo, un tramposo determinado puede manipular todos los relojes y temporizadores en el sistema.
- Leer con precisión tanto la frecuencia del procesador como el tiempo a prueba de manipulaciones requerirá acceso a nivel del kernel. Esto implica la firma del controlador para Windows.
No hay forma portátil de obtener la frecuencia del procesador:
La forma "fácil" de obtener la frecuencia de la CPU es llamar a rdtsc
dos veces con una duración de tiempo fija en el medio. Luego dividir la diferencia te dará la frecuencia.
El problema es que rdtsc
no da la verdadera frecuencia del procesador. Debido a que las aplicaciones en tiempo real, como los juegos, dependen de él, rdtsc
debe ser coherente mediante la aceleración de CPU y Turbo Boost. Entonces, una vez que arranque su sistema, rdtsc
siempre funcionará a la misma velocidad (a menos que empiece a interferir con las velocidades del bus con SetFSB o algo así).
Por ejemplo, en mi Core i7 2600K, rdtsc
siempre mostrará la frecuencia a 3.4 GHz
. Pero en realidad, está inactivo a 1.6 GHz
y alcanza hasta 4.6 GHz
bajo carga a través del multiplicador Turbo Boost overclockeado a 46x
.
Pero una vez que encuentras una forma de medir la frecuencia real (o te sientes satisfecho con rdtsc
), puedes obtener fácilmente la frecuencia de cada núcleo usando thread-affinities .
Obteniendo la verdadera frecuencia:
Para obtener la frecuencia real del procesador, necesita acceder a los MSR (registros específicos del modelo) o a los contadores de rendimiento del hardware.
Estas son instrucciones a nivel de núcleo y, por lo tanto, requieren el uso de un controlador. Si está intentando hacer esto en Windows con el fin de distribuirlo, necesitará pasar por el protocolo de firma del controlador adecuado. Además, el código será diferente según la marca y el modelo del procesador, por lo que necesitará un código de detección diferente para cada generación de procesadores.
Una vez que llegas a esta etapa, hay una variedad de formas de leer la frecuencia.
En los procesadores Intel, los contadores de hardware le permiten contar los ciclos de CPU sin procesar. Combinado con un método para medir con precisión el tiempo real (siguiente sección), puede calcular la frecuencia real. Los MSR le dan acceso a otra información, como el multiplicador de frecuencia de la CPU.
Todos los métodos conocidos para medir la frecuencia requieren una medición precisa del tiempo:
Este es quizás el problema más grande. Necesita un temporizador para poder medir la frecuencia. Un hacker capaz será capaz de manipular todos los relojes que pueda usar en C / C ++. Esto incluye todo lo siguiente:
-
clock()
-
gettimeofday()
-
QueryPerformanceCounter()
- etc ...
La lista sigue y sigue. En otras palabras, no puede confiar en ninguno de los temporizadores ya que un pirata informático capaz será capaz de falsificarlos a todos. Por ejemplo, clock()
y gettimeofday()
pueden engañarse cambiando el reloj del sistema directamente dentro del sistema operativo. Fooling QueryPerformanceCounter()
es más difícil.
Obtener una verdadera medida del tiempo:
Todos los relojes enumerados anteriormente son vulnerables porque a menudo se derivan del mismo reloj base del sistema de una forma u otra. Y ese reloj base del sistema a menudo está vinculado al reloj base del sistema, que puede cambiarse después de que el sistema ya haya arrancado por medio de utilidades de overclocking.
Por lo tanto, la única forma de obtener una medición de tiempo confiable e inalterable es leyendo relojes externos como HPET o ACPI . Desafortunadamente, estos también parecen requerir acceso a nivel kernel.
Para resumir:
La construcción de cualquier tipo de punto de referencia a prueba de manipulaciones casi seguramente requerirá escribir un controlador en modo kernel que requiera la firma de certificados para Windows. Esto es a menudo una carga excesiva para los escritores de benchmark casuales.
Esto ha dado lugar a una escasez de puntos de referencia a prueba de manipulación que probablemente ha contribuido a la disminución general de la comunidad competitiva de overclocking en los últimos años.
Me doy cuenta de que esto ya ha sido respondido. También me doy cuenta de que esto es básicamente un arte negro, así que tómenlo o déjenlo u ofrezcan comentarios.
En una búsqueda para encontrar la velocidad del reloj en throttled (gracias microsft, hp, y dell) hosts HyperV (contador de rendimiento no confiable), e invitados HyperV (solo pueden obtener velocidad de CPU de stock, no actual), logré, a través de error de prueba y Fluke, para crear un bucle que se repite exactamente una vez por reloj.
Codifique de la siguiente manera: C # 5.0, SharpDev, 32 bits, Target 3.5, Optimize on (crucial), no debuger active (crucial)
long frequency, start, stop;
double multiplier = 1000 * 1000 * 1000;//nano
if (Win32.QueryPerformanceFrequency(out frequency) == false)
throw new Win32Exception();
Process.GetCurrentProcess().ProcessorAffinity = new IntPtr(1);
const int gigahertz= 1000*1000*1000;
const int known_instructions_per_loop = 1;
int iterations = int.MaxValue;
int g = 0;
Win32.QueryPerformanceCounter(out start);
for( i = 0; i < iterations; i++)
{
g++;
g++;
g++;
g++;
}
Win32.QueryPerformanceCounter(out stop);
//normal ticks differs from the WMI data, i.e 3125, when WMI 3201, and CPUZ 3199
var normal_ticks_per_second = frequency * 1000;
var ticks = (double)(stop - start);
var time = (ticks * multiplier) /frequency;
var loops_per_sec = iterations / (time/multiplier);
var instructions_per_loop = normal_ticks_per_second / loops_per_sec;
var ratio = (instructions_per_loop / known_instructions_per_loop);
var actual_freq = normal_ticks_per_second / ratio;
Console.WriteLine( String.Format("Perf counhter freq: {0:n}", normal_ticks_per_second));
Console.WriteLine( String.Format("Loops per sec: {0:n}", loops_per_sec));
Console.WriteLine( String.Format("Perf counter freq div loops per sec: {0:n}", instructions_per_loop));
Console.WriteLine( String.Format("Presumed freq: {0:n}", actual_freq));
Console.WriteLine( String.Format("ratio: {0:n}", ratio));
Notas
- 25 instrucciones por ciclo si el depurador está activo
- Considere ejecutar un bucle de 2 o 3 segundos antes de la mano para hacer girar el procesador (o al menos intentar girar hacia arriba, sabiendo lo fuertemente que se estrangulan los servidores en estos días)
- Probado en un Core2 de 64 bits y Haswell Pentium y comparado con la CPU-Z
Necesita utilizar CallNtPowerInformation . Aquí hay una muestra de código del proyecto putil . Con esto puedes obtener la frecuencia de CPU actual y máxima. Por lo que sé, no es posible obtener la frecuencia por CPU.
Se debe consultar este documento: tecnología Intel® Turbo Boost en procesadores basados en microarquitectura Intel® Core ™ (Nehalem) . Básicamente, produce varias lecturas del contador de rendimiento fijo UCC durante un período de muestra T.
Relative.Freq = Delta(UCC) / T
Where:
Delta() = UCC @ period T
- UCC @ period T-1
Comenzando con la arquitectura de Nehalem, UCC aumenta y disminuye el número de tics de clic en relación con el estado de núcleo de Unhalted.
Cuando se activan SpeedStep o Turbo Boost, la frecuencia estimada usando UCC se medirá en consecuencia; mientras que TSC permanece constante. Por ejemplo, Turbo Boost en acción revela que Delta (UCC) es mayor o igual a Delta (TSC)
Ejemplo en la función Función Core_Cycle
en Cyring | CoreFreq GitHub.
Una de las maneras más sencillas de hacerlo es usar RDTSC
, pero dado que esto es para mecanismos RDTSC
, lo pondría como un controlador de kernel o una pieza de código residente de hipervisores.
Probablemente también necesites rodar tu propio código de tiempo **, que de nuevo se puede hacer con RDTSC
(QPC como se usa en el ejemplo a continuación usa RDTSC
, y de hecho es muy simple aplicar ingeniería inversa y usar una copia local de, que significa manipularlo, necesitaría manipular su controlador).
void GetProcessorSpeed()
{
CPUInfo* pInfo = this;
LARGE_INTEGER qwWait, qwStart, qwCurrent;
QueryPerformanceCounter(&qwStart);
QueryPerformanceFrequency(&qwWait);
qwWait.QuadPart >>= 5;
unsigned __int64 Start = __rdtsc();
do
{
QueryPerformanceCounter(&qwCurrent);
}while(qwCurrent.QuadPart - qwStart.QuadPart < qwWait.QuadPart);
pInfo->dCPUSpeedMHz = ((__rdtsc() - Start) << 5) / 1000000.0;
}
** Esto sería por seguridad como se menciona en @Mystical, pero como nunca he sentido el impulso de subvertir los mecanismos de sincronización del sistema de bajo nivel, podría haber más involucrados, sería bueno si Mystical pudiera agregar algo sobre eso :)