Slide 1

Slide 1 text

JavaScript Need for Speed @ariyahidayat San Francisco, April 11, 2013 1

Slide 2

Slide 2 text

whoami 2

Slide 3

Slide 3 text

Performance Principles 3

Slide 4

Slide 4 text

Secrets of High Performance Avoid slow code Write fast code 4

Slide 5

Slide 5 text

Computer Programming as an Art, Knuth’s Turing Award lecture (1974) The real problem is that programmers have spent far too much time worrying about efficiency in the wrong places and at the wrong times; premature optimization is the root of all evil (or at least most of it) in programming. 5

Slide 6

Slide 6 text

“The Big Rock” “First Things First”, Steven Covey 6

Slide 7

Slide 7 text

Journey: The Quadrant http://ariya.ofilabs.com/2012/11/optimization-journey-vs-destination.html 7

Slide 8

Slide 8 text

Under the Hood 8

Slide 9

Slide 9 text

JavaScript Engines SpiderMonkey Firefox JavaScriptCore/Nitro Safari V8 Node.js, Chrome JScript/Chakra Internet Explorer Carakan Opera 9

Slide 10

Slide 10 text

Building Blocks Virtual Machine/ Interpreter Parser Runtime Source Syntax Tree Built-in objects, host objects, ... 10

Slide 11

Slide 11 text

var answer = 42 Parser keyword equal sign identifier number Variable Declaration Identifier Literal Constant Tokenization → Tokens Parsing → Syntax Tree 11

Slide 12

Slide 12 text

Syntax Visualization http://esprima.org/demo/parse.html 12

Slide 13

Slide 13 text

Execution in Interpreter Tree Visitor Traverse the syntax tree Run every syntax node Bytecode Interpreter Traverse and produce bytecodes Execute bytecodes in VM JIT Compiler Generate native machine codes Transfer the control to the codes 13

Slide 14

Slide 14 text

Execution Steps http://toolness.github.io/slowmo-js/ 14

Slide 15

Slide 15 text

Execution Visualization http://int3.github.io/metajs/ 15

Slide 16

Slide 16 text

Tiered Execution Baseline Optimized Fast startup Minimal memory usage Decent execution speed Intensive initialization Potentially eat more memory Very fast execution button.onclick = function() { doSomething(); } for (i = 0; i < employees.length; ++i) total += employees[i].salary; 16

Slide 17

Slide 17 text

Playing with V8 17

Slide 18

Slide 18 text

Playing with V8 git clone git://github.com/v8/v8.git cd v8 make dependencies make x64.release -j4 make x64.debug -j4 18

Slide 19

Slide 19 text

Performance Analysis 19

Slide 20

Slide 20 text

http://ariya.ofilabs.com/2012/12/javascript-performance-analysis-sampling-tracing-and-timing.html Timing Prepare the stopwatch Mark the start and the end Sampling Periodically check which function is being executed Tracing Track all function calls and exits 20

Slide 21

Slide 21 text

Stopwatch Measurement http://calendar.perfplanet.com/2010/bulletproof-javascript-benchmarks/ var start = Date.now(); for (var i = 0; i < 100; i++) { doSomething(); } console.log(Date.now() - start, 'ms'); 21

Slide 22

Slide 22 text

Measurement Confidence Accuracy Precision 22

Slide 23

Slide 23 text

Using Benchmark.js var suite = new Benchmark.Suite; suite.add('String#indexOf', function() { 'Hello World!'.indexOf('o') > -1; }) .on('complete', function() { console.log('Fastest is ' + this.filter('fastest').pluck('name')); }) .run(); http://benchmarkjs.com/ 23

Slide 24

Slide 24 text

Loop-invariant Code Motion What you write Optimized by the engine for (var i = 0; i < 100; i++) { sum += Math.sqrt(2) * i; } var temp = Math.sqrt(2); for (var i = 0; i < 100; i++) { sum += temp * i; } Offset → Loss of accuracy 24

Slide 25

Slide 25 text

Dead-Code Elimination http://blogs.msdn.com/b/ie/archive/2010/11/17/html5-and-real-world-site- performance-seventh-ie9-platform-preview-available-for-developers.aspx What you write Optimized by the engine function test() { var a = 0, b = 0; for (var i = 0; i < 100; i++) { a += i; b += i * i; } return a; } function test() { var a = 0; for (var i = 0; i < 100; i++) { a += i; } return a; } 25

Slide 26

Slide 26 text

Run-Time Optimization 26

Slide 27

Slide 27 text

What (Not)to Optimize Function Calls Garbage Collection Fixed Object Shape Profile-Guided 27

Slide 28

Slide 28 text

Function Calls 28

Slide 29

Slide 29 text

“Function Call is Expensive” sortDepartment totalDepartementExpense totalEmployeesSalaries getEmployeeData 29

