Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

Récursion et gestion de mé moire

9 réponses
Avatar
Louis Balourdet
Bonjour,

J'ai entrepris d'écrire un programme de jeu d'Echecs. Je suis venu à bout,
non sans mal, des préliminaires imposés: définition des règles de
déplacement des pièces, interface graphique pour les déplacer,
enregistrement écrit des coups joués et déplacement dans la partie par des
boutons avant / arrière.

J'en suis maintenant au vif du sujet, à savoir la recherche du meilleur coup
au moyen de l'algorithme MinMax avec coupures alpha-beta.
La difficulté vient de ce que cet algorithme est récursif. Voici sa
structure.

"pos" décrit l'échiquier sur 84 octets." minDepth" est la profondeur en
demi-coups à laquelle on limite la recherche. "alpha" est une limite
inférieure du score et "beta" une limite supérieure. Au fil de itérations,
la fourchette alpha beta se rétrécit:

-(int) minMax:(LBPosition)pos minDepth:(int)minDepth alpha:(int)A
beta:(int)B
{
.......
str = [self setMovesString:pos]; // mouvements possibles: a1a2/b1b2/....
moves = [str componentsSeparatedByString:@"/"]; //NSArray coups autorisés
......
for (i=0; i<[moves count] ;i++)
{
......
moveStr = [moves objectAtIndex:i];
nextPos = [self doMove:moveStr atPos:pos]; //nouvel échiquier
alpha = A > alphaPos ? A : alphaPos; //alpha = Max(A, alphaPos)
/**********************************************************************/
//Ici intervient la récursion
moveScore = [self minMax:nextPos minDepth:minDepth alpha:alpha beta:B];

/**********************************************************************/
if (alphaPos < moveScore) //alphaPos = Max (alphaPos, moveScore);
alphaPos = moveScore;

if (alphaPos >= B)
return alphaPos; //coupure beta;
}
return alphaPos;
}

(Cet extrait ne comporte que la partie Max. La partie Min est symétrique et
concerne les coups de l'adversaire.)

Comme une position donne lieu en moyenne à 30 coups possibles, la récursion
est intense. une exploration totale sur une profondeur 4 donnerait lieu à
810 000 appel de MinMax. La coupure alpha-beta limite de 5/6 fois ce nombre,
mais je suis parvenu à 76000 appels sur une profondeur 4, en 4 minutes 38''

Le problème que je rencontre alors est que quand je devrais reprendre la
main, j'obtiens la roulette arc-en-ciel si longtemps que je suis obligé de
forcer le Quit...

Je me suis livré à un examen via ObjectAlloc qui donne ceci:

-Au départ, programme initialisé: Global allocation Total, Current = 1.9 Mo
-Pendant la recherche du premier coup blanc 1.Cc3, trouvé en 3 sec. et <
2000 appels de MinMax (Hors ObjectAlloc), Global allocation Total, Current
atteint 17 Mo puis revient à sa valeur initiale.
-Pendant la recherche de la réponse noire 1...c7-c6 (bizarre comme
ouverture... trouvé en 12" et < 6000 appels) Global allocation Total,
Current atteint 64 Mo puis revient à 5,3 Mo (Memory leakage possible...)

Si j'extrapole vers la situation où le programme trouve une bonne réfutation
à une menace de mat en 2 coups en 4' 38" et 76000 appels de MinMax , cela
donne 800Mo d'allocation courante! (Pour les initiés, il s'agit du Mat de
Legal, où les Blancs offrent leur Dame pour faire ensuite Mat en deux coups.
Normal que le programme aie pris son temps avant de renoncer au cadeau)

J'ai installé un pool d'autoRelease, mais c'est complètement inopérant. Si
je comprends bien ça doit être normal, puisqu'on ne sort pas de l'EventLoop
tant que la récursion est en cours.

Merci à qui a eu la patience de me lire, et plus encore à qui pourrait
m'indiquer la bonne méthode pour gérer proprement les désallocations.
Mes premiers résultats sont encourageants et ça m'ennuie d'être bloqué par
ce problème.

Louis Balourdet

9 réponses

Avatar
jeromelebel
Louis Balourdet wrote:

Le problème que je rencontre alors est que quand je devrais reprendre la
main, j'obtiens la roulette arc-en-ciel si longtemps que je suis obligé de
forcer le Quit...


Fait tourné ton algo dans un deuxième thread afin que l'utilisateur ai
la main et puisse arrêter les calculs en cours.

J'ai installé un pool d'autoRelease, mais c'est complètement inopérant. Si
je comprends bien ça doit être normal, puisqu'on ne sort pas de l'EventLoop
tant que la récursion est en cours.


Crée ta pool en début de minMax:minDepth:alpha:beta: et relache la en
fin de méthode. Si tu veux optimiser un peu éviter l'utilisation
-[NSString componentsSeparatedByString:] (et autres méthodes qui te
renvoie des object dans la pool d'autorelease, car tu ne peux pas
controller la déallocation directement) et ca t'évitera l'utilisation de
pool d'autorelease à chaque fois que tu appeles ta méthode (ce qui doit
être très fréquent !).

