lunes, diciembre 03, 2007

10 formas de reducir la contención por bloqueos

lunes, diciembre 03, 2007 por Martín

En Thinking Paralell, Michael Suess publica un excelente artículo en el que comenta 10 formas para reducir la contención por bloqueos en programas concurrentes.

Me permito castellanizarlos aquí:

  1. Proteger sólo los datos y no todo el código.

  2. Evitar cálculos complejos y largos dentro de locks.

  3. Evitar el utilizar un único lock para todos los elementos.

  4. Utilizar las operaciones atómicas disponibles en numerosos lenguajes.

  5. Utilizar estructuras de datos ya sincronizadas (y si es posible lock-free).

  6. Utilizar read-write locks cuando sea posible. Muchos lenguajes disponen de este tipo de bloqueos.

  7. Utilizar datos de sólo lectura siempre que sea posible.

  8. Evitar pools de objetos.

  9. Utilizar variables locales o aprovechar el almacenamiento local en hilos.

  10. Evitar puntos calientes (e.g. variables compartidas que se llaman mucho).

  11. Bonus: Utilizar un sistema de memoria transaccional.



La programación concurrente es compleja, de eso no hay duda. Algunos dirían que hasta da miedo :-) Pero bueno, todavía daba mucho más miedo hace años cuando en lenguajes como Java variables como volatile funcionaban como querían. Lo peor de la concurrencia en Java es que simplemente empezó a ser decente demasiado tarde, con el paquete java.util.concurrent de Java 5. ¿Por qué es tarde? Pues porque ahora mismo coges a muchos programadores y les preguntas el significado de las palabras synchronized, volatile o qué es un ThreadLocal y muchos no lo van a saber, pues cual puede ser el resultado de preguntar que es un ReadWriteLock o un AtomicInteger.

¿Culpa de los desarrolladores? No creo. Si el 90% de los libros sobre programación básica en Java no hablan de estas cosas y se quedan en Thread.start() y Thread.run() difícilmente vas a concienciar de la dificultad de la programación concurrente. (Hago aquí un inciso porque yo fuí de los de programación concurrente en C, pero supongo que ahora mismo se estará dando en Java así que quizás la situación no esté tan mal en este lenguaje).

De todos modos, yo me quedo con lo que comenta Brian Goetz, autor del excelente libro Java Concurrenty in Practice en los comentarios a la entrada, y es que estas medidas deberían aplicarse con mucho cuidado y únicamente cuando se compruebe que realmente existe un cuello de botella debido al uso indebido de bloqueos. El intentar aplicar estas reglas de buenas a primeras sería adentrarse dentro del mundo de la optimización prematura (que es siempre algo malo, pero que hay que diferenciar del diseño en busca de rendimiento como comentaba hace unos días).

Me iba a enrollar ahora comentando algunas de las entradas, pero es que tampoco creo que valga mucho la pena. El que se lea el libro de Brian Goetz, es que ya va a saber todo lo que necesita saber. Y aunque el libro está basado en Java, creo que es una buena lectura en general independientemente del lenguaje. Eso sí, mejor os dejo con una captura de pantalla que hice hace unos meses y de la que iba a hablar en un post a ver que os parece:



¿Qué representa? Pues es nada menos que un servidor WebLogic que de repente se va a un 100% de uso de la CPU como quien no quiere la cosa. ¿Qué malvado código puede crear ese efecto? ¿Quizás un EJB que está procesando todos nuestros proveedores? ¿Un malvado timer que ha saltado y está lanzando un proceso batch de esos qeu todos tememos? mmm Pues no. Un simple put() en un HashMap puede hacer eso, especialmente si tienes la mala suerte de que el HashMap esté compartido (ejem, malo malo) y dos threads se les ocurra hacer el put() al mismo tiempo (efecto conocido en HashMap), ya que en ese caso es muy probable que entren en un bucle infinito intentando insertar el elemento, que es justo lo que podéis ver en la imagen y que era exactamente lo que se estaba comiendo toda la CPU de un pedazo servidor.

Imaginaros que esto pase en producción. En fin, en este caso la solución es utilizar en su lugar un ConcurrentHashMap que garantiza un cierto grado de concurrencia (o también no compartir el HashMap :-) ), pero supongo que os dará una idea de lo peligrosas que pueden ser cosas que en un principio pueden parecer realmente inocentes.

Bueno, mientras tanto, yo todavía estoy con ganas de leer algo que leí en en el blog de Ferrán hace unos meses y que me dio mucha envidia.

comments

0 Respuestas a "10 formas de reducir la contención por bloqueos"