five principles to improve the raw javascript performance of your app based on what modern...

Click here to load reader

Upload: dominic-bryant

Post on 14-Dec-2015

220 views

Category:

Documents


0 download

TRANSCRIPT

  • Slide 1

Slide 2 Slide 3 Five principles to improve the raw JavaScript performance of your app Based on what modern JavaScript engines like Chakra do with your code Slide 4 Raw JS code: http://aka.ms/FastJS Slide 5 Arrays Objects and properties NumbersAnimation loop Memory allocations Repeat until neighbors list empty Components and control flow Slide 6 Slide 7 Low frame rate causes GLITCHGLITCH Slide 8 What impacts visual throughput? http://aka.ms/IESubsystems + Code Slide 9 What really matters? t 0 (Launch) t n (Close or navigate) 60 fps 16.67ms Paint Slide 10 Raw JS code: http://aka.ms/FastJS Slide 11 Always start with a good profiler Windows Performance Toolkit http://aka.ms/WinPerfKit F12 UI Responsiveness Tool Slide 12 Is JavaScript the bottleneck? Chakra (GC, etc.) Your Code (Script, Execute, etc.) Other Subsystems Slide 13 Do we expect so much of GC to happen? GC Slide 14 Slide 15 Raw JS code: http://aka.ms/FastJS Slide 16 Results Overall FG GC time reduced to 1/3 rd Raw JavaScript perf improved ~3x Slide 17 Obj1Obj1aObj1bRootObj2Obj2aObj2bObj2c Obj2dC-1C-2Obj2eObj3... C-1a...C-1bC-1cC-1a1 C-1b1 Garbage Collector: Flow Program Initialize POOL #1 (SMALL OBJECTS) POOL #2 (LARGE OBJECTS) Zero out Slide 18 What triggers a garbage collection? Every call to new or implicit memory allocation reserves GC memory - Allocations are cheap until current pool is exhausted When the pool is exhausted, engines force a collection - Collections cause program pauses - Pauses could take milliseconds Be careful with object allocation patterns in your apps - Every allocation brings you closer to a GC pause Slide 19 Concurrent Garbage Collection: Advantages Utilizing spare hardware capacity to reduce pauses Shorter pauses Partial, time restricted collections Program Setup Sweep Program Sweep Zero Pages FOREGROUND THREAD BACKGROUND THREAD Initialize Program RescanMark Slide 20 Best practices for staying lean Avoid unnecessary object creation Use object pools, when possible Be aware of allocation patterns - Setting closures to event handlers - Dynamically creating DOM (sub) trees - Implicit allocations in the engine Slide 21 Slide 22 Internal Type System: Fast Object Types var p1; p1.north = 1; p1.south = 0; var p2; p2.south = 0; p2.north = 1; north1 south0 north1 south0 north1 south0 Base Type {} Type {north}Type {south} Base Type {} Type {south, north}Type {north, south} Slide 23 Create fast types and avoid type mismatches Dont add properties conditionally function Player(direction) { if (direction = NE) { this.n = 1; this.e = 1; } else if (direction = ES) { this.e = 1; this.s = 1; }... } var p1 = new Player(NE); // p1 type {n,e} var p2 = new Player(ES); // p2 type {e,s} function Player(north,east,south,west) { this.n = north; this.e = east; this.s = south; this.w = west; } var p1 = new Player(1,1,0,0);//p1 type {n,e,s,w} var p2 = new Player(0,0,1,1);//p2 type {n,e,s,w} p1.type != p2.type p1.type == p2.type Slide 24 Create fast types and avoid type mismatches Dont default properties on prototypes function Player(name) {... }; Player.prototype.n = null; Player.prototype.e = null; Player.prototype.s = null; Player.prototype.w = null; var p1 = new Player("Jodi"); //p1 type{} var p2 = new Player("Mia"); //p2 type{} var p3 = new Player("Jodi"); //p3 type{} p1.n = 1; //p1 type {n} p2.e = 1; //p2 type {e} function Player(name) { this.n = null; this.e = null; this.s = null; this.w = null;... } var p1 = new Player("Jodi"); //p1 type{n,e,s,w} var p2 = new Player("Mia"); //p2 type{n,e,s,w} var p3 = new Player("Jodi"); //p3 type{n,e,s,w} p1.n = 1; //p1 type{n,e,s,w} p2.e = 1; //p2 type{n,e,s,w} p1.type != p2.type != p3.typep1.type == p2.type == p3.type Slide 25 Internal Type System: Slower Property Bags var p1 = new Player(); //prop bag p1.north = 1; p1.south = 1; var p2 = new Player(); //prop bag p2.north = 1; p2.south = 1; Slower Bag {p1} Slower Bag {p2} Slide 26 Avoid conversion from fast type to slower property bags Deleting properties forces conversion function Player(north,east,south,west) { this.n = north; this.e = east; this.s = south; this.w = west; } var p1 = new Player(); delete p1.n; function Player(north,east,south,west) { this.n = north; this.e = east; this.s = south; this.w = west; } var p1 = new Player(); p1.n = 0; // or undefined SLOW FAST Slide 27 Avoid creating slower property bags Add properties in constructor, restrict total properties function Player(north,east,south,west) { this.n = north; this.e = east; this.s = south; this.w = west;... // Restrict to few if possible } var p1 = new Player(); function Player() { this.prop01 = 1; this.prop02 = 2;... this.prop256 = 256;... // Do you need an array? } var p1 = new Player(); SLOWFAST Slide 28 Avoid creating slower property bags Restrict using getters, setters and property descriptors in perf critical paths function Player(north, east, south, west) { Object.defineProperty(this, "n", { get : function() { return nVal; }, set : function(value) { nVal=value; }, enumerable: true, configurable: true }); Object.defineProperty(this, "e", { get : function() { return eVal; }, set : function(value) { eVal=value; }, enumerable: true, configurable: true });... } var p = new Player(1,1,0,0); var n = p.n; p.n = 0;... function Player(north, east, south, west) { this.n = north; this.e = east; this.s = south; this.w = west;... } var p = new Player(1,1,0,0); var n = p.n; p.n = 0;... SLOWFAST Slide 29 Raw JS code: http://aka.ms/FastJS Slide 30 Results Time in script execution reduced ~30% Raw JS performance improved ~30% 3.8s 2.2s Slide 31 Type mismatches may lead to inline cache misses Keep types same; distribute multiple types uniformly function Player(n,e,s,w) {...} //type {1} function rotate(p) { if (p.n == 1){...}//store slot# for n in type{1}... } for (var i = 0; i < boardSize; i++) { matrix[i] = new Player(1,1,0,0); // type {1}... } matrix[boardSize].last = true; // type {2} for (var i=0;i < boardSize; i++){ rotate(matrix[i]); } function Jodi(n,e,s,w){this.name = "Jodi";...} Jodi.prototype = new Player(); // type {1} function Mia(n,e,s,w){...} // type {2} Mia.prototype = new Player(); function rotate(p){ if (p.n == 1){...} //store slot# for type{1}&{2} } for (var i = 0; i < boardSize; i++) { if (i%2 == 0) matrix[i] = new Mia(1,1,0,0); // type {1} else matrix[j] = new Jodi(1,1,0,0); // type {2}... for (var i=0;i < boardSize; i++){ rotate(matrix[i]); } Inline cache miss for different typesMultiple (slower) caches for different types Slide 32 Impact of inline caches Caches provided 2x speedup Slide 33 Best practices for fast objects and manipulations Create and use fast types Keep shapes of objects consistent Avoid type mismatches for fast types Avoid inline cache misses Slide 34 Slide 35 Numbers in JavaScript All numbers are IEEE 64-bit floating point numbers - Great for flexibility - Performance and optimization challenge 31 bits 31-bit (tagged) Integers 1 bit 1 31 bits Object pointer 1 bit 0 32 bits Floats 32-bit Integers STACKHEAP FIXED LENGTH, FASTER ACCESS VARIABLE LENGTH, SLOWER ACCESS Boxed Slide 36 Use 31-bit integers for faster arithmetic var north = 1; var east = "east"; var south = 0.1; var west = 0x1; function Player(north, south, east, west) {... } var p = new Player(north,south,east,west); STACK 0x00000003 north: 0x005e4148 east: 0x005e4160 south: String east Number 0.1 Number 0x1 0x005e4170 west: HEAP 0x005e4148: 001001000 0x03 represents 1: 000000011 SLOW Slide 37 Avoid creating floats if they are not needed Fastest way to indicate integer math is |0 var r = 0; function doMath(){ var a = 5; var b = 2; r = ((a + b) / 2) |0 ; // r = 3 r = Math.round((a + b) / 2); // r = 4 } var r = 0; function doMath(){ var a = 5; var b = 2; r = ((a + b) / 2); // r = 3.5 }... var intR = Math.floor(r); 0x005e4148 r: 0x00000007 r: 0x00000009 r: Number 3.5 STACK HEAP STACK SLOWFAST SLOW Slide 38 Take advantage of type-specialization for arithmetic Create separate functions for ints and floats; use consistent argument types function Distance(p1, p2) { var dx = p1.x - p2.x; var dy = p1.y - p2.y; var d2 = dx * dx + dy * dy; return Math.sqrt(d2); } var point1 = {x:10, y:10}; var point2 = {x:20, y:20}; var point3 = {x:1.5, y:1.5}; var point4 = {x:0x0AB, y:0xBC}; Distance(point1, point3); Distance(point2, point4); function DistanceFloat(p1, p2) { var dx = p1.x - p2.x; var dy = p1.y - p2.y; var d2 = dx * dx + dy * dy; return Math.sqrt(d2); } function DistanceInt(p1,p2) { var dx = p1.x - p2.x; var dy = p1.y - p2.y; var d2 = dx * dx + dy * dy; return (Math.sqrt(d2) | 0); } var point1 = {x:10, y:10}; var point2 = {x:20, y:20}; var point3 = {x:1.5, y:1.5}; var point4 = {x:0x0AB, y:0xBC}; DistanceInt(point1, point2); DistanceFloat(point3, point4); SLOWFAST Slide 39 Best practices for fast arithmetic Use 31-bit integer math when possible Avoid floats if they are not needed Design for type specialized arithmetic Slide 40 Slide 41 Chakra: Array internals 01 var a = new Array(); 02 a[0] = 1; 03 a[1] = 2.3; 04 a[2] = str; Type: Int Array 1 Type: Float Array Type: Var Array 1 2.3 str 1 2.3 Slide 42 Pre-allocate arrays var a = new Array(100); for (var i = 0; i < 100; i++) { a[i] = i + 2; } var a = new Array(); for (var i = 0; i < 100; i++) { a.push(i + 2); } 0? ?+1?? 0100 SLOW FAST Slide 43 var a = new Array(100000); for (var i = 0; i < a.length; i++) { a[i] = i; }... //operations on the array... a[99] = str; For mixed arrays, provide an early hint Avoid delayed type conversion and copy var a = new Array(100000); a[0] = hint; for (var i = 0; i < a.length; i++) { a[i] = i; }... //operations on the array... a[99] = str; SLOWFAST Slide 44 Use Typed Arrays when possible Avoids tagging of integers and allocating heap space for floats var value = 5; var a = new Array(100); a[0] = value; // 5 - tagged a[1] = value / 2; // 2.5 - boxed a[2] = "text"; // "text" var array var value = 5; var a = new Float64Array(100); a[0] = value; // 5 - no tagging required a[1] = value / 2; // 2.5 - no boxing required a[2] = "text"; // 0 var a = new Int32Array(100); a[0] = value; // 5 - no tagging required a[1] = value / 2; // 2 - no tagging required a[2] = "text"; // 0 SLOWFAST Slide 45 Keep values in arrays consistent Numeric arrays treated like Typed Arrays internally var a = [1,0x2,99.1,5]; //mixed array var b = [0x10,8,9]; //mixed array function add(a,i,b,j) { return a[i] + b[j]; } add(a,0,b,0); add(a,1,b,1); var a = [1,5,8,9]; //int array var b = [0x02,0x10,99.1]; //float array function add(a,i,b,j) { return a[i] + b[j]; } add(a,0,b,0); add(a,1,b,1); SLOWFAST Slide 46 Keep arrays dense Deleting elements can force type change and de-optimization var a = new Array(1000); //type int... for (var i = 0; i < boardSize; i++) { matrix[i] = [1,1,0,0]; } //operating on the array... delete matrix[23];... //operating on the array var a = new Array(1000); //type int... for (var i = 0; i < boardSize; i++) { matrix[i] = [1,1,0,0]; } //operating on the array... matrix[23] = 0;... //operating on the array SLOWFAST Slide 47 Enumerate arrays efficiently Explicit caching of length avoids repetitive property accesses var a = new Array(100); var total = 0; for (var item in a) { total += item; }; a.forEach(function(item){ total += item; }); for (var i = 0; i < a.length; i++) { total += a[i]; } var a = new Array(100); var total = 0; cachedLength = a.length; for (var i = 0; i < cachedLength; i++) { total += a[i]; } SLOWFAST Slide 48 Best practices for using arrays efficiently Pre-allocate arrays Keep array type consistent Use typed arrays when possible Keep arrays dense Enumerate arrays efficiently Slide 49 Slide 50 Avoid chattiness with the DOM... //for each rotation document.body.game.getElementById(elID).classList.remove(oldClass) document.body.game.getElementById(elID).classList.add(newClass)... var element = document.getElementById(elID).classList; //for each rotation element.remove(oldClass) element.add(newClass)... JavaScript DOM JavaScript DOM Slide 51 Avoid automatic conversions of DOM values Values from DOM are strings by default this.boardSize = document.getElementById("benchmarkBox").value; for (var i = 0; i < this.boardSize; i++) { //this.boardSize is 25 for (var j = 0; j < this.boardSize; j++) { //this.boardSize is 25... } this.boardSize = parseInt(document.getElementById("benchmarkBox").value); for (var i = 0; i < this.boardSize; i++) { //this.boardSize is 25 for (var j = 0; j < this.boardSize; j++) { //this.boardSize is 25... } FAST (25% marshalling cost reduction in init function) SLOW Slide 52 Paint as much as your users can see Align timers to display frames setInterval(animate, 0); setTimeout(animate, 0); requestAnimationFrame(animate); setInterval(animate, 1000 / 60); setTimeout(animate, 1000 / 60); MORE WORK LESS WORK Slide 53 Raw JS code: http://aka.ms/FastJS Slide 54 Results Save CPU cycles 75% 65% setTimeout(animate, 0); requestAnimationFrame(animate); Slide 55 Optimized JS code: http://aka.ms/FasterJS Slide 56 Slide 57 DEV-314: TypeScript: Application-Scale JavaScript, Anders Hejlsberg WCL-066: New IE Developer Tools, Jonathan Carter and Carl Edlund WCL-067: New Platform Capabilities for Advancing Web Development, Israel Hilerio WCL-068: Web Runtime Performance, Tobin Titus WCL-071: Lighting up your site on Windows 8.1, Jacob Rossi WCL-072: Hyper-fast web graphics with WebGL, Frank Oliver DEV-312:Diagnosing issues in JavaScript Windows Store Apps with Visual Studio, Andrew Hall WCL-073: Inspecting & Debugging Using IEs New F12 Developer Tools, Andy Sterland and Will Kennedy DEV-316: Developing high performance websites and modern apps with JavaScript performance tools, Jonathan Carter IE Blog: http://blogs.msdn.com/iehttp://blogs.msdn.com/ie Slide 58 Q& A Slide 59 Slide 60