¿Tiene algún sentido utilizar la instrucción LFENCE en procesadores x86 / x86_64?

A menudo en Internet encuentro que LFENCE no tiene sentido en los procesadores x86, es decir, no hace nada, por lo tanto, en vez de MFENCE , podemos usar absolutamente absolutamente SFENCE , porque MFENCE = SFENCE + LFENCE = SFENCE + NOP = SFENCE .

Pero si LFENCE no tiene sentido, entonces ¿por qué tenemos cuatro enfoques para hacer consistencia secuencial en x86 / x86_64:

  1. LOAD (sin valla) y STORE + MFENCE
  2. LOAD (sin valla) y LOCK XCHG
  3. MFENCE + LOAD y STORE (sin valla)
  4. LOCK XADD (0) y STORE (sin valla)

Tomado de aquí: http://www.cl.cam.ac.uk/~pes20/cpp/cpp0xmappings.html

Además de las actuaciones de Herb Sutter en la página 34 en la parte inferior: https://skydrive.live.com/view.aspx?resid=4E86B0CF20EF15AD!24884&app=WordPdf&wdo=2&authkey=!AMtj_EflYn2507c

Si LFENCE no hizo nada, entonces el enfoque (3) tendría los siguientes significados: SFENCE + LOAD and STORE (without fence) , pero no tiene sentido hacer SFENCE antes de LOAD . Es decir, si LFENCE no hace nada, el enfoque (3) no tiene sentido.

¿Tiene sentido alguna instrucción LFENCE en procesadores x86 / x86_64?

RESPONDER:

1. LFENCE requiere LFENCE en los casos que se describen en la respuesta aceptada, a continuación.

2. El enfoque (3) debe verse no de forma independiente, sino en combinación con los comandos anteriores. Por ejemplo, enfoque (3):

 MFENCE MOV reg, [addr1] // LOAD-1 MOV [addr2], reg //STORE-1 MFENCE MOV reg, [addr1] // LOAD-2 MOV [addr2], reg //STORE-2 

Podemos reescribir el código de enfoque (3) de la siguiente manera:

 SFENCE MOV reg, [addr1] // LOAD-1 MOV [addr2], reg //STORE-1 SFENCE MOV reg, [addr1] // LOAD-2 MOV [addr2], reg //STORE-2 

Y aquí SFENCE tiene sentido para prevenir el reordenamiento de STORE-1 y LOAD-2. Para esto, después del comando STORE-1, SFENCE Store-Buffer.

Conclusión (TL; DR): LFENCE sí solo parece inútil para ordenar la memoria, sin embargo, no hace que SFENCE un sustituto de MFENCE . La lógica “aritmética” en la pregunta no es aplicable.


Aquí hay un extracto del Manual de Desarrolladores de Software de Intel, volumen 3 , sección 8.2.2 (la edición 325384-052US de septiembre de 2014), el mismo que utilicé en otra respuesta

  • Las lecturas no se reordenan con otras lecturas.
  • Las escrituras no se reordenan con lecturas antiguas.
  • Las escrituras en la memoria no se reordenan con otras escrituras, con las siguientes excepciones:
    • escrituras ejecutadas con la instrucción CLFLUSH;
    • almacenes de transmisión (escrituras) ejecutados con las instrucciones de movimiento no temporal (MOVNTI, MOVNTQ, MOVNTDQ, MOVNTPS y MOVNTPD); y
    • operaciones de cuerda (ver Sección 8.2.4.1).
  • Las lecturas pueden reordenarse con escrituras anteriores en diferentes ubicaciones, pero no con escrituras anteriores en la misma ubicación.
  • Las lecturas o escrituras no pueden reordenarse con instrucciones de E / S, instrucciones bloqueadas o instrucciones de serialización.
  • Las lecturas no pueden pasar las instrucciones anteriores de LFENCE y MFENCE.
  • Las escrituras no pueden pasar las instrucciones anteriores LFENCE, SFENCE y MFENCE.
  • Las instrucciones de LFENCE no pueden pasar lecturas anteriores.
  • Las instrucciones SFENCE no pueden pasar escrituras anteriores.
  • Las instrucciones MFENCE no pueden pasar lecturas o escrituras anteriores.

A partir de aquí, se deduce que:

  • MFENCE es una valla de memoria completa para todas las operaciones en todos los tipos de memoria, ya sea no temporal o no.
  • SFENCE solo evita el reordenamiento de escrituras (en otra terminología, es una barrera de StoreStore), y solo es útil junto con tiendas no temporales y otras instrucciones enumeradas como excepciones.
  • LFENCE evita el reordenamiento de las lecturas con lecturas y escrituras posteriores (es decir, combina las barreras LoadLoad y LoadStore). Sin embargo, las primeras dos viñetas dicen que las barreras LoadLoad y LoadStore están siempre en su lugar, sin excepciones. Por LFENCE tanto, LFENCE solo es inútil para ordenar la memoria.

Para respaldar el último reclamo, miré todos los lugares donde se menciona LFENCE en los 3 volúmenes del manual de Intel, y no encontré ninguno que diga que se requiere LFENCE para la consistencia de la memoria. Incluso MOVNTDQA , la única instrucción de carga no temporal hasta ahora, menciona MFENCE pero no LFENCE .


Actualización: vea las respuestas sobre ¿Por qué es (o no es) SFENCE + LFENCE equivalente a MFENCE? para respuestas correctas a las conjeturas a continuación

