ejecutar - ¿Cómo pueden los compiladores C++ soportar C++ 11 atómico, pero no son compatibles con el modelo de memoria C++ 11?
gcc linux (3)
Al observar el estado de implementación de Clang y g ++ C ++ 11 noté algo extraño:
son compatibles con C ++ 11 atómica, pero no son compatibles con C ++ 11 modelo de memoria.
Estaba bajo la impresión de que debe tener el modelo de memoria C ++ 11 para usar átomos. Entonces, ¿cuál es exactamente la diferencia entre el soporte para atómico y el modelo de memoria?
¿La falta de compatibilidad con el modelo de memoria significa que los programas legales de C ++ 11 que usan std::atomic<T>
no son consistentes?
referencias:
http://clang.llvm.org/cxx_status.html
http://gcc.gnu.org/gcc-4.7/cxx0x_status.html
No es tanto que no sean compatibles con el modelo de memoria, sino que no (todavía) admiten la API en el estándar para interactuar con el modelo de memoria. Esa API incluye una cantidad de mutexes.
Sin embargo, tanto Clang como GCC han sido tan conscientes de los hilos como es posible sin un estándar formal por algún tiempo. No tiene que preocuparse por las optimizaciones moviendo las cosas al lado equivocado de las operaciones atómicas.
Supongo que el "Modelo de falta de memoria" en estos casos solo significa que los optimizadores se escribieron antes de que se publicara el modelo de memoria C ++ 11, y podrían realizar ahora optimizaciones no válidas. Es muy difícil y lleva mucho tiempo validar las optimizaciones en comparación con el modelo de memoria, por lo que no es una gran sorpresa que los equipos clang / gcc aún no hayan terminado.
¿La falta de compatibilidad con el modelo de memoria significa que los programas legales de C ++ 11 que usan std :: atomic no son consecuentes?
Sí, esa es una posibilidad. Es aún peor: el compilador podría introducir carreras de datos en (de acuerdo con el estándar C ++ 11) programas libres de raza, por ejemplo, introduciendo escrituras especulativas.
Por ejemplo, varios compiladores de C ++ utilizados para realizar esta optimización:
for (p = q; p = p -> next; ++p) {
if (p -> data > 0) ++count;
}
Podría ser optimizado en:
register int r1 = count;
for (p = q; p = p -> next; ++p) {
if (p -> data > 0) ++r1;
}
count = r1;
Si todos los p->data
no son negativos, el código fuente original no se escribió para count
, pero sí el código optimizado. Esto puede introducir una carrera de datos en un programa sin carreras, por lo que la especificación C ++ 11 no permite dichas optimizaciones. Los compiladores existentes ahora deben verificar (y ajustar si es necesario) todas las optimizaciones.
Consulte las consecuencias del compilador del modelo de memoria simultanea para obtener detalles.
Uno de los problemas es la definición de "ubicación de la memoria", que permite (y obliga al compilador a admitir) el bloqueo de diferentes miembros de la estructura por diferentes bloqueos. Hay una discusión sobre un problema de RL causado por esto .
Básicamente, el problema es tener una struct
definida así:
struct x {
long a;
unsigned int b1;
unsigned int b2:1;
};
el compilador es libre de implementar la escritura en b2
sobrescribiendo también a b1
(y aparentemente, a juzgar por el informe, lo hace). Por lo tanto, los dos campos deben estar bloqueados como uno. Sin embargo, como consecuencia del modelo de memoria C ++ 11, esto está prohibido (bueno, no está realmente prohibido, pero el compilador debe asegurarse de que las actualizaciones simultáneas a b1
y b2
no interfieran; podría hacerlo mediante bloqueo o CAS-ing cada una). tal actualización, bueno, la vida es difícil en algunas arquitecturas). Citando del informe:
He planteado el problema con nuestros chicos de GCC y me dijeron que: "C no proporciona esa garantía, ni puede bloquear de manera confiable diferentes campos de estructura con diferentes bloqueos si comparten regiones de memoria de tamaño de palabra alineadas naturalmente. El modelo de memoria +11 lo garantizaría, pero eso no está implementado ni construye el kernel con un compilador C ++ 11 ".
Buena información también se puede encontrar en la wiki .