Slide 1

Slide 1 text

@maccaw October 2013 Faster JavaScript web apps Optimizing Web Performance

Slide 2

Slide 2 text

Speed matters

Slide 3

Slide 3 text

A second is a 0.65% increase in bounce rate 2,000 ms delay on Bing search pages decreased per-user revenue by 4.3%! It’s really easy justify spending time on speed optimizations, because it directly affects your bottom line.

Slide 4

Slide 4 text

“Speed is the second biggest engagement driver on the internet - just after perceived speed.” - Thomas Gorissen

Slide 5

Slide 5 text

Delay User reaction 0 - 100ms Instant 100 - 300ms Feels Sluggish 300 - 1000ms Machine is working... 1s + Mental Context Switch 10s + I’ll come back later

Slide 6

Slide 6 text

Travel site analysis Top five travel sites - Priceline/Kayak/Travelocity/Orbit Study done by Jatinder Mann at Microsoft.

Slide 7

Slide 7 text

# Total Size (k) Number of Elements CSS Rules Image Files Script Lines Render time (ms) Site #1 3,697 1,504 1,392 41 77,768 Site #2 2,278 1,100 5,325 29 39,183 Site #3 1,061 2,673 1,105 66 12,643 Site #4 1,812 4,252 1,672 12 10,284 Site #5 1,372 900 3,902 6 38,269 Top five travel sites - Priceline/Kayak/Travelocity/Orbit Study done by Jatinder Mann at Microsoft. Changes a factor of 4.

Slide 8

Slide 8 text

# Total Size (k) Number of Elements CSS Rules Image Files Script Lines Render time (ms) Site #1 3,697 1,504 1,392 41 77,768 2,700 Site #2 2,278 1,100 5,325 29 39,183 2900 Site #3 1,061 2,673 1,105 66 12,643 2500 Site #4 1,812 4,252 1,672 12 10,284 2600 Site #5 1,372 900 3,902 6 38,269 1500 Many developers would assume that the fastest site would be the one with the least number of formatted lines of JavaScript, like Site #4, or the one with the least bytes downloaded, like Site #3. However, that’s not the case. Site #5 is actually the fastest, even though it has more JavaScript and bytes downloaded.

Slide 9

Slide 9 text

The execution of a web program primarily involves three tasks: fetching resources, page layout and rendering, and JavaScript execution. Notice that site 5 renders the quickest, and evaluates JavaScript the quickest.

Slide 10

Slide 10 text

Resource Waterfall Dark green is DNS resolution. Orange is TCP handshake. Green is the HTTP request and Blue the time taken to download a resource. Notice that while monocle.io is being fetched, new HTTP requests are being dispatched. HTML is parsed incrementally. Scheduling of when the resource is fetched is in large part determined by the structure of the markup.

Slide 11

Slide 11 text

Resource Waterfall DNS resolution & handshake You can see that DNS resolution and TCP handshaking is actually the bottleneck in many network requests. In other words bandwidth is not the limiting factor here, but network roundtrip latency between server and client. You can see further requests down the page don’t need to do DNS resolution / TCP handshake because of keepalive.

Slide 12

Slide 12 text

Resource Waterfall DOM Content Loaded Start Render Complete

Slide 13

Slide 13 text

Resource Waterfall The other thing to notice is that rendering on the client is taking up about half the time, and network the other half.

Slide 14

Slide 14 text

Network & Rendering Let’s address the two main bottlenecks:

Slide 15

Slide 15 text

Network Checklist 12 Rules

Slide 16

Slide 16 text

“eliminate and reduce unnecessary network latency, and minimize the amount of transferred bytes” - Ilya Grigorik User only gets value when work is painted to the screen.

Slide 17

Slide 17 text

Quick response Use KeepAlive Don’t redirect Use CDNs Reduce resources Minify JavaScript / CSS Concat resources GZip responses Fingerprint & cache static assets Cache Ajax requests Remove duplicate code Minimize & compress images Network Use standards mode for IE Put stylesheets first Stop blocking JavaScript, use async Delay loading unnecessary JS Client