Slide 30

Slide 30 text

Automatic Inlining What you write Optimized by the engine function square(x) { return x * x; } function f(x) { var sum = 0; for (var i = 0; i < x; i++) { sum += square(i); } return sum; } function f(x) { var sum = 0; for (var i = 0; i < x; i++) { sum += i * i; } return sum; } http://ariya.ofilabs.com/2013/04/automatic-inlining-in-javascript-engines.html 30

Slide 31

Slide 31 text

V8 Inlining Tracing http://floitsch.blogspot.com/2012/03/optimizing-for-v8-inlining.html d8 --trace-inlining example.js Did not inline InstantiateFunction called from Instantiate (target text too big). Did not inline Instantiate called from Instantiate (target not inlineable). Did not inline called from Instantiate (target not inlineable). Did not inline ConfigureTemplateInstance called from Instantiate (target not inlineable). Did not inline ToObject called from valueOf (target not inlineable). Did not inline FunctionSourceString called from toString (target not inlineable). Did not inline InstantiateFunction called from Instantiate (target text too big). Did not inline Instantiate called from Instantiate (target not inlineable). Did not inline ConfigureTemplateInstance called from Instantiate (target not inlineable). Inlined square called from f. Inlined square called from f. 31

Slide 32

Slide 32 text

Deferred Parsing To further reduce the time to first executed instruction, Chakra processes and emits bytecode only for functions that are about to be executed using a mechanism called deferred parsing. http://blogs.msdn.com/b/ie/archive/2012/06/13/advances-in-javascript- performance-in-ie10-and-windows-8.aspx 32

Slide 33

Slide 33 text

Code, Tree, Memory http://ariya.ofilabs.com/2012/07/lazy-parsing-in-javascript-engines.html function add(x, y) { return x + y; } function mul(x, y) { return x * y; } alert(add(40, 2)); { "type": "Program", "body": [ { "type": "FunctionDeclaration", "id": { "type": "Identifier", "name": "add" }, "params": [ { "type": "Identifier", "name": "x" }, { "type": "Identifier", "name": "y" } ], "defaults": [], "body": { "type": "BlockStatement", "body": [ { "type": "ReturnStatement", "argument": { "type": "BinaryExpression", "operator": "+", "left": { "type": "Identifier", "name": "x" }, "right": { "type": "Identifier", "name": "y" } } } ] }, "rest": null, "generator": false, "expression": false }, { "type": "FunctionDeclaration", "id": { "type": "Identifier", "name": "mul" }, "params": [ { "type": "Identifier", "name": "x" }, { "type": "Identifier", "name": "y" } ], "defaults": [], "body": { "type": "BlockStatement", "body": [ { "type": "ReturnStatement", "argument": { "type": "BinaryExpression", "operator": "*", "left": { "type": "Identifier", "name": "x" }, "right": { "type": "Identifier", "name": "y" } } } ] }, "rest": null, "generator": false, "expression": false }, { "type": "ExpressionStatement", "expression": { "type": "CallExpression", "callee": { "type": "Identifier", "name": "alert" }, "arguments": [ { "type": "CallExpression", "callee": { "type": "Identifier", "name": "add" }, "arguments": [ { "type": "Literal", "value": 40, "raw": "40" }, { "type": "Literal", "value": 2, "raw": "2" } ] } ] } } ] } { "type": "FunctionDeclaration", "id": { "type": "Identifier", "name": "add" }, "params": [ { "type": "Identifier", "name": "x" }, { "type": "Identifier", "name": "y" } ] }; 33

Slide 34

Slide 34 text

Deep Construction function add(x, y) { return x + y; } Declare a function called add. It accepts x and y as the arguments. It has one statement, a return statement. The return value is a binary operation + of x and y. function mul(x, y) { return x * y; } Declare a function called mul. It accepts x and y as the arguments. It has one statement, a return statement. The return value is a binary operation * of x and y. alert(add(40, 2)); Create a function call to alert. The argument is the result of function add with 40 and 2 as the arguments. 34

Slide 35

Slide 35 text

Lazy Construction function add(x, y) { return x + y; } Declare a function call add with the function body “{ return x + y; }”. function mul(x, y) { return x * y; } Declare a function call mul with the function body “{ return x * y; }”. alert(add(40, 2)); Create a function call to alert. The argument is the result of function add with 40 and 2 as the arguments. Call function add. Hmm, it is not parsed yet. Call the real parser for “{ return x + y; }”. It accepts x and y as the arguments. The return value is a binary operation + of x and y. 35

Slide 36

Slide 36 text

Fixed Object Shape 36

Slide 37

Slide 37 text

