Pregunta ¿Cuándo es útil una barrera de memoria de solo compilador (como std :: atomic_signal_fence)?


La noción de un cerca del compilador a menudo aparece cuando leo sobre modelos de memoria, barreras, orden, átomos, etc., pero normalmente está en el contexto de además siendo emparejado con un Cerca de la CPUcomo es de esperar

De vez en cuando, sin embargo, leo sobre construcciones de vallas que solamente aplicar al compilador. Un ejemplo de esto es el C ++ 11 std::atomic_signal_fence función, que establece a 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 reordenando el   las instrucciones del compilador se suprimen a medida que la orden ordena.

Tengo cinco preguntas relacionadas con este tema:

  1. Como implica el nombre std::atomic_signal_fence, es un interrupción asincrónica (como un subproceso del kernel para ejecutar un manejador de señal) solamente caso en que una compilador solo valla es útil?

  2. ¿Su utilidad se aplica a todas las arquitecturas, incluidas fuertemente ordenado unos tales como x86?

  3. Puede específico ejemplo, para demostrar la utilidad de un compilador solo ¿cerca?

  4. Cuando usas std::atomic_signal_fence, ¿hay alguna diferencia entre usar acq_rel y seq_cst ordenando? (Yo esperaría que no haga la diferencia).

  5. Esta pregunta podría estar cubierta por la primera pregunta, pero soy lo suficientemente curioso como para preguntar específicamente al respecto de todos modos: ¿Es nunca necesario usar vallas con thread_local accesos? (Si alguna vez lo sería, esperaría compilador solo vallas como atomic_signal_fence ser la herramienta de elección.)

Gracias.


32
2017-08-26 17:07


origen


Respuestas:


Para responder a las 5 preguntas:


1) Una valla compiladora (por sí mismo, sin una valla de CPU) solo es útil en dos situaciones:

  • Hacer cumplir restricciones de orden de memoria  entre un hilo único y controlador de interrupción asíncrono vinculado a ese mismo hilo (como un manejador de señal).

  • Hacer cumplir restricciones de orden de memoria  entre múltiples hilos cuando se garantiza que cada hilo se ejecutará en el mismo núcleo de CPU. En otras palabras, la aplicación solo se ejecutará núcleo simple sistemas, o la aplicación toma medidas especiales (a través de afinidad del procesador) para garantizar que cada hilo que comparte los datos esté unido al mismo núcleo.


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


3) Aquí está pseudo-código que demuestra el uso de una valla compiladora, por sí misma, para sincronizar suficientemente el acceso a la memoria entre un hilo y un manejador de señal asíncrono unido al mismo hilo:

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á ligado al mismo hilo que inicializa shared_datay establece el is_initialized bandera, lo que significa que la aplicación es de subproceso único, o establece las máscaras de señal de subproceso en consecuencia. De lo contrario, la valla del compilador sería insuficiente, y una Cerca de la CPU también sería necesario.


4) Deberían ser iguales.  acq_rel y seq_cst ambos deberían tener como resultado una compilación completa (bidireccional) del compilador, sin que se emitan instrucciones de la CPU relacionadas con la valla. El concepto de "coherencia secuencial" solo entra en juego cuando se utilizan múltiples núcleos e hilos, y atomic_signal_fence solo se refiere a un hilo de ejecución.


5) No. (A menos que, por supuesto, se acceda a los datos locales del subproceso desde un manejador de señal asíncrono, en cuyo caso podría ser necesaria una compilación). De lo contrario, las cercas nunca deberían ser necesarias con datos de subprocesos locales ya que el compilador (y la CPU) solo están permitidos reordenar los accesos a la memoria de manera que no cambien el comportamiento observable del programa con respecto a su puntos de secuencia desde una perspectiva de subproceso único. Y lógicamente uno puede pensar que la estática de subprocesos locales en un programa de subprocesos múltiples es lo mismo que la estática global en un programa de subproceso único. En ambos casos, solo se puede acceder a los datos desde un único hilo, lo que evita que se produzca una carrera de datos.


21
2017-08-27 00:04



En realidad, hay algunos modismos de programación C no portatiles pero útiles donde las vallas del compilador son útiles, incluso en código multinúcleo (particularmente en código pre-C11). La situación típica es cuando el programa está haciendo algunos accesos que normalmente se volverían volátiles (porque están relacionados con variables compartidas), pero desea que el compilador pueda mover los accesos. Si sabe que los accesos son atómicos en la plataforma de destino (y toma otras precauciones), puede dejar los accesos como no volátiles, pero contienen movimiento de código utilizando barreras de compilación.

Afortunadamente, la mayoría de los programas como este se vuelven obsoletos con C11 / C ++ 11 atomics relajados.


2
2017-11-05 09:19