Fast Apps and Sites with JavaScriptGaurav SethProgram Manager, JavaScript4-313
Five principles to improvethe raw JavaScript performance of your appBased on what modern JavaScript engines like Chakra do with your code…
Single PlayerSingle PageCasual Game
High Five Yeah - Overview
Raw JS code: http://aka.ms/FastJS
Matrix of player(s)
Each Player has a set of directions
Generate list of
neighbors to rotate
Rotate the player
On Touch, select a player
Arrays Objects and properties Numbers Animation loop
Memory allocations
Repeat until neighbors list empty
Components and control flow
We’ve got a problem…
Low frame rate causes …
GLITCH
What really matters?
t0(Launch)
tn(Close or navigate)
…
60 fps16.67ms 16.67ms 16.67ms
Paint
Paint
Paint
Let’s investigate …
Raw JS code: http://aka.ms/FastJS
Always start with a good profiler
Windows Performance Toolkithttp://aka.ms/WinPerfKit
F12 UI Responsiveness Tool
Is JavaScript the bottleneck?
Chakra(GC, etc.)
Your Code(Script, Execute, etc.)
Other Subsystems
Do we expect so much of GC to happen?
GC
Principle #1:Memory usage: Stay lean
Let’s fix…
Raw JS code: http://aka.ms/FastJS
Results
• Overall FG GC time reduced to 1/3rd • Raw JavaScript perf improved ~3x
Obj1 Obj1a Obj1b Root Obj2 Obj2a Obj2b Obj2c
Obj2d C-1 C-2 Obj2e Obj3 ... ... ...
... ... ... ... ... ... ... ...
... C-1a ... C-1b C-1c C-1a1
C-1b1 C-1b1 C-1b1 C-1b1 C-1b1 C-1b1 C-1b1 C-1b1
C-1b1 C-1b1 C-1b1 C-1b1 C-1b1 C-1b1 C-1b1 C-1b1
C-1b1 C-1b1 C-1b1 C-1b1 C-1b1 C-1b1 C-1b1 C-1b1
C-1b1 C-1b1 C-1b1 C-1b1 C-1b1 C-1b1 C-1b1 C-1b1
C-1b1 C-1b1 C-1b1 C-1b1 C-1b1 C-1b1 C-1b1 C-1b1
C-1b1 C-1b1 C-1b1 C-1b1 C-1b1 C-1b1 C-1b1 C-1b1
C-1b1 C-1b1 C-1b1 C-1b1 C-1b1 C-1b1 C-1b1 C-1b1
C-1b1 C-1b1 C-1b1 C-1b1
C-1b1 C-1b1 C-1b1 C-1b1 C-1b1 C-1b1 C-1b1 C-1b1
C-1b1 C-1b1 C-1b1 C-1b1 C-1b1 C-1b1 C-1b1 C-1b1
C-1b1 C-1b1 C-1b1 C-1b1 C-1b1 C-1b1 C-1b1 C-1b1
C-1b1 C-1b1 C-1b1 C-1b1 C-1b1 C-1b1 C-1b1 C-1b1
C-1b1 C-1b1 C-1b1 C-1b1 C-1b1 C-1b1 C-1b1 C-1b1
C-1b1 C-1b1 C-1b1 C-1b1 C-1b1 C-1b1 C-1b1 C-1b1
C-1b1 C-1b1 C-1b1 C-1b1 C-1b1 C-1b1 C-1b1 C-1b1
C-1b1 C-1b1 C-1b1 C-1b1 C-1b1 C-1b1 C-1b1 C-1b1
Garbage Collector: Flow Program ProgramInitialize
POOL #1 (SMALL OBJECTS
)
POOL #2 (LARGE
OBJECTS)
Zero outMark Sweep
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
Concurrent Garbage Collection: AdvantagesUtilizing spare hardware capacity to reduce pauses
• Shorter pauses• Partial, time restricted collections
Program SetupSweep Program
Sweep Zero Pages
FOREGROUND THREAD
BACKGROUND THREAD
Initialize
Mark
Program Rescan Mark
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
Principle #2Use fast objects and manipulations
Internal Type System: Fast Object Types
var p1;p1.north = 1;p1.south = 0;
var p2;p2.south = 0;p2.north = 1;
north 1south 0
north 1
north
south
south 0north 1
south 0
south
north
Base Type “{}”
Type “{north}” Type “{south}”
Base Type “{}”
Type “{south, north}”Type “{north, south}”
Create fast types and avoid type mismatchesDon’t 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.typep1.type == p2.type
Create fast types and avoid type mismatchesDon’t 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.type
p1.type == p2.type == p3.type
Internal Type System: Slower Property Bags var p1 = new Player(); //prop
bagp1.north = 1;p1.south = 1;
var p2 = new Player(); //prop bagp2.north = 1;p2.south = 1;
always
Slower Bag “{p1}” Slower Bag “{p2}”
always
Avoid conversion from fast type to slower property bagsDeleting 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
Avoid creating slower property bagsAdd 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();
SLOW FAST
Avoid creating slower property bagsRestrict 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;...
SLOW FAST
Let’s fix…
Raw JS code: http://aka.ms/FastJS
Results
• Time in script execution reduced ~30%• Raw JS performance improved ~30%
3.8s
2.2s
Type mismatches may lead to inline cache missesKeep types same; distribute multiple types uniformlyfunction 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 types
Multiple (slower) caches for different types
Impact of inline caches
Caches provided 2x speedup
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
Principle #3Use fast arithmetic
Numbers in JavaScript• All numbers are IEEE 64-bit floating point
numbers - Great for flexibility - Performance and optimization challenge
31 bits31-bit (tagged) Integers
1 bit1
31 bitsObject pointer
1 bit0
32 bits
32 bits
Floats32-bit
Integers
STACK HEAPFIXED LENGTH, FASTER ACCESS VARIABLE LENGTH, SLOWER ACCESS
Boxed
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);
STACK0x00000003north:0x005e4148east:0x005e4160south:
String“east”
Number0.1
Number0x1
0x005e4170west:
HEAP
0x005e4148: 0…01001000 0x03 represents 1: 0…00000011
SLOW
SLOW
SLOW
Avoid creating floats if they are not neededFastest 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);
0x005e4148r: 0x00000007r:
0x00000009r:Number3.5
STACK HEAP STACK
SLOW FAST
SLOW
Take advantage of type-specialization for arithmeticCreate 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);
SLOW FAST
Best practices for fast arithmetic
• Use 31-bit integer math when possible • Avoid floats if they are not needed• Design for type specialized arithmetic
Principle #4Use fast Arrays
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
Pre-allocate arraysvar 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 ??…0 100
SLOW
FAST
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 hintAvoid 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”;
SLOW
FAST
Use Typed Arrays when possibleAvoids tagging of integers and allocating heap space for floats
var value = 5;
var a = new Array(100);a[0] = value; // 5 - taggeda[1] = value / 2; // 2.5 - boxeda[2] = "text"; // "text" – var array
var value = 5;
var a = new Float64Array(100);a[0] = value; // 5 - no tagging requireda[1] = value / 2; // 2.5 - no boxing requireda[2] = "text"; // 0
var a = new Int32Array(100);a[0] = value; // 5 - no tagging requireda[1] = value / 2; // 2 - no tagging requireda[2] = "text"; // 0
SLOW
FAST
Keep values in arrays consistent Numeric arrays treated like Typed Arrays internally
var a = [1,0x2,99.1,5]; //mixed arrayvar 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 arrayvar 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);SLO
WFAST
Keep arrays denseDeleting 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
SLOW
FAST
Enumerate arrays efficientlyExplicit 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];}
SLOW
FAST
Best practices for using arrays efficiently
• Pre-allocate arrays• Keep array type consistent• Use typed arrays when possible• Keep arrays dense• Enumerate arrays efficiently
Principle #5Do less (cross-subsystem) work
Avoid chattiness with the DOM
...//for each rotationdocument.body.game.getElementById(elID).classList.remove(oldClass)document.body.game.getElementById(elID).classList.add(newClass)...
var element = document.getElementById(elID).classList;
//for each rotationelement.remove(oldClass)element.add(newClass)...
JavaScript
DOM
JavaScript
DOM
Avoid automatic conversions of DOM valuesValues from DOM are strings by defaultthis.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
Paint as much as your users can seeAlign timers to display frames
setInterval(animate, 0);setTimeout(animate, 0);
requestAnimationFrame(animate);
setInterval(animate, 1000 / 60);setTimeout(animate, 1000 / 60);
MORE WORK
LESS WORK
Let’s fix…
Raw JS code: http://aka.ms/FastJS
ResultsSave CPU cycles
75% 65
%
setTimeout(animate, 0); requestAnimationFrame(animate);
Problem solved…Why performance matters?
• Great user experience• Longer battery life• Make your apps richer
Optimized JS code: http://aka.ms/FasterJS
In-review: Writing fast sites & apps with JavaScriptUnderstand and target modern engines• Principle#1: Stay lean – use less memory
• Principle#2: Use fast objects and do fast manipulations
• Principle#3: Write fast arithmetic
• Principle#4: Use fast arrays
• Principle#5: Do less (cross-subsystem) work
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 IE’s 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/ie
Go forth and code it … !!!
Q&AThanks for
attending/watching!!!
Evaluate this session
Scan this QR code to evaluate this session and be automatically entered in a drawing to win a prize!
© 2012 Microsoft Corporation. All rights reserved. Microsoft, Windows, Windows Vista and other product names are or may be registered trademarks and/or trademarks in the U.S. and/or other countries.The information herein is for informational purposes only and represents the current view of Microsoft Corporation as of the date of this presentation. Because Microsoft must respond to changing market conditions, it should not be interpreted to be a commitment on the part of Microsoft, and Microsoft cannot guarantee the accuracy of any information provided after the date of this presentation. MICROSOFT MAKES NO WARRANTIES, EXPRESS, IMPLIED OR STATUTORY, AS TO THE INFORMATION IN THIS PRESENTATION.
Top Related