¿Cuándo es útil una barrera de memoria solo para compiladores (como std::atomic signal fence)?


La noción de un compilador fence a menudo aparece cuando estoy leyendo sobre modelos de memoria, barreras, orden, átomos, etc., pero normalmente está en el contexto de también estar emparejado con un CPU fence, como uno esperaría.

De vez en cuando, sin embargo, leo acerca de construcciones fence que solo se aplican al compilador. Un ejemplo de esto es la función C++11 std::atomic_signal_fence, que establece en cppreference.com :

Std:: atomic_signal_fence es equivalente a std:: atomic_thread_fence, excepto que no hay CPU se emiten instrucciones para ordenar la memoria. Solo reordenamiento de la las instrucciones del compilador se suprimen como instrucciones de orden.

Tengo cinco preguntas relacionadas con este tema:

  1. Como está implícito en el nombre std::atomic_signal_fence, es una interrupción asíncrona (como un subproceso que el kernel preimprime para ejecutar un manejador de señales) el solo caso en el que un solo compilador ¿fence es útil?

  2. ¿Su utilidad se aplica a todas las arquitecturas, incluyendo fuertemente ordenadas como x86?

  3. ¿Puede proporcionarse un ejemplo específico para demostrar la utilidad de un compilador-solamente fence?

  4. Al usar std::atomic_signal_fence, ¿hay alguna diferencia entre usar acq_rel y seq_cst ordenar? (Espero que no haga ninguna diferencia.)

  5. Esta pregunta podría estar cubierto por la primera pregunta, pero tengo la curiosidad de preguntar específicamente al respecto de todos modos: ¿Es alguna vez necesario usar cercas con thread_local accesos? (Si alguna vez lo fuera, esperaría que solo compilador cercas como atomic_signal_fence para ser la herramienta de elección.)

Gracias.

Author: etherice, 2013-08-26

2 answers

Para responder a las 5 preguntas:


1) Un compilador fence ( por sí mismo, sin un cpu fence ) solo es útil en dos situaciones:

  • Para aplicar restricciones de orden de memoria entre un único subproceso y un manejador de interrupciones asíncronas enlazado a ese mismo subproceso (como un manejador de señales).

  • Para aplicar restricciones de orden de memoria entre múltiples hilos cuando se garantiza que cada el subproceso se ejecutará en el mismo núcleo de la CPU . En otras palabras, la aplicación solo se ejecutará en sistemas single core, o la aplicación toma medidas especiales (a través de processor affinity) para garantizar que cada subproceso que comparte los datos esté vinculado al mismo núcleo.


2) El modelo de memoria de la arquitectura subyacente, ya sea que esté fuertemente o débilmente ordenado, no tiene relación con si se necesita un compilador-fence en un situación.


3) Aquí está pseudo-código que demuestra el uso de un compilador fence, por sí mismo, para sincronizar suficientemente el acceso a la memoria entre un subproceso y un controlador de señal asíncrona vinculado al mismo subproceso:

void async_signal_handler()
{
    if ( is_shared_data_initialized )
    {
        compiler_only_memory_barrier(memory_order::acquire);
        ... use shared_data ...
    }
}

void main()
{
// initialize shared_data ...
    shared_data->foo = ...
    shared_data->bar = ...
    shared_data->baz = ...
// shared_data is now fully initialized and ready to use
    compiler_only_memory_barrier(memory_order::release);
    is_shared_data_initialized = true;
}

Nota importante: Este ejemplo asume que async_signal_handler está vinculado al mismo hilo que inicializa shared_data y establece la bandera is_initialized, lo que significa que la aplicación es de un solo hilo, o establece máscaras de señal de hilo en consecuencia. De lo contrario, el compiler fence sería insuficiente, y también se necesitaría un CPU fence.


4) Deberían ser iguales. acq_rel y seq_cst ambos deberían resultar en un compilador completo (bidireccional) fence, sin instrucciones de CPU relacionadas con fence emitidas. El concepto de "consistencia secuencial" solo entra en juego cuando están involucrados varios núcleos e hilos, y atomic_signal_fence solo pertenece a un hilo de ejecución.


5) No. (A menos que, por supuesto, el se accede a los datos locales de subprocesos desde un manejador de señales asíncrono, en cuyo caso podría ser necesario un compilador. De lo contrario, las cercas nunca deberían ser necesarias con datos locales de subprocesos, ya que el compilador (y la CPU) solo pueden reordenar los accesos a la memoria de manera que no cambien el comportamiento observable del programa con respecto a sus puntos de secuencia desde una perspectiva de subproceso único. Y uno puede pensar lógicamente en la estática local de hilo en un programa multi-hilo para ser el lo mismo que la estática global en un programa de un solo hilo. En ambos casos, los datos solo son accesibles desde un único subproceso, lo que evita que se produzca una carrera de datos.

 21
Author: MikeTusar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-08-27 10:54:17

En realidad hay algunos modismos de programación en C no portables pero útiles donde las cercas del compilador son útiles, incluso en código multinúcleo (particularmente en código pre-C11). La situación típica es donde el programa está haciendo algunos accesos que normalmente se harían volátiles (porque son a variables compartidas), pero desea que el compilador sea capaz de mover los accesos alrededor. Si sabe que los accesos son atómicos en la plataforma de destino (y toma algunas otras precauciones), puede dejar el accesos no volátiles, pero contienen movimiento de código usando barreras de compilador.

Afortunadamente, la mayoría de la programación como esta se vuelve obsoleta con C11/C++11 relaxed atomics.

 2
Author: user2949652,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-11-05 09:19:35