¿El uso de xor reg, reg da ventaja sobre mov reg, 0? [duplicar]


Esta pregunta ya tiene una respuesta aquí:

Hay dos formas bien conocidas de establecer un registro entero en valor cero en x86.

O bien

mov reg, 0

O

xor reg, reg

Hay una opinión de que la segunda variante es mejor ya que la el valor 0 no se almacena en el código y eso guarda varios bytes del código de máquina producido. Esto es definitivamente bueno - se usa menos caché de instrucciones y esto a veces puede permitir una ejecución de código más rápida. Muchos compiladores producen dicho código.

Sin embargo, existe formalmente una dependencia inter-instrucción entre la instrucción xor y cualquier instrucción anterior que cambie el mismo registro. Puesto que hay un depedency la última instrucción necesita esperar hasta que el anterior completa y esto podría reducir la carga de las unidades de procesador y dañar el rendimiento.

add reg, 17
;do something else with reg here
xor reg, reg

Es obvio que el resultado de xor será exactamente el mismo independientemente del valor inicial del registro. Pero es el procesador capaz de reconocer esto?

Probé la siguiente prueba en VC++7:

const int Count = 10 * 1000 * 1000 * 1000;
int _tmain(int argc, _TCHAR* argv[])
{
    int i;
    DWORD start = GetTickCount();
    for( i = 0; i < Count ; i++ ) {
        __asm {
            mov eax, 10
            xor eax, eax
        };
    }
    DWORD diff = GetTickCount() - start;
    start = GetTickCount();
    for( i = 0; i < Count ; i++ ) {
        __asm {
            mov eax, 10
            mov eax, 0
        };
    }
    diff = GetTickCount() - start;
    return 0;
}

Con optimizaciones fuera de ambos bucles toman exactamente el mismo tiempo. ¿Prueba esto razonablemente que el procesador reconoce que no hay dependencia de la instrucción xor reg, reg en la instrucción anterior mov eax, 0? Lo ¿podría ser una mejor prueba para comprobar esto?

Author: starblue, 2009-07-16

6 answers

Una respuesta real para ti:

Manual de Referencia de Optimización de Arquitecturas Intel 64 e IA-32

La sección 3.5.1.8 es donde desea buscar.

En resumen, hay situaciones en las que se puede preferir un xor o un mov. Los problemas se centran en las cadenas de dependencia y la preservación de los códigos de condición.

 29
Author: Mark,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-05-09 13:35:52

Dejé de ser capaz de arreglar mis propios coches después de vender mi 1966 HR station wagon. Estoy en una solución similar con las CPU modernas: -)

Realmente dependerá del microcódigo o circuito subyacente. Es muy posible que la CPU pueda reconocer "XOR Rn,Rn" y simplemente poner cero todos los bits sin preocuparse por el contenido. Pero, por supuesto, puede hacer lo mismo con un "MOV Rn, 0". Un buen compilador elegirá la mejor variante para la plataforma de destino de todos modos, por lo que esto suele ser solo un problema si codificación en ensamblador.

Si la CPU es lo suficientemente inteligente, su dependencia XOR desaparece ya que sabe que el valor es irrelevante y lo pondrá a cero de todos modos (de nuevo esto depende de la CPU real que se esté utilizando).

Sin embargo, hace mucho que no me preocupo por unos pocos bytes o unos pocos ciclos de reloj en mi código, esto parece que la microoptimización se volvió loca.

 13
Author: paxdiablo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-11-09 03:12:11

X86 tiene instrucciones de longitud variable. MOV EAX, 0 requiere uno o dos bytes más en el espacio de código que XOR EAX, EAX.

 10
Author: ajs410,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-10-15 13:42:30

En las CPU modernas se prefiere el patrón XOR. Es más pequeño y más rápido.

Lo más pequeño realmente importa porque en muchas cargas de trabajo reales uno de los principales factores que limitan el rendimiento es la falla de i-cache. Esto no se capturaría en un micro-benchmark comparando las dos opciones, pero en el mundo real hará que el código se ejecute un poco más rápido.

Y, ignorando los errores de i-cache reducidos, XOR en cualquier CPU en los últimos años es la misma velocidad o más rápido que MOV. Lo que podría ser más rápido que ejecutar una instrucción MOV? No ejecutar ninguna instrucción en absoluto! En procesadores Intel recientes, la lógica dispatch/rename reconoce el patrón XOR, 'se da cuenta' de que el resultado será cero, y solo apunta el registro a un registro cero físico. Luego tira la instrucción porque no hay necesidad de ejecutarla.

El resultado neto es que el patrón XOR utiliza cero recursos de ejecución y puede, en CPU Intel recientes, 'ejecutar' cuatro instrucciones por ciclo. MOV tops a cabo en tres instrucciones por ciclo.

Para obtener más detalles, consulte esta entrada del blog que escribí:

Https://randomascii.wordpress.com/2012/12/29/the-surprising-subtleties-of-zeroing-a-register/

La mayoría de los programadores no deberían preocuparse por esto, pero los escritores de compiladores sí tienen que preocuparse, y es bueno entender el código que se está generando, ¡y es jodidamente genial!

 7
Author: Bruce Dawson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-03-19 23:28:26

Creo que en arquitecturas anteriores la instrucción mov eax, 0 solía tomar un poco más que la xor eax, eax también... no puedo recordar exactamente por qué. A menos que tenga muchos más mov s sin embargo, me imagino que no es probable que cause errores de caché debido a que un literal almacenado en el código.

También tenga en cuenta que desde la memoria el estado de las banderas no es idéntico entre estos métodos, pero puedo recordar mal esto.

 2
Author: jerryjvl,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-07-16 06:10:58

Como otros han señalado, la respuesta es "¿a quién le importa?". Estás escribiendo un compilador?

Y en una segunda nota, su evaluación comparativa probablemente no funcionará, ya que tiene una rama allí que probablemente toma todo el tiempo de todos modos. (a menos que su compilador desenrolle el bucle por usted)

Otra razón por la que no puede comparar una sola instrucción en un bucle es que todo su código se almacenará en caché (a diferencia del código real). Así que ha tomado gran parte de la diferencia de tamaño entre mov eax,0 y xor eax, eax fuera de la imagen al tenerlo en caché L1 todo el tiempo.

Mi conjetura es que cualquier diferencia de rendimiento medible en el mundo real se debe a la diferencia de tamaño que consume la caché, y no al tiempo de ejecución de las dos opciones.

 -8
Author: Thomas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-07-16 06:54:41