J'ai une collection de modèles informatiques qui pourraient être décrits comme des automates cellulaires asynchrones. Ces modèles ressemblent au modèle Ising, mais sont légèrement plus compliqués. Il semble que de tels modèles gagneraient à être exécutés sur un GPU plutôt que sur un CPU. Malheureusement, il n'est pas très simple de paralléliser un tel modèle, et je ne sais pas du tout comment procéder. Je suis conscient qu'il existe de la littérature sur le sujet, mais tout semble viser des informaticiens inconditionnels qui s'intéressent aux détails de la complexité algorithmique, plutôt que quelqu'un comme moi qui veut juste une description de quelque chose que je peux implémenter, et par conséquent, je le trouve plutôt intangible.
Pour plus de clarté, je ne recherche pas un algorithme optimal autant que quelque chose que je peux rapidement implémenter dans CUDA qui est susceptible de donner une accélération significative sur mon implémentation CPU. Le temps programmeur est beaucoup plus un facteur limitant que le temps ordinateur dans ce projet.
Je dois également préciser qu'un automate cellulaire asynchrone est quelque chose d'assez différent d'un automate synchrone, et les techniques de parallélisation des AC synchrones (comme la vie de Conway) ne peuvent pas être facilement adaptées à ce problème. La différence est qu'une autorité de certification synchrone met à jour chaque cellule simultanément à chaque pas de temps, tandis qu'une asynchrone met à jour une région locale choisie au hasard à chaque pas de temps, comme indiqué ci-dessous.
Les modèles que je souhaite paralléliser sont implémentés sur un réseau (généralement hexagonal) composé de ~ 100000 cellules (bien que j'aimerais en utiliser plus), et l'algorithme non parallélisé pour les exécuter ressemble à ceci:
Choisissez une paire de cellules voisine au hasard
Calculer une fonction "énergie" basée sur un voisinage local entourant ces cellules
Avec une probabilité qui dépend de (avec β un paramètre), permutez les états des deux cellules ou ne faites rien.
Répétez les étapes ci-dessus indéfiniment.
Il y a aussi quelques complications liées aux conditions aux limites, mais j'imagine qu'elles ne poseront pas beaucoup de difficultés pour la parallélisation.
Il convient de mentionner que je m'intéresse à la dynamique transitoire de ces systèmes plutôt qu'à simplement l'état d'équilibre, j'ai donc besoin de quelque chose qui a une dynamique équivalente à ce qui précède, plutôt que simplement quelque chose qui s'approchera de la même distribution d'équilibre. (Les variations de l'algorithme du damier ne sont donc pas ce que je recherche.)
La principale difficulté pour paralléliser l'algorithme ci-dessus est les collisions. Étant donné que tous les calculs dépendent uniquement d'une région locale du réseau, il est possible que de nombreux sites de réseau soient mis à jour en parallèle, tant que leurs quartiers ne se chevauchent pas. La question est de savoir comment éviter de tels chevauchements. Je peux penser à plusieurs façons, mais je ne sais pas laquelle, s'il en est, est la meilleure à mettre en œuvre. Ce sont les suivants:
Utilisez le processeur pour générer une liste de sites de grille aléatoires et vérifier les collisions. Lorsque le nombre de sites de grille est égal au nombre de processeurs GPU, ou si une collision est détectée, envoyez chaque ensemble de coordonnées à une unité GPU pour mettre à jour le site de grille correspondant. Cela serait facile à implémenter mais ne donnerait probablement pas beaucoup d'accélération, car la vérification des collisions sur le CPU ne serait probablement pas beaucoup moins chère que de faire toute la mise à jour sur le CPU.
Divisez le réseau en régions (une par unité GPU) et demandez à une unité GPU de sélectionner et de mettre à jour au hasard les cellules de la grille dans sa région. Mais il y a beaucoup de problèmes avec cette idée que je ne sais pas comment résoudre, le plus évident étant ce qui devrait arriver exactement quand une unité choisit un quartier chevauchant le bord de sa région.
Approximer le système comme suit: laisser le temps se dérouler par étapes discrètes. Divisez le réseau en un autreensemble de régions à chaque pas de temps selon un schéma prédéfini, et que chaque unité GPU sélectionne et met à jour au hasard une paire de cellules de grille dont le voisinage ne chevauche pas la frontière de la région. Comme les limites changent à chaque pas de temps, cette contrainte peut ne pas trop affecter la dynamique, tant que les régions sont relativement grandes. Cela semble facile à mettre en œuvre et susceptible d'être rapide, mais je ne sais pas dans quelle mesure il rapprochera la dynamique ou quel est le meilleur schéma pour choisir les limites de la région à chaque pas de temps. J'ai trouvé quelques références aux "automates cellulaires synchrones par blocs", qui peuvent ou non être les mêmes que cette idée. (Je ne sais pas car il semble que toutes les descriptions de la méthode soient en russe ou sont dans des sources auxquelles je n'ai pas accès.)
Mes questions spécifiques sont les suivantes:
L'un des algorithmes ci-dessus est-il un moyen judicieux d'approcher la parallélisation GPU d'un modèle CA asynchrone?
Y a-t-il une meilleure façon?
Existe-t-il un code de bibliothèque pour ce type de problème?
Où puis-je trouver une description claire en anglais de la méthode "block-synchronous"?
Le progrès
Je pense avoir trouvé un moyen de paralléliser une autorité de certification asynchrone qui pourrait convenir. L'algorithme décrit ci-dessous est pour une autorité de certification asynchrone normale qui met à jour une seule cellule à la fois, plutôt qu'une paire de cellules voisine comme la mienne. Il y a quelques problèmes à généraliser à mon cas spécifique, mais je pense avoir une idée de la façon de les résoudre. Cependant, je ne sais pas quel avantage de vitesse cela donnera, pour les raisons décrites ci-dessous.
L'idée est de remplacer l'AC asynchrone (désormais ACA) par une AC synchrone stochastique (SCA) qui se comporte de manière équivalente. Pour ce faire, nous imaginons d'abord que l'ACA est un processus de Poisson. Autrement dit, le temps se déroule en continu, et chaque cellule comme une probabilité constante par unité de temps d'exécuter sa fonction de mise à jour, indépendamment des autres cellules.
est un paramètre dont la valeur peut être choisie arbitrairement.)
À chaque pas de temps logique, les cellules du SCA sont mises à jour comme suit:
Je crois que cela garantit que les cellules seront mises à jour dans un ordre qui peut être "décodé" pour correspondre à l'ACA d'origine, tout en évitant les collisions et en permettant à certaines cellules d'être mises à jour en parallèle. Cependant, en raison du premier point ci-dessus, cela signifie que la plupart des processeurs GPU seront principalement inactifs à chaque pas de temps du SCA, ce qui est loin d'être idéal.
Je dois réfléchir un peu plus à la question de savoir si les performances de cet algorithme peuvent être améliorées et comment étendre cet algorithme pour faire face au cas où plusieurs cellules sont mises à jour simultanément dans l'ACA. Cependant, cela semble prometteur, j'ai donc pensé le décrire ici au cas où quelqu'un (a) connaîtrait quelque chose de similaire dans la littérature, ou (b) pourrait offrir un aperçu de ces problèmes restants.
exp()
), donc je n'aurais pas pensé qu'il était très logique de l'étaler sur plusieurs threads. Je pense qu'il est préférable (et plus facile pour moi) d'essayer de mettre à jour plusieurs paires en parallèle, avec une paire par thread.