Bonjour,
J'ai un algo assez simple de marcheur qui se promène sur une grille. La grille représente un relief terrestre. Le marcheur fait ceci :
On voit qu'il suffit d'initialiser la file avec une maille graine G pour que ce marcheur explore l'ensemble des mailles accessibles par un chemin strictement montant depuis G. Dans l'algo complet, j'initialise la pile avec toutes les mailles de la périphérie du maillage. Le résultat est que les mailles non visitées représentent l'intérieur des dépressions : il n'existe pas de chemin continuellement montant pour les atteindre depuis le bord du maillage.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6 tant que la file d'attente n'est pas vide { <div style="margin-left:40px">prendre x : la maille de tête dans la file si la maille x n'a jamais été visitée, alors { <div style="margin-left:40px">aller x marquer que x a été visitée pousser en queue de file toutes les mailles voisines de x qui sont non visitées et qui sont d'altitude supérieure à celle de x</div>}</div>}
Mon but est de mettre en concurrence plusieurs de ces marcheurs pour diminuer le temps d'exploration de la grille. D'ou une première question au forum : ce problème a-t'il déja été résolu ?
En attendant d'explorer ce qui se fait déjà, mon idée de départ est de constituer plusieurs files et de confier chacune à un thread séparé. D'où ma deuxième question au forum : Quelle stratégie adopter pour éviter à la fois une "race condition" et des bloquages entrecroisés ?
L'algo s'adresse à des grilles très grandes (jusqu'à des centaines de millions de mailles). Il ne faut pas que les besoins en mémoire des objets de synchronisation soient aussi gros que les données elle-même. je dois donc assembler les mailles en dalles, et synchroniser des dalles entières, pas des cellules (enfin, je crois...).
Pour l'instant, je vois deux stratégies :
1/ marcheurs indépendants
chaque marcheur à toute la liberté d'explorer l'ensemble de la grille. Avant de faire ses entrée-sortie, il locke les dalles qui contiennent les 9 points qu'il a besoin de consulter. (donc entre 1 et 4 dalles lockées). Si les dalles dont il a besoin dont déjà lockées par un autre marcheur, il renvoie la maille à traiter au fond de sa file d'attente, débloque le cas échéant les dalles qui avaient été réservées, et essaie de traiter la maille suivante.
=> avantage :
assez simple à concevoir
la fin du travail est simple à détecter : les threads meurent quand leur file est vide, don un simple 'join' dans le main suffit.
=> inconvénient :
perte d'efficacité dans la concurrence entre les threads pour accéder aux mêmes mailles
j'ai peur de générer des blocages croisés : deux threads ou plus ont besoin des deux mêmes dalles. Chacun en bloque une en espérant avoir l'autre, puis la relâche quand il voir qu'il ne l'aura pas. Si c'est la dernière maille dans sa file, il recommence aussitôt. Les concurrents peuvent jouer comme ça longtemps avant que l'un réussisse enfin à bloquer les deux dalles à la fois.
2/ marcheurs localisés
j'ai une file d'attente par dalle, et un thread associé à cette file d'attente.
chaque thread dépile ce qu'il y a dans sa file d'attente. par contre, les mailles à empiler sont empilées dans la file de la dalle à laquelle elles appartiennent.
=> avantage :
la perte d'efficacité ne se fait qu'aux frontières des dalles
=> inconvénient :
plus complexe à implémenter (un thread attaché à une dalle du centre peut attendre longtemps que sa file soit peuplée)
Les files elle-même deviennent des objets partagés qu'il faut synchroniser
la fin du travail est plus complexe à détecter : fin du travail quand toutes les files sont vident en même temps. Il faut détecter cela sans créer un golot d'étranglement au niveau synchronisation
Merci de partager vos idées. Le temps que je passerai sur ce fil grâce à vous sera gagné au centuple au moment de débugger l'engin![]()
Partager