Tengo una colección de modelos computacionales que podrían describirse como autómatas celulares asíncronos. Estos modelos se parecen al modelo de Ising, pero son un poco más complicados. Parece que tales modelos se beneficiarían de ejecutarse en una GPU en lugar de una CPU. Desafortunadamente, no es muy sencillo paralelizar un modelo así, y no tengo muy claro cómo hacerlo. Soy consciente de que hay literatura sobre el tema, pero todo parece estar dirigido a científicos informáticos expertos que están interesados en los detalles de la complejidad algorítmica, en lugar de alguien como yo que solo quiere una descripción de algo que pueda implementar, y en consecuencia lo encuentro bastante inpenetrable.
Para mayor claridad, no estoy buscando un algoritmo óptimo, sino algo que pueda implementar rápidamente en CUDA que probablemente dé una aceleración significativa sobre la implementación de mi CPU. El tiempo del programador es mucho más un factor limitante que el tiempo de la computadora en este proyecto.
También debería aclarar que un autómata celular asíncrono es algo bastante diferente de uno sincrónico, y las técnicas para paralelizar CA sincrónicas (como la vida de Conway) no pueden adaptarse fácilmente a este problema. La diferencia es que una CA síncrona actualiza cada celda simultáneamente en cada paso de tiempo, mientras que una asíncrona actualiza una región local elegida al azar en cada paso de tiempo como se describe a continuación.
Los modelos que deseo paralelizar se implementan en una red (generalmente hexagonal) que consta de ~ 100000 celdas (aunque me gustaría usar más), y el algoritmo no paralelizado para ejecutarlos se ve así:
Elige un par de celdas vecinas al azar
Calcule una función de "energía" basada en un vecindario local que rodea estas células
Con una probabilidad que depende de (con β un parámetro), intercambie los estados de las dos celdas o no haga nada.
Repita los pasos anteriores indefinidamente.
También hay algunas complicaciones que ver con las condiciones de contorno, pero imagino que no supondrán mucha dificultad para la paralelización.
Vale la pena mencionar que estoy interesado en la dinámica transitoria de estos sistemas en lugar de solo el estado de equilibrio, por lo que necesito algo que tenga una dinámica equivalente a la anterior, en lugar de algo que se acerque a la misma distribución de equilibrio. (Entonces, las variaciones del algoritmo de tablero de ajedrez no son lo que estoy buscando).
La principal dificultad para paralelizar el algoritmo anterior son las colisiones. Debido a que todos los cálculos dependen solo de una región local de la red, es posible que muchos sitios de red se actualicen en paralelo, siempre que sus vecindarios no se superpongan. La pregunta es cómo evitar tales superposiciones. Se me ocurren varias formas, pero no sé cuál es la mejor para implementarla. Estos son los siguientes:
Use la CPU para generar una lista de sitios de cuadrícula aleatorios y verificar las colisiones. Cuando el número de sitios de cuadrícula es igual al número de procesadores de GPU, o si se detecta una colisión, envíe cada conjunto de coordenadas a una unidad de GPU para actualizar el sitio de cuadrícula correspondiente. Esto sería fácil de implementar, pero probablemente no aceleraría mucho, ya que verificar las colisiones en la CPU probablemente no sería mucho más barato que hacer toda la actualización en la CPU.
Divida la red en regiones (una por unidad de GPU) y haga que una unidad de GPU se encargue de seleccionar y actualizar aleatoriamente las celdas de la cuadrícula dentro de su región. Pero hay muchos problemas con esta idea que no sé cómo resolver, el más obvio es qué debería suceder exactamente cuando una unidad elige un vecindario que se superpone al borde de su región.
Aproxime el sistema de la siguiente manera: deje que el tiempo avance en pasos discretos. Divide el enrejado en otroconjunto de regiones en cada paso de tiempo de acuerdo con un esquema predefinido, y haga que cada unidad de GPU seleccione y actualice aleatoriamente un par de celdas de cuadrícula cuyo vecindario no se superponga con el límite de la región. Como los límites cambian cada vez que pasa, esta restricción podría no afectar demasiado la dinámica, siempre que las regiones sean relativamente grandes. Esto parece fácil de implementar y es probable que sea rápido, pero no sé qué tan bien se aproximará a la dinámica, o cuál es el mejor esquema para elegir los límites de la región en cada paso de tiempo. Encontré algunas referencias a "autómatas celulares síncronos en bloque", que pueden o no ser lo mismo que esta idea. (No lo sé porque parece que todas las descripciones del método están en ruso o en fuentes a las que no tengo acceso).
Mis preguntas específicas son las siguientes:
¿Alguno de los algoritmos anteriores es una forma sensata de abordar la paralelización de GPU de un modelo de CA asíncrono?
¿Hay una mejor manera?
¿Existe un código de biblioteca existente para este tipo de problema?
¿Dónde puedo encontrar una descripción clara en inglés del método "bloque sincrónico"?
Progreso
Creo que se me ocurrió una forma de paralelizar una CA asincrónica que podría ser adecuada. El algoritmo que se describe a continuación es para una CA asíncrona normal que actualiza solo una celda a la vez, en lugar de un par de celdas vecinas como la mía. Hay algunos problemas para generalizarlo en mi caso específico, pero creo que tengo una idea de cómo resolverlos. Sin embargo, no estoy seguro de cuánto beneficio de velocidad dará, por las razones que se analizan a continuación.
La idea es reemplazar la CA asíncrona (en adelante ACA) con una CA síncrona estocástica (SCA) que se comporta de manera equivalente. Para hacer esto, primero imaginamos que el ACA es un proceso de Poisson. Es decir, el tiempo transcurre continuamente, y cada celda como una probabilidad constante por unidad de tiempo de realizar su función de actualización, independientemente de las otras celdas.
es un parámetro cuyo valor puede elegirse arbitrariamente).
En cada paso de tiempo lógico, las celdas del SCA se actualizan de la siguiente manera:
Creo que esto garantiza que las celdas se actualizarán en un orden que puede "decodificarse" para que corresponda con el ACA original, evitando colisiones y permitiendo que algunas celdas se actualicen en paralelo. Sin embargo, debido al primer punto anterior, significa que la mayoría de los procesadores de GPU estarán inactivos en cada paso de tiempo del SCA, que es menos que ideal.
Necesito pensar un poco más si se puede mejorar el rendimiento de este algoritmo y cómo extender este algoritmo para tratar el caso en el que varias celdas se actualizan simultáneamente en el ACA. Sin embargo, parece prometedor, así que pensé en describirlo aquí en caso de que alguien (a) conozca algo similar en la literatura, o (b) pueda ofrecer alguna idea sobre estos temas restantes.
exp()
), por lo que no habría pensado que tiene mucho sentido distribuirlo en múltiples hilos. Creo que es mejor (y más fácil para mí) intentar actualizar varios pares en paralelo, con un par por hilo.