Ich habe eine Sammlung von Rechenmodellen, die als asynchrone zellulare Automaten beschrieben werden könnten. Diese Modelle ähneln dem Ising-Modell, sind jedoch etwas komplizierter. Es scheint, als würden solche Modelle davon profitieren, auf einer GPU anstatt auf einer CPU betrieben zu werden. Leider ist es nicht ganz einfach, ein solches Modell zu parallelisieren, und mir ist überhaupt nicht klar, wie ich damit umgehen soll. Mir ist bewusst, dass es Literatur zu diesem Thema gibt, aber alles scheint sich an Hardcore-Informatiker zu richten, die sich für Details der algorithmischen Komplexität interessieren, und nicht an jemanden wie mich, der nur eine Beschreibung von etwas möchte, das ich implementieren kann, und folglich finde ich es eher undurchdringlich.
Der Klarheit halber suche ich nicht so sehr nach einem optimalen Algorithmus, als nach etwas, das ich schnell in CUDA implementieren kann und das wahrscheinlich zu einer deutlichen Beschleunigung meiner CPU-Implementierung führt. Die Programmierzeit ist in diesem Projekt ein wesentlich begrenzenderer Faktor als die Computerzeit.
Ich sollte auch klarstellen, dass ein asynchroner zellularer Automat etwas anderes ist als ein synchroner, und dass Techniken zur Parallelisierung synchroner CAs (wie Conways Leben) nicht einfach an dieses Problem angepasst werden können. Der Unterschied besteht darin, dass eine synchrone Zertifizierungsstelle jede Zelle zu jedem Zeitschritt gleichzeitig aktualisiert, während eine asynchrone Zertifizierungsstelle zu jedem Zeitschritt eine zufällig ausgewählte lokale Region aktualisiert, wie unten dargestellt.
Die Modelle, die ich parallelisieren möchte, sind auf einem Gitter (normalerweise einem hexagonalen) implementiert, das aus ~ 100000 Zellen besteht (obwohl ich gerne mehr verwenden würde), und der nicht parallelisierte Algorithmus für deren Ausführung sieht folgendermaßen aus:
Wählen Sie ein benachbartes Zellenpaar nach dem Zufallsprinzip
Berechnen Sie eine "Energie" -Funktion basierend auf einer diese Zellen umgebenden lokalen Nachbarschaft
Vertauschen Sie mit einer Wahrscheinlichkeit, die von abhängt (mit a parameter), entweder die Zustände der beiden Zellen oder tun Sie nichts.
Wiederholen Sie die obigen Schritte auf unbestimmte Zeit.
Es gibt auch einige Komplikationen im Zusammenhang mit den Randbedingungen, aber ich kann mir vorstellen, dass diese für die Parallelisierung keine großen Schwierigkeiten bereiten.
Es ist erwähnenswert, dass ich mich eher für die transiente Dynamik dieser Systeme als nur für den Gleichgewichtszustand interessiere. Deshalb brauche ich etwas, das der obigen Dynamik entspricht, und nicht nur etwas, das sich der gleichen Gleichgewichtsverteilung annähert. (Daher sind Variationen des Schachbrett-Algorithmus nicht das, wonach ich suche.)
Die Hauptschwierigkeit bei der Parallelisierung des obigen Algorithmus sind Kollisionen. Da alle Berechnungen nur von einer lokalen Region des Gitters abhängen, können viele Gitterstandorte parallel aktualisiert werden, sofern sich ihre Nachbarschaften nicht überlappen. Die Frage ist, wie solche Überschneidungen vermieden werden können. Ich kann mir verschiedene Möglichkeiten vorstellen, aber ich weiß nicht, welche, wenn überhaupt, die beste ist, die ich implementieren kann. Dies sind wie folgt:
Verwenden Sie die CPU, um eine Liste von zufälligen Gitterstandorten zu erstellen und auf Kollisionen zu prüfen. Wenn die Anzahl der Grid-Standorte der Anzahl der GPU-Prozessoren entspricht oder wenn eine Kollision festgestellt wird, senden Sie jeden Koordinatensatz an eine GPU-Einheit, um den entsprechenden Grid-Standort zu aktualisieren. Dies wäre einfach zu implementieren, würde aber wahrscheinlich keine große Beschleunigung bringen, da das Überprüfen auf Kollisionen auf der CPU wahrscheinlich nicht viel billiger wäre, als das gesamte Update auf der CPU durchzuführen.
Teilen Sie das Gitter in Regionen auf (eine pro GPU-Einheit) und lassen Sie eine GPU-Einheit für die zufällige Auswahl und Aktualisierung von Gitterzellen in ihrer Region verantwortlich sein. Aber es gibt viele Probleme mit dieser Idee, die ich nicht lösen kann. Das offensichtlichste ist, was genau passieren sollte, wenn eine Einheit eine Nachbarschaft auswählt, die den Rand ihrer Region überlappt.
Annähern Sie das System wie folgt: Lassen Sie die Zeit in diskreten Schritten ablaufen. Teilen Sie das Gitter in ein anderes aufSatz von Regionen in jedem Zeitschritt gemäß einem vordefinierten Schema, und jede GPU-Einheit wählt zufällig ein Paar von Gitterzellen aus und aktualisiert sie, deren Nachbarschaft die Grenzen der Region nicht überlappt. Da sich die Grenzen bei jedem Schritt ändern, wirkt sich diese Einschränkung möglicherweise nicht zu stark auf die Dynamik aus, solange die Bereiche relativ groß sind. Dies scheint einfach zu implementieren und wahrscheinlich schnell zu sein, aber ich weiß nicht, wie gut es die Dynamik annähert, oder was das beste Schema für die Auswahl der Regionsgrenzen bei jedem Zeitschritt ist. Ich habe einige Verweise auf "block-synchrone zellulare Automaten" gefunden, die möglicherweise mit dieser Idee identisch sind oder nicht. (Ich weiß es nicht, weil es den Anschein hat, dass alle Beschreibungen der Methode entweder in Russisch sind oder sich in Quellen befinden, auf die ich keinen Zugriff habe.)
Meine spezifischen Fragen lauten wie folgt:
Sind die oben genannten Algorithmen eine sinnvolle Methode, um die GPU-Parallelisierung eines asynchronen CA-Modells zu erreichen?
Gibt es einen besseren Weg?
Gibt es einen Bibliothekscode für diese Art von Problem?
Wo finde ich eine eindeutige Beschreibung der block-synchronen Methode in englischer Sprache?
Fortschritt
Ich glaube, ich habe eine Möglichkeit gefunden, eine möglicherweise geeignete asynchrone Zertifizierungsstelle zu parallelisieren. Der im Folgenden beschriebene Algorithmus gilt für eine normale asynchrone Zertifizierungsstelle, die jeweils nur eine Zelle aktualisiert, und nicht für ein benachbartes Zellenpaar wie meins. Es gibt einige Probleme bei der Verallgemeinerung auf meinen speziellen Fall, aber ich denke, ich habe eine Idee, wie ich sie lösen kann. Ich bin mir jedoch nicht sicher, wie viel Geschwindigkeitsvorteil sich daraus ergibt, und zwar aus den nachfolgend erläuterten Gründen.
Die Idee ist, die asynchrone Zertifizierungsstelle (fortan ACA) durch eine stochastische synchrone Zertifizierungsstelle (SCA) zu ersetzen, die sich gleichwertig verhält. Dazu stellen wir uns zunächst vor, dass der ACA ein Poisson-Prozess ist. Das heißt, die Zeit läuft kontinuierlich ab, und jede Zelle hat eine konstante Wahrscheinlichkeit pro Zeiteinheit, ihre Aktualisierungsfunktion unabhängig von den anderen Zellen auszuführen.
ist ein Parameter, dessen Wert beliebig gewählt werden kann.)
Bei jedem logischen Zeitschritt werden die Zellen der SCA wie folgt aktualisiert:
Ich glaube, dies garantiert, dass die Zellen in einer Reihenfolge aktualisiert werden, die "decodiert" werden kann, um der ursprünglichen ACA zu entsprechen, während Kollisionen vermieden werden und ermöglicht wird, dass einige Zellen parallel aktualisiert werden. Aufgrund des ersten Aufzählungspunkts oben bedeutet dies jedoch, dass die meisten GPU-Prozessoren in jedem Zeitschritt des SCA größtenteils im Leerlauf sind, was weniger als ideal ist.
Ich muss etwas mehr darüber nachdenken, ob die Leistung dieses Algorithmus verbessert werden kann und wie dieser Algorithmus erweitert werden kann, um mit dem Fall umzugehen, dass mehrere Zellen gleichzeitig im ACA aktualisiert werden. Da es jedoch vielversprechend aussieht, dachte ich, ich würde es hier beschreiben, falls jemand (a) etwas Ähnliches in der Literatur kennt oder (b) einen Einblick in diese verbleibenden Probleme geben kann.
exp()
), daher hätte ich nicht gedacht, dass es viel Sinn macht, es auf mehrere Threads zu verteilen. Ich denke, es ist besser (und für mich einfacher), mehrere Paare parallel zu aktualisieren, mit einem Paar pro Thread.