Kugelsichere JavaScript-Tests

Ursprünglicher Autor: Mathias Bynens
  • Übersetzung
Das Schreiben von JS-Geschwindigkeitstests ist nicht so einfach, wie es sich anhört. Ohne auch nur auf browserübergreifende Kompatibilitätsprobleme einzugehen, können Sie in viele Fallen geraten.

Deshalb habe ich jsPerf gemacht . Eine einfache Webschnittstelle, mit der jeder Tests erstellen und austauschen sowie die Leistung verschiedener Codeteile überprüfen kann. Sie müssen sich um nichts kümmern - geben Sie einfach den Code ein, dessen Leistung Sie messen müssen, und jsPerf erstellt eine neue Testaufgabe für Sie, die Sie dann auf verschiedenen Geräten und in verschiedenen Browsern ausführen können.

Hinter den Kulissen verwendete jsPerf zuerst die JSLitmus- Bibliothek , die ich Benchmark.js nannte . Im Laufe der Zeit erwarb er neue Fähigkeiten, und vor kurzem John-David Daltonalles von Grund auf neu geschrieben.

Dieser Artikel beleuchtet verschiedene knifflige Situationen, die bei der Entwicklung von JS-Tests auftreten können.

Testbilder


Es gibt verschiedene Möglichkeiten, einen Test eines Teils des JS-Codes durchzuführen, um die Leistung zu testen. Die am häufigsten verwendete Option, Vorlage A :

var totalTime,
    start = new Date,
    iterations = 6;
while (iterations--) {
  // Здесь идёт фрагмент кода
}
// totalTime → количество миллисекунд, потребовавшихся на шестикратное выполнение кода
totalTime = new Date - start;


Der Testcode wird in eine Schleife eingefügt, die eine bestimmte Anzahl von Malen (6) ausgeführt wird. Danach wird das Startdatum vom Enddatum abgezogen. Diese Vorlage wird verwendet testirovochnye Frameworks SlickSpeed , Taskspeed , die SunSpider und Kraken .

Die Probleme

Bei einer stetigen Leistungssteigerung von Geräten und Browsern ergeben Tests, die eine feste Anzahl von Wiederholungen verwenden, zunehmend 0 ms als Ergebnis der Arbeit, die wir nicht benötigen.

Muster B

Der zweite Ansatz besteht darin, zu berechnen, wie viele Vorgänge in einer festgelegten Zeit abgeschlossen sind. Plus: Die Anzahl der Iterationen muss nicht gewählt werden.

var hz,
    period,
    startTime = new Date,
    runs = 0;
do {
  // Здесь идёт фрагмент кода
  runs++;
  totalTime = new Date - startTime;
} while (totalTime < 1000);
// преобразуем ms в секунды
totalTime /= 1000;
// period → сколько времени занимает одна операция
period = totalTime / runs;
// hz → количество операций в секунду
hz = 1 / period;
// или можно записать короче
// hz = (runs * 1000) / totalTime;


Es führt den Code für ungefähr eine Sekunde aus, d. H. bis totalTime 1000 ms überschreitet.

Vorlage B wird in der Dromaeo und V8 Benchmark Suite verwendet .

Die Probleme

Aufgrund von Garbage Collection, Suchmaschinenoptimierungen und anderen Hintergrundprozessen kann sich die Ausführungszeit desselben Codes ändern. Daher ist es ratsam, den Test viele Male durchzuführen und die Ergebnisse zu mitteln. V8 Suite führt Tests nur einmal aus. Dromaeo - fünfmal, aber manchmal ist das nicht genug. Reduzieren Sie beispielsweise die minimale Testausführungszeit von 1000 auf 50 ms, damit mehr Zeit für wiederholte Durchläufe verbleibt.

Muster C

JSLitmus kombiniert zwei Vorlagen. Es verwendet Vorlage A, um den Test n-mal in einer Schleife auszuführen, aber die Schleifen passen sich an und erhöhen n zur Laufzeit, bis die minimale Laufzeit des Tests eingegeben wird - d. H. wie in Muster B.

Die Probleme

JSLitmus vermeidet die Probleme von Vorlage A, kommt aber nicht von den Problemen von Vorlage B weg. Zur Kalibrierung werden die 3 schnellsten Wiederholungen des Tests ausgewählt, die von den Ergebnissen der anderen subtrahiert werden. Leider ist das „Beste aus drei“ statistisch gesehen nicht die beste Methode. Selbst wenn Sie die Tests viele Male ausführen und den Kalibrierungsmittelwert vom Durchschnittsergebnis abziehen, wird der erhöhte Fehler des Ergebnisses die gesamte Kalibrierung aufzehren.

Muster D

Die Probleme früherer Muster können durch Kompilieren von Funktionen und Entfalten von Schleifen beseitigt werden.

function test() {
  x == y;
}
while (iterations--) {
  test();
}
// …скомпилируется в →
var hz,
    startTime = new Date;
x == y;
x == y;
x == y;
x == y;
x == y;
// …
hz = (runs * 1000) / (new Date - startTime);


Die Probleme

Aber es gibt Nachteile. Das Kompilieren von Funktionen erhöht die Speichernutzung und verlangsamt die Arbeit. Wenn Sie den Test mehrere Millionen Mal wiederholen, erstellen Sie eine sehr lange Zeile und kompilieren eine gigantische Funktion.

Ein weiteres Problem beim Entfalten der Schleife besteht darin, dass der Test zu Beginn der Arbeit den Ausstieg per Rückgabe organisieren kann. Es macht keinen Sinn, eine Million Zeilen zu kompilieren, wenn die Funktion in der dritten Zeile zurückkehrt. Sie müssen diese Punkte verfolgen und in solchen Fällen die Vorlage A verwenden.

