JavaScript Engines: Performance-Steigerung der Browser
Seite 5: Inline Caches in mehreren Varianten
Leider ist es in JavaScript selten, dass wirklich immer nur ein Typ vorhanden ist. Insbesondere im Rahmen der funktionalen Programmierung ist das sogenannte "Duck Typing" verbreitet. Dabei ist es nicht wichtig, dass es immer derselbe Typ ist, sondern dass nur eine Methode oder Property vorhanden ist und eine Funktion somit auf unterschiedliche Typen angewandt werden kann. Im Beispiel bedeutet das, dass der Funktion getName nur wichtig ist, dass die übergebenen Parameter eine Property namens lastname haben. Ob sich die Objekte voneinander unterscheiden, ist vollkommen egal.
Leider führt das zu unterschiedlichen Typen. Zum Glück können Inline Caches mehrere Typen speichern. Man spricht von einem polymorphen Inline Cache, im Gegensatz zu einem monomorphen, der wirklich nur einen Typ speichert.
Bei Googles Engine V8 gibt es den polymorphen Cache nur bei bis zu vier verschiedenen Typen. Gibt es mehr als vier, verwendet sie einen megamorphen Inline Cache. Sie lagert jedoch die Informationen zu den Typen in einen globalen Cachesspeicher aus, wodurch die Variante wesentlich langsamer ist als mono- oder polymorph.
Inline Caches !== Typen
Es ist wichtig anzumerken, dass Inline Caches keine wirkliche statische Typisierung integrieren. Man versucht nur vielmehr, die Geschwindigkeitsvorteile einer statischen Typisierung mit einer dynamischen wettzumachen. Zwecks besserem Verständnis hat der Autor immer den Ausdruck "Typ" verwendet. Es ist jedoch so, dass es sich nicht wirklich um Typen im klassischen Sinne wie JavaScript-Datentypen handelt. Die technische Fachbezeichnung hierfür tritt oftmals unter den Namen "Object Shapes" oder "Hidden Classes" auf. Dazu hat jede Engine eine eigene spezielle Bezeichnung. Nichtsdestotrotz sollte das Prinzip eines Typs dem Leser geläufig sein und zum besseren Verständnis der Funktionsweise von Inline Caches dienen.
Runde Zwei des Codebeispiels
Es sollte nun klar sein, dass das Codebeispiel durch das Hinzufügen weiterer Properties fünf verschiedene "Typen" oder Object Shapes erzeugt hat. Dabei wechselte es von einem monomorphen zu einem megamorphen Inline Cache. Wie können Entwickler nun trotzdem zu einem monomorphen Inline Cache gelangen? Die Lösung dazu ist relativ simpel.
Man muss sicherstellen, dass alle Objekte dieselben Properties haben und dass sie auch immer in derselben Reihenfolge definiert sind. Um das zu erreichen, helfen die mit ECMAScript 6 eingeführten Klassen. Im Konstruktur können Nutzer alle Properties definieren und mit Standardwerten sicherstellen, dass wirklich jede Property mit dem richtigen Typ initialisiert wird:
(() => {
class Person {
constructor({
firstname = "",
lastname = "",
spaceship = "",
job = "",
gender = "",
retired = false
} = {}) {
Object.assign(this, {
firstname,
lastname,
spaceship,
job,
gender,
retired
});
}
}
const han = new Person({
firstname: "Han",
lastname: "Solo",
spaceship: "Falcon"
});
const luke = new Person({
firstname: "Luke",
lastname: "Skywalker",
job: "Jedi"
});
const leia = new Person({
firstname: "Leia",
lastname: "Organa",
gender: "female"
});
const obi = new Person({
firstname: "Obi-Wan",
lastname: "Kenobi",
retired: true
});
const yoda = new Person({ lastname: "Yoda" });
const people = [han, luke, leia, obi, yoda, luke, leia, obi];
const getName = person => person.lastname;
console.time("engine");
for (var i = 0; i < 1000 * 1000 * 1000; i++) {
getName(people[i & 7]);
}
console.timeEnd("engine");
})();
1,319 Sekunden (Dell XPS 15 (9570) mit i7-8750H, Windows 10 Pro, Chrome 71)
Führt man nun das Skript aus, erfolgt die Ausführung genauso schnell wie im ersten Beispiel.