nicholas' performance talk at google

122
Nicholas C. Zakas Principal Front End Engineer, Yahoo! Google, September 29, 2010 Nicholas' Performance Talk at Google

Upload: nicholas-zakas

Post on 10-May-2015

5.609 views

Category:

Technology


0 download

DESCRIPTION

Half about general JavaScript performance, half about performance on the Yahoo! homepage.

TRANSCRIPT

Page 1: Nicholas' Performance Talk at Google

Nicholas C. Zakas Principal Front End Engineer, Yahoo!

Google, September 29, 2010

Nicholas' Performance Talkat Google

Page 2: Nicholas' Performance Talk at Google

Who's this guy?

Principal Front End Engineer

Contributor,Creator of YUI Test

Author Lead Author Contributor Lead Author

Page 3: Nicholas' Performance Talk at Google

Part 1: JavaScript Performance

Page 4: Nicholas' Performance Talk at Google

JavaScript performancedirectly

affects user experience

Page 5: Nicholas' Performance Talk at Google
Page 6: Nicholas' Performance Talk at Google

The UI ThreadThe brains of the operation

Page 7: Nicholas' Performance Talk at Google

The browser UI thread is responsible forboth UI updates and JavaScript execution

Only one can happen at a time

Page 8: Nicholas' Performance Talk at Google

Jobs for UI updates and JavaScript execution areadded to a UI queue if the UI thread is busy

Each job must wait in line for its turn to execute

Page 9: Nicholas' Performance Talk at Google

<button id="btn" style="font-size: 30px; padding: 0.5em 1em">Click Me</button>