De plus, essaye l'outil "Shark", ca devrait beaucoup t'aider !

Avatar
Louis Balourdet
Fait tourné ton algo dans un deuxième thread afin que l'utilisateur ai
la main et puisse arrêter les calculs en cours.


Crée ta pool en début de minMax:minDepth:alpha:beta: et relache la en
fin de méthode. Si tu veux optimiser un peu éviter l'utilisation
-[NSString componentsSeparatedByString:] (et autres méthodes qui te
renvoie des object dans la pool d'autorelease, car tu ne peux pas
controller la déallocation directement) et ca t'évitera l'utilisation de
pool d'autorelease à chaque fois que tu appeles ta méthode (ce qui doit
être très fréquent !).

De plus, essaye l'outil "Shark", ca devrait beaucoup t'aider !


Merci pour ces explications. Je vais essayer d'installer un thread, bien que
ça n'ai pas l'air très facile.
L'outil "Shark" ne figure pas dans ma panoplie. Je suis encore sous Project
Builder et 10.2.6 . Est-ce qu'on le trouve dans les outils 10.3 ?

Avatar
jeromelebel
Louis Balourdet wrote:

Merci pour ces explications. Je vais essayer d'installer un thread, bien que
ça n'ai pas l'air très facile.


C'est pas tres compliqué :
* utilise un booléen, "cancel", que tu passes à vrai des que tu veux
annuler ton calcul
* vérifie ce booléen dans ton algo plus ou moins fréquement (genre dans
ta boucle "for" que tu as donné) et arrête des qu'il est a vrai
* dans ton UI, lorsque l'utilisateur clique sur "cancel", passe ton
booléen, "cancel", à vrai
* utilise +[NSThread detachNewThreadSelector:toTarget:withObject:] pour
créer un thread
* dès que ton calcul est fini, passe un autre booléen "done" à vrai
* quand tu lances ton thread de calcul, -[NSObject
performSelector:withObject:afterDelay:] pour savoir quand ton calcul est
fini (sur panther, il y a un moyen beaucoup plus propre de faire ca...),
et appele cette méthode avec un délai de .5 secondes (en gros).
* evidement, initialise correctement "done" et "cancel", de plus
n'oublie pas de désactiver certaine partie de ton UI pour éviter que
l'utilisateur puisse jouer pendant que l'ordinateur réflichi

L'outil "Shark" ne figure pas dans ma panoplie. Je suis encore sous Project
Builder et 10.2.6 . Est-ce qu'on le trouve dans les outils 10.3 ?


Oui, c'est exacte.

Avatar
Frédéric Testuz
"Jérôme Lebel" a écrit dans le message de
news:1gk20xa.10pioi0dz4c38N%
Louis Balourdet wrote:
L'outil "Shark" ne figure pas dans ma panoplie. Je suis encore sous
Project