Slide 18

Slide 18 text

Quick response Use KeepAlive Don’t redirect Use CDNs Reduce resources Minify JavaScript / CSS Concat resources GZip responses Fingerprint & cache static assets Cache Ajax requests Remove duplicate code Minimize & compress images Network Use standards mode for IE Put stylesheets first Stop blocking JavaScript, use async Delay loading unnecessary JS Client You need to respond as quick as possible. Google search actually returns the header of the page before it’s even parsed the request. You’re only as fast as your weakest link. Do not preload data / do lots of SQL queries in first request. Put that in a second (async) script.

Slide 19

Slide 19 text

Quick response Use KeepAlive Don’t redirect Use CDNs Reduce resources Minify JavaScript / CSS Concat resources GZip responses Fingerprint & cache static assets Cache Ajax requests Remove duplicate code Minimize & compress images Network Use standards mode for IE Put stylesheets first Stop blocking JavaScript, use async Delay loading unnecessary JS Client Reuse established connections. TCP handshakes are expensive. Three way SYN/SYN ACK/ACK. Especially if there’s SSL negotiation. It’s enabled by default in HTTP 1.1

Slide 20

Slide 20 text

Quick response Use KeepAlive Don’t redirect Use CDNs Reduce resources Minify JavaScript / CSS Concat resources GZip responses Fingerprint & cache static assets Cache Ajax requests Remove duplicate code Minimize & compress images Network Use standards mode for IE Put stylesheets first Stop blocking JavaScript, use async Delay loading unnecessary JS Client Can cost you 200ms at least. 63% of the top websites have a top level redirect.

Slide 21

Slide 21 text

Quick response Use KeepAlive Don’t redirect Use CDNs Reduce resources Minify JavaScript / CSS Concat resources GZip responses Fingerprint & cache static assets Cache Ajax requests Remove duplicate code Minimize & compress images Network Use standards mode for IE Put stylesheets first Stop blocking JavaScript, use async Delay loading unnecessary JS Client Put the data as close as possible to people. Reduce the hops. Especially with static resources.

Slide 22

Slide 22 text

Quick response Use KeepAlive Don’t redirect Use CDNs Reduce resources Minify JavaScript / CSS Concat resources GZip responses Fingerprint & cache static assets Cache Ajax requests Remove duplicate code Minimize & compress images Network Use standards mode for IE Put stylesheets first Stop blocking JavaScript, use async Delay loading unnecessary JS Client Average website has 777k of resources. The vast amount of them are images/ JavaScript. No code is faster than no code. According to HTTP Archive, an average page is now composed of 90+ individual resources.

Slide 23

Slide 23 text

Quick response Use KeepAlive Don’t redirect Use CDNs Reduce resources Minify JavaScript / CSS Concat resources GZip responses Fingerprint & cache static assets Cache Ajax requests Remove duplicate code Minimize & compress images Network Use standards mode for IE Put stylesheets first Stop blocking JavaScript, use async Delay loading unnecessary JS Client Smaller assets. Gzip already remove duplications, but minifying JS will actually rewrite it.

Slide 24

Slide 24 text

Quick response Use KeepAlive Don’t redirect Use CDNs Reduce resources Minify JavaScript / CSS Concat resources GZip responses Fingerprint & cache static assets Cache Ajax requests Remove duplicate code Minimize & compress images Network Use standards mode for IE Put stylesheets first Stop blocking JavaScript, use async Delay loading unnecessary JS Client TCP handshakes are expensive. Three way SYN/ACK/ACK. SSL is even more expensive. Latency is the bottleneck.

Slide 25

Slide 25 text

Quick response Use KeepAlive Don’t redirect Use CDNs Reduce resources Minify JavaScript / CSS Concat resources GZip responses Fingerprint & cache static assets Cache Ajax requests Remove duplicate code Minimize & compress images Network Use standards mode for IE Put stylesheets first Stop blocking JavaScript, use async Delay loading unnecessary JS Client If you GZip something before giving it to a CDN, then you don’t have to deal with the server overheads of it. Not turned on by default in nginx/apache.

