sincronizacion - productor consumidor java
Paralelización: ¿Qué causa que los hilos de Java bloqueen aparte de la sincronización y E/S? (7)
La versión corta está en el título.
Versión larga: estoy trabajando en un programa de optimización científica utilizando Java. La carga de trabajo del programa se puede dividir en fases paralelas y en serie: fases paralelas, lo que significa que se está realizando un trabajo altamente paralelizable. Para acelerar el programa (se ejecuta durante horas / días), creo una cantidad de subprocesos igual al número de núcleos de CPU en la máquina que estoy utilizando, generalmente 4 u 8, y divido el trabajo entre ellos. Luego, comienzo estos hilos y los uno (a) antes de pasar a una fase serial.
Hasta aquí todo bien. Lo que me molesta es que la utilización de la CPU y la aceleración de las fases paralelas no están cerca del "máximo teórico"; por ejemplo, si tengo 4 núcleos, espero ver entre el 350 y el 400% de "utilización" (según lo informado arriba) pero en su lugar rebota alrededor de entre 180 y aproximadamente 310. Al usar solo un hilo, obtengo el 100% de la utilización de la CPU.
Las únicas razones que conozco para que los subprocesos no se ejecuten a toda velocidad son: -bloqueo debido a bloqueos de E / S debido a la sincronización
No hay E / S que esté sucediendo en mis hilos paralelos, ni ninguna sincronización: las únicas estructuras de datos compartidas por los hilos son de solo lectura, y son tipos básicos o colecciones (no concurrentes). Entonces estoy buscando otras explicaciones. Una posibilidad sería que varios hilos bloqueen repetidamente la recolección de basura, pero eso solo parece tener sentido en una situación con presión de memoria, y estoy asignando muy por encima del espacio de montón máximo requerido.
Cualquier sugerencia sera apreciada.
Actualización: por si acaso alguien tiene curiosidad, después de investigar un poco más modifiqué el código para el rendimiento general y estoy viendo una mejor utilización, aunque nada de lo que he cambiado tiene que ver con la sincronización. Sin embargo, algunos de los cambios deberían haber dado lugar a un menor número de asignaciones de heap en particular. Me libré del uso de iteradores y números de recuadro temporales (la biblioteca "Colt" de CERN para la informática Java de alto rendimiento fue útil aquí: proporciona colecciones como IntArrayList , DoubleArrayList, etc. para tipos básicos.). Entonces creo que la recolección de basura probablemente fue la culpable.
En primer lugar, GC no sucederá solo "en situación de presión de la memoria", sino que en cualquier momento la JVM lo considera (impredecible, hasta donde yo sé).
En segundo lugar, si tus subprocesos asignan memoria en el montón (mencionas que usan colecciones así que supongo que asignan memoria en el montón), nunca puedes estar seguro de si esta memoria está actualmente en RAM o en una página de memoria virtual (el SO decide ), y por lo tanto, el acceso a la "memoria" puede generar un bloqueo de acceso de E / S.
Finalmente, como se sugirió en una respuesta anterior, puede que le resulte útil verificar lo que sucede al usar un generador de perfiles (o incluso la monitorización JMX puede proporcionar algunos consejos).
Creo que será difícil obtener más pistas sobre su problema a menos que proporcione información más concreta (código).
En primer lugar, supongo que no estás haciendo ningún otro trabajo importante en la caja. Si lo eres, eso va a complicar las cosas.
Suena muy extraño si realmente no estás compartiendo nada. ¿Puede darnos más idea de lo que realmente está haciendo el código?
¿Qué ocurre si ejecuta n copias del programa como diferentes procesos de Java, cada uno de los cuales solo usa un solo hilo? Si eso utiliza cada CPU por completo, entonces al menos sabemos que no puede ser un problema con el sistema operativo. Hablando del sistema operativo, ¿en qué se está ejecutando y qué JVM? Si puede probar diferentes JVM y diferentes sistemas operativos, los resultados pueden darle una pista sobre lo que está mal.
Intenta usar la capacidad completa de la CPU para sus cálculos, pero el sistema operativo también usa recursos. Tenga en cuenta que el sistema operativo bloqueará parte de su ejecución para satisfacer sus necesidades.
También un punto importante: ¿qué hardware usas? Por ejemplo, 4-8 Cores podría significar que trabajas en una de las CPU Suns Niágara. Y a pesar de tener 4-8 núcleos, tienen menos FPU . Al computar cosas científicas podría suceder, que la FPU es el cuello de botella.
Todas las operaciones gráficas se ejecutan en un único hilo en swing. Si están renderizando en la pantalla, contendrán efectivamente por el acceso a este hilo.
Si está ejecutando en Windows, todas las operaciones de gráficos se ejecutan en un único hilo sin importar nada. Otros sistemas operativos tienen limitaciones similares.
En realidad, es bastante difícil obtener la granularidad adecuada de los trabajadores con hilos a veces, y en ocasiones es fácil hacerlos demasiado grandes o demasiado pequeños, lo que normalmente le dará menos del 100% de uso de todos los núcleos.
Si no está procesando mucho, el culpable más probable es que compita más de lo que piensa por algún recurso compartido. Esto se ve fácilmente con herramientas de perfilador como jprofiler. Algunas máquinas virtuales como la jrockit de Bea incluso pueden decirte esto directamente de la caja.
Este es uno de esos lugares donde no quieres actuar en conjeturas. Obtener un generador de perfiles!
Estás haciendo sincronización en algún nivel.
Tal vez solo en el sistema de asignación de memoria, incluida la recolección de basura. Si bien el proveedor de JVM ha trabajado para mantener el bloqueo en estas áreas al mínimo, no pueden reducirlo a cero. Quizás algo acerca de su aplicación está presionando en un punto débil en esta área.
La sabiduría aceptada es "no construyas tu propio conjunto de recuperación de memoria, deja que el GC funcione para ti". Esto es cierto la mayor parte del tiempo, pero no en al menos una parte del código que mantengo (probado con el perfil). Tal vez necesite volver a trabajar su asignación de Objetos de alguna manera importante.
Pruebe el analizador de latencia que viene con JRockit Mission Control. Le mostrará lo que está haciendo la CPU cuando no está haciendo nada, si la aplicación está esperando la E / S de archivos, recuperaciones TLA, asignaciones de objetos, suspensión de hilos, bloqueos JVM, pausas gc, etc. También puede ver transiciones , por ejemplo, cuando un hilo despierta a otro. La sobrecarga es insignificante, 1% o menos.
Mira este blog para más información. La herramienta es gratuita para el desarrollo y puedes descargarla aquí