Builder et 10.2.6 . Est-ce qu'on le trouve dans les outils 10.3 ?


Oui, c'est exacte.


De mon coté, je viens de faire une installation standard de Xcode 1.5 et
Shark n'est pas présent.

--
Frédéric Testuz


Avatar
jeromelebel
Frédéric Testuz wrote:

De mon coté, je viens de faire une installation standard de Xcode 1.5 et
Shark n'est pas présent.


/Developer/Applications/Performances Tools/CHUD/Shark ?

Avatar
ybvpf
Frédéric Testuz wrote:

De mon coté, je viens de faire une installation standard de Xcode 1.5 et
Shark n'est pas présent.


Dispo ici sinon (Download the CHUD Tools) :
http://developer.apple.com/tools/performance/

--
loïc

Avatar
Frédéric Testuz
"Jérôme Lebel" a écrit dans le message de
news:1gk2aje.mrhtqgbqxibkN%
Frédéric Testuz wrote:

De mon coté, je viens de faire une installation standard de Xcode 1.5 et
Shark n'est pas présent.


/Developer/Applications/Performances Tools/CHUD/Shark ?


Ah, ben voilà, dans l'installation standard, les CHUD tools ne sont pas
installé.

C'est vraiment utile ? De mon côté, je viens seulement de me mettre à
ObjectAlloc. Je sais qu'il était temps ;-)


PS : je programme juste pour moi, donc je ne me préocuppais pas trop
d'optimisation.
--
Frédéric Testuz


Avatar
jeromelebel
Frédéric Testuz wrote:

Ah, ben voilà, dans l'installation standard, les CHUD tools ne sont pas
installé.

C'est vraiment utile ? De mon côté, je viens seulement de me mettre à
ObjectAlloc. Je sais qu'il était temps ;-)


PS : je programme juste pour moi, donc je ne me préocuppais pas trop
d'optimisation.



Ben je ne sais pas si ca existe pour 10.2.
Ca peut toujours etre utile, ou sympa quand on est curieux. De plus,
même si c'est personnel, on peut toujours avoir des gros problemes de
perf.

Sinon, il y a toujours "sample" peut aider a savoir ce qu'il se passe...

Avatar
Louis Balourdet
C'est pas tres compliqué :
* utilise un booléen, "cancel", que tu passes à vrai des que tu veux
annuler ton calcul
* vérifie ce booléen dans ton algo plus ou moins fréquement (genre dans
ta boucle "for" que tu as donné) et arrête des qu'il est a vrai
* dans ton UI, lorsque l'utilisateur clique sur "cancel", passe ton
booléen, "cancel", à vrai
* utilise +[NSThread detachNewThreadSelector:toTarget:withObject:] pour
créer un thread
* dès que ton calcul est fini, passe un autre booléen "done" à vrai
* quand tu lances ton thread de calcul, -[NSObject
performSelector:withObject:afterDelay:] pour savoir quand ton calcul est
fini (sur panther, il y a un moyen beaucoup plus propre de faire ca...),
et appele cette méthode avec un délai de .5 secondes (en gros).
* evidement, initialise correctement "done" et "cancel", de plus
n'oublie pas de désactiver certaine partie de ton UI pour éviter que
l'utilisateur puisse jouer pendant que l'ordinateur réflichi



Merci. Avec ces explications et les exemples de thread dans "Building Cocoa
Applications" de Simon Garfinkel, je pense que je devrais m'en sortir.
L'outil "Shark" ne figure pas dans ma panoplie. Je suis encore sous Project
Builder et 10.2.6 . Est-ce qu'on le trouve dans les outils 10.3 ?


Oui, c'est exacte.


Ce serait une raison de plus de m'upgrader vers Panther. Mais
personnellement, j'évite les mises à jour tant que je peux m'en passer.
Souvent ça résoud des problèmes qui ne me concernent pas, et ça en apporte
de nouveaux qui eux me font perdre du temps. Pour l'instant je me dis que
j'attendrai Tiger...