c++ - entre - ¿Cuándo es útil una barrera de memoria de solo compilador(como std:: atomic_signal_fence)?
que es la memoria ram (2)
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.
La noción de una valla compiladora aparece a menudo cuando leo sobre modelos de memoria, barreras, ordenamientos, átomos, etc., pero normalmente está en el contexto de estar emparejado con una valla de CPU , como era de esperar.
Ocasionalmente, sin embargo, leo sobre construcciones de vallas que solo se aplican al compilador. Un ejemplo de esto es la función C ++ 11 std::atomic_signal_fence
, que se establece en cppreference.com :
std :: atomic_signal_fence es equivalente a std :: atomic_thread_fence, excepto que no se emiten instrucciones de la CPU para ordenar la memoria. Solo el reordenamiento de las instrucciones por parte del compilador se suprime a medida que el orden lo ordena.
Tengo cinco preguntas relacionadas con este tema:
Tal como lo implica el nombre
std::atomic_signal_fence
, ¿hay una interrupción asincrónica (como un subproceso que el núcleostd::atomic_signal_fence
para ejecutar un manejador de señal) el único caso en el que es útil un cercador solo de compilador ?¿Su utilidad se aplica a todas las arquitecturas, incluidas las fuertemente ordenadas como
x86
?¿Se puede proporcionar un ejemplo específico para demostrar la utilidad de una valla solo de compilador ?
Al usar
std::atomic_signal_fence
, ¿hay alguna diferencia entre usaracq_rel
yseq_cst
? (Yo esperaría que no haga la diferencia).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 necesario usar vallas con
thread_local
? (Si alguna vez lo fuera, esperaría que las vallas compiladoras comoatomic_signal_fence
fueran la herramienta de elección).
Gracias.
Para responder a las 5 preguntas:
1) Una valla de compilación ( por sí misma, sin una valla de CPU ) solo es útil en dos situaciones:
Para imponer restricciones de orden de memoria entre un único subproceso y el controlador de interrupción asíncrono vinculado a ese mismo subproceso (como un controlador de señal).
Para imponer restricciones de orden de memoria entre varios subprocesos cuando se garantiza que cada subproceso se ejecutará en el mismo núcleo de CPU . En otras palabras, la aplicación solo se ejecutará en sistemas de núcleo único , o la aplicación tomará medidas especiales (a través de la afinidad del procesador ) para garantizar que cada hilo que comparte los datos esté vinculado al mismo núcleo.
2) El modelo de memoria de la arquitectura subyacente, ya sea con una orden fuerte o débil, no tiene ninguna relación con si se necesita una compilación en una situación.
3) Aquí está el 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á vinculado al mismo hilo que inicializa shared_data
y establece el indicador is_initialized
, lo que significa que la aplicación es de un solo subproceso, o establece máscaras de señal de subproceso en consecuencia. De lo contrario, la cerca del compilador sería insuficiente, y una valla de la CPU también sería necesaria.
4) Deberían ser lo mismo. acq_rel
y seq_cst
deberían dar 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 trata de 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 desde el compilador (y CPU ) solo pueden reordenar accesos de memoria de forma que no cambien el comportamiento observable del programa con respecto a sus 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.