<script type="text/javascript">window.onload = function(){ document.getElementById("btn").onclick = function(){ //do something };};</script>

Page 10: Nicholas' Performance Talk at Google

Before Click

UI Thread

UI Queue

time

Page 11: Nicholas' Performance Talk at Google

When Clicked

UI Thread

UI Queue

time

onclick

UI Update

UI Update

Page 12: Nicholas' Performance Talk at Google

When Clicked

UI Thread

UI Queue

time

onclick

UI Update

UI Update

Draw down state

Page 13: Nicholas' Performance Talk at Google

When Clicked

UI Thread

UI Queue

time

onclick

UI Update

UI Update

Page 14: Nicholas' Performance Talk at Google

When Clicked

UI Thread

UI Queue

time

onclick UI UpdateUI Update

Draw up state

Page 15: Nicholas' Performance Talk at Google

No UI updates while JavaScript is executing

Page 16: Nicholas' Performance Talk at Google

JavaScript May Cause UI Update

<button id="btn" style="font-size: 30px; padding: 0.5em 1em">Click Me</button>

<script type="text/javascript">window.onload = function(){ document.getElementById("btn").onclick = function(){ var div = document.createElement(“div”); div.className = “tip”; div.innerHTML = “You clicked me!”; document.body.appendChild(div); };};</script>

Page 17: Nicholas' Performance Talk at Google

A UI update must use the latest info available

Page 18: Nicholas' Performance Talk at Google

Long-running JavaScript=

Unresponsive UI

Page 19: Nicholas' Performance Talk at Google

Responsive UI

UI Thread

time

JavaScript UI UpdateUI Update

Page 20: Nicholas' Performance Talk at Google

Unresponsive UI

UI Thread

time

JavaScript UI UpdateUI Update

Page 21: Nicholas' Performance Talk at Google

The longer JavaScript runs,the worse the user experience

Page 22: Nicholas' Performance Talk at Google

How Long Is Too Long?

“0.1 second [100ms] is about the limit for having the user feel that the system is reacting instantaneously, meaning that no special feedback is necessary except to display the result.”

- Jakob Nielsen

Page 23: Nicholas' Performance Talk at Google

Translation:No single JavaScript job should execute for more than 100ms to

ensure a responsive UI

Page 24: Nicholas' Performance Talk at Google

Recommendation:Limit JavaScript execution to no more

than 50ms

measured on IE6 :)

Page 25: Nicholas' Performance Talk at Google

Runtime TechniquesWays to ensure JavaScript doesn't run away

Page 26: Nicholas' Performance Talk at Google

function processArray(items, process, callback){ for (var i=0,len=items.length; i < len; i++){ process(items[i]); } callback();}

Page 27: Nicholas' Performance Talk at Google

Technique #1: Timers

Page 28: Nicholas' Performance Talk at Google

JavaScript Timers

• Created using setTimeout()• Schedules a new JavaScript execution job for

some time in the future• When the delay is up, the job is added to the

UI queue– Note: This does not guarantee execution

after the delay, just that the job is added to the UI queue and will be executed when appropriate

Page 29: Nicholas' Performance Talk at Google

JavaScript Timers

• For complex processing, split up into timed functionality

• Use timers to delay some processing for later

Page 30: Nicholas' Performance Talk at Google

function timedProcessArray(items, process, callback){ //create a clone of the original

var todo = items.concat(); setTimeout(function(){ var start = +new Date(); do { process(todo.shift()); } while (todo.length > 0 && (+new Date() - start < 50)); if (todo.length > 0){ setTimeout(arguments.callee, 25); } else { callback(items); } }, 25);}

Page 31: Nicholas' Performance Talk at Google

When Clicked

UI Thread

UI Queue

time

onclick

UI Update

UI Update

Page 32: Nicholas' Performance Talk at Google

When Clicked

UI Thread

UI Queue

time

onclick

UI Update

UI Update

Page 33: Nicholas' Performance Talk at Google

When Clicked

UI Thread

UI Queue

time

onclick

UI Update

UI Update

Page 34: Nicholas' Performance Talk at Google

When Clicked

UI Thread

UI Queue

time

UI UpdateUI Update onclick

Page 35: Nicholas' Performance Talk at Google

After 25ms

UI Thread

UI Queue

time

UI UpdateUI Update onclick

JavaScript

Page 36: Nicholas' Performance Talk at Google

After 25ms

UI Thread

UI Queue

time

UI UpdateUI Update onclick JavaScript

Page 37: Nicholas' Performance Talk at Google

After Another 25ms

UI Thread

UI Queue

time

UI UpdateUI Update onclick JavaScript

JavaScript

Page 38: Nicholas' Performance Talk at Google

After Another 25ms

UI Thread

UI Queue

time

UI UpdateUI Update onclick JavaScript JavaScript

Page 39: Nicholas' Performance Talk at Google

Technique #2: Web Workers

Page 40: Nicholas' Performance Talk at Google
Page 41: Nicholas' Performance Talk at Google

Web Workers

• Asynchronous JavaScript execution• Execution happens in a separate process

– Not on the UI thread = no UI delays• Data-driven API

– Data is serialized when sending data into or out of Worker

– No access to DOM, BOM– Completely separate execution

environment

Page 42: Nicholas' Performance Talk at Google

//in pagevar worker = new Worker("process.js");worker.onmessage = function(event){ useData(event.data);};worker.postMessage(values);

//in process.jsself.onmessage = function(event){ var items = event.data; for (var i=0,len=items.length; i < len; i++){ process(items[i]); } self.postMessage(items);};

Page 43: Nicholas' Performance Talk at Google

When Clicked

UI Thread

UI Queue

time

onclick

UI Update

UI Update

Page 44: Nicholas' Performance Talk at Google

When Clicked

UI Thread

UI Queue

time

onclick

UI Update

UI Update

Page 45: Nicholas' Performance Talk at Google

When Clicked

UI Thread

UI Queue

time

onclick

UI Update

UI Update

Page 46: Nicholas' Performance Talk at Google

When Clicked

UI Thread

UI Queue

time

onclick

UI Update

UI Update

Worker Thread

Page 47: Nicholas' Performance Talk at Google

When Clicked

UI Thread

UI Queue

time

UI UpdateUI Update onclick

Worker Thread

JavaScript

Page 48: Nicholas' Performance Talk at Google

Worker Thread Complete

UI Thread

UI Queue

time

UI UpdateUI Update onclick

onmessage

Page 49: Nicholas' Performance Talk at Google

Worker Thread Complete

UI Thread

UI Queue

time

UI UpdateUI Update onclick onmessage

Page 50: Nicholas' Performance Talk at Google

4.04.03.53.5 4.04.0 10.610.6??

Support for Web Workers

Page 51: Nicholas' Performance Talk at Google

Part 2: Yahoo! Homepage Performance

Page 52: Nicholas' Performance Talk at Google

The Challenge:Create a new Yahoo! homepage*

*add a ton of new functionality

**without sacrificing performance

Page 53: Nicholas' Performance Talk at Google

By the Numbers

345million

unique users per month worldwide

110million

unique users per month in United States

(no pressure)

Page 54: Nicholas' Performance Talk at Google

Performance is hardThe best features for users aren't always the fastest

flickr.com/photos/thetruthabout/2831498922/

Page 55: Nicholas' Performance Talk at Google

Content Optimization Enginedetermines which stories todisplay at request time

Page 56: Nicholas' Performance Talk at Google

Sites can be completelycustomized by the user

Page 57: Nicholas' Performance Talk at Google

Popular search topics aredetermined at request timeto display up-to-date info

Page 58: Nicholas' Performance Talk at Google

Random information aboutother parts of the Yahoo!network

Page 59: Nicholas' Performance Talk at Google

Apps provide more infoon-demand

Page 60: Nicholas' Performance Talk at Google

The Cost of Customization• Spriting is difficult

– Hard to know which images will be on the page together

• Limited image caching– With content constantly changing, getting

images into cache doesn't help much• A lot more JavaScript/CSS

– And very different, depending on how the user has customized the page

Page 61: Nicholas' Performance Talk at Google

flickr.com/photos/hape_gera/2123257808/

Areas of Focus• Time to interactivity• Ajax Responsiveness

Page 62: Nicholas' Performance Talk at Google

The time to interactivity is the time between the initial page

request and when the user can complete an action

Page 63: Nicholas' Performance Talk at Google

Time to Interactivity• For most pages, happens between

DOMContentLoaded and onload– Can actually happen earlier

• Links work, forms can be submitted even while the page is loading– As long as JavaScript isn't running

• Difficult to measure

Page 64: Nicholas' Performance Talk at Google

Net tab reveals some informationWhere DOMContentLoaded and onload occur

Page 65: Nicholas' Performance Talk at Google

YSlow reports onload timeUseful, but doesn't really determine time to interactivity

Page 66: Nicholas' Performance Talk at Google

Goal:Ensure interactivity by DOMContentLoaded

Page 67: Nicholas' Performance Talk at Google

Simple User Actions• Clicking a headline to read the story• Performing a search• Clicking on a favorite

Wait a second!You don't need JavaScript

for any of that!

flickr.com/photos/marcoarment/2035853550/

Page 68: Nicholas' Performance Talk at Google

Progressive Enhancement FTW!The more tasks that don't require JavaScript,

the faster the user can complete an action

alistapart.com/articles/understandingprogressiveenhancement

Page 69: Nicholas' Performance Talk at Google

The page is very functionaleven without JavaScript

Page 70: Nicholas' Performance Talk at Google

Not relying on JavaScript for everything allows us an

opportunity to deliver what appears to be a faster experience

Page 71: Nicholas' Performance Talk at Google

JavaScriptLoading onto the page without pain

Page 72: Nicholas' Performance Talk at Google

Traditional thinking was put scripts at the bottom

Page 73: Nicholas' Performance Talk at Google

<html><head> <!-- head contents --></head><body> <!-- body contents --> <script type="text/javascript" src="yourfile.js"> </script> <script type="text/javascript" src="yourfile2.js"> </script></body></html>

Page 74: Nicholas' Performance Talk at Google

Our results were upsetting

Putting scripts at the bottom actually caused other problems

flickr.com/photos/kartik_m/2724121901/

Page 75: Nicholas' Performance Talk at Google

flickr.com/photos/kartik_m/2724121901/

Results• Page would fully render, but be frozen

– User can't interact while JavaScript is being fetched/parsed/executed

• Delayed onload to after 5s on fast connection• Time to interactivity tied to onload• Experience was especially bad over slower

connections– Page was unresponsive for 30s or more

Page 76: Nicholas' Performance Talk at Google

In order to fix things, we had to get lazy

flickr.com/photos/19613690@N05/3687563687/

Page 77: Nicholas' Performance Talk at Google

stevesouders.com/blog/2009/04/27/loading-scripts-without-blocking/

Page 78: Nicholas' Performance Talk at Google

nczonline.net/blog/2009/07/28/the-best-way-to-load-external-javascript/

Page 79: Nicholas' Performance Talk at Google

function loadScript(url, callback){

var script = document.createElement("script") script.type = "text/javascript";

if (script.readyState){ //IE script.onreadystatechange = function(){ if (script.readyState == "loaded" || script.readyState == "complete"){ script.onreadystatechange = null; callback(); } }; } else { //Others script.onload = function(){ callback(); }; }

script.src = url; document.getElementsByTagName("head")[0].appendChild(script);}

Dynamically loaded scriptsdon't block page load

Page 80: Nicholas' Performance Talk at Google

<html><head> <!-- head contents --></head><body> <!-- body contents --> <script type="text/javascript" src="smallfile.js"> </script> <script type="text/javascript"> loadScript(filename, function(){ //initialization }); </script></body></html>

Page 81: Nicholas' Performance Talk at Google

Y.Get.script(YUI.presentation.lazyScriptList, { onSuccess: function()

{ Y.use("*"); Y.ModulePlatform.init(Y.dali.config, true); }});

Page 82: Nicholas' Performance Talk at Google

Everything else

First script file

Page 83: Nicholas' Performance Talk at Google

flic

kr.c

om/p

hoto

s/n

atea

ndm

iran

da/2

62

539

96

53/

Results• Page is interactive as

soon as each section is rendered

• Reduced onload time to ~2.5s on fast connections

• Slow connection experience vastly improved

Page 84: Nicholas' Performance Talk at Google

JavaScript Loads• Small amount on page load• Larger amount loaded in non-blocking manner

– Everything necessary for core JavaScript interactivity

• Ajax responses can specify more JavaScript is needed to handle the response– True, on-demand loading

Page 85: Nicholas' Performance Talk at Google

flickr.com/photos/hape_gera/2123257808/

Areas of Focus• Time to interactivity• Ajax Responsiveness

Page 86: Nicholas' Performance Talk at Google

The biggest area of concern regarding Ajax performance was around the apps. For our very first test, it sometimes took as long as 7 seconds to load a single app.

Page 87: Nicholas' Performance Talk at Google

What exactly is taking 7 seconds?The measurement itself was a huge black box – before doing anything,

we had to figure out exactly what was happening in that time

start stop

Page 88: Nicholas' Performance Talk at Google

Roundtrip TimeThe first step is the amount of time between when the browser sends

the request and the time it receives the response

start stoproundtrip

Page 89: Nicholas' Performance Talk at Google

Parse TimeNext, the JSON response returned from the server has to be parsed

start stoproundtrip parse

Page 90: Nicholas' Performance Talk at Google

JavaScript/CSS Download TimeEach response can indicate it needs more JavaScript/CSS before the

content can be used

start stoproundtrip parse download

Page 91: Nicholas' Performance Talk at Google

Render TimeThe amount of time it takes to actually change the display via innerHTML

start stoproundtrip parse download render

Page 92: Nicholas' Performance Talk at Google

Where We Started

Page 93: Nicholas' Performance Talk at Google

Fixing Roundtrip TimeWhat's taking so damn long?

Page 94: Nicholas' Performance Talk at Google

The right-side ads were a roundtrip issueThe server-side ad call took extra time plus the ad markup represented

50-60% of the returned markup

Page 95: Nicholas' Performance Talk at Google

“Fixing” the adEntire right column now renders in an iframe. This defers the ad call

until after the app has been loaded in the browser, saving bothserver time for app rendering and bytes in the response.

Page 96: Nicholas' Performance Talk at Google

Fixing Parse TimeWhat's taking so freakin' long??

Page 97: Nicholas' Performance Talk at Google
Page 98: Nicholas' Performance Talk at Google

{ "msg": "Hello world!", "day": 6, "found": true,}

JSON is super-efficient for transporting numbers, Booleans, simple strings, objects, and arrays

Page 99: Nicholas' Performance Talk at Google

{ "html":"<div id=\"default-p_26583360\" class=\"mod view_default\"> <div

id=\"default-p_26583360-bd\" class=\"bd type_pacontainer type_pacontainer_default\"><div class=\" pa-app-col1 y-pa-ln-open-dk \"><div class=\"hd pa-app-hd\"><h2 class=\"x-large\"><a href=\"_ylt=ArPtckll5ytFOZy32_Tg07qbvZx4\/SIG=10u61l0b2\/**http%3A\/\/finance.yahoo.com\/\">Finance<\/a><\/h2> \t<div class=\"pa-menu-options\">\n \t\t<a role=\"button\" class=\"pa-menu-optionsbtn small pa-app-header\" href=\"#\" data-b=\"_ylt=AhzOmRGiUKjiPuGRaW8LrGabvZx4\">Options<span class=\"y-fp-pg-controls arrow\"><\/span><\/a>\n\t\t\t\t<ul id=\"p_26583360-settings-menu\" class=\"y-menu medium\">\n\t\t\t\t\t\n\t\t\t\t\t<li><a href=\"_ylt=AtqN.M70D5mHiPrcLvHF9vibvZx4\/SIG=1254msah3\/**http%3A\/\/help.yahoo.com\/l\/us\/yahoo\/homepage\/homepage\/myapps\/stocks\" class=\"y-link-1 help-option\"><span class=\"y-fp-pg-controls\"><\/span>Help<\/a><\/li>\n\t\t\t\t\t\n\t\t\t <\/ul>\n\t\t <\/div><\/div><div id=\"default-u_93109\" class=\"mod view_default\"> <div id=\"default-u_93109-bd\" class=\"bd type_finance type_finance_default\"> <div class=\"finance-nav clearfix\">\n <a href=\"_ylt=AvKZuIwh_mvmWInFE6c7Zc.bvZx4\/SIG=10u61l0b2\/**http%3A\/\/finance.yahoo.com\/\" class=\"small text-color goto-link\"><span class=\"goto\">Go to:<\/span> <span class=\"property\"><img src=\"http:\/\/d.yimg.com\/a\/i\/ww\/met\/mod\/ybang_22_061509.png\" alt=\"Finance\"> Finance<\/span><\/a>\n <ul class=\"y-tablist med-small\" id=\"u_93109-tabs\"> <li class=\"selected\"><a href=\"#\" data-b=\"_ylt=AhW8HKKgyZxBNcux07hCVxGbvZx4\">Overview<\/a><\/li> <li class=\"\"><a href=\"#\" data-b=\"_ylt=AuEzZyDTq.4N_vTGBXpu2VubvZx4\">My Portfolios<\/a><\/li> <\/ul>\n <\/div>\n <div class=\"y-tabpanels y-tp-default\">\n <div class=\"tabpanel selected\">\n <div class=\"menu menu-empty y-glbl-mod-grad\"><\/div>\n <div class=\"market-overview\">\n <div class=\"holder\">\n <p class=\"x-small date text-color\">Sat, Jun 12, 2010 10:10am PDT<\/p>\n <table class=\"index-update\">\n <tbody>\n <tr class=\"med-large\">\n <td class=\"hide-contents hide-textindent\">&nbsp;<\/td>\n"

}

Very inefficient for large HTML stringsEscapement adds a lot of extra bytes

Page 100: Nicholas' Performance Talk at Google

The larger the JSON string, the longer it took to parse

Keep in mind there was no native browser JSON parsing when we began testing the new page

The regular expression validation in the YUI JSON utility (based on json2.js) could take up to 40% of the parse time

Page 101: Nicholas' Performance Talk at Google

Shrinking the size of the HTML by deferring the ad helped

But we still wanted to see if we could eek out better gains

Page 102: Nicholas' Performance Talk at Google

[{ "msg": "Hello world!", "day": 6, "found": true,}]=====<div class="foo"><a href="http://www.yahoo.com">Yahoo!

</a></div>

We experimented with an alternate response format where meta data was in JSON form but HTML was included afterward in plain

text

Page 103: Nicholas' Performance Talk at Google

Results were good

But then native JSON parsing hit and a lot of problems went away

flickr.com/photos/mattymatt/3017263513/

Page 104: Nicholas' Performance Talk at Google

Fixing Download TimeWhat's taking so (*&#$Q@! long???

Page 105: Nicholas' Performance Talk at Google

On-demand JavaScript/CSS downloading hurt app loading

timeIntended to decrease page load time, and did – but left us with this

side effect

Page 106: Nicholas' Performance Talk at Google

Waiting until the user takes an action ensures paying the cost of download

What if you knew which apps the user was going to use?

Solution: predictive fetch of JavaScript/CSS before you need it

flickr.com/photos/mcgraths/3248483447/

Page 107: Nicholas' Performance Talk at Google

After page load, we start todownload JavaScript/CSS forthe apps on the page

Page 108: Nicholas' Performance Talk at Google

After onload

onload

Page 109: Nicholas' Performance Talk at Google

Fixing Render TimeWTF is taking so (*&#$Q@! long?!?!?

Page 110: Nicholas' Performance Talk at Google

Actually, render time was okay

Page 111: Nicholas' Performance Talk at Google

Results

Page 112: Nicholas' Performance Talk at Google

In the end, user testing showed that perceived performance of the new page

was the same as on the old page

Page 113: Nicholas' Performance Talk at Google

Recap

Page 114: Nicholas' Performance Talk at Google

Responsive UI

UI Thread

time

JavaScript UI UpdateUI Update

Page 115: Nicholas' Performance Talk at Google

Unresponsive UI

UI Thread

time

JavaScript UI UpdateUI Update

Page 116: Nicholas' Performance Talk at Google

Avoid Slow Loading JavaScript• Put scripts at the bottom• Concatenate scripts into as few files as

possible• Choose the right way to load your scripts

– Dynamically created scripts– Deferred scripts– Asynchronous scripts

Page 117: Nicholas' Performance Talk at Google

Avoid Slow JavaScript• Don't allow JavaScript to execute for more

than 50ms• Break up long JavaScript processes using:

– Timers– Web Workers

Page 118: Nicholas' Performance Talk at Google

Lessons Learned• Time to interactivity is a big deal• Progressive enhancement creates a better

experience– Allows for delayed load of JavaScript

• Load as much JavaScript as possible in a non-blocking manner

• Ajax performance is a macro measurement– Get more insight by looking at the parts

Page 119: Nicholas' Performance Talk at Google

flickr.com/photos/ficken/1813744832/

Achievements• Reduced time to onload from ~5s to ~2.5s

– Actually better than previous version• Very short time to interactivity• Reduced time to open apps from ~7s to ~2s• Maintained perception of speed from previous

version

Page 120: Nicholas' Performance Talk at Google

Questions?

Page 121: Nicholas' Performance Talk at Google

Etcetera• My blog:

www.nczonline.net• My email:

[email protected]• Twitter:

@slicknet• These Slides:

http://slideshare.net/nzakas/

Page 122: Nicholas' Performance Talk at Google

Creative Commons Images Used• http://www.flickr.com/photos/hippie/2406411610/

• http://www.flickr.com/photos/55733754@N00/3325000738/

• http://www.flickr.com/photos/eurleif/255241547/

• http://www.flickr.com/photos/off_the_wall/3444915939/

• http://www.flickr.com/photos/wwarby/3296379139/

• http://www.flickr.com/photos/derekgavey/4358797365/

• http://www.flickr.com/photos/mulad/286641998/