Slide 26

Slide 26 text

Quick response Use KeepAlive Don’t redirect Use CDNs Reduce resources Minify JavaScript / CSS Concat resources GZip responses Fingerprint & cache static assets Cache Ajax requests Remove duplicate code Minimize & compress images Network Use standards mode for IE Put stylesheets first Stop blocking JavaScript, use async Delay loading unnecessary JS Client Set an ‘Expires’ header for a year. Fingerprint assets with a MD5 checksum of their data.

Slide 27

Slide 27 text

Quick response Use KeepAlive Don’t redirect Use CDNs Reduce resources Minify JavaScript / CSS Concat resources GZip responses Fingerprint & cache static assets Cache Ajax requests Remove duplicate code Minimize & compress images Network Use standards mode for IE Put stylesheets first Stop blocking JavaScript, use async Delay loading unnecessary JS Client Only 1% of Ajax requests are cached.

Slide 28

Slide 28 text

Quick response Use KeepAlive Don’t redirect Use CDNs Reduce resources Minify JavaScript / CSS Concat resources GZip responses Fingerprint & cache static assets Cache Ajax requests Remove duplicate code Minimize & compress images Network Use standards mode for IE Put stylesheets first Stop blocking JavaScript, use async Delay loading unnecessary JS Client 58% of the web has duplicate code on it. I found two versions of jQuery on Stripe.

Slide 29

Slide 29 text

Quick response Use KeepAlive Don’t redirect Use CDNs Reduce resources Minify JavaScript / CSS Concat resources GZip responses Fingerprint & cache static assets Cache Ajax requests Remove duplicate code Minimize & compress images Network Use standards mode for IE Put stylesheets first Stop blocking JavaScript, use async Delay loading unnecessary JS Client More than half of the bytes on most websites today are images. Avoid large images. Remember PNGs are lossless. Compress jpgs. Sprites.

Slide 30

Slide 30 text

Quick response Use KeepAlive Don’t redirect Use CDNs Reduce resources Minify JavaScript / CSS Concat resources GZip responses Fingerprint & cache static assets Cache Ajax requests Remove duplicate code Minimize & compress images Network Client Use standards mode for IE Put stylesheets first Stop blocking JavaScript, use async Delay loading unnecessary JS Client In IE, set the X-UA-Compatible header. This will make it render HTML more intelligently. Much faster to put it in the header than a tag.

Slide 31

Slide 31 text

Quick response Use KeepAlive Don’t redirect Use CDNs Reduce resources Minify JavaScript / CSS Concat resources GZip responses Fingerprint & cache static assets Cache Ajax requests Remove duplicate code Minimize & compress images Network Client Use standards mode for IE Put stylesheets first Stop blocking JavaScript, use async Delay loading unnecessary JS Client You want the browser to issue that network request first. Browser will also block painting until that network request is done. Inline styles invalidate the CPU cache. Only keep the required styles for the page. Don’t share between pages unless theme.

Slide 32

Slide 32 text

Quick response Use KeepAlive Don’t redirect Use CDNs Reduce resources Minify JavaScript / CSS Concat resources GZip responses Fingerprint & cache static assets Cache Ajax requests Remove duplicate code Minimize & compress images Network Client Use standards mode for IE Put stylesheets first Stop blocking JavaScript, use defer Delay loading unnecessary JS Client Remove *all* blocking JavaScript from the page. Use ‘defer’ attribute. Sometimes causes bugs. Remove JS that doesn’t need to be there on page load, like analytics.

Slide 33

Slide 33 text

Quick response Use KeepAlive Don’t redirect Use CDNs Reduce resources Minify JavaScript / CSS Concat resources GZip responses Fingerprint & cache static assets Cache Ajax requests Remove duplicate code Minimize & compress images Network Client Use standards mode for IE Put stylesheets first Stop blocking JavaScript, use async Delay loading unnecessary JS Client Remove things like google analytics and mixpanel until the window ‘load’ event.