Shape Transition function Vehicle() { this.color = 'white'; this.speed = 0; } var car = new Vehicle(); car.color = 'black'; color speed var motorbike = new Vehicle(); motorbike.wheels = ['front', 'back']; car.maker = 'BMW'; color speed maker color speed wheels 37

Slide 38

Slide 38 text

Property Access console.log(car.color); Check if the object has the property ‘color’. If it is not found, check the property chain. Get the value of the property. Quiz: What if color does not exist? 38

Slide 39

Slide 39 text

Faster Property Access Execute the generic property access color speed Shape check Get the value from property #1 console.log(car.color); 39

Slide 40

Slide 40 text

“Freeze” the Shape http://ariya.ofilabs.com/2012/02/javascript-object-structure-speed-matters.html var universe = { answer: 42 }; // do something else universe.panic = false; var universe = { answer: 42, panic: true }; // do something else universe.panic = false; Note: Monomorphic inline-cache reduces the impact 40

Slide 41

Slide 41 text

Avoid Conditional Transition var vehicle = { color: 'blue' }; if (currentYear > 2014) vehicle.backupCamera = new Camera(); var vehicle = { color: 'blue', backupCamera: null }; if (currentYear > 2014) vehicle.backupCamera = new Camera(); 41

Slide 42

Slide 42 text

Garbage Collector 42

Slide 43

Slide 43 text

Minimize Object Construction http://www.youtube.com/watch?v=Vo72W1HWeFI var tick = new Date(); for (var i = 0, j = 0; i < 4e4; ++i) { var delta = new Date(); delta = delta - tick; tick = new Date(); j += delta; } var tick = Date.now(); for (var i = 0, j = 0; i < 4e4; ++i) { var delta = Date.now() - tick; tick = Date.now(); j += delta; } A lot of small Date objects 43

Slide 44

Slide 44 text

V8 Garbage Collector Tracing d8 --trace-gc example.js new Date() Date.now() 56 ms: Scavenge 1.8 (36.0) -> 0.9 (36.0) MB, 2.8 ms 73 ms: Scavenge 1.8 (36.0) -> 0.9 (37.0) MB, 2.8 ms 89 ms: Scavenge 1.9 (37.0) -> 0.9 (37.0) MB, 1.0 ms 109 ms: Scavenge 1.9 (37.0) -> 0.9 (37.0) MB, 1.0 ms 126 ms: Scavenge 1.9 (37.0) -> 0.9 (37.0) MB, 0.9 ms 141 ms: Scavenge 1.9 (37.0) -> 0.9 (37.0) MB, 0.9 ms 159 ms: Scavenge 1.9 (37.0) -> 0.9 (37.0) MB, 0.9 ms 176 ms: Scavenge 1.9 (37.0) -> 0.9 (37.0) MB, 1.0 ms 192 ms: Scavenge 1.9 (37.0) -> 0.9 (37.0) MB, 0.9 ms 207 ms: Scavenge 1.9 (37.0) -> 0.9 (37.0) MB, 1.0 ms 54 ms: Scavenge 1.8 (36.0) -> 0.9 (36.0) MB, 2.9 ms 67 ms: Scavenge 1.8 (36.0) -> 0.9 (37.0) MB, 2.7 ms. 44

Slide 45

Slide 45 text

Profile-Guided 45

Slide 46

Slide 46 text

Classifier Task Array [1, 2, 3] Object { a: 1, b: 3 } Expression in brackets (x + y) Function function test() { } Identifier x Literal 42 ? 46

Slide 47

Slide 47 text

Naive Classifier function parsePrimaryExpression() { if (match('[')) return parseArrayInitialiser(); if (match('{')) return parseObjectInitialiser(); if (match('(')) return parseBracketExpression(); if (matchKeyword('function')) return parseFunctionExpression() if (matchKeyword('this')) return createThisExpression(); if (match('/') || match('/=')) return createRegExpLiteral(); var token = lex(); if (token.type === Token.Identifier) return createIdentifier(token); if (token.type === Token.NullLiteral) return createNullLiteral(); if (token.type === Token.NumericLiteral) return createNumericLiteral(token); if (token.type === Token.StringLiteral) return createStringLiteral(token); if (token.type === Token.BooleanLiteral) return createBooleanLiteral(token); return throwUnexpected(token); } 47

Slide 48

Slide 48 text

Input Distribution 54362 Identifier 10419 Keyword 8170 String 5820 Punctuator 5213 Numeric 1575 Boolean 909 Null 48

Slide 49

Slide 49 text

