Nicholas' Performance Talk at Google

6,619 views

Published on

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

Published in: Technology
0 Comments
20 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
6,619
On SlideShare
0
From Embeds
0
Number of Embeds
25
Actions
Shares
0
Downloads
115
Comments
0
Likes
20
Embeds 0
No embeds

No notes for slide

Nicholas' Performance Talk at Google

  1. 1. Nicholas' Performance Talk at Google Nicholas C. Zakas Principal Front End Engineer, Yahoo! Google, September 29, 2010
  2. 2. Who's this guy? Principal Front End Contributor, Engineer Creator of YUI Test Author Lead Author Contributor Lead Author
  3. 3. Part 1: JavaScript Performance
  4. 4. JavaScript performance directly affects user experience
  5. 5. The UI Thread The brains of the operation
  6. 6. The browser UI thread is responsible for both UI updates and JavaScript execution Only one can happen at a time
  7. 7. Jobs for UI updates and JavaScript execution are added to a UI queue if the UI thread is busy Each job must wait in line for its turn to execute
  8. 8. <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>
  9. 9. Before Click UI Thread time UI Queue
  10. 10. When Clicked UI Thread time UI Queue UI Update onclick UI Update
  11. 11. When Clicked UI Thread UI Update time UI Queue onclick Draw down state UI Update
  12. 12. When Clicked UI Thread UI Update onclick time UI Queue UI Update
  13. 13. When Clicked UI Thread UI Update onclick UI Update time UI Queue Draw up state
  14. 14. No UI updates while JavaScript is executing
  15. 15. 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>
  16. 16. A UI update must use the latest info available
  17. 17. Long-running JavaScript = Unresponsive UI
  18. 18. Responsive UI UI Thread UI Update JavaScript UI Update time
  19. 19. Unresponsive UI UI Thread UI Update JavaScript UI Update time
  20. 20. The longer JavaScript runs, the worse the user experience
  21. 21. 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
  22. 22. Translation: No single JavaScript job should execute for more than 100ms to ensure a responsive UI
  23. 23. Recommendation: Limit JavaScript execution to no more than 50ms measured on IE6 :)
  24. 24. Runtime Techniques Ways to ensure JavaScript doesn't run away
  25. 25. function processArray(items, process, callback){ for (var i=0,len=items.length; i < len; i++){ process(items[i]); } callback(); }
  26. 26. Technique #1: Timers
  27. 27. 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
  28. 28. JavaScript Timers • For complex processing, split up into timed functionality • Use timers to delay some processing for later
  29. 29. 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); }
  30. 30. When Clicked UI Thread time UI Queue UI Update onclick UI Update
  31. 31. When Clicked UI Thread UI Update time UI Queue onclick UI Update
  32. 32. When Clicked UI Thread UI Update onclick time UI Queue UI Update
  33. 33. When Clicked UI Thread UI Update onclick UI Update time UI Queue
  34. 34. After 25ms UI Thread UI Update onclick UI Update time UI Queue JavaScript
  35. 35. After 25ms UI Thread UI Update onclick UI Update JavaScript time UI Queue
  36. 36. After Another 25ms UI Thread UI Update onclick UI Update JavaScript time UI Queue JavaScript
  37. 37. After Another 25ms UI Thread UI Update onclick UI Update JavaScript JavaScript time UI Queue
  38. 38. Technique #2: Web Workers
  39. 39. 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
  40. 40. //in page var worker = new Worker("process.js"); worker.onmessage = function(event){ useData(event.data); }; worker.postMessage(values); //in process.js self.onmessage = function(event){ var items = event.data; for (var i=0,len=items.length; i < len; i++){ process(items[i]); } self.postMessage(items); };
  41. 41. When Clicked UI Thread time UI Queue UI Update onclick UI Update
  42. 42. When Clicked UI Thread UI Update time UI Queue onclick UI Update
  43. 43. When Clicked UI Thread UI Update onclick time UI Queue UI Update
  44. 44. When Clicked UI Thread UI Update onclick time Worker Thread UI Queue UI Update
  45. 45. When Clicked UI Thread UI Update onclick UI Update time Worker Thread UI Queue JavaScript
  46. 46. Worker Thread Complete UI Thread UI Update onclick UI Update time UI Queue onmessage
  47. 47. Worker Thread Complete UI Thread UI Update onclick UI Update onmessage time UI Queue
  48. 48. Support for Web Workers 3.5 4.0 4.0 ? 10.6
  49. 49. Part 2: Yahoo! Homepage Performance
  50. 50. The Challenge: Create a new Yahoo! homepage* *add a ton of new functionality **without sacrificing performance
  51. 51. By the Numbers 345 110 million million unique users per month unique users per month worldwide in United States (no pressure)
  52. 52. Performance is hard The best features for users aren't always the fastest flickr.com/photos/thetruthabout/2831498922/
  53. 53. Content Optimization Engine determines which stories to display at request time
  54. 54. Sites can be completely customized by the user
  55. 55. Popular search topics are determined at request time to display up-to-date info
  56. 56. Random information about other parts of the Yahoo! network
  57. 57. Apps provide more info on-demand
  58. 58. 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
  59. 59. Areas of Focus • Time to interactivity • Ajax Responsiveness flickr.com/photos/hape_gera/2123257808/
  60. 60. The time to interactivity is the time between the initial page request and when the user can complete an action
  61. 61. 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
  62. 62. Net tab reveals some information Where DOMContentLoaded and onload occur
  63. 63. YSlow reports onload time Useful, but doesn't really determine time to interactivity
  64. 64. Goal: Ensure interactivity by DOMContentLoaded
  65. 65. 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/
  66. 66. alistapart.com/articles/understandingprogressiveenhancement Progressive Enhancement FTW! The more tasks that don't require JavaScript, the faster the user can complete an action
  67. 67. The page is very functional even without JavaScript
  68. 68. Not relying on JavaScript for everything allows us an opportunity to deliver what appears to be a faster experience
  69. 69. JavaScript Loading onto the page without pain
  70. 70. Traditional thinking was put scripts at the bottom
  71. 71. <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>
  72. 72. flickr.com/photos/kartik_m/2724121901/ Our results were upsetting Putting scripts at the bottom actually caused other problems
  73. 73. 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
  74. 74. flickr.com/photos/19613690@N05/3687563687/ In order to fix things, we had to get lazy
  75. 75. stevesouders.com/blog/2009/04/27/loading-scripts-without-blocking/
  76. 76. nczonline.net/blog/2009/07/28/the-best-way-to-load-external-javascript/
  77. 77. 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(); }; Dynamically loaded scripts } don't block page load script.src = url; document.getElementsByTagName("head")[0].appendChild(script); }
  78. 78. <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>
  79. 79. Y.Get.script(YUI.presentation.lazyScriptList, { onSuccess: function() { Y.use("*"); Y.ModulePlatform.init(Y.dali.config, true); }});
  80. 80. First script file Everything else
  81. 81. flickr.com/photos/nateandmiranda/2625399653/ 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
  82. 82. 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
  83. 83. Areas of Focus • Time to interactivity • Ajax Responsiveness flickr.com/photos/hape_gera/2123257808/
  84. 84. 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.
  85. 85. start stop 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
  86. 86. roundtrip start stop Roundtrip Time The first step is the amount of time between when the browser sends the request and the time it receives the response
  87. 87. roundtrip parse start stop Parse Time Next, the JSON response returned from the server has to be parsed
  88. 88. roundtrip parse download start stop JavaScript/CSS Download Time Each response can indicate it needs more JavaScript/CSS before the content can be used
  89. 89. roundtrip parse download render start stop Render Time The amount of time it takes to actually change the display via innerHTML
  90. 90. Where We Started
  91. 91. Fixing Roundtrip Time What's taking so damn long?
  92. 92. The right-side ads were a roundtrip issue The server-side ad call took extra time plus the ad markup represented 50-60% of the returned markup
  93. 93. “Fixing” the ad Entire right column now renders in an iframe. This defers the ad call until after the app has been loaded in the browser, saving both server time for app rendering and bytes in the response.
  94. 94. Fixing Parse Time What's taking so freakin' long??
  95. 95. { "msg": "Hello world!", "day": 6, "found": true, } JSON is super-efficient for transporting numbers, Booleans, simple strings, objects, and arrays
  96. 96. { "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 tt<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>ntttt<ul id="p_26583360-settings-menu" class="y-menu medium">ntttttnttttt<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>ntttttnttt </ul>ntt </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 Very inefficient for large HTML strings 10:10am PDT</p>n <table class="index-update">n class="med-large">n textindent">&nbsp;</td>n" <tbody>n <td class="hide-contents hide- <tr } Escapement adds a lot of extra bytes
  97. 97. 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
  98. 98. Shrinking the size of the HTML by deferring the ad helped But we still wanted to see if we could eek out better gains
  99. 99. [{ "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
  100. 100. flickr.com/photos/mattymatt/3017263513/ Results were good But then native JSON parsing hit and a lot of problems went away
  101. 101. Fixing Download Time What's taking so (*&#$Q@! long???
  102. 102. On-demand JavaScript/CSS downloading hurt app loading time Intended to decrease page load time, and did – but left us with this side effect
  103. 103. 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/
  104. 104. After page load, we start to download JavaScript/CSS for the apps on the page
  105. 105. After onload onload
  106. 106. Fixing Render Time WTF is taking so (*&#$Q@! long?!?!?
  107. 107. Actually, render time was okay
  108. 108. Results
  109. 109. In the end, user testing showed that perceived performance of the new page was the same as on the old page
  110. 110. Recap
  111. 111. Responsive UI UI Thread UI Update JavaScript UI Update time
  112. 112. Unresponsive UI UI Thread UI Update JavaScript UI Update time
  113. 113. 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
  114. 114. Avoid Slow JavaScript • Don't allow JavaScript to execute for more than 50ms • Break up long JavaScript processes using: – Timers – Web Workers
  115. 115. 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
  116. 116. 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 flickr.com/photos/ficken/1813744832/
  117. 117. Questions?
  118. 118. Etcetera • My blog: www.nczonline.net • My email: nzakas@yahoo-inc.com • Twitter: @slicknet • These Slides: http://slideshare.net/nzakas/
  119. 119. 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/

×