Rolf b: JavaScript parser animation

Beitrag lesen

Es wird wohl keine JavaScript Engine geben, die den Quelltext ständig neu interpretiert. So dumm wäre ja nicht mal ich.

Es wird immer so sein, dass Syntaxprüfung und Umwandlung in ein Format, das man "nur noch" ausführen muss, beim Laden stattfinden. Das resultierende Format ist dann sehr verschieden, und bereits auf dieser Stufe sind unterschiedliche Grade von Optimierung möglich.

Es gibt auch JavaScript Engines, die nativen Maschinencode erzeugen. Wobei das auch nicht UNBEDINGT performanter sein muss. Man kann auch Maschinencode erzeugen, der aus einem Bibliotheksaufruf nach dem anderen besteht (Auabeispiel: IBM PC BASIC Compiler aus den 80ern: Reserviert die ganze Soft-Interrupt Tabelle für sich und macht aus jedem Befehl einen Interrupt-Aufruf). Viele Pseudocode-Interpreter laufen aber auf das gleiche Vorgehen heraus: Lies Pseudocode-Byte, hole Bibliotheksadresse dazu aus Tabelle, CALL, lies Pseudocodebyte, etc. Das KANN gelegentlich sehr fix sein und kompakten Code erzeugen (Clipper Compiler für dBase, 90er Jahre).

Der Unterschied in der Performance entsteht im Ausführungsmodell. Viele Pseudocode-Engines verfolgen ein Stackmodell: Sie schieben alle Arbeitsdaten auf einen Stack und rufen dann Bibliotheksfunktionen auf, die auf der Stackspitze operieren. (Push 3, Push 5, Add -> nimmt die 5 vom Stack und ersetzt die 3 durch eine 8). Wer mal einen HP Taschenrechner mit UPN hatte, kennt das :). Dabei finden viele Speicher-zu-Speicher Transfers statt. Ein guter JIT (just-in-time Compiler) ist im Stande, diese Stackoperationen in Registeroperationen passend zur Architektur des Prozessors zu übersetzen, und ggf. zu erkennen, dass ein Wert nur temporär ist, gleich wiederverwendet wird und daher im Register bleiben kann ohne im Speicher geparkt werden zu müssen. Schlaurere JIT sortieren auch deine Anweisungen um, wenn das Ergebnis logisch gleich bleibt und dadurch die Registerausnutzung besser ist oder die internen Abhängigkeiten im Prozessor besser koordiniert werden. Natürlich - je schlauer der JIT, desto länger muss er grübeln und desto länger braucht es, bis dein Programm anfängt, etwas zu tun. Es ist immer ein Balanceakt. Auf deinem Handy wirst Du eher einen dummen JIT finden, in Umgebungen wie node.js wird ein besserer JIT sitzen weil diese Umgebungen für länger laufende Scripte ausgelegt sind.

JavaScript ist ein relativ schwieriger Optimierungskunde, weil jedes Objekt in JavaScript eine Key-Value Map ist, die dynamisch modifiziert werden kann und nur die Spitze eines Eisbergs (sprich: Prototype-Chain) ist. Bei jedem Zugriff auf ein Property muss neu geprüft werden, wo das Property denn nun tatsächlich steckt. Und bei jedem Zugriff auf eine nicht-lokale Variable muss die gerade gültige Scope-Chain abgegrast werden. Die Optimierung dieser Zugriffe ist zentral für JavaScript, da ist ein JIT der nativen Maschinencode ausspuckt weniger wichtig. Diese Aufgabe löst jede JS Engine anders und unterschiedlich gut.

Rolf