2010-12-27 7 views

Respuesta

1

Advertencia: No soy un experto en esto. Todavía estoy tratando de aprender esto yo mismo. Pero dado que nadie ha respondido en los últimos dos días, parece que los expertos en las instrucciones de la valla de memoria no son abundantes. Así que aquí está mi entendimiento ...

Intel es un sistema de memoria weakly-ordered. Esto significa que su programa puede ejecutar

array[idx+1] = something 
idx++ 

pero el cambio a idx puede ser globalmente visible (por ejemplo, para hilos/procesos que se ejecutan en otros procesadores) antes del cambio a array. Colocando sfence entre las dos declaraciones se asegurará el orden en que las escrituras se envían al FSB.

Mientras tanto, otro procesador corre

newestthing = array[idx] 

pueden haber almacenado en caché de la memoria para gama y tiene una copia inactiva, pero obtiene la actualización IDX debido a un fallo de caché. La solución es usar lfence justo antes para asegurar que las cargas estén sincronizadas.

This article o this article puede dar una mejor información

+0

No, las tiendas x86 están fuertemente ordenadas por defecto. El reordenamiento en tiempo de compilación podría producir el reordenamiento que usted describe (si no puede usar 'std :: atomic' con' memory_order_release' o más fuerte), pero las tiendas de las instrucciones x86 'mov [array + rcx], eax' /' mov [idx], rcx' se volvería globalmente visible para otros hilos en ese orden. Solo las tiendas de transmisión 'MOVNT' están débilmente ordenadas (por lo que necesita' sfence' después de ellas antes de almacenarlas en un indicador 'buffer_ready'). Normalmente, nunca necesita '' fence '', a menos que esté utilizando cargas débilmente ordenadas de la memoria de video o algo así. –

+0

Vea también [mi respuesta en una pregunta más reciente] (https://stackoverflow.com/a/44866652/224132). Además, los excelentes artículos de Jeff Preshing, como este [modelo de memoria débil vs. fuerte] (http://preshing.com/20120930/weak-vs-strong-memory-models/) publican. (Fue escrito 2 años después de que publicaste esto. No tengo la intención de ser grosero con una respuesta anterior, pero es casi totalmente erróneo, xD) –

+0

@ PeterCordes Estás completamente equivocado. 1) no necesita std :: atomic para lograr el orden correcto. De hecho std :: atomic no existía hasta C++ 11. Entonces no existía en el momento en que se realizó la publicación. 2) La cerca se usa para garantizar el orden correcto de instrucciones del lado del consumidor. Entonces, si quiere leer su búfer después de ver el indicador 'buffer_ready', entonces usa lfence para asegurarse de que la lectura del buffer no ocurra antes. –

3

Aquí está mi entendimiento, es de esperar precisa y suficientemente simple como para tener sentido:

(Itanium) arquitectura IA64 permite que la memoria lee y escribe para ser ejecutados en cualquier orden, por lo que el orden de los cambios de memoria desde el punto de vista de otro procesador no es predecible, a menos que utilice vallas para hacer cumplir que las escrituras se completen en un orden razonable.

A partir de ahora, estoy hablando de x86, x86 está fuertemente ordenado.

En x86, Intel no garantiza que una tienda hecha en otro procesador siempre estará visible de inmediato en este procesador. Es posible que este procesador ejecutó especulativamente la carga (lectura) justo antes de perder la memoria del otro procesador (escritura).

Las instrucciones de lectura/modificación/escritura bloqueadas son totalmente consistentes de forma secuencial, por lo que rara vez tiene que usar cercas en x86. Debido a esto, en general, usted ya maneja la pérdida de las operaciones de memoria del otro procesador porque un xchg bloqueado o cmpxchg lo sincronizarán todo.

Por lo que yo lo entiendo, la llanura drena la cola de carga de la memoria y espera a que la tubería de la unidad de carga termine las operaciones que están en progreso. mfence va más allá y espera a que se lean y escriban todas las memorias, sfence hace lo mismo solo con las tiendas (y limpia el combinador de escritura).

En esencia, lfence descarta cualquier carga especulativamente ejecutada.Las cargas que pueden haberse ejecutado previamente especulativamente se volverán a emitir. sfence es lo menos necesario en la práctica, por lo general no es necesario a menos que se use la memoria de combinación de escritura, algo que rara vez se hace si no se es un desarrollador de modo kernel (controlador).

Por lo tanto, para resumir, los algoritmos que usan instrucciones bloqueadas como xchg, o xadd, o cmpxchg, etc., funcionarán sin vallas porque la instrucción bloqueada (en la mayoría de los casos) hace todo por sincronizar. Cualquier código complicado sin candado que (por ejemplo) tenga rutas de acceso de código de salida anticipada que no usen esas instrucciones de bloqueo puede necesitar una ubicación en algún lugar para evitar que falle una tienda realizada por otro procesador. Código que es difícil y no es una buena práctica, pero podría ser necesario en rutas de código extremadamente calientes.

+0

. Normalmente, no necesita '' lfence' alguna vez. Solo necesita 'sfence' [después de tiendas de transmisión' movnt' débilmente ordenadas] (https://stackoverflow.com/a/44866652/224132). Necesita 'mfence' (o una operación' lock'ed) para obtener consistencia secuencial en lugar de simplemente liberar/adquirir. (Consulte [Reordenación de la memoria captada en la ley] (http://preshing.com/20120515/memory-reordering-caught-in-the-act/) para obtener un ejemplo). –

+0

Normalmente necesita 'lfence' porque el compilador de C++. –

Cuestiones relacionadas