volatil - La diferencia entre asm, asm volátil y memoria de cloqueo
no volatil significado (3)
Al implementar estructuras de datos y código de tiempo sin bloqueo, a menudo es necesario suprimir las optimizaciones del compilador. Normalmente la gente hace esto usando una memory
asm volatile
con memory
en la lista de clobber, pero a veces se ve tan asm volatile
o simplemente una memoria de cloqueo de asm
.
¿Qué impacto tienen estas declaraciones diferentes en la generación de código (particularmente en GCC, ya que es poco probable que sea portátil)?
Solo como referencia, estas son las variaciones interesantes:
asm (""); // presumably this has no effect on code generation
asm volatile ("");
asm ("" ::: "memory");
asm volatile ("" ::: "memory");
Consulte la página "Asm Extendida" en la documentación de GCC .
Puede evitar que se borre una instrucción
asm
escribiendo la palabra clavevolatile
después delasm
. [...] La palabra clavevolatile
indica que la instrucción tiene importantes efectos secundarios. GCC no eliminará un asmvolatile
si es alcanzable.
y
Una instrucción
asm
sin ningún operando de salida se tratará de manera idéntica a una instrucción volátil deasm
.
Ninguno de sus ejemplos tiene operandos de salida especificados, por lo que las formas asm volatile
asm
y asm volatile
comportan de manera idéntica: crean un punto en el código que no se puede eliminar (a menos que se pruebe que no se puede alcanzar).
Esto no es lo mismo que no hacer nada. Vea esta pregunta para un ejemplo de un asm
ficticio que cambia la generación de código: en ese ejemplo, el código que gira alrededor de un bucle 1000 veces se vectoriza en un código que calcula 16 iteraciones del bucle a la vez; pero la presencia de un asm
dentro del bucle inhibe la optimización (el asm
debe alcanzarse 1000 veces).
El trozo de "memory"
hace que GCC asuma que cualquier memoria puede ser leída o escrita arbitrariamente por el bloque asm
, por lo que evitará que el compilador vuelva a ordenar cargas o almacenes a través de ella:
Esto hará que GCC no guarde los valores de memoria en caché en los registros a través de la instrucción del ensamblador y no optimice los almacenes o cargas a esa memoria.
(Eso no impide que una CPU vuelva a ordenar cargas y almacenes con respecto a otra CPU, sin embargo, necesita instrucciones reales de barrera de memoria para eso).
Para completar la respuesta de Kevin Ballard, Visual Studio 2010 ofrece _ReadBarrier (), _WriteBarrier () y _ReadWriteBarrier () para hacer lo mismo (VS2010 no permite el ensamblado en línea para aplicaciones de 64 bits).
Estos no generan instrucciones, pero afectan el comportamiento del compilador. Un buen ejemplo está here
MemoryBarrier () genera lock or DWORD PTR [rsp], 0
asm ("")
no hace nada (o al menos, se supone que no debe hacer nada).
asm volatile ("")
tampoco hace nada.
asm ("" ::: "memory")
es una valla de compilación simple.
asm volatile ("" ::: "memory")
AFAIK es el mismo que el anterior. La palabra clave volatile
le dice al compilador que no puede mover este bloque de ensamblaje. Por ejemplo, se puede sacar de un bucle si el compilador decide que los valores de entrada son los mismos en cada invocación. No estoy seguro de en qué condiciones el compilador decidirá que entiende lo suficiente sobre el conjunto para tratar de optimizar su ubicación, pero la palabra clave volatile
suprime por completo. Dicho esto, me sorprendería mucho si el compilador intentara mover una declaración de asm
que no tuviera entradas o salidas declaradas.
Incidentalmente, volatile
también evita que el compilador elimine la expresión si decide que los valores de salida no se utilizan. Esto solo puede suceder si hay valores de salida, por lo que no se aplica a asm ("" ::: "memory")
.