Performance-Test in Javascript mit dem SpeedTest-Tool

Veröffentlicht: 29. April 2011 in Web
Schlagwörter:, , , , ,

Zeitmessung

In meiner Bachelor-Thesis habe ich u.a. auch Optimierungen und Optimierungsmöglichkeiten in Javascript untersucht. An der ein oder anderen Stelle bin ich immer wieder über das Problem gestolpert, meinen Javascript Code auf Performance zu testen. Die einfachste Möglichkeit ist sicherlich vor und nach dem auszuführenden Code die Zeit zu messen und daraus ein Delta zu berechnen.

var time1 = new Date;

// run code

var time2 = new Date;
var delta = time1 - time2;

alert("runtime of the code fragment: " + delta);

Für einige Fälle und für längere Codeabschnitte ist das Verfahren ausreichend. Es gibt jedoch Fälle, in denen nur ein Codefragment auf seine Laufzeit hin untersucht werden soll. Das Codefragment wird dabei so schnell durchlaufen, dass über die Laufzeit keine konkrete Aussage gemacht werden kann. Das folgende Beispiel soll diese Problematik verdeutlichen, indem die Laufzeit des Codefragments parseInt( ... ) untersucht wird.

var time1 = new Date;

var number = parseInt( "12.55" );

var time2 = new Date;
var delta = time1 - time2;

alert("runtime of the code fragment: " + delta);

In dem Beispiel wird in die Variablen delta = 0 sein. Für solche Fälle reicht eine einfache Zeitmessung vor und nach dem Codeabschnitt nicht mehr aus. Um aussagekräftigere Messergebnisse zu bekommen ist, könnte das Codefragment iterativ mehrmals aufgerufen und ein Mittelwert gebildet werden.

jsPerf

Das Tool jsPerf erfüllt genau diese Anforderungen. Mit Hilfe dieses Tools können sogar mehrere Testfälle erzeugt und miteinander verglichen werden. jsPerf geht sogar noch einen Schritt weiter und schickt die Ergebnisse der Tests an den Server zurück. Dadurch können die Ergebnisse auch unter verschiedenen Browsern und Browserversionen verglichen werden.

SpeedTest-Tool

Für einfache Testfälle und Codefragmente sind die vorgestellten Verfahren ausreichend. Mit jsPerf können jedoch nur Codefragmente in einer definierten Umgebung getestet werden. Während meiner Praktikums und Bachelor-Arbeit habe ich jedoch mit einem sehr komplexen System mit vielen Javascript Dateien gearbeitet. In solchen Systemen gibt es viele Randbedingungen und Abhängigkeiten. Dadurch ist es an einigen Stellen schwer, ein Codefragment aus seinem Kontext zu „reißen“.

Seit längerem arbeite ich schon mit dem Javascript-Unittest-Tool QUnit. Dort ist es im Code möglich Test zu definieren, die auch auf Variablen und Inhalte aus dem aktuellen Kontext zugreifen können. Dieses Konzept und die Implementierung habe ich mir näher angeschaut. Die Umsetzung hat mir sehr gut gefallen.

Daraus ist die Idee entstanden, ein Performance-Test-Tool zu entwickeln das ähnlich flexibel aufgerufen werden kann. Die Idee für das SpeedTest-Tool war gebohren. Das Tool sollte dabei die folgenden Anforderungen erfüllen:

  • Testname definieren
  • Testmodus
    • einfache Zeitmessung
    • Codefragment N mal in einer Schleife ausführen
    • Codefragment mehrmals hintereinander N mal in einer Schleife ausführen, um einen Mittelwert zu bilden
  • ggf. Anzahl Iterationen festlegen
  • angeben eines optionalen Ausführungskontextes
  • erstellen einer GUI um einfach Codefragmente miteinander vergleichen zu können
Über einen Zeitraum von mehreren Monaten ist dabei ein sehr nützliches Tool entstanden, das sowohl über die GUI Oberfläche einfache Test als auch im Code komplexere Performance-Test ausführen kann. Die aktuelle Version ist auf meiner Homepage www.m-raith.de.vu unter Projekte & Präsis zu finden oder kann in der Version 0.3 hier direkt herunter geladen werden.

