Pregunta

La otra semana, escribí un poco de clase rosca y un tubo de mensaje de una manera de permitir la comunicación entre los hilos (dos tubos por hilo, obviamente, para la comunicación bidireccional). Todo funcionaba bien en mi Athlon 64 X2, pero me preguntaba si me surge algún problema si ambos hilos estaban mirando a la misma variable y el valor almacenado en caché local para esta variable en cada núcleo estaba fuera de sincronía.

Sé que el volátil palabra clave obligará a una variable para refrescar la memoria, pero ¿hay alguna manera en los procesadores de múltiples núcleos x86 para obligar a los cachés de todos los núcleos de sincronizar? ¿Esto es algo que necesito que preocuparse, o a volátil y el uso adecuado de los mecanismos de bloqueo de peso ligero (yo estaba usando _InterlockedExchange para establecer mi variables de tubería volátiles) manejar todos los casos en que quiero escribir "lock libre" código para CPUs multinúcleo x86?

Ya estoy al tanto de y han utilizado secciones críticas, mutex, eventos, etcétera. Lo que más me pregunto si hay intrínsecos x86 que no estoy al tanto de que fuerzan o puede ser utilizado para hacer cumplir la coherencia de caché.

¿Fue útil?

Solución

volatile solamente las fuerzas de su código para volver a leer el valor, que no puede controlar donde el valor se lee de. Si el valor recientemente fue leído por el código a continuación, es probable que sea en la memoria caché, en cuyo caso volátil obligará a volver a leer desde la memoria caché, no de la memoria.

No hay una gran cantidad de instrucciones de caché de coherencia en x86. Hay instrucciones de recuperación previa como prefetchnta , pero que no afecta a la semántica de memoria de realizar el pedido. Lo que solía ser implementado por lo que el valor de caché L1 L2 sin contaminar, pero las cosas son más complicadas para Intel modernos diseños con una gran compartida inclusiva caché L3.

CPUs x86 utilizan una variación en el protocolo MESI (MESIF para Intel, MOESI para AMD) para mantener sus cachés coherentes entre sí (incluyendo las memorias caché L1 privadas de diferentes núcleos). Un núcleo que quiere escribir una línea de caché tiene que obligar a otros núcleos de invalidar su copia del mismo antes de que pueda cambiar su propia copia de Compartido de estado modificado.


No necesita ninguna instrucción de la cerca (como MFENCE) para producir datos en un único hilo y lo consumen en otro en x86, porque las cargas / x86 tiendas tienen adquirir la semántica / liberación incorporado. Sí es necesario MFENCE (barrera completa) para obtener la consistencia secuencial. (Una versión anterior de esta respuesta sugirió que era necesario clflush, lo cual es incorrecto).

Es necesario para prevenir en tiempo de compilación reordenación , porque modelo de memoria C ++ 's se débilmente ordenado. volatile es una mala manera de edad, para hacer esto; C ++ 11 std :: atómica es una forma mucho mejor para escribir código libre de bloqueo.

Otros consejos

coherencia de caché está garantizada entre los núcleos debido al protocolo MESI empleado por los procesadores x86. Sólo tiene que preocuparse por la coherencia de memoria cuando se trata de hardware externo que puede acceder a la memoria de datos, mientras que todavía es localización en cachés núcleos. No se ve como si fuera el caso aquí, sin embargo, ya que el texto sugiere que está programando en espacio de usuario.

Usted no necesita preocuparse por la coherencia de caché. El hardware se encargará de eso. Lo que es posible que tenga que preocuparse por problemas de rendimiento es debido a que la coherencia de caché.

Si el núcleo # 1 escribe a una variable, que invalida todas las demás copias de la línea de caché en otros núcleos (porque tiene que conseguir exclusiva propiedad de la línea de caché antes de cometer la tienda). Cuando el núcleo # 2 dice que la misma variable, que se perderá en la memoria caché (a menos core # 1 ya se ha escrito de nuevo por lo que un nivel común de caché).

Desde una línea de caché completa (64 bytes) tiene que ser leído desde la memoria (o escrita vuelta a la memoria compartida y luego leer por el núcleo # 2), que tendrá algún costo de rendimiento. En este caso, es inevitable. Este es el comportamiento deseado.


El problema es que cuando usted tiene múltiples variables en la misma línea de caché, el procesador podría pasar más tiempo manteniendo las cachés en sincronía, incluso si los núcleos son la lectura / escritura de diferentes variables dentro de la misma línea de caché.

Ese costo se puede evitar asegurándose de esas variables no están en la misma línea de caché. Este efecto se conoce como Falso Sharing , ya que están obligando a los procesadores para sincronizar los valores de los bienes que no son en realidad compartido entre hilos.

volátil no lo hará. En C ++, volátil solamente afecta a lo optimizaciones del compilador tales como almacenamiento de una variable en un registro en lugar de la memoria, o eliminar por completo.

No se ha especificado compilador que está utilizando, pero si estás en Windows, echar un vistazo a este artículo aquí . También echa un vistazo a las funciones ynchronization el disponibles aquí s . Es posible que desee tener en cuenta que en volatile en general no es suficiente para hacer lo que quiere que haga, pero bajo VC 2005 y 2008, no son la semántica no estándar añaden a ella que añaden a entender las barreras de memoria en torno leen y escriben.

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

Hay una serie de artículos explicando modernas arquitecturas de memoria aquí , incluyendo < a href = "http://duartes.org/gustavo/blog/post/intel-cpu-caches" rel = "nofollow noreferrer"> Intel Core 2 almacena en caché y muchos temas más moderna arquitectura.

Los artículos son muy legible y bien ilustrado. Disfrutar!

Hay varias sub-preguntas en su pregunta para que voy a responder a lo mejor de mi conocimiento.

  1. En este momento hay manera portátil de la aplicación de las interacciones sin bloqueo en C ++. La propuesta C ++ 0x resuelve este mediante la introducción de la biblioteca atómica.
  2. volátil no está garantizada para proporcionar atomicidad en un multi-núcleo y su aplicación es específica del proveedor.
  3. En el 86, no es necesario hacer nada especial, excepto las variables de declarar compartida como volátil para prevenir algunas optimizaciones del compilador que puede romper el código multiproceso. Volátil indica al compilador que no hagan caché valores.
  4. Hay algunos algoritmos (Dekker, por ejemplo) que no funcionan incluso en un sistema x86 con variables volátiles.
  5. A menos que sepa con certeza que la aprobación de acceso a los datos entre hilos es un importante cuello de botella en su programa, se mantenga alejado de soluciones libres de bloqueo. Utilizar los datos que pasan por valor o cerraduras.

El siguiente es un buen artículo en referencia a la utilización de volatile w / programas roscadas.

volátil casi inútil para multiproceso de programación .

Herb Sutter parecía simplemente sugieren que dos variables deben residir en líneas de caché separadas. Lo hace en su cola concurrente con relleno entre sus cerraduras y punteros a nodos.

Editar: Si está utilizando el compilador Intel o GCC, se puede utilizar el noreferrer órdenes internas atómicas , que parece hacer todo lo posible para anticiparse a la caché cuando sea posible.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top