Si MFENCE es equivalente a una “sum” de otras dos vallas o no, es una pregunta difícil. A simple vista, entre las tres instrucciones de la valla, solo MFENCE proporciona la barrera StoreLoad, es decir, evita el reordenamiento de lecturas con escrituras anteriores. Sin embargo, la respuesta correcta requiere saber más que las reglas anteriores; es decir, es importante que todas las instrucciones de la cerca estén ordenadas una con respecto a la otra. Esto hace que la secuencia SFENCE LFENCE más poderosa que una simple unión de efectos individuales: esta secuencia también previene el reordenamiento de StoreLoad (porque las cargas no pueden pasar LFENCE , que no puede pasar SFENCE , que no puede pasar tiendas), y constituye una cerca de memoria completa (pero también vea la nota (*) a continuación). Sin embargo, LFENCE SFENCE cuenta que el orden es importante aquí, y la secuencia LFENCE SFENCE no tiene el mismo efecto de sinergia.

Sin embargo, aunque se puede decir que MFENCE ~ SFENCE LFENCE y LFENCE ~ NOP , eso no significa MFENCE ~ SFENCE . Deliberadamente uso la equivalencia (~) y no la igualdad (=) para enfatizar que las reglas aritméticas no se aplican aquí. El efecto mutuo de SFENCE seguido de LFENCE hace la diferencia; aunque las cargas no se reordenan entre sí, se requiere LFENCE para evitar el reordenamiento de las cargas con SFENCE .

(*) Todavía podría ser correcto decir que MFENCE es más fuerte que la combinación de las otras dos vallas. En particular, una nota para la instrucción CLFLUSH en el volumen 2 del manual de Intel dice que ” CLFLUSH solo está ordenado por la instrucción MFENCE . No se garantiza que esté ordenado por ninguna otra instrucción de esgrima o serialización ni por otra instrucción CLFLUSH “.

(Actualización, clflush ahora se define como fuertemente ordenado (como una tienda normal, por lo que solo necesitas mfence si deseas bloquear cargas posteriores), pero clflushopt está débilmente ordenado, pero puede ser cercado por sfence .

Considere la siguiente situación: este es el caso crítico en el que la ejecución de carga especulativa puede dañar teóricamente la consistencia secuencial

inicialmente [x] = [y] = 0

 CPU0: CPU1: store [x]< --1 store [y]<--1 load r1<--[y] load r2<--[x] 

Como x86 permite que las cargas se reordenen con tiendas anteriores a direcciones diferentes, ambas cargas pueden devolver 0. Agregar una línea sola después de cada tienda no evitaría eso, ya que solo previenen el reordenamiento dentro del mismo contexto, pero dado que las tiendas se envían después de la jubilación, puede tener ambas lfences y ambas cargas confirmadas antes de que se realicen y observen las tiendas.

Por otro lado, una defensa obligaría a las tiendas a funcionar, y solo entonces permitirá que se ejecuten las cargas, por lo que verá los datos actualizados en al menos un contexto.

En cuanto a las amenazas , como se señaló en el comentario, en teoría no es lo suficientemente fuerte como para evitar que la carga se reordene por encima de ella, por lo que aún podría leer datos obsoletos. Si bien esto es cierto en lo que se refiere a las reglas de orden oficial de la memoria, creo que la implementación actual de x86 uarch lo hace un poco más fuerte (aunque no me comprometo a hacerlo en el futuro, supongo). De acuerdo con esta descripción :

Debido al fuerte modelo de ordenamiento x86, el buffer de carga es fisgoneado por el tráfico de coherencia. Una tienda remota debe invalidar todas las demás copias de una línea de caché. Si una carga lee una línea de caché y luego la invalida un almacén remoto, la carga debe cancelarse, ya que potencialmente puede leer datos no válidos. El modelo de memoria x86 no requiere husmear en el buffer de la tienda.

Por lo tanto, cualquier carga aún no comprometida en la máquina debe ser asimilable por almacenes de otros núcleos, haciendo así el tiempo efectivo de observación de la carga en el punto de compromiso , y no el punto de ejecución (que de hecho está fuera de servicio y puede haberse realizado) mucho más temprano). Commit está hecho en orden y, por lo tanto, la carga debe observarse después de las instrucciones anteriores, lo que hace que las aplicaciones sean bastante inútiles, como dije antes en los comentarios, ya que la coherencia se puede mantener de la misma manera sin ellos. Esto es principalmente especulación, tratar de explicar la concepción común de que las herramientas carecen de sentido en x86, no estoy del todo seguro de dónde se originó y si hay otras consideraciones a la mano, estaría feliz de que cualquier experto apruebe / cuestione esta teoría.

Todo lo anterior se aplica solo para los tipos de membranas de WB por supuesto

¡Por supuesto que hace sentido!

LFENCE de la hoja de datos de Intel:

Realiza una operación de serialización en todas las instrucciones de carga desde la memoria que se emitieron antes de la instrucción LFENCE. Esta operación de serialización garantiza que cada instrucción de carga que precede en el orden del progtwig, la instrucción LFENCE esté visible globalmente antes de que cualquier instrucción de carga que siga a la instrucción LFENCE sea visible globalmente.

Las instrucciones de escritura de memoria como MOV son atómicas si están alineadas correctamente. Pero esta instrucción normalmente se ejecuta en la memoria caché de la CPU y no será visible en este momento para todos los demás hilos, ya que la memoria LFENCE/SFENCE or MFENCE debe preformar primero.

Caso típico:

Si el escritor de subprocesos desbloquea la región de memoria con instrucciones de escritura como MOV alineado con la memoria, por lo que no está en uso ninguna instrucción de prefijo LOCK , la línea de caché donde se configuró MOV debería ser visible en muy poco futuro para todos los demás subprocesos. LFENCE asegura al lector de hilos que también todas las demás líneas de caché de la escritura de hilos son visibe global .