Slide 1

Slide 1 text

Daniel Espeset @danielespeset Velocity NYC – September 17, 2014 Benchmarking JS Parsing and Execution on Mobile Devices Unpacking the Black Box

Slide 2

Slide 2 text

talks.desp.in/unpacking-the-black-box Resources for this talk

Slide 3

Slide 3 text

3 https://www.flickr.com/photos/tiarescott/

Slide 4

Slide 4 text

Daniel Espeset @danielespeset Frontend Infrastructure 4 Seth Walker Daniel Espeset Daniel Na Takashi Mizohata Etsy

Slide 5

Slide 5 text

Daniel Espeset @danielespeset Performance 5 Lara Hogan Natalya Hoota Jonathan Klein Allison McKnight Etsy

Slide 6

Slide 6 text

Daniel Espeset @danielespeset >50% Mobile Traffic 10 Languages 6

Slide 7

Slide 7 text

Daniel Espeset @danielespeset 7

Slide 8

Slide 8 text

8 Thinking about Mobile Performance

Slide 9

Slide 9 text

Daniel Espeset @danielespeset 9 1. The Network

Slide 10

Slide 10 text

Daniel Espeset @danielespeset 10 2. The Browser

Slide 11

Slide 11 text

Daniel Espeset @danielespeset 11 degraded render performance

Slide 12

Slide 12 text

Daniel Espeset @danielespeset 12 JavaScript initializing

Slide 13

Slide 13 text

Daniel Espeset @danielespeset 13 jquery.js

Slide 14

Slide 14 text

Daniel Espeset @danielespeset 14

Slide 15

Slide 15 text

Daniel Espeset @danielespeset 15

Slide 16

Slide 16 text

Daniel Espeset @danielespeset 16 ?

Slide 17

Slide 17 text

Daniel Espeset @danielespeset 17 parse execute

Slide 18

Slide 18 text

Daniel Espeset @danielespeset How long does our JavaScript payload take to parse and execute, once it hits the browser? 18

Slide 19

Slide 19 text

Daniel Espeset @danielespeset To the lab! 19

Slide 20

Slide 20 text

Let’s Add Some Timers!

Slide 21

Slide 21 text

Adding naive timers to our JS bundle var start = new Date().getTime(); // ... bulk of your code var duration = new Date().getTime() - start; 1 - 600 21

Slide 22

Slide 22 text

Daniel Espeset @danielespeset Results! 271ms for our main shared JS bundle 22

Slide 23

Slide 23 text

Daniel Espeset @danielespeset This is awesome. Do this if you don’t already. 23

Slide 24

Slide 24 text

var start = new Date().getTime(); // ... bulk of your code var duration = new Date().getTime() - start; 1 - 600 24 A bunch of work happens before this runs. Adding naive timers to our JS bundle

Slide 25

Slide 25 text

Daniel Espeset @danielespeset 25 parse execute

Slide 26

Slide 26 text

Daniel Espeset @danielespeset Next questions 26 1. How long does it take to parse? 2. Can we measure this on every device in our lab?

Slide 27

Slide 27 text

What Are Our Options?

Slide 28

Slide 28 text

Daniel Espeset @danielespeset Remote Debugging 28

Slide 29

Slide 29 text

Daniel Espeset @danielespeset 29

Slide 30

Slide 30 text

Daniel Espeset @danielespeset 30

Slide 31

Slide 31 text

Daniel Espeset @danielespeset Pure JS Solutions 31

Slide 32

Slide 32 text

We could wrap timers around a tag <script>var beforeJquery = new Date().getTime() var took = new Date().getTime() - beforeJquery<script> 20 21 22 32

Slide 33

Slide 33 text

Daniel Espeset @danielespeset This doesn’t work, it may include the network or exclude parse time. 33

Slide 34

Slide 34 text

Same idea, but in a loop var runs = 1000; results = []; while (runs--) { d.write(“var start = Date.now();”); d.write(“”); d.write(“”); d.write(“results.push(Date.now() - start);”); d.write(“var e = d.getElementById(‘test’);”); d.write(“e.parentNode.removeChild(e);”); d.write(“”); } 22 23 24 25 26 27 28 29 30 31 32 34

Slide 35

Slide 35 text

Daniel Espeset @danielespeset Carlos Bueno did this in 2010 “Measuring Javascript Parse and Load” bit.ly/js-parse-and-load 35

Slide 36

Slide 36 text

Daniel Espeset @danielespeset Runtime is complicated JIT Optimizations Garbage Collection 36 bit.ly/js-parse-and-load

Slide 37

Slide 37 text

Our approach

Slide 38

Slide 38 text

