Nicholas' Performance Talk at Google
-
Upload
nicholas-zakas -
Category
Technology
-
view
5.613 -
download
0
description
Transcript of Nicholas' Performance Talk at Google
Nicholas C. Zakas Principal Front End Engineer, Yahoo!
Google, September 29, 2010
Nicholas' Performance Talkat Google
Who's this guy?
Principal Front End Engineer
Contributor,Creator of YUI Test
Author Lead Author Contributor Lead Author
Part 1: JavaScript Performance
JavaScript performancedirectly
affects user experience
The UI ThreadThe brains of the operation
The browser UI thread is responsible forboth UI updates and JavaScript execution
Only one can happen at a time
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
<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>
Before Click
UI Thread
UI Queue
time
When Clicked
UI Thread
UI Queue
time
onclick
UI Update
UI Update
When Clicked
UI Thread
UI Queue
time
onclick
UI Update
UI Update
Draw down state
When Clicked
UI Thread
UI Queue
time
onclick
UI Update
UI Update
When Clicked
UI Thread
UI Queue
time
onclick UI UpdateUI Update
Draw up state
No UI updates while JavaScript is executing
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>
A UI update must use the latest info available
Long-running JavaScript=
Unresponsive UI
Responsive UI
UI Thread
time
JavaScript UI UpdateUI Update
Unresponsive UI
UI Thread
time
JavaScript UI UpdateUI Update
The longer JavaScript runs,the worse the user experience
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
Translation:No single JavaScript job should execute for more than 100ms to
ensure a responsive UI
Recommendation:Limit JavaScript execution to no more
than 50ms
measured on IE6 :)
Runtime TechniquesWays to ensure JavaScript doesn't run away
function processArray(items, process, callback){ for (var i=0,len=items.length; i < len; i++){ process(items[i]); } callback();}
Technique #1: Timers
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
JavaScript Timers
• For complex processing, split up into timed functionality
• Use timers to delay some processing for later
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);}
When Clicked
UI Thread
UI Queue
time
onclick
UI Update
UI Update
When Clicked
UI Thread
UI Queue
time
onclick
UI Update
UI Update
When Clicked
UI Thread
UI Queue
time
onclick
UI Update
UI Update
When Clicked
UI Thread
UI Queue
time
UI UpdateUI Update onclick
After 25ms
UI Thread
UI Queue
time
UI UpdateUI Update onclick
JavaScript
After 25ms
UI Thread
UI Queue
time
UI UpdateUI Update onclick JavaScript
After Another 25ms
UI Thread
UI Queue
time
UI UpdateUI Update onclick JavaScript
JavaScript
After Another 25ms
UI Thread
UI Queue
time
UI UpdateUI Update onclick JavaScript JavaScript
Technique #2: Web Workers
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
//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);};
When Clicked
UI Thread
UI Queue
time
onclick
UI Update
UI Update
When Clicked
UI Thread
UI Queue
time
onclick
UI Update
UI Update
When Clicked
UI Thread
UI Queue
time
onclick
UI Update
UI Update
When Clicked
UI Thread
UI Queue
time
onclick
UI Update
UI Update
Worker Thread
When Clicked
UI Thread
UI Queue
time
UI UpdateUI Update onclick
Worker Thread
JavaScript
Worker Thread Complete
UI Thread
UI Queue
time
UI UpdateUI Update onclick
onmessage
Worker Thread Complete
UI Thread
UI Queue
time
UI UpdateUI Update onclick onmessage
4.04.03.53.5 4.04.0 10.610.6??
Support for Web Workers
Part 2: Yahoo! Homepage Performance
The Challenge:Create a new Yahoo! homepage*
*add a ton of new functionality
**without sacrificing performance
By the Numbers
345million
unique users per month worldwide
110million
unique users per month in United States
(no pressure)
Performance is hardThe best features for users aren't always the fastest
flickr.com/photos/thetruthabout/2831498922/
Content Optimization Enginedetermines which stories todisplay at request time
Sites can be completelycustomized by the user
Popular search topics aredetermined at request timeto display up-to-date info
Random information aboutother parts of the Yahoo!network
Apps provide more infoon-demand
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
flickr.com/photos/hape_gera/2123257808/
Areas of Focus• Time to interactivity• Ajax Responsiveness
The time to interactivity is the time between the initial page
request and when the user can complete an action
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
Net tab reveals some informationWhere DOMContentLoaded and onload occur
YSlow reports onload timeUseful, but doesn't really determine time to interactivity
Goal:Ensure interactivity by DOMContentLoaded
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/
Progressive Enhancement FTW!The more tasks that don't require JavaScript,
the faster the user can complete an action
alistapart.com/articles/understandingprogressiveenhancement
The page is very functionaleven without JavaScript
Not relying on JavaScript for everything allows us an
opportunity to deliver what appears to be a faster experience
JavaScriptLoading onto the page without pain
Traditional thinking was put scripts at the bottom
<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>
Our results were upsetting
Putting scripts at the bottom actually caused other problems
flickr.com/photos/kartik_m/2724121901/
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
In order to fix things, we had to get lazy
flickr.com/photos/19613690@N05/3687563687/
stevesouders.com/blog/2009/04/27/loading-scripts-without-blocking/
nczonline.net/blog/2009/07/28/the-best-way-to-load-external-javascript/
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
<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>
Y.Get.script(YUI.presentation.lazyScriptList, { onSuccess: function()
{ Y.use("*"); Y.ModulePlatform.init(Y.dali.config, true); }});
Everything else
First script file
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
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
flickr.com/photos/hape_gera/2123257808/
Areas of Focus• Time to interactivity• Ajax Responsiveness
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.
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
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
Parse TimeNext, the JSON response returned from the server has to be parsed
start stoproundtrip parse
JavaScript/CSS Download TimeEach response can indicate it needs more JavaScript/CSS before the
content can be used
start stoproundtrip parse download
Render TimeThe amount of time it takes to actually change the display via innerHTML
start stoproundtrip parse download render
Where We Started
Fixing Roundtrip TimeWhat's taking so damn long?
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
“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.
Fixing Parse TimeWhat's taking so freakin' long??
{ "msg": "Hello world!", "day": 6, "found": true,}
JSON is super-efficient for transporting numbers, Booleans, simple strings, objects, and arrays
{ "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\"> <\/td>\n"
}
Very inefficient for large HTML stringsEscapement adds a lot of extra bytes
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
Shrinking the size of the HTML by deferring the ad helped
But we still wanted to see if we could eek out better gains
[{ "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
Results were good
But then native JSON parsing hit and a lot of problems went away
flickr.com/photos/mattymatt/3017263513/
Fixing Download TimeWhat's taking so (*&#$Q@! long???
On-demand JavaScript/CSS downloading hurt app loading
timeIntended to decrease page load time, and did – but left us with this
side effect
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/
After page load, we start todownload JavaScript/CSS forthe apps on the page
After onload
onload
Fixing Render TimeWTF is taking so (*&#$Q@! long?!?!?
Actually, render time was okay
Results
In the end, user testing showed that perceived performance of the new page
was the same as on the old page
Recap
Responsive UI
UI Thread
time
JavaScript UI UpdateUI Update
Unresponsive UI
UI Thread
time
JavaScript UI UpdateUI Update
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
Avoid Slow JavaScript• Don't allow JavaScript to execute for more
than 50ms• Break up long JavaScript processes using:
– Timers– Web Workers
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
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
Questions?
Etcetera• My blog:
www.nczonline.net• My email:
[email protected]• Twitter:
@slicknet• These Slides:
http://slideshare.net/nzakas/
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/