Optimized Classifier function parsePrimaryExpression() { var token = lookahead(); if (token.type === Token.Identifier) return createIdentifier(token); if (token.type === Token.NumericLiteral) return createNumericLiteral(token); if (token.type === Token.StringLiteral) return createStringLiteral(token); if (matchKeyword('this')) return createThisExpression(); if (matchKeyword('function')) return parseFunctionExpression() if (token.type === Token.BooleanLiteral) return createBooleanLiteral(token); if (token.type === Token.NullLiteral) return createNullLiteral(); if (match('[')) return parseArrayInitialiser(); if (match('{')) return parseObjectInitialiser(); if (match('(')) return parseBracketExpression(); if (match('/') || match('/=')) return createRegExpLiteral(); return throwUnexpected(lex()); } 49

Slide 50

Slide 50 text

Short Circuit http://ariya.ofilabs.com/2011/11/matching-a-decimal-digit.html function isDigit(ch) { return '0123456789'.indexOf(ch) >= 0; } function isDigit(ch) { return ch !==' ' && '0123456789'.indexOf(ch) >= 0; } Letters Digits Spaces 50

Slide 51

Slide 51 text

Object in a Set http://ariya.ofilabs.com/2012/08/determining-objects-in-a-set-examples-in-javascript.html var valid_words = { 'foobar': true, 'bar': true, 'baz': true, 'quux': true }; function is_valid(word) { return valid_words.hasOwnProperty(word); } is_valid('fox'); // false Dictionary Spell checker Alternatives: array lookup, switch case, prefix/suffix tree, perfect hash 51

Slide 52

Slide 52 text

Tiered Conditionals function is_valid(word) { switch (word.length) { case 3: return word === 'bar' || word === 'baz'; case 4: return word === 'quux'; case 6: return word === 'foobar'; } return false; } Filter #1: Length http://ariya.ofilabs.com/2012/08/determining-objects-in-a-set-examples-in-javascript.html 52

Slide 53

Slide 53 text

Scalability 53

Slide 54

Slide 54 text

Fast = Enough? Alice Bob Chuck Dan ... Bob Alice Dan Chuck ... Address Book Application Sort How’s the speed? 2 ms to sort 10 contacts 54

Slide 55

Slide 55 text

The Code Array.prototype.swap = function (i, j) { var k = this[i]; this[i] = this[j]; this[j] = k; } function sort(list) { var items = list.slice(0), swapped = false, p, q; for (p = 1; p < items.length; ++p) { for (q = 0; q < items.length - p; ++q) { if (items[q + 1] < items[q]) { items.swap(q, q + 1); swapped =true; } } if (!swapped) break; } return items; } Bubble Sort ??? 55

Slide 56

Slide 56 text

Function Instrumentation Array.prototype.swap = function (i, j) { var k = this[i]; this[i] = this[j]; this[j] = k; } Array.prototype.swap = function (i, j) { Log({ name: 'Array.prototype.swap', lineNumber: 1}); var k = this[i]; this[i] = this[j]; this[j] = k; } http://ariya.ofilabs.com/2012/01/scalable-web-apps-the-complexity-issue.html 56

Slide 57

Slide 57 text

Run-time Analysis 0 250000 500000 0 500 1000 Calls to swap() Input Size http://ariya.ofilabs.com/2012/01/scalable-web-apps-the-complexity-issue.html 57

Slide 58

Slide 58 text

Start-up Execution Tracking https://gist.github.com/1823129 jQuery Mobile startup log 4640 function calls jquery.js 26 jQuery jquery.js 103 init undefined, undefined, [object Object] jquery.js 274 each (Function) jquery.js 631 each [object Object], (Function), undefined jquery.js 495 isFunction [object Object] jquery.js 512 type [object Object] jquery.mobile.js 1857 [Anonymous] jquery.mobile.js 642 [Anonymous] jquery.mobile.js 624 enableMouseBindings jquery.mobile.js 620 disableTouchBindings http://ariya.ofilabs.com/2012/02/tracking-javascript-execution-during-startup.html 58

Slide 59

Slide 59 text

Final Words 59

Slide 60

Slide 60 text

Multi-dimensional Application revision Execution time Baseline 60

Slide 61

Slide 61 text

http://www.trollquotes.org/5-gandalf-troll-quote/ 61

Slide 62

Slide 62 text

Measure twice, cut once Under the hood: learn & explore Be advised of every best practice 62

Slide 63

Slide 63 text

Thank You [email protected] @AriyaHidayat ariya.ofilabs.com Many artworks are from OpenClipArt speakerdeck.com/ariya 63

Slide 64

Slide 64 text

More Stuff Vyacheslav Egorov http://mrale.ph/ Florian Loitsch http://floitsch.blogspot.com/ Andy Wingo http://wingolog.org/ http://blip.tv/jsconf/jsconf2012-andy-wingo-6139109 Michael Starzinger: The Footprint of Performance http://www.youtube.com/watch?v=ZhshEZIV2F4 Google I/O 2012 - Breaking the JavaScript Speed Limit with V8 http://www.youtube.com/watch?v=UJPdhx5zTaw 64