2011-01-27 11 views
5

Estoy copiando elementos de una matriz a otra en C++. Encontré la instrucción rep movs en x86 que parece copiar una matriz en ESI a una matriz en EDI de tamaño ECX. Sin embargo, ni los for ni los while loops intenté compilar a una instrucción rep movs en VS 2008 (en un procesador Intel Xeon x64). ¿Cómo puedo escribir el código que se compilará en esta instrucción?¿Qué código de C++ se compila en la instrucción x86 REP?

+2

Déjeme aclarar esto. ¿Desea usar C++ (un lenguaje de nivel medio a alto) para escribir instrucciones de ensamblador? ¿Que sigue? ¿Desea usar C++ para conectar un diodo a su placa base? –

+0

@JUST ¿Se da cuenta de que C++ tiene bloques de ensamblaje? –

+2

@Michael: no portátil. Por ejemplo, para MSVC ni siquiera es compatible con x64, y está en desuso (a favor de intrínsecos) en x86. –

Respuesta

3

Si necesita exactamente esa instrucción, use el ensamblador incorporado y escriba esa instrucción manualmente. You can't rely on the compiler to produce any specific machine code - incluso si lo emite en una compilación, puede decidir emitir algún otro equivalente durante la siguiente compilación.

+0

Escribir las instrucciones manualmente a menudo alterará la optimización del compilador y, en tales casos, si la velocidad es importante, es mejor llamar a las rutinas de la biblioteca. –

+0

@Olof Forshell: Bueno, sí. Pero ¿por qué alguien necesitaría específicamente esta instrucción de todos modos? – sharptooth

+0

Como he escrito aquí en una respuesta, hay situaciones específicas en las que una representación en línea movsb/movsw/movsd y otros será más rápida y más compacta, lo que dará como resultado menos trabajo de caché en el lado de la instrucción. Si quiero copiar menos de 32 bytes, ¿por qué llamar a una rutina en otro lugar que está optimizado para fragmentos de 32 bytes cuando puedo hacerlo más rápido y menos perturbador en línea? –

10

Honestamente, no deberías. REP es una especie de holdover obsoleto en el conjunto de instrucciones, y en realidad es bastante lento, ya que tiene que llamar a una subrutina microcodificada dentro de la CPU, que tiene una latencia de búsqueda ROM y no tiene derivación también.

En casi todas las implementaciones, encontrará que el compilador memcpy() intrínseco es más fácil de usar y más rápido.

+0

REP no es una instrucción, es un prefijo de instrucción. También está lejos de ser obsoleto (ver conjunto de instrucciones amd64). –

+5

@Michael Foukarakis Consulte la "Guía de optimización de software AMD para procesadores AMD64", sección 8.3. "Evite usar el prefijo REP al realizar operaciones de cadena, especialmente al copiar bloques de la memoria .En general, usar el prefijo REP para realizar repetidamente las instrucciones de cadena es menos óptimo que otros métodos, especialmente cuando se copian bloques de memoria. " – Crashworks

+0

Interesante. Sé que esto no está relacionado con el tema, pero lo que sería - en términos de ensamblador x86 o amd64 - una forma óptima de copiar un bloque de memoria? – avakar

5

Debajo de MSVC hay __movsxxx__stosxxx intrínsecos que generarán una instrucción prefijada REP.

También hay un 'truco' para forzar intrínseca memset aka REP STOS en vc9 +, ya que el intrínseco ya no sale, debido a la ramificación sse2 en el crt. esto es mejor que __stosxxx debido a que el compilador puede optimizarlo para constantes y ordenarlo correctamente.