Extraktion der Körperfunktionen

Benchmark.js verwendet eine andere Technologie. Wir können sagen, dass es die besten Seiten aller dieser Muster enthält. Wir setzen keine Zyklen ein, um Speicherplatz zu sparen. Um die die Genauigkeit beeinflussenden Faktoren zu reduzieren und die Tests mit lokalen Methoden und Variablen arbeiten zu lassen, extrahieren wir den Funktionskörper für jeden Test. Z.B:

var x = 1,
    y = '1';
function test() {
  x == y;
}
while (iterations--) {
  test();
}
// …скомпилируется в →
var x = 1,
    y = '1';
while (iterations--) {
  x == y;
}


Danach führen wir den extrahierten Code in der while-Schleife (Muster A) aus, wiederholen, bis die angegebene Zeit verstrichen ist (Muster B), wiederholen alles so oft, bis statistisch signifikante Ergebnisse erzielt werden.

Was Sie beachten müssen


Nicht ganz richtig Timer arbeiten

In einigen Kombinationen von Betriebssystem und Browser funktionieren Timer aus verschiedenen Gründen möglicherweise nicht richtig . Wenn Sie beispielsweise Windows XP laden, beträgt die Unterbrechungszeit normalerweise 10-15 ms. Das heißt, alle 10 ms empfängt das Betriebssystem eine Unterbrechung vom Systemzeitgeber. Einige ältere Browser (IE, Firefox 2) basieren auf dem OS-Timer, der beispielsweise Date () aufruft. GetTime () empfängt Daten direkt vom OS. Und wenn der Timer nur alle 10-15 ms aktualisiert wird, kommt es zu einer Anhäufung von Messungenauigkeiten.

Dies kann jedoch umgangen werden. In JS können Sie die minimale Zeiteinheit abrufen . Danach müssen Sie rechnenTestlaufzeit, so dass der Fehler nicht mehr als 1% beträgt. Um den Fehler zu erhalten, müssen Sie diese Mindesteinheit in zwei Hälften teilen. Beispielsweise verwenden wir IE6 unter Windows XP und die Mindesteinheit beträgt 15 ms. Der Fehler beträgt 15 ms / 2 = 7,5 ms. Damit dieser Fehler nicht mehr als 1% der Messzeit beträgt, teilen wir ihn durch 0,01: 7,5 / 0,01 = 750 ms.

Andere Timer

Wenn Chrome und Chromium mit dem Parameter --enable-benchmarking flag gestartet werden, erhalten Sie Zugriff auf die chrome.Interval-Methode, mit der Sie einen hochauflösenden Timer mit einer Dauer von bis zu Mikrosekunden verwenden können. Bei der Arbeit an Benchmark.js hat John-David Dalton einen Nanosekunden-Timer in Java kennengelernt und von JS über ein kleines Java-Applet darauf zugegriffen .

Mit einem hochauflösenden Timer können Sie weniger Testzeit einstellen, was zu weniger Fehlern führt.

Firebug deaktiviert JIT in Firefox

Das gestartete Firebug-Add-On deaktiviert die Just-in-Time- Kompilierung , sodass alle Tests im Interpreter ausgeführt werden. Sie werden dort viel langsamer als gewöhnlich arbeiten. Denken Sie daran, Firebug vor den Tests zu deaktivieren.

Das Gleiche gilt, wenn auch in geringerem Maße, für Web Inspector und Opera Dragonfly. Schließen Sie sie, bevor Sie die Tests ausführen, damit sie die Ergebnisse nicht beeinflussen.

Features und Browser Bugs

Tests, die Schleifen verwenden, sind mit verschiedenen Browser-Fehlern behaftet - ein Beispiel wurde im IE9 mit seiner Funktion zum Entfernen von totem Code gezeigt . Fehler in der Mozilla TraceMonkey- Engine oder beim Zwischenspeichern von querySelectorAll- Ergebnissen in Opera 11 können ebenfalls dazu führen, dass Sie nicht die richtigen Ergebnisse erzielen . Sie müssen sie im Auge behalten.

Statistische Signifikanz

Ein Artikel von John Rezig beschreibt, warum die meisten Tests keine statistisch signifikanten Ergebnisse liefern. Kurz gesagt, man sollte immer die Größe des Fehlers jedes Ergebnisses bewerten und auf alle möglichen Arten reduzieren.

Browserübergreifendes Testen

Testen Sie Skripte in verschiedenen Browserversionen. Verlassen Sie sich beispielsweise nicht auf Kompatibilitätsmodi im Internet Explorer. Außerdem beschränkte IE das Skript bis zur 8. Version auf 5 Millionen Anweisungen. Wenn Ihr System schnell ist, kann das Skript sie in einer halben Sekunde ausführen. In diesem Fall erhalten Sie im Browser eine „Skript-Warnung“. Anschließend müssen Sie die Anzahl der zulässigen Vorgänge in der Registrierung bearbeiten. Oder verwenden Sie das Programm , das diese Einschränkung behebt. Glücklicherweise wurde es bereits in IE9 entfernt.

Fazit

Ob Sie mehrere Tests durchführen, Ihre Testsuite schreiben oder sogar eine Bibliothek - das JS-Testproblem birgt viele versteckte Probleme. Benchmark.js und jsPerf aktualisiert wöchentlich, behebe Fehler und füge neue Funktionen hinzu, um die Genauigkeit der Tests zu erhöhen.

Jetzt auch beliebt: