Barreras de memoria C++ para Atomics
windows visual-c++ (2)
Soy un novato cuando se trata de esto. ¿Podría alguien proporcionar una explicación simplificada de las diferencias entre las siguientes barreras de memoria?
- Las ventanas
MemoryBarrier();
- La cerca
_mm_mfence();
- El ensamblaje en línea
asm volatile ("" : : : "memory");
- El
_ReadWriteBarrier();
intrínseco_ReadWriteBarrier();
Si no hay una explicación simple, algunos enlaces a buenos artículos o libros probablemente me ayuden a entenderlo. Hasta ahora, estaba bien con el uso de objetos escritos por otros envolviendo estas llamadas, pero me gustaría tener una mejor comprensión que mi pensamiento actual, que básicamente se basa en que hay más de una forma de implementar barreras de memoria debajo de las cubiertas.
Tanto MemoryBarrier
( MemoryBarrier
) como _mm_mfence
(admitidos por varios compiladores) proporcionan una valla de memoria de hardware, que impide que el procesador mueva lecturas y escrituras a través de la valla.
La principal diferencia es que MemoryBarrier tiene implementaciones específicas de plataforma para x86, x64 e IA64, donde como _mm_mfence utiliza específicamente la instrucción mfence
SSE2, por lo que no siempre está disponible.
En x86 y x64, MemoryBarrier se implementa con un xchg
y lock or
respectivamente, y he visto algunas afirmaciones de que esto es más rápido que mfence. Sin embargo, mis propios puntos de referencia muestran lo contrario, por lo que aparentemente depende mucho del modelo de procesador.
Otra diferencia es que mfence también se puede usar para ordenar tiendas / cargas no temporales ( movntq
etc.).
GCC también tiene __sync_synchronize
que genera una valla de hardware.
asm volatile ("" : : : "memory")
_ReadWriteBarrier
asm volatile ("" : : : "memory")
en GCC y _ReadWriteBarrier
en _ReadWriteBarrier
solo proporcionan una valla de memoria de nivel de compilación, evitando que el compilador _ReadWriteBarrier
a _ReadWriteBarrier
accesos a la memoria. Eso significa que el procesador todavía está en libertad de hacer un nuevo pedido.
Las cercas de compilador generalmente se usan en combinación con operaciones que tienen algún tipo de valla de hardware implícita. Por ejemplo, en x86 / x64 todas las tiendas tienen una valla de liberación y las cargas tienen una valla de adquisición, por lo que solo necesitas una valla de compilación cuando implementes load-acquire y store-release.
Vea mi respuesta here sobre la semántica a nivel de hardware de las vallas. Lo que no se menciona aquí es que también evitan el reordenamiento de cargas, tiendas o cargas y tiendas (dependiendo de la valla) a través de vallas, tanto a nivel de compilador como a nivel de hardware.