[AS3] Nombre maximum de récursions en AS3 dans Flash CS3
Par eKameleon le samedi, juin 28 2008, 10:36 - AS3 - Flex - Air - Lien permanent
Voici un petit point que je n'avais pas encore eu le temps de regarder depuis que j'utilise Flash CS3 à propos du "nombre de récursions" dans Flash CS3 lorsque nous publions une animation avec les paramètres Flash Player 9 et ActionScript 3.
Ce n'est pas forcément un mystère pour tout le monde mais je me rends compte que malgré tout ce point n'est jamais vraiment traité.
En ActionScript il est impossible d'utiliser un algorithme basé sur une ou des fonctions récursives indéfiniment.
Pour rappel, une fonction récursive est une fonction qui s'appelle elle même.
Par exemple si l'on veut calculer très simplement le produit factoriel d'un nombre entier (multiplication des valeurs n-1 du nombre) nous pouvons écrire la fonction suivante :
var factoriel:Function = function( n:uint ):uint { if ( n <= 1 ) { return 1 ; } else { return n * factoriel(n-1) ; } } trace( factoriel(10) ) ; // 3628800
La fonction précédente n'est pas la plus optimale (pas le but de cet article) mais elle illustre bien que la fonction "factoriel" s'appelle elle même plusieurs fois avant d'obtenir le bon résultat.
Il faut donc faire attention en utilisant une fonction récursive car si elle est mal définie il est possible "en principe" d'obtenir une boucle infinie qui pourrait faire planter l'application.
C'est pour cela que l'ActionScript (ou plus généralement les langages basés sur l'ECMAScript comme le Javascript) limitent leur nombre de récursions.
En AS2 et AS1 nous avions l'habitude de cette limite, avec un nombre maximum de récursion basé sur le nombre 255. Je vais reprendre l'exemple précédent mais en AS2 pour illustrer cette limite :
var factoriel:Function = function( n:Number ):Number { if ( n <= 1 ) { return 1 ; } else { return n * factoriel(n-1) ; } } trace( factoriel(10) ) ; trace( factoriel(255) ) ; trace( factoriel(256) ) ;
Nous obtenons dans le panneau de sortie de Flash :
3628800 Infinity 256 levels of recursion were exceeded in one action list. This is probably an infinite loop. Further execution of actions has been disabled in this movie.
La limite de récursion en AS1/AS2 est donc très vite atteinte. L'idée à la base était que l'ActionScript (comme le Javascript) étaient des langages qui ne nécessités pas une aussi forte sollicitation algorithmique. De façon générale les langages basés sur les normes ECMAScript préfèrent l'utilisation d'algorithmes itératifs que récursifs.
A noter qu'il est possible de changer le nombre de récursions ainsi que le temps maximum d'exécution d'un script en AS1 ou AS2 avec un utilitaire externe en ligne de commande : SWF ScriptLimits Injector. Cet utilitaire fonctionne avec un swf au format FP7 et + (je ne l'ai pas testé avec un code en AS3 ?)
Maintenant que nous avons vu qu'il existe une limite dans l'AVM1 (virtual machine du FlashPlayer en AS1 et l'AS2), il est temps de voir ce qu'il se passe dans Flash CS3 en AS3 avec un bout de code super simple
var cpt:Number = 0 ; var max:Number = 3035 ; var test:Function = function() { cpt++ ; if ( cpt < max ) { test() ; } } test() ; trace(cpt) ; // 3035
Avec le code ci-dessus nous obtenons un résultat évidant, avec une valeur 3035 dans le panneau de sortie. Première constatation, nous pouvons dépasser dans Flash CS3 la limite de 255 récursions et donc il est possible d'aller bien au delà !
Voyons maintenant le même script mais avec une valeur maximale de 3036 :
var cpt:Number = 0 ; var max:Number = 3036; var test:Function = function() { cpt++ ; if ( cpt < max ) { test() ; } } test() ; trace(cpt) ; // Error...
Nous obtenons une erreur dans le panneau de sortie de Flash :
Error: Error #1023: Il s'est produit un débordement de pile.
at MethodInfo-1()
at MethodInfo-1()
at MethodInfo-1()
at MethodInfo-1()
at MethodInfo-1()
at MethodInfo-1()
at MethodInfo-1()
....
Par défaut dans Flash CS3 nous avons donc une limite du nombre de récursions avec 3035 récursions possibles.
A noter que dans le compilateur de Flex (mxmlc.exe) il est possible de changer via une commande le nombre limite de récursion du swf compilé :
-default-script-limits <max-recursion-depth> <max-execution-time>
Pour plus d'informations à ce sujet vous pouvez consulter la documentation de Flex ou le très bon article sur senocular.com : Beginners Guide to Getting Started with AS3 (Without Learning Flex)
Je n'ai pas encore trouvé comment... mais je me demande si il est possible de faire de même avec Flash CS3 ?
Pour toutes vos questions sur cet article vous pouvez utiliser les commentaires de ce blog mais je vous propose d'aller discuter sur le Google Groups de mon blog.
Commentaires
Intéressant, merci
"De façon générale les langages basés sur les normes ECMAScript préfèrent l'utilisation d'algorithmes itératifs que récursifs."
euh... "les langages" tout court non ? Ôo
hello
Si mes souvenirs sont bons quand j'étais à la fac je faisais en première année du CAML et c'était un langage essentiellement orienté sur la récursion .. mais je peux me tromper, car j'ai un doute sur l'orientation que donnait pédagogiquement les profs à l'époque sur l'apprentissage de ce langage
Sinon il est vrai que l'essentiel des langages modernes orientent le code vers un langage itératif
Mais faut se méfier car je ne les connais pas tous 
EKA+
"de nos jours" les profs d'info enseignent la récursion en première année pour aborder le sujet (souvent proche de la logique pour résoudre un problème), et nous apprennent à dérécursifier en deuxième année, pour l'efficacité du code (optimisation de la pile).
enfin pour le cpp c'est comme ca, après je sais pas comment ils vont aborder le sujet pour les autres langages...
(au passage, sympa tes articles)
Hello
Pour ma part en ActionScript, j'explique rapidement les outils itératifs (au moment de l'apprentissage des types primitifs et natifs) et ensuite lorsque j'explique le type Function j'introduis la notion de récursion (surtout avec le mot clé arguments.callee qui est fait pour cela)
Je pense que dans tous les cas il faut maitriser les 2 en sachant très vite passer de l'un à l'autre et surtout prendre en compte les limites de l'un et de l'autre.
EKA+
chez moi je n arrive pas a aller au dela de 600 iteration ce qui me laisse suposer que c'est aussi en fonction de la puissance du processeur ?
Hello
ah tiens ? Tu peux nous en dire plus sur ta machine et la version du flash player que tu utilises ?
EKA+
9.0 r115 iMacG5 PPC donc un player mac
Il n'est pas possible de modifier le nombre maximum de récursions depuis Flash CS3.
On peut effectuer cette modification en utilisant Flex (comme tu l'indiquse dans l'article) ou en modifiant la valeur du TAG correspondant au nombre maximum de récursions de l'animation Flash dans le SWF (voir Flash Player SDK pour plus de détails).
Salut Eka,
Sur un VAIO équipé de Vista (et oui... :s) et le FP9, j'atteins 3076.
Et avec une fonction nommée, çà va bien au delà :
var t:int=0;
function go ():void
{
try
{
lockMachine ();
}
catch (e:StackOverflowError)
{
trace (t); // 6895
}
}
function lockMachine ():void
{
t++;
lockMachine ();
}
go ();
Désolé pour le flood ^^
Pour aller plus loin dans la récursion, sans limite, si ce n'est celle du blocage du FP
:
var t:int=0;
function go ():void
{
try
{
lockMachine ();
}
catch (e:StackOverflowError)
{
// trace (t);
setTimeout(go, 0);
}
}
function lockMachine ():void
{
if (++t >= 1000000)
{
trace("faut quand même s'arrêter un jour ou l'autre ", t);
return;
}
lockMachine ();
}
go ();
Je m'étais servi d'une technique similaire en AS2 pour découper une trop longue boucle for en plusieurs "paquets" mais avec un interval minimum de 1ms. En augmentant l'interval, çà permet de laisser la main sur le FlashPlayer, et d'afficher par exemple une progression de la boucle, une estimation du temps restant, etc. Dans certains cas, j'avais même observé un gain en performance, malgré l'interval (plusieurs "petites" boucles étaient exécutées plus rapidement qu'une seule "grande").
++
Hello
En AS2 tu arrives à avoir une vitesse de setTimeout de 1ms ? Je pensais vraiment qu'en dessous de 150 ms... cela devient super instable et du coup on se retrouve sur une vitesse liée à la timeline (fr/s) ? Faudra que je fasse des tests car cela m'étonne
Ta méthode au dessus est super intéressante pour éviter de se taper une boucle for ou autre.. reste qu'à mon avis tout dépend de ce que l'on cherche à faire
A mon sens.. si il y a trop de boucles... c'est qu'il faut trouver une solution autre au départ pour optimiser (Object Pooling, solution pour bloquer certains processus etc...)
Sinon en tout cas ce qui est bien c'est qu'avec le FP9 et surtout ensuite avec le FP10 qui arrive on se retrouve face à des solutions de plus en plus rapides pour réaliser de plus en plus de choses
EKA+
Oui, le setTimeout est plus ou moins dépendant de la cadence d'animation. Je dis "plus ou moins" car j'avais fait des tests à cette époque, et la relation entre les deux n'était pas toujours claire il me semble.
En fait j'avais utilisé cette "bidouille" pour parcourir tout un BitmapData et faire des getPixel, mais je ne voulais pas bloquer le FP. Le timeout était plutôt de 5 ms pour laisser la main sur le FP pendant ce temps. Ceci encapsulé dans une classe qui balance un Event quand le process est fini.
Hello,
Je viens de tester ton code récursif en AS3 et chez moi j'obtiens le message de débordement de pile avec un max de 3077.
Pour info j'ai changé le player dans cs3 par la version 10.
ça viendrait des mises à jour adobe ou du FP10 ?
Nairus
A++