Slide 34

Slide 34 text

A few network tips Network is fairly straightforward. Here’s some specific tips.

Slide 35

Slide 35 text

#1. Use ngx_pagespeed throwing ngx_pagespeed at an app will automate most of the network optimizations you should be making.

Slide 36

Slide 36 text

• Image optimization: stripping meta-data, dynamic resizing, recompression • CSS & JavaScript minification, concatenation, inlining, and outlining • Small resource inlining • Deferring image and JavaScript loading • HTML rewriting • Cache lifetime extension #1. Use ngx_pagespeed throwing ngx_pagespeed at an app will automate most of the network optimizations you should be making.

Slide 37

Slide 37 text

#2. Don’t use large cookies throwing ngx_pagespeed at an app will automate most of the network optimizations you should be making.

Slide 38

Slide 38 text

GET /posts/you-must-try-and-then-you-must-ask-the-akamai-blog HTTP/1.1 Host: monocle.io Connection: keep-alive Cache-Control: no-cache Accept: text/html,application/xhtml+xml,application/ xml;q=0.9,image/webp,*/*;q=0.8 Pragma: no-cache Accept-Encoding: gzip,deflate,sdch Accept-Language: en-US,en;q=0.8 #2. Don’t use large cookies Don’t store session data in large cookies on the client. Just use a session ID.

Slide 39

Slide 39 text

Cookie: mp_c4d87fc5196ab5af8545f3669063cf08_mixpanel=%7B%22distinct_id%22%3A%20%229b34ce05- ca4f-49bc-98a5-6d8c4a070ae5%22%2C%22%24initial_referrer%22%3A%20%22%24direct%22%2C %22%24initial_referring_domain%22%3A%20%22%24direct%22%7D; _ga=GA1.2.1343163750.1375193368; mp_2f5bb6e9a3a423678df4d18e5f268000_mixpanel=%7B%22distinct_id%22%3A%20%229b34ce05- ca4f-49bc-98a5-6d8c4a070ae5%22%2C%22%24initial_referrer%22%3A%20%22%24direct%22%2C %22%24initial_referring_domain%22%3A%20%22%24direct%22%2C%22__mps%22%3A%20%7B%7D%2C%22__mpso%22%3A%20%7B %7D%2C%22__mpa%22%3A%20%7B%7D%2C%22__mpap%22%3A%20%5B%5D%2C%22%24search_engine%22%3A%20%22google%22%7D; rack.session=BAh7C0kiD3Nlc3Npb25faWQGOgZFVEkiRWQ5ODY0OTEwOWViNjljZTM1MWMz %0AMzU2ODgyZDhjN2E0Y2NjNzJjZjE1MzA4YWVmMjY5ZTEyMDg2NzU0MjAwNWUG %0AOwBGSSIPY3NyZi50b2tlbgY7AFRJIkVhZGU0MjFiYzg5ZTIyZGE5YzY5NDA1%0AOTA3ZjM5NzliZTg2Y2E2NzA4NWZiNzM0NjVkMGE5 YTc4YzFjOGIyNGI5BjsA%0ARkkiCWNzcmYGOwBGSSJFMmEzZjcxOThkY2ExMDA4OGU3ZWEyYjk0MTg1MzJl %0AMDg3NzMyNGU0Y2Y1YWFjYjM0OGFlMGRiMzU3Y2QxMGMxYgY7AEZJIgpvYXV0%0AaAY7AFR7BkkiDHR3aXR0ZXIGOwBUewZJIhdjYWxs YmFja19jb25maXJtZWQG%0AOwBUVEkiDHVzZXJfaWQGOwBGSSIpOWIzNGNlMDUtY2E0Zi00OWJjLTk4YTUt %0ANmQ4YzRhMDcwYWU1BjsAVEkiD3JhbmRvbWl6ZWQGOwBGVA%3D%3D%0A--b7d6f828d50b640902913b2ca3c47422eb02544f GET /posts/you-must-try-and-then-you-must-ask-the-akamai-blog HTTP/1.1 Host: monocle.io Connection: keep-alive Cache-Control: no-cache Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 Pragma: no-cache Accept-Encoding: gzip,deflate,sdch Accept-Language: en-US,en;q=0.8 #2. Don’t use large cookies Don’t store session data in large cookies on the client. Just use a session ID.

Slide 40

Slide 40 text

Cookie: mp_c4d87fc5196ab5af8545f3669063cf08_mixpanel=%7B%22distinct_id%22%3A%20%229b34ce05- ca4f-49bc-98a5-6d8c4a070ae5%22%2C%22%24initial_referrer%22%3A%20%22%24direct%22%2C %22%24initial_referring_domain%22%3A%20%22%24direct%22%7D; _ga=GA1.2.1343163750.1375193368; mp_2f5bb6e9a3a423678df4d18e5f268000_mixpanel=%7B%22distinct_id%22%3A%20%229b34ce05- ca4f-49bc-98a5-6d8c4a070ae5%22%2C%22%24initial_referrer%22%3A%20%22%24direct%22%2C %22%24initial_referring_domain%22%3A%20%22%24direct%22%2C%22__mps%22%3A%20%7B%7D%2C%22__mpso%22%3A%20%7B %7D%2C%22__mpa%22%3A%20%7B%7D%2C%22__mpap%22%3A%20%5B%5D%2C%22%24search_engine%22%3A%20%22google%22%7D; rack.session=BAh7C0kiD3Nlc3Npb25faWQGOgZFVEkiRWQ5ODY0OTEwOWViNjljZTM1MWMz %0AMzU2ODgyZDhjN2E0Y2NjNzJjZjE1MzA4YWVmMjY5ZTEyMDg2NzU0MjAwNWUG %0AOwBGSSIPY3NyZi50b2tlbgY7AFRJIkVhZGU0MjFiYzg5ZTIyZGE5YzY5NDA1%0AOTA3ZjM5NzliZTg2Y2E2NzA4NWZiNzM0NjVkMGE5 YTc4YzFjOGIyNGI5BjsA%0ARkkiCWNzcmYGOwBGSSJFMmEzZjcxOThkY2ExMDA4OGU3ZWEyYjk0MTg1MzJl %0AMDg3NzMyNGU0Y2Y1YWFjYjM0OGFlMGRiMzU3Y2QxMGMxYgY7AEZJIgpvYXV0%0AaAY7AFR7BkkiDHR3aXR0ZXIGOwBUewZJIhdjYWxs YmFja19jb25maXJtZWQG%0AOwBUVEkiDHVzZXJfaWQGOwBGSSIpOWIzNGNlMDUtY2E0Zi00OWJjLTk4YTUt %0ANmQ4YzRhMDcwYWU1BjsAVEkiD3JhbmRvbWl6ZWQGOwBGVA%3D%3D%0A--b7d6f828d50b640902913b2ca3c47422eb02544f GET /posts/you-must-try-and-then-you-must-ask-the-akamai-blog HTTP/1.1 Host: monocle.io Connection: keep-alive Cache-Control: no-cache Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 Pragma: no-cache Accept-Encoding: gzip,deflate,sdch Accept-Language: en-US,en;q=0.8 #2. Don’t use large cookies Don’t store session data in large cookies on the client. Just use a session ID.

Slide 41

Slide 41 text

#3. Preload data via deferred JavaScript TODO - improve.

Slide 42

Slide 42 text

Slide 43

Slide 43 text

jQuery(function(){ var options = <%== @options.to_json %>; window.app = new App(options); }); get '/setup.js' do content_type 'application/javascript' @options = { user: current_user, posts: Post.popular.all, } erb :setup end

Slide 44

Slide 44 text

#4. Pre-fetch & Pre-render resources

Slide 45

Slide 45 text

DNS-prefetch will just do a DNS resolution. Prefetch will go and fetch the page’s data. Supported in Firefox/ Chrome. Prerender will actually go and fetch and pre render the page. subresource?

Slide 46

Slide 46 text

prerender = $('') prerender.attr('href', url) prerender.appendTo('body') You can actually use these things programatically. For monocle.io, I detect if a person has hovered over an article, and pre-render it’s contents. When they click on the link, Chrome will actually just swap in and out the page - instant.

Slide 47

Slide 47 text

You can only pre-render one page at a time. You can see this happening inside Chrome’s Task manager.

Slide 48

Slide 48 text

You can only pre-render one page at a time. You can see this happening inside Chrome’s Task manager.

Slide 49

Slide 49 text

Let’s talk about

Slide 50

Slide 50 text

Let’s talk about Rendering Rendering is often unoptimized, and is less talked about. It’s the other bottleneck.

Slide 51

Slide 51 text

Reflows Reflow in a web browser refers to the process where the render engine calculates positions and geometries of elements in the document. In other words it figures out how elements should be displayed before the paint. It’s also known as redraw in WebKit.

Slide 52

Slide 52 text

This is a visualization of Gecko rendering the mozilla.org homepage, calculating the layout and position of elements. at 0:16 something happens, which invalidates the layout and means it has to re-calculate the whole thing again. Unnecessary - this is the kind of thing we want to avoid.

Slide 53

Slide 53 text

What triggers a reflow? Adding/removing/showing/hiding DOM nodes. User actions such as scrolling/ resizing etc. Adding classes to DOM nodes.

Slide 54

Slide 54 text

What triggers a reflow? clientHeight, clientLeft, clientTop, clientWidth, focus(), getBoundingClientRect(), getClientRects(), innerText, offsetHeight, offsetLeft, offsetParent, offsetTop, offsetWidth, outerText, scrollByLines(), scrollByPages(), scrollHeight, scrollIntoView(), scrollIntoViewIfNeeded(), scrollLeft, scrollTop, scrollWidth, scrollTo(), scrollX, scrollY Accessing any of these properties/functions can cause a reflow. Adding/removing/ showing/hiding DOM nodes. User actions such as scrolling/resizing etc. Adding classes to DOM nodes. http://gent.ilcore.com/2011/03/how-not-to-trigger-layout-in-webkit.html

Slide 55

Slide 55 text

Reflows are often expensive Reflows are sometimes unavoidable, but you can reduce their impact. Three places you really notice reflows. First page load. Animation loops. Scrolling. We need to optimize all three use cases.

Slide 56

Slide 56 text

Change classes as low in the DOM tree as possible Change classes as low in the DOM tree as possible and thus limit the scope of the reflow to as few nodes as possible.

Slide 57

Slide 57 text

Avoid DOM thrashing

Slide 58

Slide 58 text

// Read var h1 = element1.clientHeight + 'px'; // Write element2.style.height = h1; // Document reflow... // Read var h2 = element3.clientHeight + 'px'; // Write element4.style.height = h2; // Document reflow... Avoid DOM thrashing

Slide 59

Slide 59 text

// Read var h1 = element1.clientHeight + 'px'; var h2 = element3.clientHeight + 'px'; // Write element2.style.height = h1; element4.style.height = h2; // Document reflow... Avoid DOM thrashing

Slide 60

Slide 60 text

// Read var h1 = element1.clientHeight + 'px'; // Write requestAnimationFrame(function() { element2.style.height = h1; }); // Read var h2 = element3.clientHeight + 'px'; // Write requestAnimationFrame(function() { element4.style.height = h2; }); Avoid DOM thrashing

Slide 61

Slide 61 text

// Slow element1.appendChild(span); element1.appendChild(div1); element1.appendChild(div2); Avoid DOM thrashing

Slide 62

Slide 62 text

// Faster var fragment = document.createDocumentFragment(); fragment.appendChild(span); fragment.appendChild(div1); fragment.appendChild(div2); element1.appendChild(fragment); Avoid DOM thrashing

Slide 63

Slide 63 text

// Fastest element1.innerHTML = myTemplate; Avoid DOM thrashing

Slide 64

Slide 64 text

Avoid DOM thrashing Before After

Slide 65

Slide 65 text

Use requestAnimationFrame for animation loops

Slide 66

Slide 66 text

function slowLoop() { setInterval(function(){ // Animation loop }, 10); }; function fasterLoop() { // Animation loop requestAnimationFrame(fasterLoop); }; You have to make sure that you’re causing reflows when it’s convenient for the browser. The interval is firing faster than the browser’s refresh rate - will cause a lot of unnecessary work that won’t be painted.

Slide 67

Slide 67 text

function slowLoop() { setInterval(function(){ // Animation loop }, 10); }; function fasterLoop() { // Animation loop requestAnimationFrame(fasterLoop); }; In fact this advice isn’t limited to animation loops. If you’re deferring any kind of rendering, use it - much kinder to the browser. You can easily shim it in older browsers.

Slide 68

Slide 68 text

Debouncing scroll events

Slide 69

Slide 69 text

var debounce = function(callback, wait) { if (wait == null) wait = 300; var timeout = null; return function() { var args = arguments; var ctx = this; var later = function() { callback.apply(ctx, args); }; clearTimeout(timeout); timeout = setTimeout(later, wait); }; } Debouncing scroll events

Slide 70

Slide 70 text

var onScroll = function () { /* ... */ }; $('#element').scroll(debounce(onScroll)); Make sure your onScroll function is as fast as possible. Maybe even put any rendering it does in a requestAnimationFrame.