Das Tool lief unter Windows bei mir erfolgreich in den folgenden Browsern:

  • Firefox 3.5, 3.6, 4.0b4
  • Chrome 5, 6, 7, 8, 9, 10
  • Safari 5
  • Opera 10, 11
  • IE 8, 9

Funktionen

Das Tool selbst ist für den Zugriff von außerhalb durch einen Closure geschützt. Deshalb stehen nur die folgenden Funktionen für den Zugriff auf das SpeedTest-Tool zur Verfügung:

  • speedTest(testName, function, environment)
    Die Methode erzeugt eine neue Testumgebung mit dem Namen „testName“. Als zweitere Parameter nimmt diese Methode eine Funktionsreferenz oder anonyme Funktion entgegen, die u.a. die Performance-Tests enthält. Als dritte Parameter kann ein optionaler Ausführungskontext angegeben werden.
  • executionTime(method, message)
    Das erste Argument definiert den Code, der innerhalb dieser Testmethode ausgeführt werden soll. Dabei wird die einmalige Laufzeit des Codes ermittelt. Das zweite Argument gibt den Namen des Tests an.
  • singleIteration(method, iterations, message)
    Das erste Argument gibt hier ebenfalls Code an, der in der Testmethode, so oft wie im zweiten Argument angegeben ist, wiederholt ausgeführt wird. Das dritte Argument ist der Testname.
  • multiIteration(method, iterations, message)
    Das erste Argument gibt hier ebenfalls Code an, der in der Testmethode, so oft wie im zweiten Argument angegeben ist, wiederholt ausgeführt wird. Der Unterschied zu „singleIteration“ ist, das der Code innerhalb der Methode nochmals wiederholt wird. Die Standardeinstellung ist 10 mal. Dadurch kann ein Mittelwert über die Ausführungzeit ermittelt werden. Das dritte Argument ist der Testname.
  • getResults()
    Die Methode „getResults“ liefert die Ergebnisse aller Test als Array zurück.
  • resetResults()
    Die Methode setzt die Testergebnisse zurück.
  • setConfigParam(key, value)
    Die Methode setzt einen internen Konfigurationswert key auf den Wert value.
  • getConfigParam(key)
    Die Methode liefert einen internen Konfigurationswert anhand von key zurück.

Beispiele

In dem folgenden Beispiel soll das Attribut cssText vom HTML-Element <body> gelesen werden.

speedTest("read attr", function(){
    var runs = 100000,
        param = document.body.cssText;

    multiIteration(function(){
        var str = param;
    }, runs, "read from local variable");

    multiIteration(function(){
        var str = document.body.cssText;
    }, runs, "read from element attribute");
});

Die Methode speedTest() eröffnet einen neue Testumgebung. Das erste Argument "read attr" definiert den Testnamen, der später in der Ergebnisausgabe steht. Das zweite Argument ist eine anonyme Funktion, der eigentliche Test. Der eigentliche Test läuft in der Methode multiIteration() ab. Das erste Argument ist das zu testende Codefragment. Das zweite Argument definiert, wie oft die Testmethode aufgerufen wird. Das dritte Argument kennzeichnet den Test später im Ergebnis.

Bei der multiIteration() Methode ist zu beachten, dass diese intern nochmals 10-mal aufgerufen wird, um einen Mittelwert bilden zu können. Dieser Wert ist auf 10 voreingestellt, kann jedoch mit der Methode setConfigParam("innerIterator", WERT ) verändert werden.

Schlussfolgerung

Der Artikel zeigt, dass es mit einfachen Verfahren möglich ist die Ausführungszeit eines Codeabschnitts zu messen. Sollen Vergleiche von Codefragmenten erstellt werden, bietet sich das Internet Tool jsPerf an. Mit der Entwicklung des SpeedTest-Tools hoffe ich meinen Beitrag für ein weiteres und nützliches Messtool geliefert zu haben.

Schreibe einen Kommentar

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden / Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden / Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden / Ändern )

Google+ Foto

Du kommentierst mit Deinem Google+-Konto. Abmelden / Ändern )

Verbinde mit %s