Zanbench : démarrage rapide

Zanbench est un framework actionscript 3 de test de performances. Pour une introduction préliminaire, consultez cet article.

Dans cet article, nous allons voir comment installer et utiliser zanbench.

Installez zanbench

Téléchargez le swc depuis cette adresse et ajoutez le au classpath de votre projet.

Paramétrez le compilateur

Ajoutez la ligne ci-dessous aux options du compilateur. Attention, zanbench utilise la reflexion et ne peut fonctionner sans que le metatag [Benchmark] ne soit retenu après compilation.

-keep-as3-metadata+=Benchmark

Créez un BenchmarkCase

package gettingStarted
{
    import com.zanshine.benchmark.core.BenchmarkCase;

    public class SimpleBenchmarkCase extends BenchmarkCase
    {
    }
}

Créez un premier test

Créez une méthode publique, choisissez n'importe quel nom et taggez là avec [Benchmark]

package gettingStarted
{
    import com.zanshine.benchmark.core.BenchmarkCase;

    import flash.utils.getTimer;

    public class SimpleBenchmarkCase extends BenchmarkCase
    {
        [Benchmark]
        public function measureGetTimer():void
        {
            var whatTimeIsItPlease:Number = getTimer();
        }
    }
}

Créez une suite, ajoutez le benchmark à cette suite et lancez !

La méthode addBenchmark ci dessous prend comme argument :

  1. un objet implémentant Benchmarkable, ce qui est le cas de BenchmarkCase qui est la superclasse de SimpleBenchmarkCase
  2. un entier indiquant le nombre d'itérations de la boucle qui va appeller chaque méthode de test
  3. un entier indiquant le nombre de répétitions de la boucle
  4. un entier indiquant le délai en millisecondes entre chaque répétition
package
{
    import simple.SimpleBenchmarkCase;

    import com.zanshine.benchmark.core.BenchmarkSuite;
    import com.zanshine.benchmark.print.ResultPrinter;

    import flash.display.Sprite;

    public class GettingStartedRunner extends Sprite
    {
        public function GettingStartedRunner()
        {

            var suite:BenchmarkSuite = new BenchmarkSuite();
            suite.addBenchmark(new SimpleBenchmarkCase(), 1000, 500, 50);

            var printer:ResultPrinter = new ResultPrinter(suite);
            suite.run();
        }
    }
}

Les résultats sont visibles dans la console de sortie

benchmark case name => simple::SimpleBenchmarkCase
test method name    => measureGetTimer
message             =>
iterations per loop => 1000
loop run count      => 100
delay between loops => 50
raw duration        => 381

Ajoutez un second test au BenchmarkCase, décidez de l'ordre d'exécution et ajoutez un message par test

Zanbench offre la possibilité de définir l'orde dans lequel les méthodes de test doivent être appelées. Il est également possible de passer un message pour chaque méthode.

package gettingStarted
{
    import com.zanshine.benchmark.core.BenchmarkCase;

    import flash.utils.getTimer;

    public class SimpleBenchmarkCase extends BenchmarkCase
    {
        [Benchmark(order="2", message="This test will run in second")]
        public function measureGetTimer():void
        {
            var whatTimeIsItPlease:Number = getTimer();
        }

        [Benchmark(order=1, message="This test will run first")]
        public function measureDate():void
        {
            var whatTimeIsItPlease:Number = (new Date()).time;
        }
    }
}
benchmark case name => simple::SimpleBenchmarkCase
test method name    => measureDate
message             => This test will run first
iterations per loop => 1000
loop run count      => 100
delay between loops => 50
raw duration        => 713

benchmark case name => simple::SimpleBenchmarkCase
test method name    => measureGetTimer
message             => This test will run in second
iterations per loop => 1000
loop run count      => 100
delay between loops => 50
raw duration        => 388

Implémenter Benchmarkable si votre classe ne peut pas hériter de BenchmarkCase

Si pour une raison ou une autre, votre classe de benchmark ne peut pas hériter de BenchmarkCase, il vous suffit d'implémenter l'interface Benchmarkable, ce qui est très simple :

package gettingStarted
{
    import com.zanshine.benchmark.core.Benchmarkable;

    public class SharedObjectCase implements Benchmarkable
    {
        public function prepare():void
        {
        }

        public function setUp():void
        {
        }

        public function tearDown():void
        {
        }

        public function clean():void
        {
        }
    }
}

Il n'y a aucune implémentation obligatoire dans chacune de ces quatre méthodes. Vous pouvez les laisser vides et lancer le runner. Ces quatre méthodes sont des callbacks, des méthodes qui sont appelées automatiquement.