Slide 71

Slide 71 text

Use onbeforeunload sparingly onbeforeunload is often used to warn the user when there are pending Ajax requests. However, DO NOT set it at all times, only when there are pending requests. Really messes up Chrome’s caching.

Slide 72

Slide 72 text

var addHandler = function() { window.onbeforeunload = function() { return 'There are some pending network requests...'; }; }; var removeHandler = function() { window.onbeforeunload = null; }; onbeforeunload is often used to warn the user when there are pending Ajax requests. However, DO NOT set it at all times, only when there are pending requests. Really messes up Chrome’s caching.

Slide 73

Slide 73 text

var addHandler = function() { window.onbeforeunload = function() { return 'There are some pending network requests...'; }; }; var removeHandler = function() { window.onbeforeunload = null; }; $(document).ajaxSend(function() { addHandler(); }); $(document).ajaxComplete(function() { removeHandler(); }); onbeforeunload is often used to warn the user when there are pending Ajax requests. However, DO NOT set it at all times, only when there are pending requests. Really messes up Chrome’s caching.

Slide 74

Slide 74 text

Essentially, do less Especially before DOMReady is fired.

Slide 75

Slide 75 text

Essentially, do less • Don’t resize images in CSS • Avoid complex CSS selectors • Don’t use inline styles • Batch up DOM communication • Take elements out of flow when animating

