¿Puedo forzar la coherencia de caché en una CPU x86 multinúcleo?


La otra semana, escribí una pequeña clase de hilo y una tubería de mensaje unidireccional para permitir la comunicación entre hilos (dos tubos por hilo, obviamente, para la comunicación bidireccional). Todo funcionó bien en mi Athlon 64 X2, pero me preguntaba si me encontraría con algún problema si ambos subprocesos estaban buscando la misma variable y el valor de caché local para esta variable en cada núcleo estaba fuera de sincronización.

Sé que la palabra clave volátil forzará a una variable a actualizarse memoria, pero ¿hay alguna forma en los procesadores x86 multinúcleo de forzar la sincronización de las cachés de todos los núcleos? ¿Es esto algo de lo que tengo que preocuparme, o volatile y el uso adecuado de mecanismos de bloqueo livianos (estaba usando _InterlockedExchange para establecer mis variables de tubería volátiles) manejarán todos los casos en los que quiero escribir código "sin bloqueo" para CPU x86 multinúcleo?

Ya conozco y he utilizado Secciones Críticas, Mutexes, Eventos, etc. Sobre todo me pregunto si hay x86 intrínsecos que no soy consciente de que la fuerza o se puede utilizar para hacer cumplir la coherencia de caché.

9 answers

volatile solo obliga a su código a volver a leer el valor, no puede controlar desde dónde se lee el valor. Si el valor fue leído recientemente por su código, entonces probablemente estará en caché, en cuyo caso volatile lo forzará a volver a leer desde caché, NO desde memoria.

No hay muchas instrucciones de coherencia de caché en x86. Hay instrucciones de prefetch como prefetchnta, pero eso no afecta la semántica que ordena la memoria. Se solía implementar aportando el valor a Caché L1 sin contaminar L2, pero las cosas son más complicadas para los diseños modernos de Intel con una gran caché compartida inclusiva L3.

Las CPU X86 usan una variación del protocolo MESI (MESIF para Intel, MOESI para AMD) para mantener sus cachés coherentes entre sí (incluyendo las cachés L1 privadas de diferentes núcleos). Un núcleo que quiere escribir una línea de caché tiene que forzar a otros núcleos a invalidar su copia antes de que pueda cambiar su propia copia de Compartida a Modificada estado.


No necesita ninguna instrucción fence (como MFENCE) para producir datos en un subproceso y consumirlos en otro en x86, porque las cargas/tiendas de x86 tienen semántica de adquisición/liberación incorporada. Necesita MFENCE (barrera completa) para obtener consistencia secuencial. (Una versión anterior de esta respuesta sugirió que clflush era necesario, lo cual es incorrecto).

Es necesario evitar que se reordene en tiempo de compilación, porque el modelo de memoria de C++está débilmente ordenado. volatile es una vieja y mala manera de hacer esto; C++11 std::atomic es una manera mucho mejor de escribir código libre de bloqueos.

 26
Author: SoapBox,
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
2016-09-03 05:20:29

La coherencia de caché está garantizada entre los núcleos debido al protocolo MESI empleado por los procesadores x86. Solo tiene que preocuparse por la coherencia de la memoria cuando se trata de hardware externo que puede acceder a la memoria mientras los datos todavía se encuentran en las cachés de los núcleos. Sin embargo, no parece que sea tu caso aquí, ya que el texto sugiere que estás programando en Userland.

 21
Author: ,
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
2009-02-17 22:06:09

No necesita preocuparse por la coherencia de la caché. El hardware se encargará de eso. Lo que puede que tenga que preocuparse son los problemas de rendimiento debido a la coherencia de la caché.

Si core#1 escribe en una variable y core#2 lee esa misma variable, el procesador se asegurará de que la caché para core#2 esté actualizada. Dado que una línea de caché completa (64 bytes) tiene que ser leída desde la memoria, tendrá algún costo de rendimiento. En este caso, es inevitable. Este es el deseado comportamiento.

El problema es que cuando tiene varias variables en la misma línea de caché, el procesador podría pasar más tiempo manteniendo las cachés sincronizadas incluso si los núcleos están leyendo/escribiendo diferentes variables dentro de la misma línea de caché. Ese costo se puede evitar asegurándose de que esas variables no estén en la misma línea de caché. Este efecto se conoce como False Sharing ya que está forzando a los procesadores a sincronizar los valores de los objetos que no están realmente compartidos entre hilo.

 12
Author: Ferruccio,
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
2014-04-16 16:10:14

Volátil no lo hará. En C++, volatile solo afecta a las optimizaciones del compilador, como almacenar una variable en un registro en lugar de memoria, o eliminarla por completo.

 6
Author: dsimcha,
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
2009-02-17 21:51:30

No especificó qué compilador está utilizando, pero si está en Windows, eche un vistazo a este artículo aquí. También eche un vistazo a las funciones de ynchronization sdisponibles aquí. Es posible que desee tener en cuenta que, en general, volatile no es suficiente para hacer lo que desea que haga, pero bajo VC 2005 y 2008, hay semántica no estándar agregada que agrega barreras de memoria implícitas alrededor de la lectura y la escritura.

Si quieres que las cosas sean portátiles, vas a tener mucho un camino más difícil por delante.

 6
Author: Eclipse,
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
2009-02-17 21:54:52

Hay una serie de artículos que explican las arquitecturas de memoria modernas aquí, incluyendo cachés Intel Core2 y muchos más temas de arquitectura moderna.

Los artículos son muy legibles y bien ilustrados. ¡Que lo disfrutes!

 3
Author: davidnr,
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
2009-02-18 13:30:52

Hay varias sub-preguntas en tu pregunta así que las responderé a mi mejor conocimiento.

  1. Actualmente no existe una forma portátil de implementar interacciones sin bloqueo en C++. La propuesta de C++0x resuelve esto introduciendo la biblioteca atomics.
  2. No se garantiza que Volatile proporcione atomicidad en un multinúcleo y su implementación es específica del proveedor.
  3. En el x86, no necesita hacer nada especial, excepto declarar variables compartidas como volátiles para evite algunas optimizaciones del compilador que pueden romper el código multiproceso. Volatile le dice al compilador que no almacene valores en caché.
  4. Hay algunos algoritmos (Dekker, por ejemplo) que no funcionarán ni siquiera en un x86 con variables volátiles.
  5. A menos que sepa con certeza que pasar el acceso a los datos entre subprocesos es un cuello de botella de rendimiento importante en su programa, manténgase alejado de las soluciones sin bloqueos. Utilice el paso de datos por valor o bloqueos.
 3
Author: Bartosz Milewski,
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
2009-04-13 20:52:40

El siguiente es un buen artículo en referencia al uso de volatile con programas roscados.

Volátil Casi Inútil para la Programación Multihilo.

 2
Author: cmcginty,
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
2009-09-22 02:18:17

Herb Sutter parecía simplemente sugerir que dos variables cualesquiera deberían residir en líneas de caché separadas. Hace esto en su cola concurrente con relleno entre sus bloqueos y punteros de nodo.

Edit: Si está utilizando el compilador Intel o GCC, puede usar los atomic builtins, que parecen hacer todo lo posible para adelantarse a la caché cuando sea posible.

 1
Author: greyfade,
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
2009-02-18 07:47:43