Step 1. “window scoper” transforms implicit globals to explicit ones. var foo = {}; (function() { // ... }); 1 2 - 99 38 window.foo = {}; (function() { // ... }); 1 2 - 99

Slide 39

Slide 39 text

Step 2. wrap the contents in a named function 39 window.foo = {}; (function() { // ... }); 1 2 - 99 function sourceCode() { window.foo = {}; (function() { // ... }); } 1 2 3 - 100 101

Slide 40

Slide 40 text

Step 3. turn the function into a string, eval and execute it var start = new Date().getTime(); eval(“function sourceCode() { window.foo = {}; ... ”); var parse = new Date().getTime(); var parseTook = start - parse; sourceCode(); var execTook = new Date().getTime() - parse; 1 2 3 4 5 6 40

Slide 41

Slide 41 text

Step 3. turn the function into a string, eval and execute it var start = new Date().getTime(); eval(“function sourceCode() { window.foo = {}; ... ”); var parse = new Date().getTime(); var parseTook = start - parse; sourceCode(); var execTook = new Date().getTime() - parse; 1 2 3 4 5 6 41

Slide 42

Slide 42 text

Step 3. turn the function into a string, eval and execute it var start = new Date().getTime(); eval(“function sourceCode() { window.foo = {}; ... ”); var parse = new Date().getTime(); var parseTook = start - parse; sourceCode(); var execTook = new Date().getTime() - parse; 1 2 3 4 5 6 42

Slide 43

Slide 43 text

Daniel Espeset @danielespeset 43 parse execute

Slide 44

Slide 44 text

Daniel Espeset @danielespeset 44 results for jquery 1.8.2 + almond Apple (Macbook Pro) Chrome 31 RAM: 16GB CPU: 2.4GHz GPU: 1058MHz 36ms total

Slide 45

Slide 45 text

Daniel Espeset @danielespeset 45 results for jquery 1.8.2 + almond 319ms total Samsung Galaxy S3 Mobile Chrome 31 RAM: 1GB CPU: 1.4GHz GPU: 400MHz

Slide 46

Slide 46 text

Daniel Espeset @danielespeset 46 results for jquery 1.8.2 + almond 1389ms total LG Optimus V (VM670) Android Browser 4.0 RAM: 512MB CPU: 600MHz GPU: 128MHz

Slide 47

Slide 47 text

Daniel Espeset @danielespeset 47 1. How long to eval a big function with our code 2. How long to execute it What does this measure?

Slide 48

Slide 48 text

Daniel Espeset @danielespeset 48 1. Eval is a reasonable proxy for 2. This effectively separates parse and exec Assumptions

Slide 49

Slide 49 text

Add timers to the top and bottom of the big wrapper function var start = new Date().getTime(), innerStart, innerEnd; eval(“function sourceCode() { innerStart = new Date().getTime(); window.foo = {}; ... }”); var execStart = new Date().getTime(); sourceCode(); var execEnd = new Date().getTime() - parse; 1 2 3 4 5 6 7 8 49

Slide 50

Slide 50 text

Add a timer to the top of the big wrapper function 50 var start = new Date().getTime(), innerStart, innerEnd; eval(“function sourceCode() { innerStart = new Date().getTime(); window.foo = {}; ... }”); var execStart = new Date().getTime(); sourceCode(); var execEnd = new Date().getTime() - parse; 1 2 3 4 5 6 7 8

Slide 51

Slide 51 text

(what this looks like at runtime) 51 var execStart = new Date().getTime(); (function sourceCode() { innerStart = new Date().getTime(); window.foo = {}; // ... })(); var execEnd = new Date().getTime() - parse; 1 2 3 4 5 6 7 8 The time between these should be ~0ms

Slide 52

Slide 52 text

(what this looks like at runtime) 52 var execStart = new Date().getTime(); (function sourceCode() { innerStart = new Date().getTime(); window.foo = {}; // ... })(); var execEnd = new Date().getTime() - parse; 1 2 3 4 5 6 7 8 Chrome on Samsung Galaxy S3 50–100ms

Slide 53

Slide 53 text

Daniel Espeset @danielespeset 53 Runtime is complicated JIT Optimizations Garbage Collection Chrome on Samsung Galaxy S3 50–100ms

Slide 54

Slide 54 text

Daniel Espeset @danielespeset 54 parse execute

Slide 55

Slide 55 text

Step 3 REDUX: turn the function into a string, eval it var start = new Date().getTime(); eval(“var parse = new Date().getTime(); !function(){ ... }()”); var execTook = new Date().getTime() - parse; var parseTook = start - parse; 1 2 3 4 55

Slide 56

Slide 56 text

Parse: the time between start, and the first line of the eval running. var start = new Date().getTime(); eval(“var parse = new Date().getTime(); !function(){ ... }()”); var execTook = new Date().getTime() - parse; var parseTook = start - parse; 1 2 3 4 56

Slide 57

Slide 57 text

Exec: the time between the first line of the eval, and the line after var start = new Date().getTime(); eval(“var parse = new Date().getTime(); !function(){ ... }()”); var execTook = new Date().getTime() - parse; var parseTook = start - parse; 1 2 3 4 57

Slide 58

Slide 58 text

Daniel Espeset @danielespeset DeviceTiming 58

Slide 59

Slide 59 text

Daniel Espeset @danielespeset 59 DeviceTiming Server

Slide 60

Slide 60 text

Daniel Espeset @danielespeset 60 DeviceTiming Server Test Devices

Slide 61

Slide 61 text

demo.desp.in DEMO TIME! The backbone example from todomvc.com

Slide 62

Slide 62 text

Daniel Espeset @danielespeset Cutting The Mustard Going Vanilla You could be blocked on CSS 62 Optimizations

Slide 63

Slide 63 text

codeascraft.com etsy.com/careers