Hay mucha información confusa y/o incorrecta sobre el TSC, así que pensé en tratar de aclarar algo.
Cuando Intel introdujo por primera vez el TSC (en las CPU Pentium originales), estaba claramente documentado para contar los ciclos (y no el tiempo). Sin embargo, en ese entonces, las CPU se ejecutaban principalmente a una frecuencia fija, por lo que algunas personas ignoraron el comportamiento documentado y lo usaron para medir el tiempo (sobre todo, los desarrolladores del kernel de Linux). Su código se rompió en CPU posteriores que no se ejecutan a una frecuencia fija (debido a la administración de energía, etc.). Alrededor de ese tiempo, otros fabricantes de CPU (AMD, Cyrix, Transmeta, etc.) estaban confundidos y algunos implementaron TSC para medir ciclos y algunos lo implementaron para medir el tiempo, y algunos lo hicieron configurable (a través de un MSR).
Luego, los sistemas de "chips múltiples" se volvieron más comunes para los servidores; e incluso más tarde se introdujo el multinúcleo. Esto condujo a diferencias menores entre los valores de TSC en diferentes núcleos (debido a diferentes tiempos de inicio); pero lo que es más importante, también condujo a grandes diferencias entre los valores de TSC en diferentes CPU causadas por CPU que se ejecutan a diferentes velocidades (debido a la administración de energía y/u otros factores).
Las personas que intentaban usarlo mal desde el principio (personas que lo usaban para medir el tiempo y no los ciclos) se quejaron mucho y, finalmente, convencieron a los fabricantes de CPU para que estandarizaran el TSC para medir el tiempo y no los ciclos.
Por supuesto, esto fue un desastre, p. se necesita mucho código para determinar qué mide realmente el TSC si admite todas las CPU de 80x86; y diferentes tecnologías de administración de energía (incluidas cosas como SpeedStep, pero también cosas como estados de suspensión) pueden afectar el TSC de diferentes maneras en diferentes CPU; por lo que AMD introdujo un indicador "TSC invariante" en CPUID para decirle al sistema operativo que el TSC se puede usar para medir el tiempo correctamente.
Todas las CPU Intel y AMD recientes han sido así desde hace un tiempo:TSC cuenta el tiempo y no mide los ciclos en absoluto. Esto significa que si desea medir ciclos, debe usar contadores de monitoreo de rendimiento (específicos del modelo). Desafortunadamente, los contadores de monitoreo de rendimiento son un desastre aún peor (debido a la naturaleza específica de su modelo y su configuración intrincada).
Mientras su subproceso permanezca en el mismo núcleo de la CPU, la instrucción RDTSC seguirá devolviendo un número creciente hasta que finalice. Para una CPU de 2 GHz, esto sucede después de 292 años, por lo que no es un problema real. Probablemente no verás que suceda. Si espera vivir tanto tiempo, asegúrese de que su computadora se reinicie, digamos, cada 50 años.
El problema con RDTSC es que no tiene garantía de que se inicie en el mismo momento en todos los núcleos de una CPU multinúcleo antigua y no hay garantía de que se inicie en el mismo momento en todas las CPU de una placa multi-CPU antigua .
Los sistemas modernos generalmente no tienen tales problemas, pero el problema también se puede solucionar en sistemas más antiguos configurando la afinidad de un subproceso para que solo se ejecute en una CPU. Esto no es bueno para el rendimiento de la aplicación, por lo que generalmente no se debe hacer, pero para medir los ticks, está bien.
(Otro "problema" es que mucha gente usa RDTSC para medir el tiempo, lo cual no lo que hace, pero escribiste que quieres ciclos de CPU, así que está bien. Si haces use RDTSC para medir el tiempo, es posible que se lleve sorpresas cuando se active el ahorro de energía o el hiperboost o como se llame la multitud de técnicas de cambio de frecuencia. En tiempo real, el clock_gettime
syscall es sorprendentemente bueno bajo Linux.)
Simplemente escribiría rdtsc
dentro del asm
declaración, que funciona bien para mí y es más legible que algún código hexadecimal oscuro. Asumiendo que es el código hexadecimal correcto (y dado que ni falla ni devuelve un número cada vez mayor, parece que sí), su código es bueno.
Si desea medir la cantidad de tics que toma un fragmento de código, desea una diferencia de tictac , solo necesita restar dos valores del contador cada vez mayor. Algo así como uint64_t t0 = rdtsc(); ... uint64_t t1 = rdtsc() - t0;
Tenga en cuenta que si se necesitan mediciones muy precisas aisladas del código circundante, debe serializar, es decir, detener la canalización, antes de llamar a rdtsc
(o use rdtscp
que solo es compatible con los procesadores más nuevos). La única instrucción de serialización que se puede usar en todos los niveles de privilegios es cpuid
.
En respuesta a la pregunta adicional en el comentario:
El TSC comienza en cero cuando enciende la computadora (y el BIOS restablece todos los contadores en todas las CPU al mismo valor, aunque algunos BIOS hace algunos años no lo hacían de manera confiable).
Por lo tanto, desde el punto de vista de su programa, el contador comenzó "en un tiempo desconocido en el pasado", y siempre aumenta con cada tic de reloj que ve la CPU. Por lo tanto, si ejecuta la instrucción que devuelve ese contador ahora y en cualquier momento posterior en un proceso diferente, devolverá un valor mayor (a menos que la CPU se haya suspendido o apagado en el medio). Distintas ejecuciones del mismo programa obtienen números más grandes, porque el contador sigue creciendo. Siempre.
Ahora, clock_gettime(CLOCK_PROCESS_CPUTIME_ID)
es un asunto diferente. Este es el tiempo de CPU que el sistema operativo le ha dado al proceso. Comienza en cero cuando comienza su proceso. Un nuevo proceso también comienza en cero. Por lo tanto, dos procesos que se ejecutan uno detrás del otro obtendrán números muy similares o idénticos, nunca crecientes.
clock_gettime(CLOCK_MONOTONIC_RAW)
está más cerca de cómo funciona RDTSC (y en algunos sistemas más antiguos se implementa con él). Devuelve un valor que siempre aumenta. Hoy en día, esto es típicamente un HPET. Sin embargo, este es realmente tiempo , y no garrapatas . Si su computadora entra en estado de bajo consumo (por ejemplo, funcionando a la mitad de la frecuencia normal), todavía avanzar al mismo ritmo.
buenas respuestas ya, y Damon ya mencionó esto de alguna manera en su respuesta, pero agregaré esto de la entrada real del manual x86 (volumen 2, 4-301) para RDTSC:
Carga el valor actual del contador de marca de tiempo del procesador (un MSR de 64 bits) en los registros EDX:EAX. El registro EDX se carga con los 32 bits de orden superior del MSR y el registro EAX se carga con los 32 bits de orden inferior. (En los procesadores que admiten la arquitectura Intel 64, se borran los 32 bits de orden superior de RAX y RDX).
El procesador incrementa monótonamente el contador de marca de tiempo MSR cada ciclo de reloj y lo restablece a 0 cada vez que se reinicia el procesador. Consulte "Contador de marca de tiempo" en el Capítulo 17 del Manual del desarrollador de software de las arquitecturas Intel® 64 e IA-32, Volumen 3B , para obtener detalles específicos del comportamiento del contador de marcas de tiempo.