Super, pas vraiment synonyme de performances
Par -Alexandre LEGOUT aka LAlex- le mardi, octobre 19 2004, 22:24 - AS2 - Lien permanent
Vaut-il mieux utiliser super pour surcharger une méthode sans la modifier ? Abordant le sujet avec petepx et ekameleon, c'est bien ce que je croyais, mais piqué dans ma curiosité, j'ai entrepris de faire quelques benchs.
La question est donc : si je veux accéder à une méthode héritée, vaut-il mieux la surcharger, et appeler explicitement la méthode de la classe mère avec super, ou laisser faire la parcours de la chaine de prototypes ? J'ai tout d'abord cru que lui dire où aller était bien plus rapide, étant donné que la langage n'a alors pas besoin de le chercher par lui-même ...
Mais trève de discussions, voici les classes qui m'ont servi a faire mes benchs. J'ai bien fait attention à utiliser le même nombre de lettres pour les différentes classes de même niveau, car pour ceux qui ne le savent pas encore, dans Flash, plus les noms de variables sont courts, plus ils sont efficaces en terme de performances :/** Classe mère */
La sortie de ce code n'a pas manqué de m'étonner
class CClass {
private static var myProp:Number = 0;
public function testMethod() {
myProp++;
}
}
/** Classe fille sans surcharge */
class CChild extends CClass {
}
/** Classe fille avec surcharge */
class SChild extends CClass {
public function testMethod() {
super.testMethod();
}
}
/** Lancement des benchs */
var loop:Number = 50000;
var tim:Number;
// Sans super
trace("Sans super");
var o1:CChild = new CChild();
tim = getTimer();
for (var i=0 ; i<loop ; i++) {
o1.testMethod();
}
trace("Temps : " + (getTimer()-tim) + "ms.");
// Avec super
var o2:SChild = new SChild();
trace("Avec super");
tim = getTimer();
for (var i=0 ; i<loop ; i++) {
o2.testMethod();
}
trace("Temps : " + (getTimer()-tim) + "ms.");Sans super
Temps : 1204ms. (en moyenne entre 1175 et 1225)
Avec super
Temps : 1535ms. (en moyenne entre 1525 et 1550)
Avec une génération supplémentaire, c'est encore plus prononcé. Alors que les performances sans super ne semblent pas très affectées, celle avec super se dégradent considérablement :/** 3ème génération sans super */
class CGrandChild extends CChild {
}
/** 3ème génération avec super */
class SGrandChild extends SChild {
public function testMethod() {
super.testMethod();
}
}
/** Lancement des benchs */
var loop:Number = 50000;
var tim:Number;
// Sans super
trace("Sans super");
var o1:CGrandChild = new CGrandChild();
tim = getTimer();
for (var i=0 ; i<loop ; i++) {
o1.testMethod();
}
trace("Temps : " + (getTimer()-tim) + "ms.");
// Avec super
var o2:SGrandChild = new SGrandChild();
trace("Avec super");
tim = getTimer();
for (var i=0 ; i<loop ; i++) {
o2.testMethod();
}
trace("Temps : " + (getTimer()-tim) + "ms.");
La sortie est plus qu'éloquente :Sans super
Temps : 1285ms. (en moyenne entre 1275 et 1305)
Avec super
Temps : 1947ms. (en moyenne entre 1925 et 1950)
Donc, il vaut mieux laisser le soin à Flash de trouver la bonne méthode plutôt que de le lui indiquer clairement. Quel manque de reconnaissance !!! ![]()
PS : Par la même occasion, j'ai découvert que super.super.testMethod() n'est pas autorisé ! Encore un des mystères de super ! ![]()
Commentaires
Hum ca parait assez evident en fait, quand tu appelles une méthode, via super ou autre tu as un appel de méthode supplémentaire, tu as l'appel la méthode de la sous classe + l'appel à la méthode de la super classe. Alors que dans l'autre cas, tu as le parcours de la chaine de prototype + l'appel d'une seule méthode.
Sachant que Flash est d'une lenteur incroyable pour l'appel des méthodes ce n'est pas etonnant.
Bon alors .. je recorrige mon code LOL ... Merci pour ce bench , j'allais le faire
sympa ton petit test, en resumer pourquoi faire compliquer quand on peut faire simple
++
désolé, mais je dois t'annoncer que ce benchmark n'est pas valide !
Les deux test doivent faire la même chose.
Le test sans le super invoque directement la méthode d'origine tandis qu'avec le super, la méthode d'origine est polymorphé. Donc le player travaille en double...
(ou en triple pour le second test ;))
class CChild extends CClass {public function uneMethode() {
testMethod();
}
}
class SChild extends CClass {
public function uneMethode() {
super.testMethod();
}
}
@++
Euh
mes tests :