Définir des actions spécifiques à des moments clés de l'exécution du Benchmark

Zanbench prend en charge six callbacks différents :

  1. prepare():void : appelée à l'initialisation du Benchmarkable
  2. setUp():void : appelée avant chaque méthode de test
  3. beforeMethodName():void où le litéral "MethodName" correspond à une méthode de test methodName():void : appelée AVANT la méthode methodName():void
  4. afterMethodName():void où le litéral "MethodName" correspond à une méthode de test methodName():void : appelée APRES la méthode methodName():void
  5. tearDown():void appelée après chaque méthode de test
  6. clean():void appelée après toute les méthodes de test et les callbacks

Ces méthodes servent à manipuler, instancier ou détruire des fixtures. Pour ceux qui ne sont pas à l'aise avec les notions de base des framework xUnit, une fixture est un ensemble de préconditions et d'états nécessaires à l'exécution d'un test. Il s'agit du contexte d'exécution du test.

Voyons un exemple illustrant l'usage de ces callbacks (c'est bien sur un exemple didactique, sans prétentions architecturales :) ). Nous voulons mesurer la différence de temps d'exécution entre l'écriture d'une collection de Person sur un SharedObject local et un distant. D'abord, les deux classes du contexte :

package gettingStarted
{

    public class Person
    {
        public function Person()
        {
            People.registerPerson(this);
        }

        public var name:String;
        public var lastLogginDate:Date;
    }
}
package gettingStarted
{

    import flash.utils.Dictionary;
    public class People
    {
        private static var people:Dictionary = new Dictionary();
        public static function registerPerson(person:Person):void
        {
            people[person.name] = person;
        }

        public static function shotEveryOne():void
        {
            people = new Dictionary();
        }
    }
}

Voici la classe de test, les commentaires explicatifs sont ci-dessous.

package gettingStarted
{
    import flash.net.NetConnection;
    import flash.net.SharedObject;

    import com.zanshine.benchmark.core.Benchmarkable;

    public class SharedObjectCase implements Benchmarkable
    {
        private var so:SharedObject;
        private var connexion:NetConnection;
        private var contacts:Array;

        public function prepare():void
        {
            connexion = new NetConnection();
            connexion.connect("rtmp://somedomain.com/applicationName");
        }

        public function setUp():void
        {
            var paul:Person = new Person();
            paul.name = "Paul";
            paul.lastLogginDate = new Date();

            var luc:Person = new Person();
            luc.name = "Paul";
            luc.lastLogginDate = new Date();

            contacts = [];
            contacts.push(paul);
            contacts.push(luc);
        }

        public function tearDown():void
        {
            so.clear();
        }

        public function clean():void
        {
            People.shotEveryOne();
        }

        /**
         *  Measuring remote SharedObject writing time
         */
        public function beforeRemoteSharedObject():void
        {
            so = SharedObject.getRemote("sharedObjectCase");
            so.connect(connexion);
        }

        [Benchmark(message="remote shared object writing", order=2)]

        public function remoteSharedObject():void
        {
            writeSO();
        }

        public function afterRemoteSharedObject():void
        {
            so.close();
        }

        /**
         *  Measuring local SharedObject writing time
         */
        public function beforeLocalSharedObject():void
        {
            so = SharedObject.getLocal("sharedObjectCase");
        }

        [Benchmark(message="local shared object writing", order="1")]

        public function localSharedObject():void
        {
            writeSO();
        }

        /**
         * Generic write So helper
         */
        private function writeSO():void
        {
            for each(var p:Person in contacts)
            {
                so.data[p.name] = p;
            }
        }
    }
}

Ici, nous avons une classe Person, qui enregistre une référence d'elle même auprès de la classe People dans son constructeur. Instancier Person n'est donc pas sans conséquences, car cela laisse une trace au niveau de la classe People. Si nous avons d'autres tests qui dépendent d'une classe People dont le registre statique est vide, nos tests seront faussés. Nous devons donc veiller à détruire tous les objets Person créés avant de passer à un autre Benchmark, ce que nous faisons dans le callback clean(). Nous utilisons les callbacks "before" spécifiques à chaque méthode de test pour préparer correctement son contexte d'exécution. Après avoir terminé le test pour le shared object distant, nous fermons la connexion dans le callback "after". Puisque l'objet référencé par so change à chaque méthode de test, nous appelons so.clear() dans le callback tearDown() afin de ne laisser de traces du test ni en local, ni sur le serveur.

Leave a comment

Comments