¿El ejemplo de código asm de lectura de marca de hora de Intel utiliza dos registros más de los que son necesarios?

Estoy estudiando la medición del rendimiento de referencia utilizando el registro de marca de tiempo (TSR) que se encuentra en las CPU x86. Es un registro útil, ya que mide en una unidad de tiempo monotónica que es inmune al cambio de velocidad del reloj. Muy genial.

Aquí hay un documento de Intel que muestra fragmentos de código asm para realizar una evaluación comparativa confiable utilizando el TSR, incluido el uso de cpuid para la sincronización de la tubería. Vea la página 16:

http://www.intel.com/content/www/us/en/embedded/training/ia-32-ia-64-benchmark-code-execution-paper.html

Para leer la hora de inicio, dice (comenté un poco):

__asm volatile ( "cpuid\n\t" // writes e[abcd]x "rdtsc\n\t" // writes edx, eax "mov %%edx, %0\n\t" "mov %%eax, %1\n\t" // :"=r" (cycles_high), "=r" (cycles_low) // outputs : // inputs :"%rax", "%rbx", "%rcx", "%rdx"); // clobber 

Me pregunto por qué se usan los registros de scratch para tomar los valores de edx y eax . ¿Por qué no eliminar los movimientos y leer el valor TSR directamente de edx y eax ? Me gusta esto:

 __asm volatile( "cpuid\n\t" "rdtsc\n\t" // : "=d" (cycles_high), "=a" (cycles_low) // outputs : // inputs : "%rbx", "%rcx"); // clobber 

Al hacer esto, se guardan dos registros, lo que reduce la probabilidad de que el comstackdor de C tenga que dertwigrse.

Estoy en lo cierto ¿O esos MOV son de alguna manera estratégicos?

(Estoy de acuerdo en que necesita registros de lectura inicial para leer el tiempo de detención , ya que en ese escenario el orden de las instrucciones se invierte: tiene rdtscp, …, cpuid. La instrucción cpuid destruye el resultado de rdtscp).

Gracias

Estás en lo cierto, el ejemplo es torpe. Por lo general, si mov es la primera o la última instrucción en una statement inline-asm, lo está haciendo mal y debería haber usado una restricción para decirle al comstackdor dónde quiere la entrada o dónde está la salida.

Vea mi colección de guías / enlaces asm en línea de GNU C , y otros enlaces en el wiki de tags de ensamblado en línea . (La wiki de la etiqueta x86 también está llena de cosas buenas para asm en general).


O, específicamente, para rdtsc , consulte ¿Recuento de ciclos de CPU? para __rdtsc() intrínseco, y buen inline en línea en la respuesta de @Misticística.


se mide en una unidad de tiempo monotónica que es inmune al cambio de velocidad del reloj.

Sí, en CPUs realizadas en los últimos 10 años más o menos.

Para la creación de perfiles, a menudo es más útil tener tiempos en los ciclos del reloj central, no en el reloj de pared, por lo que los resultados de la marca de microbado no dependen del ahorro de energía / turbo. Los contadores de rendimiento pueden hacer esto y mucho más.

Aún así, si lo que desea es tiempo real, rdtsc es la forma más rdtsc de obtenerlo.


Y re: discusión en los comentarios: sí, cpuid está ahí para serializar, asegurándose de que rdtsc y las siguientes instrucciones no puedan comenzar a ejecutarse hasta después de la CPUID. Podría colocar otro CPUID después de RDTSC, pero eso boostía la sobrecarga de la medición, y creo que proporciona una ganancia de precisión casi nula.

LFENCE es una alternativa más barata que es útil con RDTSC. La entrada de instrucciones manuales ref documenta el hecho de que no permite que las instrucciones posteriores comiencen a ejecutarse hasta que ésta y las instrucciones anteriores se hayan retirado (desde el ROB / RS en la parte fuera de orden del núcleo). Consulte ¿Las cargas y las tiendas son las únicas instrucciones que se reordenan? , y para un ejemplo específico de su uso, vea clflush para invalidar la línea de caché a través de la función C. A diferencia de las instrucciones de serialización verdaderas como cpuid , no cpuid el búfer de almacenamiento.

(En las CPU AMD recientes sin la función de mitigación Spectre habilitada, lfence ni siquiera se serializa parcialmente, y se ejecuta a 4 por reloj de acuerdo con las pruebas de Agner Fog . ¿LFENCE se serializa en los procesadores AMD? )

Margaret Bloom desenterró este útil enlace , que también confirma que LFENCE serializa RDTSC de acuerdo con el SDM de Intel, y tiene otras cosas sobre cómo realizar la serialización en torno a RDTSC.

No, no parece haber una buena razón para las instrucciones MOV redundantes en el ensamblaje en línea. El documento primero introduce el ensamblaje en línea con la siguiente statement:

 asm volatile ( "RDTSC\n\t" "mov %%edx, %0\n\t" "mov %%eax, %1\n\t": "=r" (cycles_high1), "=r" (cycles_low1)); 

Esto tiene el problema obvio de que no le dice al comstackdor que EAX y EDX han sido modificados por la instrucción RDTSC. El documento señala este error y lo corrige usando clobbers:

 asm volatile ("RDTSC\n\t" "mov %%edx, %0\n\t" "mov %%eax, %1\n\t": "=r" (cycles_high), "=r" (cycles_low):: “%eax”, “%edx”) 

No se da ninguna otra justificación para escribirlo de esta manera, excepto para corregir el error en el ejemplo anterior. Parece que el autor del artículo simplemente ignora que podría escribirse más simplemente como:

 asm volatile ("RDTSC\n\t" : "=d" (cycles_high), "=a" (cycles_low)); 

De manera similar, el autor aparentemente desconoce que hay una versión más simple de la statement asm mejorada que usa RDTSC en combinación con CPUID, como lo demuestra en su publicación.

Tenga en cuenta que el autor del artículo hace un uso indebido repetido del término “IA64” para referirse al conjunto de instrucciones x86 de 64 bits y a la architecture (a la que se hace referencia como x86_64, AMD64 e Intel 64). La architecture IA-64 es en realidad algo completamente diferente, es la que usan las CPUs Itaninum de Intel. No tiene registros EAX o RAX, y no hay instrucción RDTSC.

Si bien no importa el hecho de que el ensamblaje en línea de los autores sea más complejo de lo que debe ser, este hecho, combinado con el uso indebido de IA64, algo que deberían haber captado los editores de Intel, me hace dudar de la credibilidad de este documento.