Ho una collezione di modelli computazionali che potrebbero essere descritti come automi cellulari asincroni. Questi modelli assomigliano al modello Ising, ma sono leggermente più complicati. Sembra che tali modelli trarrebbero beneficio dall'esecuzione su una GPU anziché su una CPU. Sfortunatamente non è abbastanza semplice parallelizzare un modello del genere, e non mi è affatto chiaro come procedere. Sono consapevole che esiste della letteratura sull'argomento, ma sembra che tutto sia rivolto a informatici informatici interessati ai dettagli della complessità algoritmica, piuttosto che a qualcuno come me che vuole solo una descrizione di qualcosa che posso implementare, e di conseguenza lo trovo piuttosto impenetrabile.
Per chiarezza, non sto cercando un algoritmo ottimale tanto quanto qualcosa che posso implementare rapidamente in CUDA che probabilmente darà una notevole velocità sulla mia implementazione della CPU. Il tempo del programmatore è molto più un fattore limitante del tempo del computer in questo progetto.
Dovrei anche chiarire che un automa cellulare asincrono è una cosa piuttosto diversa da uno sincrono e che le tecniche per parallelizzare le CA sincrone (come la vita di Conway) non possono essere facilmente adattate a questo problema. La differenza è che una CA sincrona aggiorna ogni cella contemporaneamente in ogni fase, mentre una asincrona aggiorna una regione locale scelta casualmente in ogni fase come indicato di seguito.
I modelli che vorrei parallelizzare sono implementati su un reticolo (di solito uno esagonale) costituito da ~ 100000 celle (anche se mi piacerebbe usarne di più), e l'algoritmo non parallelizzato per eseguirle si presenta così:
Scegli una coppia di celle vicine a caso
Calcola una funzione "energetica" sulla base di un quartiere locale che circonda queste cellule
Con una probabilità che dipende da (con β un parametro), scambiare gli stati delle due celle o non fare nulla.
Ripeti i passaggi precedenti indefinitamente.
Ci sono anche alcune complicazioni a che fare con le condizioni al contorno, ma immagino che queste non costituiranno molte difficoltà per la parallelizzazione.
Vale la pena ricordare che sono interessato alle dinamiche transitorie di questi sistemi piuttosto che solo allo stato di equilibrio, quindi ho bisogno di qualcosa che abbia dinamiche equivalenti a quelle sopra, piuttosto che qualcosa che si avvicini alla stessa distribuzione di equilibrio. (Quindi le variazioni dell'algoritmo chequerboard non sono ciò che sto cercando.)
La principale difficoltà nel parallelizzare l'algoritmo sopra è le collisioni. Poiché tutti i calcoli dipendono solo da una regione locale del reticolo, è possibile che molti siti reticolari vengano aggiornati in parallelo, purché i loro quartieri non si sovrappongano. La domanda è come evitare tali sovrapposizioni. Posso pensare a diversi modi, ma non so quale sia il migliore da implementare. Questi sono i seguenti:
Utilizzare la CPU per generare un elenco di siti di griglia casuali e verificare la presenza di collisioni. Quando il numero di siti della griglia è uguale al numero di processori GPU o se viene rilevata una collisione, inviare ciascun set di coordinate a un'unità GPU per aggiornare il sito della griglia corrispondente. Questo sarebbe facile da implementare ma probabilmente non darebbe molta velocità, dal momento che il controllo delle collisioni sulla CPU probabilmente non sarebbe molto più economico rispetto all'intero aggiornamento sulla CPU.
Dividi il reticolo in regioni (una per unità GPU) e disponi di un'unità GPU responsabile della selezione casuale e dell'aggiornamento delle celle della griglia all'interno della sua regione. Ma ci sono molti problemi con questa idea che non so come risolvere, il più ovvio è esattamente cosa dovrebbe accadere quando un'unità sceglie un quartiere che si sovrappone al limite della sua regione.
Approssimare il sistema come segue: lasciare che il tempo proceda in passaggi discreti. Dividi il reticolo in un altroinsieme di regioni su ogni passaggio temporale secondo uno schema predefinito e ogni unità GPU seleziona e aggiorna casualmente una coppia di celle della griglia il cui vicinato non si sovrappone al confine della regione. Poiché i limiti cambiano ogni volta che questo passaggio può non influire troppo sulle dinamiche, purché le regioni siano relativamente grandi. Questo sembra facile da implementare e probabilmente sarà veloce, ma non so quanto si avvicinerà alla dinamica, o qual è lo schema migliore per scegliere i confini della regione in ogni fase temporale. Ho trovato alcuni riferimenti a "automi cellulari sincroni a blocchi", che possono essere o meno gli stessi di questa idea. (Non lo so perché sembra che tutte le descrizioni del metodo siano in russo o in fonti alle quali non ho accesso.)
Le mie domande specifiche sono le seguenti:
Qualcuno dei suddetti algoritmi è un modo sensato per avvicinarsi alla parallelizzazione GPU di un modello CA asincrono?
C'è un modo migliore?
Esiste un codice libreria esistente per questo tipo di problema?
Dove posso trovare una chiara descrizione in lingua inglese del metodo "block-sincrono"?
Progresso
Credo di aver escogitato un modo per parallelizzare una CA asincrona che potrebbe essere adatta. L'algoritmo descritto di seguito è per una normale CA asincrona che aggiorna solo una cella alla volta, anziché una coppia di celle vicine come la mia. Ci sono alcuni problemi con la generalizzazione nel mio caso specifico, ma penso di avere un'idea di come risolverli. Tuttavia, non sono sicuro di quanto possa essere utile in termini di velocità, per i motivi discussi di seguito.
L'idea è di sostituire la CA asincrona (d'ora in poi ACA) con una CA sincrona stocastica (SCA) che si comporta in modo equivalente. Per fare ciò, immaginiamo innanzitutto che l'ACA sia un processo di Poisson. Cioè, il tempo procede continuamente e ogni cella è una probabilità costante per unità di tempo di eseguire la sua funzione di aggiornamento, indipendentemente dalle altre celle.
è un parametro il cui valore può essere scelto arbitrariamente.)
Ad ogni fase temporale logica, le celle dell'SCA vengono aggiornate come segue:
Credo che ciò garantisca che le celle verranno aggiornate in un ordine che può essere "decodificato" per corrispondere all'ACA originale, evitando collisioni e consentendo l'aggiornamento di alcune celle in parallelo. Tuttavia, a causa del primo punto elenco sopra, ciò significa che la maggior parte dei processori GPU sarà in gran parte inattiva su ogni fase del SCA, che è tutt'altro che ideale.
Devo riflettere ancora sul fatto che le prestazioni di questo algoritmo possano essere migliorate e su come estenderlo per affrontare il caso in cui più celle vengono aggiornate contemporaneamente nell'ACA. Tuttavia, sembra promettente, quindi ho pensato di descriverlo qui nel caso in cui qualcuno (a) sia a conoscenza di qualcosa di simile in letteratura, o (b) possa offrire informazioni su questi problemi rimanenti.
exp()
), quindi non avrei pensato che avesse molto senso diffonderlo su più thread. Penso che sia meglio (e più facile per me) provare ad aggiornare più coppie in parallelo, con una coppia per thread.