Eu tenho uma coleção de modelos computacionais que podem ser descritos como autômatos celulares assíncronos. Esses modelos se assemelham ao modelo de Ising, mas são um pouco mais complicados. Parece que esses modelos se beneficiariam de serem executados em uma GPU, e não em uma CPU. Infelizmente, não é muito simples paralelizar esse modelo, e não está claro para mim como fazê-lo. Estou ciente de que há literatura sobre o assunto, mas tudo parece ser direcionado a cientistas da computação que estão interessados nos detalhes da complexidade algorítmica, em vez de alguém como eu, que quer apenas uma descrição de algo que eu possa implementar, e consequentemente, acho bastante inpenetrável.
Para maior clareza, não estou procurando um algoritmo ideal, mas algo que eu possa implementar rapidamente no CUDA que provavelmente dará uma aceleração significativa na implementação da minha CPU. O tempo do programador é muito mais um fator limitante do que o tempo do computador neste projeto.
Devo também esclarecer que um autômato celular assíncrono é algo bem diferente de um síncrono, e as técnicas para paralelizar CAs síncronas (como a vida de Conway) não podem ser facilmente adaptadas a esse problema. A diferença é que uma CA síncrona atualiza todas as células simultaneamente a cada etapa, enquanto uma assíncrona atualiza uma região local escolhida aleatoriamente a cada etapa, conforme descrito abaixo.
Os modelos que eu desejo paralelizar são implementados em uma rede (geralmente hexagonal) que consiste em ~ 100000 células (embora eu gostaria de usar mais), e o algoritmo não paralelo para executá-las se parece com o seguinte:
Escolha um par de células vizinho aleatoriamente
Calcular uma função "energia" base em uma vizinhança local ao redor dessas células
Com uma probabilidade que depende de (com a parâmetro), troque os estados das duas células ou não faça nada. β
Repita as etapas acima indefinidamente.
Também existem algumas complicações relacionadas às condições de contorno, mas imagino que elas não representem muita dificuldade para a paralelização.
Vale ressaltar que estou interessado na dinâmica transitória desses sistemas, e não apenas no estado de equilíbrio, por isso preciso de algo que tenha uma dinâmica equivalente à acima, em vez de apenas algo que se aproxime da mesma distribuição de equilíbrio. (Portanto, variações do algoritmo do tabuleiro de damas não são o que estou procurando.)
A principal dificuldade em paralelizar o algoritmo acima é a colisão. Como todos os cálculos dependem apenas de uma região local da rede, é possível que muitos sites da rede sejam atualizados em paralelo, desde que suas vizinhanças não estejam sobrepostas. A questão é como evitar essas sobreposições. Posso pensar em várias maneiras, mas não sei qual é a melhor para implementar. Estes são os seguintes:
Use a CPU para gerar uma lista de sites de grade aleatórios e verificar colisões. Quando o número de sites da grade for igual ao número de processadores GPU, ou se uma colisão for detectada, envie cada conjunto de coordenadas para uma unidade GPU para atualizar o site da grade correspondente. Isso seria fácil de implementar, mas provavelmente não daria muita velocidade, pois verificar colisões na CPU provavelmente não seria muito mais barato do que fazer toda a atualização na CPU.
Divida a estrutura em regiões (uma por unidade de GPU) e tenha uma unidade de GPU responsável por selecionar e atualizar aleatoriamente as células da grade em sua região. Mas há muitos problemas com essa ideia que não sei resolver, sendo o mais óbvio o que exatamente deveria acontecer quando uma unidade escolhe um bairro que se sobrepõe à extremidade de sua região.
Aproxime o sistema da seguinte maneira: deixe o tempo prosseguir em etapas discretas. Divida a estrutura em um diferenteconjunto de regiões em todas as etapas, de acordo com algum esquema predefinido, e cada unidade da GPU seleciona e atualiza aleatoriamente um par de células da grade cuja vizinhança não se sobrepõe ao limite da região. Como os limites mudam a cada passo, essa restrição pode não afetar muito a dinâmica, desde que as regiões sejam relativamente grandes. Parece fácil de implementar e provavelmente rápido, mas não sei até que ponto isso aproximará a dinâmica ou qual é o melhor esquema para escolher os limites da região a cada etapa do tempo. Encontrei algumas referências a "autômatos celulares síncronos em bloco", que podem ou não ser iguais a essa idéia. (Não sei porque parece que todas as descrições do método estão em russo ou em fontes às quais não tenho acesso.)
Minhas perguntas específicas são as seguintes:
Algum dos algoritmos acima é uma maneira sensata de abordar a paralelização por GPU de um modelo de CA assíncrono?
Existe uma maneira melhor?
Existe código de biblioteca existente para esse tipo de problema?
Onde posso encontrar uma descrição clara em inglês do método "síncrono em bloco"?
Progresso
Acredito que criei uma maneira de paralelizar uma autoridade de certificação assíncrona que possa ser adequada. O algoritmo descrito abaixo é para uma CA assíncrona normal que atualiza apenas uma célula por vez, em vez de um par de células vizinho como o meu. Existem alguns problemas com a generalização no meu caso específico, mas acho que tenho uma idéia de como resolvê-los. No entanto, não tenho certeza de quanto benefício de velocidade ele oferecerá, pelos motivos discutidos abaixo.
A idéia é substituir a CA assíncrona (doravante ACA) por uma CA síncrona estocástica (SCA) que se comporte de maneira equivalente. Para fazer isso, primeiro imaginamos que o ACA é um processo de Poisson. Ou seja, o tempo continua continuamente e cada célula tem uma probabilidade constante por unidade de tempo de executar sua função de atualização, independentemente das outras células.
Construímos um SCA cujas células armazenam duas coisas: o estado da célula (ou seja, os dados que seriam armazenados em cada célula na implementação seqüencial) e um número de ponto flutuante representando o (contínuo ) hora em que será atualizada na próxima. Esse tempo contínuo não corresponde às etapas de atualização do SCA. Vou me referir a este último como "tempo lógico". Os valores de tempo são inicializados aleatoriamente de acordo com uma distribuição exponencial: . (Onde é um parâmetro cujo valor pode ser escolhido arbitrariamente.)
Em cada etapa do tempo lógico, as células do SCA são atualizadas da seguinte maneira:
Se, para qualquer na vizinhança de , o tempo , não faça nada.
Acredito que isso garanta que as células sejam atualizadas em uma ordem que possa ser "decodificada" para corresponder à ACA original, evitando colisões e permitindo que algumas células sejam atualizadas em paralelo. No entanto, devido ao primeiro ponto acima, isso significa que a maioria dos processadores GPU ficará ociosa a cada etapa do SCA, o que é menos do que o ideal.
Eu preciso pensar um pouco mais sobre se o desempenho desse algoritmo pode ser aprimorado e como estender esse algoritmo para lidar com o caso em que várias células são atualizadas simultaneamente no ACA. No entanto, parece promissor, então pensei em descrevê-lo aqui, caso alguém (a) saiba de algo semelhante na literatura ou (b) possa oferecer qualquer insight sobre esses problemas restantes.
exp()
), então eu não pensaria que faz muito sentido espalhá-lo por vários segmentos. Eu acho que é melhor (e mais fácil para mim) tentar atualizar vários pares em paralelo, com um par por thread.