#define memset(mem,fill,size) memset((DWORD*)mem,((fill) << 24|(fill) << 16|(fill) << 8|(fill)),size) 
__forceinline void memset(DWORD* pStart, unsigned long dwFill, size_t nSize) 
{ 
    //credits to Nepharius for finding this 
    DWORD* pLast = pStart + (nSize >> 2); 
    while(pStart < pLast) 
     *pStart++ = dwFill; 

    if((nSize &= 3) == 0) 
     return; 

    if(nSize == 3) 
    { 
     (((WORD*)pStart))[0] = WORD(dwFill); 
     (((BYTE*)pStart))[2] = BYTE(dwFill); 
    } 
    else if(nSize == 2) 
     (((WORD*)pStart))[0] = WORD(dwFill); 
    else 
     (((BYTE*)pStart))[0] = BYTE(dwFill); 
} 

por supuesto REP no siempre es el mejor que se puede utilizar, imo su camino mejor usar memcpy, que va rama en cualquiera SSE2 o REPS MOV basado en su sistema (bajo msvc), a menos que sentirse como escritura de ensamblaje personalizado para áreas 'calientes' ...

0

Utilizo las variantes de prefijo rep * con las variantes de instrucción cmps *, movs *, scas * y stos * para generar código en línea que minimiza el tamaño del código, evita llamadas innecesarias/salta y por lo tanto mantiene el trabajo realizado por los cachés. La alternativa es configurar parámetros y llamar a un memset o memcpy en otro lugar que, en general, puede ser más rápido si quiero copiar cien bytes o más, pero si solo es una cuestión de 10-20 bytes, usar rep es más rápido (o al menos era la última vez que medí).

Como mi compilador permite la especificación y el uso de las funciones de ensamblaje en línea e incluye su uso/modificación de registros en las actividades de optimización, es posible que los use cuando las circunstancias sean las correctas.

0

En una nota histórica, sin tener ninguna idea de las estrategias del fabricante, hubo un momento en que las instrucciones "rep movs *" (etc.) fueron muy lentas. Creo que fue alrededor de la época del Pentium/Pentium MMX. Un colega mío (que tenía más conocimiento que yo) dijo que los fabricantes habían reducido el área de los chips (< => menos transistores/más microcódigo) asignados al manejo de rep y lo usaron para hacer más rápidas otras instrucciones más utilizadas.

En los quince años más o menos desde que el representante se ha vuelto relativamente más rápido hablando nuevamente, lo que sugeriría más transistores/menos microcódigo.

0

REP y amigos eran agradables érase una vez, cuando la CPU x86 era un procesador industrial CISC de una sola tubería.

Pero eso ha cambiado.Hoy en día, cuando el procesador encuentra cualquier instrucción, lo primero que hace es traducirla a un formato más fácil (microoperaciones tipo VLIW) y programarla para su futura ejecución (esto es parte de la ejecución fuera de orden, parte de programación entre diferentes núcleos de CPU lógicos, se puede usar para simplificar las secuencias de escritura después de escritura en escrituras únicas, et.c.). Esta maquinaria funciona bien para obtener instrucciones que se traducen en algunos códigos de operación similares a VLIW, pero no en códigos de máquina que se traducen en bucles. El código de máquina traducido en bucle probablemente hará que la tubería de ejecución se bloquee.

En lugar de gastar cientos de miles de transistores en construir circuitos de CPU para manejar porciones de bucle de las microoperaciones en la tubería de ejecución, simplemente lo manejan en algún tipo de modo heredado que bloquea la tubería, y ¡pida a los programadores modernos que escriban sus propios malditos bucles!

Por lo tanto, rara vez se utiliza cuando las máquinas escriben código. Si encuentra REP en un ejecutable binario, es probable que sea un humano-ensamblador-muppet que no lo conocía mejor, o un cracker que realmente necesitaba los pocos bytes que guardó para usarlo en lugar de un bucle real, que lo escribió.

(Sin embargo, tome todo lo que acabo de escribir con un grano de sal. Tal vez esto ya no sea cierto. Ya no estoy 100% actualizado con los componentes de las CPU x86, me metí en otros pasatiempos ...)

Cuestiones relacionadas