// CChildet dans flash :class CChild extends CClass {
public function testSuperMethod() {
super.testMethod();
}
}
// SChild
class SChild extends CChild {
public function SChild () {}
public function testMethod1() {
super.testMethod();
}
public function testMethod2() {
super.testSuperMethod();
}
}
/** Lancement des benchs */var loop:Number = 50000;
var tim:Number;
// Avec Super
trace("Avec super");
var o1:CChild = new CChild();
tim = getTimer();
for (var i=0 ; i<loop ; i++) {
o1.testSuperMethod();
}
trace("Temps : " + (getTimer()-tim) + "ms.");
// Sans super
var o2:SChild = new SChild();
trace("Sans super");
tim = getTimer();
for (var i=0 ; i<loop ; i++) {
o2.testMethod();
}
trace("Temps : " + (getTimer()-tim) + "ms.");
var o2:SChild = new SChild();
trace("Avec super");
tim = getTimer();
for (var i=0 ; i<loop ; i++) {
o2.testMethod1();
}
trace("Temps : " + (getTimer()-tim) + "ms.");
var o2:SChild = new SChild();
trace("Avec super");
tim = getTimer();
for (var i=0 ; i<loop ; i++) {
o2.testMethod2();
}
trace("Temps : " + (getTimer()-tim) + "ms.");
Résultat :
bye
mouin, reste que ce que j'ai dis ci-dessus est encore vrai !
ces test ne font pas la même chose... mis à part les tests #1 et #3 où les temps sont assez près...
bon, au lieu de parler j'ai fait moi aussi des test de mon exemple avec as2lib. Voici les résultats :
import org.as2lib.test.speed.*;
dans le flash ://Sans le super
class CChild extends CClass implements TestCase{
public function run(Void):Void{
testMethod();
}
}
//Avec le super
class SChild extends CClass implements TestCase{
public function run(Void):Void{
super.testMethod();
}
}
import org.as2lib.*;import org.as2lib.test.speed.*;
var test:Test = new Test();
test.addTestCase(new CChild());
test.addTestCase(new SChild());
test.setOut(Config.getOut());
test.setCalls(50000);
test.run(true);
sortie :
104% CChild:
total time:2372ms;
calls/second:21079;
average time/call:0.04744ms;
(+0.002ms)
[fastest]
100% SChild:
total time:2284ms;
calls/second:21891;
average time/call:0.04568ms;
@++
Sans super... avec super.... mais vous testez quoi? Flash ou un plein de voiture. lol
Oki, je sorts ^^
Je viens de faire des tests, c'est vrai, 'super' est un peu moins performant, mais comme liguorien, je crois pas qu'il faille s'en inquièter, les différences sont vraiment minimes.
Sur un Array.push avec deux générations, sur 500.000 entrées, j'obtiens 12725 contre 12502 ms.
Et effectivement le super.super est totalement à la rue. ^^
En effet, le super.super ne passe mm pas le compilo
Ceci dit si on doit surcharger une méthode dans une classe enfant tout en appelant celle de la classe mère (en y ajoutant des fonctionnalités) ben tu es obligé de passer par le super.maFonction !
Bref encore un truc à ajouter dans la wishlist de 8ball je pense ^^
Le fait de ne pas pouvoir faire super.super me parait logique car une classe n'hérite que d'une seule autre classe et ne peut donc appeler que les méthodes de la classe dont elle hérite.
Si par exemple une classe fille hérite d'une classe mère et surcharge l'une de ces méthodes il me parait logique qu'une classe petite-fille héritant de la classe fille pourrra accéder à la méthode de celle-ci mais pas de la classe mère étant donné qu'elle n'en hérite pas.
@++
en venir à super.super, c'est qu'il y a un problème de conception
une méthode a un but
surcharger une méthode, c'est atteindre ce but d'une autre facon ou de manière complémentaire
c'est comme dire que la première surchage n'était pas bonne, ou que l'héritage est bizarre
ou que la méthode fait trop de choses différentes, dans ce cas, il faut exploser en plusieurs méthodes
[quote="thecaptain":14f9c9b622]Ceci dit si on doit surcharger une méthode dans une classe enfant tout en appelant celle de la classe mère (en y ajoutant des fonctionnalités) ben tu es obligé de passer par le super.maFonction !
Bref encore un truc à ajouter dans la wishlist de 8ball je pense ^^[/quote]
C'est tout à fait normal.
S'ils implémentent cette feature dans le 8ball, comment fera-t-on pour faire une méthode récursive si une classe ne peut invoquer sa propre méthode ? :o
l'équivalent de
super.testMethod();dans CChild sans super ce serait plutot:CClass.prototype.testMethod.apply( this )dans SChild
et encore, apply devrait prendre un peu plus de temps qu'un appel de method normal
de plus il faudrait tester (si ce n'est deja fait) directement dans le player plutot que le player de l' IDE flash qui est presque 2x fois plus lent en temps d'execution de code
me rappelant d'un parser sur de gros XML
qui prenait ~4000ms dans l'IDE
mais que ~2000ms dans le player flash dans le browser
C'est bien de faire un tel bench par curiosité, mais je vois vraiment pas l'intérêt d'alourdir son code en surchargeant des méthodes inutilement.
En poo je le vois l'intérêt
Enfin .. là c'est qu'un exemple .. il y en a pleins d'autres 

Exemple tu as une classe Rectangle et pleins de sous classes RoundedRectangle, BevelRectangle, CurveRectangle etc.. etc... si tu utilises selon les paramètres de chaque rectangle une méthode draw() différente mais qui peut croiser d'autres méthodes se trouvant dans la superclasse Rectangle ... et bien pas de raison de surcharger les méthodes en ajoutant des fonctionnalités et en réutilisant au besoin celles de la classe d'origine
bye
Je dois avouer que j'ai relu quelques fois ton poste Eka, mais ton exemple m'apparaît embrouillé dans ma tête. Vraiment désolé, mais je réussi pas à très bien saisir pourquoi la surchage d'une méthode en ne faisant appel qu'à celle de la classe parent est utile dans ce cas.
Vraiment dsl
un exemple sur 2 de mes classes .. là je te mets juste les méthodes draw() pour une classe Rectangle et une classe RoundedRectangle :
Méthode draw() dans la classe Rectangle :
public function draw () : Void {donc ici on voit trace simplement un rectangle .. en utilisant des propriétés spécifiques à la classe Rectangle Ensuite la méthode draw() de la classe RoundedRectangle qui hérite de Rectangle :getMinMax () ;
clear () ;
lineStyle(ep, lc, la);
beginFill (fc, fa) ;
moveTo (_min.x, _min.y) ;
lineTo (_max.x, _min.y);
lineTo (_max.x, _max.y);
lineTo (_min.x, _max.y);
lineTo (_min.x, _min.y) ;
}
public function draw () : Void {if (_cornerRadius == undefined || _cornerRadius == 0) super.draw () ;
else {
getMinMax () ;
clear () ;
lineStyle(ep, lc, la) ;
beginFill (fc, fa) ;
_currentRadius = (_cornerRadius > Math.min ($w, $h) / 2 ) ? Math.max ($w, $h) / 2 : _cornerRadius ;
moveTo ( _min.x + _currentRadius, _min.y);
lineTo ( _max.x - _currentRadius , _min.y) ;
if ($tr) {
drawCorner (_max.x - _currentRadius , _min.y + _currentRadius) ;
} else {
lineTo ( _max.x , _min.y ) ;
lineTo (_max.x , _min.y + _currentRadius) ;
simulateCorner () ;
}
lineTo ( _max.x , _max.y - _currentRadius ) ;
if ($br) {
drawCorner (_max.x - _currentRadius, _max.y - _currentRadius) ;
} else {
lineTo ( _max.x , _max.y ) ;
lineTo (_max.x - _currentRadius , _max.y ) ;
simulateCorner () ;
}
lineTo( _min.x + _currentRadius , _max.y ) ;
if ($bl) {
drawCorner (_min.x + _currentRadius , _max.y - _currentRadius) ;
} else {
lineTo ( _min.x , _max.y ) ;
lineTo (_min.x , _max.y - _currentRadius ) ;
simulateCorner () ;
}
lineTo(_min.x, _min.y + _currentRadius) ;
if ($tl) {
drawCorner (_min.x + _currentRadius, _min.y + _currentRadius) ;
} else {
lineTo ( _min.x , _min.y ) ;
}
endFill () ;
_angle = undefined ;
}
}
Là tu vois que si le rayon du coin arrondi détermine est indéfini ou est égal à 0 on utilise directement la méthode de la classe Rectangle (pas besoin de se payer tout le code qui suit ....) sinon on utilise alors la surcharge de la méthode draw().
+ clair ?
Oui c'est plus clair Eka, mais j'avais bien marqué quand la surchage était inutile. Dans ton cas, elle est tout à fait valable et tout à fait normal.
C'est de ce cas là que je parle quand je dis que c'est inutile et illogique.
class SChild extends CClass {public function testMethod() {
super.testMethod();
}
}
oui non ici l'exemple n'est pas bon en effet
C'est juste un test 
Bien, trés intéressant mais est ce que c'est toujours vrai si on a un enchevetrement de classe du type object-componentObject-maclasseComponent ?
Je vais regarder...
c'est quoi comme type object-componentObject-maclasseComponent ??
Tu peux préciser j'ai pas compris ? 
Disons :
object une classe qui hérite de movieClip
componentObject une classe qui herite de object
maClasseObject qui herite de componentObject
est ce que les tests serait toujours viable si l'on appelle une méthode de la classe object depuis maClasseObject ?
Toutes les classes héritent de Object ... du coup je pige rien car tu utilises des minuscules/majscules dans tes noms au dessus ... et c'est franchement pas clair


En gros si j'ai bien compris ce que tu dis ... tu veux par exemple faire :
class UIComponent extends MovieClip {}class MyComponent extends UIComponent {}Evite d'utiliser des noms style object ... pour tes classes car le type object existe et cela embrouille tout comme tu en parles
Sinon là on parle de faire un test sur le super() .. donc forcément super() cela marche pour toutes les classes qui héritent d'une autre.. pas de raison que cela change quoi que ce soit au niveau des tests au dessus
OK, je pensais qu'aller chercher une chercher une méthode d'une classe deux niveaux de parenté au dessus serait plus long qu'une méthode un niveau de parenté au dessus... en ce qui concerne le nommage tu m'en a déjà parler et j'en pris bonne note et j'ai renommer toutes mes classes mais ici c'était juste un exemple, la prochine fois je ferais attention
slt
salut emilie quoi de neuf
comme quoi dés fois vaut mieux laisser le player faire sa tambouille interne les gars ...
Fil des commentaires de ce billet