java multithreading synchronization memory-model cpu-cache

¿El modelo de memoria de Java(JSR-133) implica que ingresar un monitor vacía la(s) caché(s) de datos de la CPU?



multithreading synchronization (4)

la necesidad absoluta de sincronizar, incluso si la coherencia de caché está garantizada en hardware

Sí, pero solo tienes que razonar contra el modelo de memoria de Java, no contra una arquitectura de hardware particular en la que se ejecute tu programa. Además, no solo se trata del hardware, el compilador y los JIT en sí mismos pueden reordenar las instrucciones que causan problemas de visibilidad. Las construcciones de sincronización en Java abordan la visibilidad y la atomicidad en todos los niveles posibles de transformación de código (por ejemplo, compilador / JIT / CPU / caché).

y, por otro lado, mal rendimiento en arquitecturas incoherentes (vacíos completos de caché)

Tal vez entendí mal s / t, pero con arquitecturas incoherentes, tienes que sincronizar las secciones críticas de todos modos. De lo contrario, se encontrará con todo tipo de condiciones de carrera debido a la reordenación. No veo por qué el modelo de memoria de Java empeora el asunto.

¿No debería ser más estricto (requiera información sobre lo que está protegido por un monitor)?

No creo que sea posible decirle a la CPU que descargue cualquier parte particular de la memoria caché. Lo mejor que puede hacer el compilador es emitir cercos de memoria y dejar que la CPU decida qué partes de la memoria caché necesita vaciarse, es más burdo que lo que estás buscando, supongo. Incluso si es posible un control más preciso, creo que dificultaría aún más la programación concurrente (ya es lo suficientemente difícil).

AFAIK, el Java 5 MM (al igual que el .NET CLR MM) es más "estricto" que los modelos de memoria de arquitecturas comunes como x86 y IA64. Por lo tanto, hace que el razonamiento sobre esto sea relativamente más simple. Sin embargo, obviamente no debería ofrecer s / t más cercano a la consistencia secuencial porque eso afectaría el rendimiento significativamente ya que se podrían aplicar menos optimizaciones de compilador / JIT / CPU / caché.

Hay algo que me molesta con el modelo de memoria Java (si es que entiendo todo correctamente). Si hay dos subprocesos A y B, no hay garantías de que B vea un valor escrito por A, a menos que A y B se sincronicen en el mismo monitor.

Para cualquier arquitectura de sistema que garantice la coherencia de caché entre los subprocesos, no hay problema. Pero si la arquitectura no admite la coherencia de la memoria caché en el hardware, esto significa esencialmente que cada vez que un subproceso entra en un monitor, todos los cambios de memoria realizados antes deben comprometerse con la memoria principal, y la memoria caché se debe invalidar. Y debe ser todo el caché de datos, no solo unas pocas líneas, ya que el monitor no tiene información sobre qué variables en la memoria guarda. Pero eso seguramente afectará el rendimiento de cualquier aplicación que necesite sincronizarse con frecuencia (especialmente cosas como las colas de trabajos con trabajos de ejecución corta). Entonces, ¿puede Java funcionar razonablemente bien en arquitecturas sin coherencia de caché de hardware? Si no, ¿por qué el modelo de memoria no ofrece garantías más firmes sobre la visibilidad? ¿No sería más eficiente si el idioma necesitara información que está protegida por un monitor?

Como lo veo, el modelo de memoria nos ofrece lo peor de ambos mundos, la absoluta necesidad de sincronizar, incluso si la coherencia de la memoria caché está garantizada en el hardware y, por otro lado, un mal rendimiento en arquitecturas incoherentes (vacíos completos de memoria caché). Entonces, ¿no debería ser más estricto (requerir información que está protegida por un monitor) o más perder y restringir las plataformas potenciales a arquitecturas coherentes con caché?

Como lo es ahora, no tiene mucho sentido para mí. ¿Alguien puede aclarar por qué se eligió este modelo de memoria específico ?

EDIT: mi uso de estricto y perder fue una mala elección en retrospectiva. Utilicé "estricto" para el caso donde se hacen menos garantías y "perder" por lo contrario. Para evitar confusiones, probablemente sea mejor hablar en términos de garantías más o menos fuertes.


La respuesta sería que la mayoría de los multiprocesadores son coherentes con la memoria caché , incluidos los grandes sistemas NUMA, ¿cuál casi? siempre son ccNUMA.