Slide 76

Slide 76 text

Solving a crime Solving a performance problem is like solving a crime. First you have to collect evidence. Then you have to interrogate suspects. Lastly you need to collect forensics.

Slide 77

Slide 77 text

Solving a crime Let’s look for evidence Solving a performance problem is like solving a crime. First you have to collect evidence. Then you have to interrogate suspects. Lastly you need to collect forensics.

Slide 78

Slide 78 text

The crime scene is Sourcing.io, an app I’m building to help source engineers. Notice we have an infinite table - people are loaded in as the table is scrolled.

Slide 79

Slide 79 text

Using Chrome’s Timeline We’re first going to use Chrome’s Timeline feature in the Inspector.

Slide 80

Slide 80 text

We can open up the Timeline, hit record, then play around with our application. In this case, we’re going to scroll down the list of people, loading more in.

Slide 81

Slide 81 text

Low and behold, we’ve got a performance issue. See, we’re almost never hitting 60fps. Look at all that purple - lots of time spent rendering.

Slide 82

Slide 82 text

If we analysis it closer, we can see an expensive whole document layout triggered by adding a class - specifically the showLoading function on PeopleTable.

Slide 83

Slide 83 text

If we analysis it closer, we can see an expensive whole document layout triggered by adding a class - specifically the showLoading function on PeopleTable.

