¿El modelo de memoria Intel hace que SFENCE y LFENCE sean redundantes?

El modelo de memoria Intel garantiza:

  • Las tiendas no se reordenarán con otras tiendas
  • Las cargas no se reordenarán con otras cargas

http://bartoszmilewski.com/2008/11/05/who-ordered-memory-fences-on-an-x86/

He visto afirmaciones de que SFENCE es redundante en x86-64 debido al modelo de memoria Intel, pero nunca LFENCE. ¿Las reglas del modelo de memoria anterior hacen que las instrucciones sean redundantes?

Correcto, LFENCE y SFENCE no son útiles en el código normal porque la semántica de adquisición / liberación de x86 para las tiendas normales las hace redundantes a menos que esté usando otras instrucciones especiales o tipos de memoria.

La única valla que importa para el código sin cerradura normal es la barrera completa (incluido StoreLoad) de una instrucción de lock o una MEFENCE lenta. Prefiere xchg para tiendas de consistencia secuencial sobre mov + mfence . ¿Las cargas y las tiendas son las únicas instrucciones que se reordenan? porque es más rápido

¿`Xchg` abarca` mfence` suponiendo que no hay instrucciones no temporales? (sí, incluso con instrucciones NT, siempre que no haya memoria WC).


El artículo de Jeff Preshing Reordenando la Memoria Atrapados en la Ley es una descripción más fácil de leer del mismo caso sobre el que habla la publicación de Bartosz, donde se necesita una barrera StoreLoad como MFENCE. Solo MEFENCE servirá; no puedes construir MFENCE fuera de SFENCE + LFENCE. ( ¿Por qué es (o no es) SFENCE + LEFENCE equivalente a MFENCE? )

Si tenía preguntas después de leer el enlace que publicó, lea las otras publicaciones de blog de Jeff Preshing. Me dieron una buena comprensión del tema. 🙂 Aunque creo que encontré el tidbit sobre SFENCE / LFENCE que normalmente no funciona en la página de Doug Lea. Las publicaciones de Jeff no consideraron NT cargas / tiendas.


