web applications html5 games windows 8 html apps basic web pages javascript execution speed dom...
TRANSCRIPT
High-Performing JavaScript for Modern EnginesAmanda Silver, John-David DaltonJavaScript TeamDev 4-000
fast and fluid.The best apps are…
The Reach of JavaScript is expandingWeb Applications
HTML5 GamesWindows 8 HTML AppsBasic Web Pages
JavaScript Execution Speed
DOM InteractionsAccelerated Graphics
Page Load Time
WebKit’s JavaScript benchmark
Measuring JavaScript performance
JavaScript Execution Performance
Perf
orm
an
ce o
f O
ther
Bro
wse
r S
ub
syst
em
s
HTML5 gamesStress Multiple Browser Subsystems
Bubbles
setInterval(animate, 1000/60)
bs[i] = new Bubble(0, 1);for (var i = 0; i < 1000; i++) { bs[i].move(); for (var j = 0; j < i + 1; j++) { Bubbles.collide(bs[i], bs[j]); }}
var distance2 = (b1.x–b2.x)*(b1.x–b2.x)+(b1.x–b2.x)*(b1.x–b2.x);var magnitude = (dvx * dx + dvy * dy) / d2;
this.elem.style.left = this.x + "px";this.elem.style.top = this.y + "px";
this.canvas.getContext("2d").putImageData(canvasImageData, 0, 0);
Bubbles overview
JavaScript in Context
Is Javascript your Bottleneck?
Is JavaScript the bottleneck?N
etw
ork
ing
HTM
L
CS
S
Colle
ctio
ns
JavaS
crip
t
Mars
halli
ng
DO
M
Form
att
ing
Blo
ck
Bu
ildin
g
Layou
t
Ren
deri
ng
Is JavaScript your bottleneck?
Is JavaScript the bottleneck?
Chakra
Your Code
Other Subsystems
Do use a good profiler
Benchmarking with jsPerf
Do use jsPerf to write micro-benchmarks Function declarations go in preparation code or setup
Include only the bare minimum in each test body
Reset reused shared variables
Don’t introduce randomness in your test
Don’t test asynchronous things synchronously
Know preparation, setup, and teardown stages
demo
Efficient animation
Don’t do useless work
setInterval(draw, 0);setTimeout(draw, 0);requestAnimationFrame(draw);setTimeout(draw, 1000 / 60);
Do avoid chattiness with the DOM
JavaScript
DOM
for (var i = 0; i < this.nOfBubbles; i++) { document.body.box.getElementById("ball0").style.left = b.x + "px"; document.body.box.getElementById("ball0").style.top = b.y + "px";}
demo
DOM interaction
Do check types of values from DOMthis.nOfBubbles =
document.getElementById(“dropDown").value;
30%of rendering time in string conversion
Slow Operations
11%
Value Conversions 18%
GC 17%
Your Code 45%
Don’t use automatic type conversions
BubbleTest.prototype.moveBubbles = function() { for (var i = 0; i < this.nOfBubbles; i++) { this.bubbles[i].move(); }
for (var i = 0; i < this.nOfBubbles; i++) { for (var j = i + 1; j < this.nOfBubbles; j++) { Bubble.prototype.collide(this.bubbles[i], this.bubbles[j]); } }}
this.nOfBubbles = document.getElementById(“dropDown").value;
this.nOfBubbles = parseInt(document.getElementById(“dropDown").value);
BubbleTest.prototype.moveBubbles = function() { for (var i = 0; i < this.nOfBubbles; i++) { this.bubbles[i].move(); }
for (var i = 0; i < this.nOfBubbles; i++) { for (var j = i + 1; j < this.nOfBubbles; j++) { Bubble.prototype.collide(this.bubbles[i], this.bubbles[j]); } }}
“128”128
Do parse numbers from stringsthis.nOfBubbles = parseInt(document.getElementById(“dropDown").value);
GC 28%
Your Code 64%
JavaScript: Flexibility or performance
Flexibility Performance
“Think C++”“Think Script”
Simple Websites Complex Apps, Games
var r = 3 * "10"; // r == 300
var a = new Array();a.push(10);
var p = {x: 0, y: 0};p.z = 5;p["some text"] = 1;p[1] = 2;eval("var s = p[1] * a[0]"); // s == 20
var r = 3 * parseInt("10");
var a = new Array(100);a[0] = 10;
var p = new Point(0, 0, 0);p.z = 5;
Lifecycle of your JavaScript code
Core #1Foreground InterpreterByte CodeASTParserSource Code
Core #2Background Native Code
Background Compiler
Create fast objects
demo
Fast property access
Fast property access
if (b1.bubbleBounceCounter >= 10) { delete b1.bubbleBounceCounter;}
Slow Property Access 41%
if (b1.bubbleBounceCounter >= 10) { b1.bubbleBounceCounter = 0;}
Objects in JavaScriptvar base = { color: "red" };base.x = 10;base["any string"] = 0;for (var p in base) {...}
function Bubble() {...}Bubble.prototype = base;var b = new Bubble();var c = b.color; // c == "red“
b.y = 20;var x = b.x; // x == 10delete base.x;var u = b.x; // u == undefined
property bags, no classes
add properties on the fly
class-like patternprototypal inheritance
even remove properties
enumerate properties
use as dictionaries
but still just property bags
b1.type b2.type
Bubble
“x”
“y”
y
Bubble
“x”
x
Bubble
10
11
10
b2
0
1
0
b1
function Bubble(x, y) { this.x = x; this.y = y;}var b1 = new Bubble(0, 1);var b2 = new Bubble(10, 11);
Internal fast type system
b1.type b2.type
Bubble
“x”
“y”
“c”
c
Bubble
“x”
“y”
y
Bubble
“x”
x
Bubble
10
11
“red”
10
11
b2
0
1
b1
function Bubble(x, y) { this.x = x; this.y = y;}var b1 = new Bubble(0, 1);var b2 = new Bubble(10, 11);b2.c = "red";
Internal fast type system
Do add all properties in constructorAvoid slow property bags
function Point() { this.x = 0; this.y = 0;}
var p1 = new Point();p1.prop01 = 1;...p1.prop99 = 99;
b1.type = fast-type({x,y})
too many properties?b1.type =
slow-property-bag
Don’t delete propertiesAvoid slow property bags
function Point() { this.x = 0; this.y = 0;}
var p1 = new Point();
delete p1.y;
b1.y = undefined;
b1.type = fast-type({x,y})
deleting properties?b1.type =
slow-property-bag
b1.type == fast-type({x,y})
Do use identifiers for property namesAvoid slow property bagsfunction Point() {
this.x = 0; this.y = 0;}
var p1 = new Point();
p1["some text"] = 1;
b1.type = fast-type({x,y})
non-identifier properties?b1.type =
slow-property-bag
Don’t use getters & setters excessivelyAvoid slow property bags
function Bubble(x, y) { Object.defineProperty(this, "x", { get : function() { return x; }, set : function(value) { x = value; }, }); Object.defineProperty(this, "y", { get : function() { return y; }, set : function(value) { y = value; }, });}
var b = new Bubble(0, 1);var x = b.x;b.y = 5;
getters & setters?b.type =
slow-property-bag
Don’t add properties conditionallyAvoid Fast Type Mismatches
function Color(alpha) { this.r = 0; this.g = 0; this.b = 0; if (alpha) { this.a = 0; }}
var c1 = new Color(true);var c2 = new Color(false);
c1.type = {r,g,b,a}
c2.type = {r,g,b}
function TreeNode(key, value) { this.key = key; this.value = value;};
TreeNode.prototype.left = null;TreeNode.prototype.right = null;
var t1 = new TreeNode("k1", "v1");var t2 = new TreeNode("k2", "v2");var t3 = new TreeNode("k3", "v3");
t1.left = t2;t2.right = t3;
Don’t default properties on prototypes Avoid Fast Type Mismatches
Property access works in C++ or C#
class Bubble { public int x; public int y;}
class Program { static void Reset(Bubble b) { b.x = 0; b.y = 0; }}
mov edx, 0mov eax, [p]mov [p+4], edx
Fast property access in JavaScriptfunction Bubble(x, y) { this.x = x; this.y = y;}
function Bubble.prototype.reset() { this.x = 0; this.y = 0;} mov eax, 0x00000001
mov ebx, [this]mov ecx, [this.type]cmp ecx, [ic.type]jne $callSetPropertymov ecx, [this.propArray]mov edx, [ic.index]mov [p.propArray[ic.index]], eax
// 100s of assembly instructionsengine.LookUpProperty(this, x)engine.SetProperty(this, x, 0)
inline cachetype
prop index
function Bubble(x, y) {...}function Bubble.prototype.reset() { this.x = 0; this.y = 0;}
var b1 = new Bubble(0, 1);var b2 = new Bubble(10, 11);
for (var i = 0; i < 1000; i++) { bubbles[i].reset();}
Fast property access worksFor objects of matching fast types
inline cache
0
0mov eax, 0x00000001mov ebx, [this]mov ecx, [this.type]cmp ecx, [ic.type]jne $callSlowSetPropertymov ecx, [this.propArray]mov edx, [ic.index]mov [p.propArray[ic.index]], eax
b1.type = “{x,y}”b2.type = “{x,y}”
b1.type = “{x,y}”b2.type = “{x,y}”
“{x, y}”
1
function Bubble(x, y) {...}function Bubble.prototype.reset() { this.x = 0; this.y = 0;}
var b1 = new Bubble(0, 1);var b2 = new Bubble(10, 11);b2.c = "red";
for (var i = 0; i < 1000; i++) { bubbles[i].reset();}
Fast property access failsFor objects of mismatched fast types or property bags
inline cache
0
0mov eax, 0x00000001mov ebx, [this]mov ecx, [this.type]cmp ecx, [ic.type]jne $callSlowSetPropertymov ecx, [this.propArray]mov edx, [ic.index]mov [p.propArray[ic.index]], eax
b1.type = “{x,y}”
b2.type = “{x,y,c}”
b1.type = “{x,y}”
b2.type = “{x,y,c}”
“{x, y}”
1
“{x, y, c}”
Do write fast objects
Add all properties in constructor Don’t delete properties Use identifiers for property names Use getters and setters sparingly Avoid conditionally adding properties Avoid default property values on prototype objects
JavaScript Arithmetic
What is causing garbage collection?
Bubble.prototype.move = function(elapsedTime) { this.x += this.vx * elapsedTime / model.animationFrameDuration; this.y += this.vy * elapsedTime / model.animationFrameDuration;}
Garbage Collection25%
JavaScript arithmetic is very permissiveExtreme flexibility + …function doMath(a, b, c, d) {
return a * b + c * d;}
var a = 3;var b = "10";var c = new Date();var d = {valueOf: function() { return 5; }}
var r = doMath(a, b, c, d);
r == 6640403003390
Flexible? Yes Fast? No
JavaScript values are dynamically typedExtreme flexibility + dynamic typing = …var a = 3;a = 2.5;a = "text";a = { x: 0, y: 1};a = new Date();var b = getSomeValue();var c = someObject.c;
function doSomething(a, b, c) { global.r = a + b + c;}
a
b
c
Number
3
Object
0
1
Number
2.5
String
“text”
Date
…
heap
stack
Generic JavaScript arithmetic is slowExtreme flexibility + dynamic typing = slow & complex algorithm
return a + b + c;
av = getValueFromHeap(a)
bv = getValueFromHeap(b)
at = getType(a)
bt = getType(b)
op = selectOperation(at, bt)
rv1 = op(av, bv)
r1 = boxOnHeap(rv1)
r1 = doPlus(a, b)
r2 = doPlus(r1, c)
return r2
Do use integer math to avoid boxingvar a = 3;var b = "text";var c = 2.5;var d = 0x80000000;
function doSomething(a, b, c, d) { global.r = a + b + c + d;}
doSomething(a, b, c, d);
stack
0x00000007a:
0x005e4148b:
0x005e4160c:
String
“text”
Number
2.5
Number
0x80000000
0x005e4170d:
Number
3
heap
0x005e4148: 0…01001000 0x07 represents 3: 0…00000111
Do use floating point math judiciously
var a = 5;var b = 2;var r = ((a + b) / 2) | 0; // r = 3var r = Math.round((a + b) / 2); // r = 4
var a = 5;var b = 2;var r = (a + b) / 2); // r = 3.5
stack
0x005e4148r:
0x00000007r:
0x00000009r:
Number
3.5
heap
Type-specialized code generationGeneric
Machine CodeChakra
Helper CodeType SpecializedMachine Code
ChakraInterpreter
Type specialized functions are fastBut require consistent input arguments
function (b1, b2) { var dx = b1.x - b2.x; var dy = b1.y - b2.y; var dvx = b1.vx - b2.vx; var dvy = b1.vy - b2.vy; var d2 = dx * dx + dy * dy; var mag = (dvx * dx + dvy * dy) / d2;
var delta_vx = dx * mag; var delta_vy = dy * mag;
b1.vx -= delta_vx; b1.vy -= delta_vy; b2.vx += delta_vx; b2.vy += delta_vy;}
stack
0.10 d2
2.49 mag
0.05 delta_vx
0.25 delta_vy
registers
2.70 eax(dx)
10.50
1.00
0.55
ebx(dy)
ecx(dvx)
edx(dvy)
Do use arguments of consistent typesfunction (b1, b2) {
(...) var dx = b1.x - b2.x; var dy = b1.y - b2.y; ... var d2 = dx * dx + dy * dy; ...}
Bubble.prototype.collide(b1, b2);
for (var i = 0; i < nOfBubbles; i++) { for (var j = i + 1; j < nOfBubbles; j++) { Bubble.prototype.collide(bs[i], bs[j]); }}
MOV eax, dxMOV ebx, dyMUL eax, eaxMUL ebx, ebxADD eax, ebx // d2 in eax now
if (b1.type != Bubble) bail out;if (b1.x.type != “float”) bail out;if (b2.type != Bubble) bail out;if (b2.x.type != “float”) bail out;...
Do use fast type-specialized arithmetic Be aware of number boxing
Avoid unnecessary floating point math
Enable type-specializing JIT compilers
JavaScript Arrays
Number boxing in JavaScript arrays
this.gaussianMask = new Array(maskSize * maskSize);this.occlusionMask = new Array(canvasWidth * canvasHeight);
Garbage Collection90% of 1 CPU
this.gaussianMask = new Float64Array(maskSize * maskSize);this.occlusionMask = new Float64Array(canvasWidth * canvasHeight);
Demo
Typed arrays and boxing
Do take advantage of typed arrays
var value = 5;
var a = new Array(100);a[0] = value; // a[0] == 5 (tagged)a[1] = value / 2; // a[1] == 2.5 (boxed)a[2] = "text"; // a[2] == "text"
var a = new Uint32Array(100);a[0] = value; // a[0] == 5 ("naked", no tagging required)a[1] = value / 2; // a[1] == 2 ("naked", no tagging required)a[2] = "text"; // a[2] == 0
var a = new Float64Array(100);a[0] = value; // a[0] == 5 ("naked", no tagging required)a[1] = value / 2; // a[1] == 2.5 ("naked", no boxing required!)a[2] = "text"; // a[2] == 0
flexib
ilit
yp
erf
orm
an
ce
Don’t use objects as arraysOr vice versa
var b = new Bubble(0, 1);for (var i = 0; i < 100; i++) { b[i] = i + 2;}
var a = new Array(100);for (var i = 0; i < 100; i++) { a[i] = i + 2;}
con
ven
ien
ce
perf
orm
an
ce
Do 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);}c
on
ven
ien
ce
perf
orm
an
ce
0 ?
?+1 ??
…0 100
Do enumerate arrays efficientlyvar 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];}
for (var i = 0, len = a.length; i < len; i++) { total += a[i];}
con
ven
ien
ce
perf
orm
an
ce
Do use arrays efficiently
Don’t use objects as arrays and vice versa
Pre-allocate on creation
Enumerate efficiently
Use typed arrays to avoid float boxing
In-review: Write fast JavaScript
Use good performance tools
Identify and focus on your bottlenecks
Understand and target modern engines
Create fast objects
Write fast arithmetic
Use arrays efficiently
Go forth and code it! 3-012: Introducing TypeScript: A language for application-scale JavaScript development3-020: Building apps for Office and SharePoint 2013 using the web technologies you know and love3-115: Intro to creating Windows Store apps using HTML and JavaScript4-101: Deep dive into WinJS
3-008: Diagnosing perf and memory issues in JavaScript-based Windows Store apps3-014: Modern JavaScript3-018: TouchDevelop: A touch-first IDE for the Web created with TypeScript3-041: Javascript from client to cloud with Windows 8, Node.js, and Windows Azure3-130: Writing Windows Store apps with jQuery2-015: Windows Phone 8: HTML5/IE10 for Developers
jsperf.com blogs.msdn.com/b/ie/
© 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.