Slide 84

Slide 84 text

showLoading: => @$el.addClass('loading') This is because I’m trying to be a bit too clever, and whenever the people table is loading, I add a class to it. And vica versa when the Ajax request has completed. It’s a fairly expensive operation though.

Slide 85

Slide 85 text

This is because I’m trying to be a bit too clever, and whenever the people table is loading, I add a class to it

Slide 86

Slide 86 text

Detecting a memory leak. The profile for a normal application should look like a saw tooth. Memory is allocated and then the garbage collector kicks in.

Slide 87

Slide 87 text

Here’s a case where we might have a memory issue. You can see more and more DOM nodes are being added to the page. Looks like we need to do a bit of manual garbage collection, and delete some of those DOM nodes outside the viewport.

Slide 88

Slide 88 text

Using Chrome’s Profiler

Slide 89

Slide 89 text

No content

Slide 90

Slide 90 text

Collect JavaScript CPU profile, hit start. Then perform some sort of action in your app - for example in Monocle, selecting a post. Then click ‘stop’.

Slide 91

Slide 91 text

You can see which operations are taking the longest. Here, anything to do with communicating with the DOM takes the longest. The sort order is ‘Bottom Up’, listing functions by impact on performance. You can also see timings by percentage, which is useful for exposing bottlenecks.