Relacionado: ¿ Cuándo debo usar _mm_sfence _mm_lfence y _mm_mfence (mi respuesta y la de @ BeeOnRope son buenas, escribí esta respuesta mucho más tiempo que esa respuesta, así que partes de esta respuesta muestran mi inexperiencia hace años. Mi respuesta allí considera el C ++ intrinsics y el orden de memoria en tiempo de comstackción de C ++, que no es en absoluto lo mismo que ordenamiento de memoria de tiempo de ejecución asm x86. Pero todavía no quiere _mm_lfence() .


SFENCE solo es relevante cuando se utilizan almacenes de transmisión movnt (no temporales) o cuando se trabaja con regiones de memoria con un tipo establecido en algo distinto al Write-Back normal. O con clflushopt , que es como una tienda débilmente ordenada. Las tiendas NT omiten la caché y se ordenan débilmente. El modelo de memoria normal de x86 está fuertemente ordenado , aparte de las tiendas NT, la memoria WC (combinación de escritura) y las operaciones de cadena ERMSB (consulte a continuación).

LFENCE solo es útil para ordenar la memoria con cargas ordenadas débilmente, que son muy raras . (¿O posible para el pedido de LoadStore con cargas regulares antes de las tiendas NT?)

Las cargas NT ( movntdqa ) de la memoria WB todavía están fuertemente ordenadas , incluso en una futura CPU hipotética que no ignora la sugerencia NT; la única forma de hacer cargas ordenadas débilmente en x86 es cuando se lee desde la memoria débilmente ordenada (WC), y luego solo pienso con movntdqa . Esto no ocurre por accidente en progtwigs “normales”, por lo que solo tiene que preocuparse por esto si mapea la RAM de video o algo así.

( El caso de uso primario para lfence no es ordenar la memoria en absoluto, es para serializar la ejecución de instrucción, por ejemplo, para la mitigación de espectro, o con RDTSC . ¿Está serializando LFENCE en procesadores AMD? Y la barra lateral “preguntas vinculadas” para esa pregunta).


Orden de memoria en C ++, y cómo se asigna a x86 asm

Tengo curiosidad por esto hace un par de semanas y publiqué una respuesta bastante detallada a una pregunta reciente: operaciones atómicas, std :: atómico <> y ordenación de escrituras . Incluí muchos enlaces a cosas sobre el modelo de memoria de C ++ frente a los modelos de memoria de hardware.

Si está escribiendo en C ++, usar std::atomic<> es una excelente manera de decirle al comstackdor qué requisitos de ordenamiento tiene, por lo que no reordena las operaciones de memoria en tiempo de comstackción. Puede y debe usar una versión más débil o adquirir semántica cuando corresponda, en lugar de la consistencia secuencial predeterminada, por lo que el comstackdor no tiene que emitir ninguna instrucción de barrera en absoluto en x86. Solo tiene que mantener las operaciones en orden de origen.


En una architecture débilmente ordenada como ARM o PPC, o x86 con movnt, necesita una instrucción de barrera StoreStore entre escribir un búfer y establecer un indicador para indicar que los datos están listos. Además, el lector necesita una instrucción de barrera LoadLoad entre verificar el indicador y leer el búfer.

Sin contar el movimiento, x86 ya tiene barreras LoadLoad entre cada carga y las barreras StoreStore entre cada tienda. (El pedido de LoadStore también está garantizado). MFENCE son los 4 tipos de barreras, incluyendo StoreLoad, que es la única barrera que x86 no hace por defecto. MFENCE se asegura de que las cargas no usen viejos valores prefacturados antes de que otros subprocesos vieran sus tiendas y potencialmente hicieran tiendas propias. (Además de ser una barrera para el orden de la tienda NT y el orden de carga).

Dato curioso: las instrucciones prefijadas de lock x86 también son barreras de memoria completas. Se pueden usar como un sustituto de MFENCE en un código antiguo de 32 bits que podría ejecutarse en CPU que no lo admitan. lock add [esp], 0 es, por lo demás, un no-op, y el ciclo de lectura / modificación / escritura en la memoria es muy probable que esté caliente en la memoria caché L1 y ya esté en el estado M del protocolo de coherencia MESI.

SFENCE es una barrera StoreStore. Después de las tiendas NT es útil crear una semántica de lanzamiento para una tienda siguiente.

LFENCE casi siempre es irrelevante como barrera de memoria porque la única carga débilmente ordenada

un LoadLoad y también una barrera LoadStore . ( loadNT / LFENCE / storeNT evita que la tienda se vuelva globalmente visible antes de la carga. Creo que esto podría suceder en la práctica si la dirección de carga fue el resultado de una larga cadena de dependencia o el resultado de otra carga que se perdió en la memoria caché).


Operaciones de cadena ERMSB

Dato @EOF # 2 (gracias @EOF ): Las tiendas de ERMSB (Enhanced rep movsb / rep stosb en IvyBridge y posterior) están débilmente ordenadas (pero no en by -caché). ERMSB se basa en Fast-String Ops (tiendas amplias de la implementación microcodificada de rep stos/movsb que ha existido desde PPro).

Intel documenta el hecho de que las tiendas ERMSB “pueden parecer ejecutadas fuera de servicio” en la sección 7.3.9.3 de su Manual de Desarrolladores de Software, vol1. Ellos también dicen

“El código dependiente del pedido debe escribir en una variable de semáforo discreto después de cualquier operación de cadena para permitir que todos los procesadores puedan ver los datos correctamente ordenados”

No mencionan ninguna instrucción de barrera que sea necesaria entre la rep movsb y la tienda a una bandera de data_ready .

La forma en que lo leí, hay un SFENCE implícito después de rep stosb / rep movsb (al menos una valla para los datos de cadena, probablemente no otras tiendas NT ordenadas débilmente en vuelo). De todos modos, la redacción implica que una escritura en la bandera / semáforo se vuelve visible globalmente después de todas las escrituras de movimiento de cadena, por lo que no se necesita SFENCE / LFENCE en el código que llena un búfer con una operación rápida y luego escribe una bandera, o en el código que lo lee.

(El ordenamiento de LoadLoad siempre ocurre, por lo que siempre ve los datos en el orden en que otras CPU lo hacen visible globalmente, es decir, usar tiendas poco ordenadas para escribir un búfer no cambia el hecho de que las cargas en otros hilos todavía están fuertemente ordenadas).

resumen: use una tienda normal para escribir una bandera que indique que un búfer está listo. No haga que los lectores solo revisen el último byte del bloque escrito con memset / memcpy .

También creo que las tiendas ERMSB impiden que las tiendas posteriores las pasen, por lo que solo necesitas SFENCE si estás utilizando movNT . es decir, el rep stosb como un todo tiene una semántica de liberación wrt. instrucciones anteriores.

Hay un bit MSR que se puede borrar para deshabilitar ERMSB en beneficio de los nuevos servidores que necesitan ejecutar binarios antiguos que rep stosb una bandera de “datos listos” como parte de un rep stosb o rep movsb o algo así. (En ese caso, creo que obtienes el viejo microcódigo de cadena rápida que puede usar un protocolo de caché eficiente, pero hace que todas las tiendas aparezcan en otros núcleos en orden).