Feiern von 10 Jahren V8
In diesem Monat jährt sich zum 10. Mal die Veröffentlichung von nicht nur Google Chrome, sondern auch des V8-Projekts. Dieser Beitrag gibt einen Überblick über die wichtigsten Meilensteine des V8-Projekts in den letzten 10 Jahren sowie die Jahre davor, als das Projekt noch geheim war.
gource
.Bevor V8 veröffentlicht wurde: die frühen Jahre
Google stellte Lars Bak im Herbst 2006 ein, um eine neue JavaScript-Engine für den Chrome-Webbrowser zu entwickeln, der zu dieser Zeit noch ein geheimes internes Google-Projekt war. Lars war kürzlich aus dem Silicon Valley nach Aarhus, Dänemark, zurückgekehrt. Da es dort kein Google-Büro gab und Lars in Dänemark bleiben wollte, begannen Lars und mehrere der ursprünglichen Ingenieure des Projekts in einem Nebengebäude auf seinem Bauernhof mit der Arbeit am Projekt. Die neue JavaScript-Laufzeit wurde „V8“ getauft, eine spielerische Anspielung auf den leistungsstarken Motor, den man in einem klassischen Muscle-Car finden kann. Später, als das V8-Team gewachsen war, zogen die Entwickler aus ihren bescheidenen Räumlichkeiten in ein modernes Bürogebäude in Aarhus um, aber das Team nahm sein einzigartiges Engagement und seinen Fokus mit, um die schnellste JavaScript-Laufzeit der Welt zu entwickeln.
Einführung und Weiterentwicklung von V8
V8 wurde am selben Tag Open Source, als Chrome veröffentlicht wurde: am 2. September 2008. Der erste Commit stammt vom 30. Juni 2008. Vor diesem Datum fand die Entwicklung von V8 in einem privaten CVS-Repository statt. Anfangs unterstützte V8 nur die ia32- und ARM-Befehlssätze und verwendete SCons als Build-System.
2009 wurde eine brandneue reguläre Ausdruck-Engine namens Irregexp eingeführt, die Leistungsverbesserungen bei regulären Ausdrücken unter realen Bedingungen brachte. Mit der Einführung eines x64-Ports erhöhte sich die Anzahl der unterstützten Befehlssätze von zwei auf drei. 2009 markierte auch die erste Veröffentlichung des Node.js-Projekts, das V8 integriert. Die Möglichkeit, V8 in Nicht-Browser-Projekte zu integrieren, wurde explizit erwähnt in dem ursprünglichen Chrome-Comic. Mit Node.js wurde dies Realität! Node.js entwickelte sich zu einem der beliebtesten JavaScript-Ökosysteme.
2010 erlebte einen großen Leistungsschub in der Laufzeit, als V8 einen brandneuen optimierenden JIT-Compiler einführte. Crankshaft erzeugte Maschinencode, der doppelt so schnell und 30 % kleiner war als der vorherige (namenlose) V8-Compiler. Im selben Jahr fügte V8 seinen vierten Befehlssatz hinzu: 32-Bit-MIPS.
2011 brachte drastische Verbesserungen in der Speicherbereinigung. Ein neuer inkrementeller Garbage Collector reduzierte die Pausenzeiten drastisch und hielt dabei Spitzenleistung und niedrigen Speicherverbrauch aufrecht. V8 führte das Konzept der Isolates ein, welches Einbettungsmodule erlaubt, mehrere Instanzen der V8-Laufzeit in einem Prozess zu starten und den Weg für leichtergewichtige Webworkers in Chrome ebnet. Die erste von zwei Migrationen des V8-Build-Systems fand statt, als wir von SCons zu GYP wechselten. Wir implementierten Unterstützung für den ES5-Strict-Modus. Währenddessen zog die Entwicklung von Aarhus nach München (Deutschland) unter neuer Leitung um, mit vielen Synergien vom ursprünglichen Team in Aarhus.
2012 war ein Meilensteinjahr für das V8-Projekt. Das Team führte Geschwindigkeits-Sprints durch, um die Leistung von V8 anhand der Benchmark-Suiten SunSpider und Kraken zu optimieren. Später entwickelten wir eine neue Benchmark-Suite namens Octane (mit V8 Bench im Kern), die den Wettbewerb um Spitzenleistung in den Vordergrund rückte und massive Verbesserungen in der Laufzeit- und JIT-Technologie aller großen JS-Engines anregte. Ein Ergebnis dieser Bemühungen war der Wechsel von zufälligem Sampling zu einer deterministischen, auf Zählung basierenden Technik zur Erkennung „heißer“ Funktionen im Laufzeitprofiler von V8. Dies machte es deutlich weniger wahrscheinlich, dass einige Seitenladungen (oder Benchmark-Läufe) zufällig wesentlich langsamer waren als andere.
2013 sah die Einführung einer Low-Level-Teilmenge von JavaScript namens asm.js. Da asm.js auf statisch typisierte Arithmetik, Funktionsaufrufe und Heap-Zugriffe mit primitiven Typen beschränkt ist, konnte validierter asm.js-Code mit vorhersehbarer Leistung laufen. Wir veröffentlichten eine neue Version von Octane, Octane 2.0, mit Updates zu bestehenden Benchmarks sowie neuen Benchmarks, die Anwendungsfälle wie asm.js abdecken. Octane förderte die Entwicklung neuer Compiler-Optimierungen wie Allocation Folding und Optimierungen auf Grundlage von Allocationsstellen für Typübergänge und Pretenuring, die die Spitzenleistung erheblich verbesserten. Im Rahmen eines intern unter dem Namen „Handlepocalypse“ bekannten Projekts wurde die V8-Handle-API vollständig umgeschrieben, um ihre korrekte und sichere Verwendung zu erleichtern. Ebenfalls im Jahr 2013 wurde die Implementierung von TypedArray
s in JavaScript von Blink zu V8 verschoben.
Im Jahr 2014 verlagerte V8 mit Concurrent Compilation einen Teil der JIT-Kompilierungsarbeit vom Hauptthread, was Ruckler verringerte und die Leistung erheblich verbesserte. Später im Jahr brachten wir die erste Version eines neuen Optimierungskompressors namens TurboFan ein landeten. In der Zwischenzeit halfen unsere Partner, V8 auf drei neue Befehlssatzarchitekturen zu portieren: PPC, MIPS64 und ARM64. Nach Chromium wechselte V8 zu einem weiteren Build-System, GN. Die Testinfrastruktur von V8 wurde erheblich verbessert, mit einem Tryserver, der nun verwendet werden konnte, um jeden Patch auf verschiedenen Build-Bots vor der Integration zu testen. Für die Versionskontrolle wechselte V8 von SVN zu Git.
2015 war ein arbeitsreiches Jahr für V8 in vielerlei Hinsicht. Wir führten Code-Caching und Skript-Streaming ein, was die Ladezeiten von Webseiten erheblich beschleunigte. Unsere Arbeit zur Verwendung von Allocations-Mementos im Laufzeitsystem wurde in ISMM 2015 veröffentlicht. Später im Jahr starteten wir die Arbeit an einem neuen Interpreter namens Ignition begannen. Wir experimentierten mit der Idee, JavaScript mit Strong Mode zu unterteilen, um stärkere Garantien und eine besser vorhersehbare Leistung zu erreichen. Wir implementierten Strong Mode hinter einer Flagge, stellten jedoch später fest, dass seine Vorteile die Kosten nicht rechtfertigten. Die Hinzufügung einer Commit Queue brachte große Produktivitäts- und Stabilitätsverbesserungen. Der Garbage Collector von V8 begann auch mit Embedders wie Blink zusammenzuarbeiten, um Garbage-Collection-Arbeiten während Leerlaufzeiten zu planen. Garbage Collection zur Leerlaufzeit verringerte die wahrnehmbaren Ruckler durch Garbage Collection sowie den Speicherverbrauch erheblich. Im Dezember landete das erste WebAssembly-Prototyp in V8.
Im Jahr 2016 haben wir die letzten Teile des ES2015 (früher bekannt als „ES6“) Feature-Sets ausgeliefert (einschließlich Promises, Klassen-Syntax, lexikalischer Bereich, Destructuring und mehr) sowie einige ES2016 Features. Wir begannen außerdem mit der Einführung der neuen Ignition- und TurboFan-Pipeline, nutzten sie, um ES2015- und ES2016-Features zu kompilieren und zu optimieren, und stellten Ignition standardmäßig für leistungsarme Android-Geräte bereit. Unsere erfolgreiche Arbeit zur Müllsammlung in Leerlaufzeiten wurde bei PLDI 2016 vorgestellt. Wir starteten das Orinoco-Projekt, einen neuen vorwiegend parallelen und gleichzeitigen Garbage Collector für V8, um die Müllsammlungszeit im Haupt-Thread zu reduzieren. Mit einer deutlichen Neuausrichtung verlagerten wir unsere Performance-Bemühungen weg von synthetischen Mikro-Benchmarks und begannen stattdessen ernsthaft, die Real-World-Performance zu messen und zu optimieren. Für Debugging wurde der V8 Inspector von Chromium zu V8 migriert, sodass jedes V8-Embedding (und nicht nur Chromium) die Chrome DevTools verwenden kann, um JavaScript, das in V8 ausgeführt wird, zu debuggen. Der WebAssembly-Prototyp wurde aus der Prototyp- in die experimentelle Unterstützung hochgestuft, in Zusammenarbeit mit anderen Browser-Anbietern experimentelle Unterstützung für WebAssembly. V8 erhielt den ACM SIGPLAN Programming Languages Software Award. Und ein weiterer Port wurde hinzugefügt: S390.
Im Jahr 2017 haben wir schließlich die mehrjährige Überarbeitung der Engine abgeschlossen, wodurch die neue Ignition- und TurboFan-Pipeline standardmäßig aktiviert wurde. Dies ermöglichte es uns später, Crankshaft zu entfernen (130.380 gelöschte Zeilen Code) und Full-codegen aus dem Code-Repository zu entfernen. Wir führten Orinoco v1.0 ein, einschließlich gleichzeitigem Markieren, gleichzeitige Räumung, paralleles Scavenging und parallele Kompaktierung. Wir haben Node.js offiziell als erstklassigen V8-Embedder neben Chromium anerkannt. Seitdem ist es unmöglich, einen V8-Patch zu implementieren, der den Node.js-Testbereich bricht. Unsere Infrastruktur erhielt Unterstützung für Korrektheitsfuzzing, um sicherzustellen, dass jedes Stück Code gleiche Ergebnisse liefert, unabhängig davon, in welcher Konfiguration es ausgeführt wird.
In einem industrieweit koordinierten Launch hat V8 WebAssembly standardmäßig aktiviert. Wir implementierten Unterstützung für JavaScript-Module sowie für die vollständigen ES2017- und ES2018-Feature-Sets (einschließlich asynchroner Funktionen, geteiltem Speicher, asynchroner Iteration, Rest/Spread-Eigenschaften und RegExp-Funktionen). Wir haben native Unterstützung für JavaScript-Codeabdeckung veröffentlicht und den Web Tooling Benchmark eingeführt, um zu messen, wie sich die Optimierungen von V8 auf die Leistung realer Entwicklertools und der von ihnen generierten JavaScript-Ausgabe auswirken. Wrapper-Tracing von JavaScript-Objekten zu C++-DOM-Objekten und zurück ermöglichte es uns, langjährige Speicherlecks in Chrome zu lösen und die transitiven Abschlüsse von Objekten über den JavaScript- und Blink-Heap effizient zu bewältigen. Später haben wir diese Infrastruktur verwendet, um die Funktionen des Entwicklertools zur Snapshot-Erfassung des Heaps zu erweitern.
Das Jahr 2018 erlebte ein industrieumspannendes Sicherheitsereignis, das unser Verständnis von CPU-Informationssicherheit mit der öffentlichen Offenlegung der Spectre/Meltdown-Schwachstellen auf den Kopf stellte. V8-Ingenieure führten umfangreiche offensive Forschungen durch, um die Bedrohung für verwaltete Sprachen besser zu verstehen und Gegenmaßnahmen zu entwickeln. V8 hat Abschwächungsmaßnahmen gegen Spectre und ähnliche Seitenkanal-Angriffe für Embeds bereitgestellt, die nicht vertrauenswürdigen Code ausführen.
Vor Kurzem haben wir einen Baseline-Compiler für WebAssembly namens Liftoff veröffentlicht, der die Startzeit für WebAssembly-Anwendungen erheblich reduziert und dabei dennoch vorhersehbare Leistung gewährleistet. Wir haben BigInt
veröffentlicht, ein neues JavaScript-Primitiv, das Ganzzahlen mit beliebiger Genauigkeit ermöglicht. Wir haben eingebettete Builtins implementiert und die Möglichkeit geschaffen, diese lazy zu deserialisieren, wodurch der Speicherverbrauch von V8 für mehrere Instanzen erheblich reduziert wird. Wir haben es möglich gemacht, Skript-Bytecode in einem Hintergrundthread zu kompilieren. Wir haben das Unified V8-Blink Heap-Projekt gestartet, um eine komponentenübergreifende V8- und Blink-Müllsammlung synchron durchzuführen. Und das Jahr ist noch nicht vorbei...
Leistungsgewinne und -verluste
Der V8-Benchmark-Score von Chrome über die Jahre hinweg zeigt die Auswirkungen der Änderungen in V8 auf die Leistung. (Wir verwenden den V8-Benchmark, da er einer der wenigen Benchmarks ist, die noch in der ursprünglichen Chrome-Beta ausgeführt werden können.)
Unser Score in diesem Benchmark ist über die letzten zehn Jahre um das Vierfache gestiegen!
Allerdings könnten Ihnen zwei Leistungseinbrüche über die Jahre aufgefallen sein. Beide sind interessant, da sie bedeutenden Ereignissen in der Geschichte von V8 entsprechen. Der Leistungseinbruch im Jahr 2015 ereignete sich, als V8 Basisversionen der ES2015-Features auslieferte. Diese Features waren querschnittlich in der V8-Codebasis, weswegen wir uns bei ihrer anfänglichen Veröffentlichung auf Korrektheit statt auf Leistung konzentrierten. Wir haben diese kleinen Leistungseinbußen akzeptiert, um die Features Entwicklern so schnell wie möglich bereitzustellen. Anfang 2018 wurde die Spectre-Sicherheitslücke offengelegt, und V8 stellte Abschwächungen bereit, um Benutzer vor möglichen Exploits zu schützen, was zu einem weiteren Leistungseinbruch führte. Glücklicherweise können wir, da Chrome jetzt Site-Isolation ausliefert, die Abschwächungen wieder deaktivieren und die Leistung wieder auf Augenhöhe bringen.
Eine weitere Erkenntnis aus diesem Diagramm ist, dass der Graph ab etwa 2013 beginnt, sich abzuflachen. Bedeutet das, dass V8 aufgegeben hat und keine Investitionen in die Leistung mehr tätigt? Ganz im Gegenteil! Das Abflachen der Grafiken stellt den Wechsel des V8-Teams von synthetischen Mikro-Benchmarks (wie V8 Bench und Octane) hin zur Optimierung für echte Leistung in der Praxis dar. V8 Bench ist ein veralteter Benchmark, der keine modernen JavaScript-Funktionen verwendet und auch keine tatsächlichen realen Produktionscodes approximiert. Im Gegensatz dazu steht die neuere Speedometer-Benchmark-Suite:
Obwohl V8 Bench von 2013 bis 2018 nur minimale Fortschritte zeigt, stieg unsere Speedometer-1-Punktzahl in derselben Zeitperiode um (weitere) 4×. (Wir verwendeten Speedometer 1, weil Speedometer 2 moderne JavaScript-Funktionen nutzt, die 2013 noch nicht unterstützt wurden.)
Heutzutage haben wir noch bessere Benchmarks, die moderne JavaScript-Anwendungen genauer widerspiegeln, und darüber hinaus messen und optimieren wir aktiv für bestehende Webanwendungen.
Zusammenfassung
Obwohl V8 ursprünglich für Google Chrome entwickelt wurde, war es von Anfang an ein eigenständiges Projekt mit einer separaten Codebasis und einer Einbettungs-API, die es jedem Programm ermöglicht, dessen JavaScript-Ausführungsdienste zu nutzen. In den letzten 10 Jahren hat die offene Natur des Projekts dazu beigetragen, dass es sich zu einer Schlüsseltechnologie nicht nur für die Webplattform, sondern auch in anderen Kontexten wie Node.js entwickelt hat. Das Projekt entwickelte sich und blieb trotz vieler Veränderungen und dramatischen Wachstums relevant.
Ursprünglich unterstützte V8 nur zwei Befehlssätze. In den letzten 10 Jahren hat die Liste der unterstützten Plattformen auf acht erweitert: ia32, x64, ARM, ARM64, 32- und 64-Bit MIPS, 64-Bit PPC und S390. Das Build-System von V8 wurde von SCons über GYP zu GN migriert. Das Projekt zog von Dänemark nach Deutschland und beschäftigt jetzt Ingenieure auf der ganzen Welt, darunter in London, Mountain View und San Francisco, sowie Mitwirkende außerhalb von Google aus vielen weiteren Orten. Wir haben die gesamte JavaScript-Kompilierungspipeline von namenlosen Komponenten zu Full-codegen (einem Basis-Compiler) und Crankshaft (einem feedbackgesteuerten Optimierungs-Compiler) zu Ignition (einem Interpreter) und TurboFan (einem besseren feedbackgesteuerten Optimierungs-Compiler) umgestaltet. V8 entwickelte sich von „nur“ einer JavaScript-Engine dazu, auch WebAssembly zu unterstützen. Die JavaScript-Sprache selbst entwickelte sich von ECMAScript 3 zu ES2018; die neueste V8-Version implementiert sogar Post-ES2018-Funktionen.
Der Handlungsbogen des Webs ist lang und beständig. Das zehnjährige Jubiläum von Chrome und V8 zu feiern, ist eine gute Gelegenheit, darüber nachzudenken, dass dies zwar ein großer Meilenstein ist, die Geschichte der Webplattform jedoch bereits seit mehr als 25 Jahren dauert. Wir sind zuversichtlich, dass die Geschichte des Webs noch mindestens genauso lange weitergehen wird. Wir setzen uns dafür ein, dass V8, JavaScript und WebAssembly weiterhin interessante Charaktere in dieser Erzählung bleiben. Wir sind gespannt darauf, was das nächste Jahrzehnt bereithält. Bleiben Sie dran!