Slide 92

Slide 92 text

Unoptimized code The yellow exclamation marks indicate where the V8 compiler falls back to ‘full’ mode, rather than ‘optimized’ mode. This can happen for a variety of reasons, but in this case it’s because jQuery’s extend has a try/catch in it. If you hover, it’ll tell you what’s wrong.

Slide 93

Slide 93 text

You can also drill down into events, and see what caused them. In this case WebKit’s ‘scrollIntoViewIfNeeded’ function is taking up about half of our render time.

Slide 94

Slide 94 text

Heap snapshots

Slide 95

Slide 95 text

No content

Slide 96

Slide 96 text

We can take two snapshots. And then compare them.

Slide 97

Slide 97 text

We can take two snapshots. And then compare them. You can see the difference in number of objects (via the delta). Again, this can help you drill down on a memory leak.

Slide 98

Slide 98 text

Lay down the law To help prevent performance crimes, let’s lay down the law. We can continually record performance, and notify you when something changes.

Slide 99

Slide 99 text

PageSpeed Insights PageSpeed insights is a service created by Google that’ll tell you a lot about the performance of your web app.

Slide 100

Slide 100 text

PageSpeed Insights http://developers.google.com/speed/pagespeed/insights/

Slide 101

Slide 101 text

PageSpeed Insights http://developers.google.com/speed/pagespeed/insights/

Slide 102

Slide 102 text

PageSpeed Insights https://code.google.com/apis/console/ Let’s automate collection of this data. Google have created a ‘free’ API for it. Continuous integration.

Slide 103

Slide 103 text

require 'nestful' require 'csv' page_speed = Nestful::Mash.get( 'https://www.googleapis.com/pagespeedonline/v1/runPagespeed', url: 'http://monocle.io', key: ENV['GOOGLE_SECRET'] ) CSV.open('stats.csv', 'a') do |csv| csv << [Time.now, page_speed.score] end Here’s an example of how you could access the API in Ruby. You could tie this in with a post-deploy hook, so it records data after every deploy. Then you can look through the data and identify deploys that change the performance significantly and get an early heads up. Would be pretty awesome if this was in a service anyone?

Slide 104

Slide 104 text

So in conclusion...

Slide 105

Slide 105 text

Have a budget, and stick to it. 300-500 ms is a good place to start.

Slide 106

Slide 106 text

Stick to good defaults, but keep performance in mind It’s pretty easy to stick to good network defaults. Remember to not forgot to optimize rendering.

Slide 107

Slide 107 text

Remember

Slide 108

Slide 108 text

Speed matters Remember

Slide 109

Slide 109 text

alexmaccaw.com @maccaw onbeforeunload