Creo que estás algo confundido en cuanto a cómo se logra la coherencia de caché en la práctica. Primero, los cachés pueden ser coherentes / incoherentes con respecto a varias otras cosas en el sistema:

  • Dispositivos
  • (Memoria modificada por) DMA
  • Cachés de datos vs cachés de instrucciones
  • Cachés en otros núcleos / procesadores (de los que trata esta pregunta)
  • ...

Hay que hacer algo para mantener la coherencia. Cuando trabaje con dispositivos y DMA, en arquitecturas con cachés incoherentes con respecto a DMA / dispositivos, omitirá el caché (y posiblemente el búfer de escritura), o invalidará / vaciará el caché alrededor de las operaciones relacionadas con DMA / dispositivos.

De manera similar, al generar código dinámicamente, es posible que deba vaciar la memoria caché de instrucciones.

Cuando se trata de cachés de CPU, la coherencia se logra mediante el uso de algún protocolo de coherencia, como MESI, MOESI, ... Estos protocolos definen los mensajes que se enviarán entre cachés en respuesta a ciertos eventos (por ejemplo: invalidar solicitudes a otros cachés cuando Se modifica la cacheline exclusiva, ...).

Si bien esto es suficiente para mantener la coherencia (eventual), no garantiza el orden, o que los cambios son visibles de manera inmediata para otras CPU. Luego, también hay buffers de escritura, que retrasan las escrituras.

Por lo tanto, cada arquitectura de CPU proporciona garantías de pedido (por ejemplo, accesos antes de que un almacén alineado no pueda ser reordenado después de la tienda) y / o proporcione instrucciones (barreras de memoria / cercas) para solicitar dichas garantías. Al final, entrar / salir de un monitor no implica vaciar la memoria caché, pero puede implicar agotar el búfer de escritura y / o detener la espera de que finalicen las lecturas.


Las arquitecturas existentes garantizan la coherencia de la memoria caché, pero no garantizan la coherencia secuencial, las dos cosas son diferentes. Desde seq. la consistencia no está garantizada, el hardware permite algunas reordenaciones y necesita secciones críticas para limitarlas. Las secciones críticas aseguran que lo que se escribe en un subproceso sea visible para otro (es decir, impiden las carreras de datos ), y también impiden las condiciones de carrera clásicas (si dos subprocesos incrementan la misma variable, es necesario que para cada hilo la lectura del actual valor y la escritura del nuevo valor son indivisibles).

Además, el modelo de ejecución no es tan caro como lo describe. En la mayoría de las arquitecturas existentes, que son coherentes con la memoria caché pero no secuencialmente coherentes, cuando libera un bloqueo debe vaciar las escrituras pendientes en la memoria, y cuando adquiera una, deberá hacer algo para asegurarse de que las lecturas futuras no leerán los valores obsoletos. en su mayoría, eso significa simplemente evitar que las lecturas se muevan demasiado pronto, ya que el caché se mantiene coherente; pero las lecturas todavía no se deben mover.

Finalmente, parece que piensa que el modelo de memoria de Java (JMM) es peculiar, mientras que los cimientos son hoy en día bastante modernos y similares a Ada, los bloqueos POSIX (según la interpretación de la norma) y el C / C ++ modelo de memoria. Es posible que desee leer el libro de cocina JSR-133 que explica cómo se implementa JMM en las arquitecturas existentes: http://g.oswego.edu/dl/jmm/cookbook.html .


los cachés a los que JVM tiene acceso son realmente solo registros de CPU. ya que no hay muchos de ellos, lavarlos al salir del monitor no es un gran problema.

EDITAR: (en general) los cachés de memoria no están bajo el control de JVM, JVM no puede elegir leer / escribir / vaciar estos cachés, así que olvídese de ellos en esta discusión

Imagina que cada CPU tiene 1.000.000 registros. JVM los explota felizmente para hacer cálculos rápidos y locos, hasta que se topa con la entrada / salida del monitor, y tiene que vaciar 1,000,000 registros a la siguiente capa de caché.

Si vivimos en ese mundo, Java debe ser lo suficientemente inteligente como para analizar qué objetos no se comparten (la mayoría de los objetos no lo son), o debe pedirles a los programadores que lo hagan.

El modelo de memoria java es un modelo de programación simplificado que permite a los programadores promedio hacer algoritmos de multihilo OK. por "simplificado" quiero decir que podría haber 12 personas en todo el mundo que realmente leyeron el capítulo 17 de JLS y realmente lo entendieron.