JavaScript Performance

Post on 12-Aug-2015

283 views 0 download

Tags:

Transcript of JavaScript Performance

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.

Performance verbessern

Branko Spoljar / pixelio.de

Karl-Heinz Laube / pixelio.de

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

Tim Reckmann / pixelio.de

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

Radka Schöne / pixelio.de

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

Tim Reckmann / pixelio.de

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 sebastian.springer@mayflower.de

Mayflower GmbH Mannhardtstr. 6 80538 München Deutschland

@basti_springer

https://github.com/sspringer82