Date post: | 12-Aug-2015 |
Category: |
Software |
Upload: | sebastian-springer |
View: | 275 times |
Download: | 0 times |
JavaScript Performance
WHO AM I?
• Sebastian Springer
• aus München
• arbeite bei Mayflower
• https://github.com/sspringer82
• @basti_springer
• Consultant, Trainer, Autor
Wie öffnet ihr eure Dev Tools im Browser?
• Was sind Dev Tools?
• Tastenkombination
• Mausklick (maximal 2)
Was erwartet euch?
Best Practices: Wie kann ich Probleme von vornherein vermeiden?
Performance Optimierung: Was mache ich, wenn meine Applikation langsam ist?
Performance
Es gibt nicht die eine JavaScript Performance. Wie sich eine Applikation benimmt, hängt von zahlreichen Komponenten ab.
Die wichtigsten sind Netzwerk, CPU, Memory und Rendering.
Vorgehensweise
Ein kleiner Ausflug:
Andreas Hermsdorf / pixelio.de
Vorgehensweise
Analyse
Bewertung
Durchführung
Kontrolle
Thorben Wengert / pixelio.de
Analyse
Tim Reckmann / pixelio.de
Developer Tools
Analyse
Meist weiß man, wo die Applikation langsam ist. Ansonsten werden CPU, Memory, Rendering und Netzwerk überprüft.
Konkrete Messwerte für den späteren Vergleich festhalten.
Profile-Daten können gespeichert, geladen und verglichen werden.
Bewertung
bschpic / pixelio.de
Bewertung
Meist findet man mehrere Problemstellungen, hat jedoch nicht genügend Ressourcen. Also Kosten-Nutzen-
Abwägung durchführen. Cheap Wins zuerst durchführen, gravierende Umbauten mit
wenig Benefits eher nicht durchführen.
Erwartete Verbesserung oder Zielmetrik festhalten.
BewertungMaßnahme Aufwand Nutzen
GZIP Compression — +
Concat & Uglify + ++
DOM verkleinern ++ +
DOM Operationen gruppieren ++ ++
… … …
Durchführung
I-vista / pixelio.de
Durchführung
Anpassung des Quellcodes. Es sollte auf jeden Fall ein Versionskontrollsystem (git, svn) eingesetzt werden. So
können die Stände verglichen und falls nötig zurückgesetzt werden.
Tests nicht vergessen!
Kontrolle
Tim Reckmann / pixelio.de
Kontrolle
Die Messwerte der Analyse werden mit den aktuellen Werten der Applikation verglichen.
Im Anschluss steht die Entscheidung, ob weitere Verbesserungen erforderlich sind oder ob weitere Maßnahmen keinen erheblichen Mehrwert bieten.
Performance
Netzwerk CPU
Memory Rendering
Netzwerk
Klicker / pixelio.de
Netzwerk
Anfragen vom Client an den Server über HTTP. Browser haben nur eine gewisse Anzahl an parallelen
Requests pro Subdomain.
Netzwerk
https://developer.chrome.com/devtools/docs/network
Navigation Timing API: https://developer.mozilla.org/en-US/docs/Navigation_timing
Netzwerk
Netzwerk• Stalled/Blocking: Zeit, bevor die Anfrage gesendet wird.
• Proxy Negotiation: Verbinden mit Proxy.
• DNS Lookup: Hostnamen auflösen.
• Initial Connection: TCP Handshake, SSL Negotiation
• SSL: SSL Handshake
• Request Sent: Versenden der Anfrage.
• Waiting (TTFB): Initiale Antwort des Servers.
• Content Download: Herunterladen der Antwort.
Netzwerk• Anzahl der Requests reduzieren
• Größe der Responses reduzieren
• gzip aktivieren
• DNS Lookups reduzieren (wenige unterschiedliche Hosts)
• Caching
CPU
Prozessor-Ressourcen, die benötigt werden, um den JavaScript-Quellcode auszuführen.
Die meiste Performance geht aber durch DOM-Operationen verloren.
CPU
JavaScript Engines weisen zahlreiche Optimierungen auf. Häufig liegen die Probleme nicht an der Ausführung des
JavaScript-Quellcodes.
Hidden ClassesOptimierung des Browsers, um schneller auf Eigenschaften
von Objekten zuzugreifen.
Maschinencode Generierung
point.x
# ebx = the point object cmp [ebx,<hidden class offset>],<cached hidden class> jne <inline cache miss> mov eax,[ebx, <cached x offset>]
Gibt es für die Eigenschaft eine Hidden Class, wird diese verwendet. Ansonsten wird der Cache Miss behandelt. Dann
wird der Wert für die Eigenschaft ausgeliefert.
Garbage Collection
Aufräumen des Speichers. Zwei Speicherbereiche. Mit unterschiedlichen Algorithmen.
GC benötigt Zeit, hält die Applikation komplett an. Das kann zu Ruckeln in Applikationen führen.
Timeline
Die Timeline der Chrome Developer Tools gibt einen ersten Überblick über CPU und Memory Daten und ist ein guter
Ausgangspunkt für die Analyse von Performance-Problemen.
Timeline
CPU
Das CPU-Profile gibt genaueren Aufschluss darüber, welche Routinen wie viele Ressourcen benötigt haben. Drei
verschiedene Darstellungsarten:
- Chart: Flame Chart - Heavy (Bottom up): Tablle mit den teuersten Routinen oben - Tree (Top Down): Baumdarstellung
CPU
CPU - ChartEine Funktion hat immer die gleiche Farbe. Erleichtert die
Mustererkennung. Simulieren Callstacks jeweils mit Verweis auf die
entsprechende Codestelle.
Name: Funktionsname Self time: Zeit der Funktion selbst
Total time: Zeit der Funktion und aller Unterfunktionen Aggregated self time: Summe der self time aller Aufrufe
Aggregated total time: Summe der total time aller Aufrufe
CPU
CPU
Call Graphs?
code2flow: experimentelles Tool zur Generierung von Call Graphs
https://github.com/scottrogowski/code2flow
https://github.com/scottrogowski/code2flow/blob/master/jqueryexample.png
Call Graph für jQuery
Memory
Jan von Bröckel / pixelio.de
Memory
Arbeitsspeicher, der durch die Applikation verbraucht wird. JavaScript-Objekte, Repräsentationen von DOM-Elementen,
…
Heap Snapshot
• Summary: Zusammenfassung
• Comparison: Vergleich zweier Snapshots
• Containment: Übersicht über die Objektstruktur
• Statistics: Verteilung des Speichers auf Objekttypen
Verschiedene Views:
Abbild des aktuellen Speichers.
Heap Snapshot
Shallow Size: Speicher, den ein Objekt selbst benötigt.
Retained Size: Speicher, den ein Objekt und die nur von ihm referenzierten Objekte benötigen.
Distance: Kürzester Weg durch die Memory Hierarchie.
Record Heap Allocations
Wie der Snapshot, nur über Zeit gesehen. Zum Auffinden von Memory Leaks.
Rendering
Burkard Vogt / pixelio.de
Rendering
Reflow: Neuberechnung der Positionen und Geometrie von DOM-Elementen.
Blockierende Operation.
Wann passieren Reflows: DOM-Elemente hinzufügen/entfernen, Klassen anpassen, …
bschpic / pixelio.de
Best Practices
Best Practices
Was kann man bereits bei der Entwicklung beachten, um Performance-Probleme zu vermeiden?
WebworkersKomplexe Berechnungen verzögern die Ausführung und
Blockieren die Applikation. Mit HTML5 kommen Webworker: Kindprozesse im Browser.
Kommunikation über Nachrichten.
Workerprozess hat Zugriff auf: navigator, location, xhr, timing,
Kein Zugriff auf: DOM, window, document, parent
Webworkersvar worker = new Worker('worker.js');worker.postMessage('Hello Worker'); worker.addEventListener('message', function (data) { console.log(data);});
index.js
worker.jsself.addEventListener('message', function (data) { self.postMessage('Hello Main'); });
Variablen und Datenstrukturen
Lokale Variablen nutzen, ist schneller als Property-Zugriff und Array-Zugriff.
Objekte und Properties cachen.
Je tiefer ein Array strukturiert ist, desto langsamer wird der Zugriff.
Prototypen und Methodenfunction Calculator() { this.add = function () {}}
function Calculator() {}Calculator.prototype.add = function () {};
vs
Für jede Instanz wird ein neues Funktionsobjekt erzeugt und eine neue Closure generiert, was alles Speicherplatz
benötigt.
Prototypen und Wertefunction Calculator() { this.a = 1; this.b = {};}
function Calculator() { this.b = {};} Calculator.prototype.a = 1;
vs
Initialisierungslogik muss nicht jedes Mal durchlaufen werden.
ClosuresEine Closure ist eine Funktion und ihr erstellender Kontext. Benötigt also für die Funktion und den erstellenden Kontext
Speicher.
Quelle für Memoryleaks.
DOM-Elemente zeigen auf Closures und umgekehrt. GC kann nicht aufräumen.
Sobald eine Callback-Funktion registriert wird, kann sie nicht durch den GC freigegeben werden. Callbacks sollten
deregistriert werden.
function async() { var msg = 'Hello World'; setTimeout(function() { console.log(msg); }, 1000); }
Inner Funktion mit Closure
function async() { setTimeout(function() { var msg = 'Hello World'; console.log(msg); }, 1000); }
Inner Funktion ohne Closure
function sayHello() { var msg = 'Hello World'; console.log(msg);} function async() { setTimeout(sayHello, 1000);}
Statische Funktion
Schnell
Langsam
ClosuresZugriff auf Variablen wird langsamer durch zusätzliche
Scopes. Je weiter nach außen, desto langsamer wird es.
function createFunctionWithClosure() { var b = 'b'; return function () { var c = 'c'; a; b; c; }; }
createFunctionWithClosure(); c > b > a
Kein with
Die Scope Chain wird modifiziert. Es wird teurer, auf Variablen zuzugreifen.
var a, x, y; var r = 10; with (Math) { a = PI * r * r; x = r * cos(PI); y = r * sin(PI / 2);}
Browser Memory Leaks
Expando Properties: Beliebige Eigenschaften auf DOM-Objekten ,die z.B. zirkuläre Referenzen halten können.
Obj A > Obj B > Obj A
Im besten Fall komplett vermeiden.
Arbeit in Schleifen reduzieren
Jedes Statement in einer Schleife wird pro Schleifendurchlauf ausgeführt.
Funktionsdefinitionen oder Ähnliches vermeiden.
Möglichst statischen Code außerhalb und wirklich nur das Nötigste in der Schleife halten.
for (var i = 0; i < arr.length; i++) {}
var length = arr.length; for (var i = 0; i < length; i++) {}
Rendering
DOM einfach und flach halten.
Möglichst wenige CSS Regeln.
Für komplexes Rendering - position: absolute oder position: fixed
Einfache CSS Selektoren.
Wenige DOM-Zugriffe
DOM-Zugriffe nur wenn erforderlich. Zusammenfassen von DOM-Zugriffen.
Elemente wiederverwenden.
Unnötige Variablen vermeiden
var name = firstname + ' ' + lastname;console.log(name);
console.log(firstname + ' ' + lastname);
vs.
Jede Variable benötigt Speicherplatz. Der GC bekommt Arbeit.
JavaScript spät laden
JavaScript im Fuß der Seite laden.
JavaScript möglichst asynchron laden.
Alternative ist <script defer=“true” src=“”></script> JS wird nach dem Parsen geladen.
<script async src=“”></script> JS wird parallel zum Parsen geladen.
JavaScript on demand laden
RequireJS erlaubt nachträgliches Laden von Dateien zur Laufzeit.
Auch mit Browserify ist Lazy Loading möglich.
Achtung
Keine Premature Optimization - die richtigen Optimierungen erst, wenn man sie braucht.
Keine Mikrooptimierung
Der Quellcode soll lesbar und wartbar bleiben.
Fragen?
Rainer Sturm / pixelio.de
KONTAKT
Sebastian Springer [email protected]
Mayflower GmbH Mannhardtstr. 6 80538 München Deutschland
@basti_springer
https://github.com/sspringer82