High Performance Kick Ass Web Apps (JavaScript edition)

43,604 views

Published on

Slides from JSConf 2009 presentation "High Performance Kick Ass Web Apps" with focus on JavaScript.

Published in: Technology
6 Comments
98 Likes
Statistics
Notes
No Downloads
Views
Total views
43,604
On SlideShare
0
From Embeds
0
Number of Embeds
2,418
Actions
Shares
0
Downloads
1,245
Comments
6
Likes
98
Embeds 0
No embeds

No notes for slide

High Performance Kick Ass Web Apps (JavaScript edition)

  1. High-Performance Kick-Ass Web Apps (with focus on JavaScript) Stoyan Stefanov, @stoyanstefanov April 25, 2009 JSConf, Washington, D.C.
  2. About me •  Yahoo! Search •  Yahoo! Exceptional Performance •  YSlow 2.0 architect •  http://smush.it •  Books, articles •  http://phpied.com
  3. Importance of performance •  500 ms slower = 20% drop in traffic (Google)
  4. Importance of performance •  500 ms slower = 20% drop in traffic (Google) •  400 ms slower = 5-9% drop in full-page traffic (Yahoo!)
  5. Importance of performance •  500 ms slower = 20% drop in traffic (Google) •  400 ms slower = 5-9% drop in full-page traffic (Yahoo!) •  100 ms slower = 1% drop in sales (Amazon)
  6. Importance of performance •  Self-regulating system •  Slow down = lose users •  It’s about user experience
  7. “The premature optimization… •  … is the root of all evil” Knuth •  “Make it right before you make it fast” Crockford
  8. Pick your battles •  measure •  profile •  monitor
  9. On trade-offs “…everything has its drawbacks, as the man said when his mother-in-law died, and they came upon him for the funeral expenses.” Jerome K. Jerome Three Man in a Boat
  10. The Life of Page 2.0 HTML page request onload settles request sent marriage? R.I.P. conception birth graduation User perceived “onload” happens somewhere here
  11. The waterfall
  12. The Waterfall 1.  Less stuff 2.  Smaller stuff 3.  Out of the way 4.  Start early
  13. The Waterfall 1.  Less stuff 2.  Smaller stuff 3.  Out of the way 4.  Start early
  14. Less HTTP requests •  Combine components
  15. Less HTTP requests •  Before: <script src=quot;jquery.jsquot;></script>  <script src=quot;jquery.twitter.jsquot;></script>  <script src=quot;jquery.cookie.jsquot;></script>  <script src=quot;myapp.jsquot;></script> 
  16. Less HTTP requests •  After: <script    src=quot;all.jsquot;    type=quot;text/javascriptquot;>  </script> 
  17. Less HTTP requests •  You just saved 3 HTTP requests 
  18. Less HTTP requests •  repeat for CSS: <link    href=quot;all.cssquot;    rel=quot;stylesheetquot;    type=quot;text/css”  />
  19. Less HTTP requests •  Inline images: CSS sprites with data: URI scheme
  20. Less HTTP requests •  data: URI scheme $ php ‐r quot;echo base64_encode(file_get_contents('my.png'));”  iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAIAAAA7ljmRAAAAGElEQVQIW2P4 DwcMDAxAfBvMAhEQMYgcACEHG8ELxtbPAAAAAElFTkSuQmCC 
  21. Less HTTP requests •  data: URI scheme background‐image: url(quot;data:image/png;base64,iVBORw0KG...quot;); 
  22. Less HTTP requests •  data: URI scheme <img src=quot;data:image/png;base64,iVBOR...quot; /> 
  23. Less HTTP requests •  data: URI scheme •  works in IE!...
  24. Less HTTP requests •  data: URI scheme •  works in IE8!
  25. Less HTTP requests •  data: URI scheme •  MHTML for IE < 8 http://www.phpied.com/mhtml-when-you-need-data-uris-in-ie7-and-under/ http://www.hedgerwow.com/360/dhtml/base64-image/demo.php
  26. Less stuff? Cache •  Cache is less universal than we think •  You can help http://yuiblog.com/blog/2007/01/04/performance-research-part-2/
  27. “never expire” policy •  Static components with far- future Expires header •  JS, CSS, img ExpiresActive On  ExpiresByType image/png quot;access plus 10 yearsquot; 
  28. Inline vs. external •  a.k.a. less http vs. more cache •  how about both?
  29. Inline vs. external •  First visit: 1. Inline 2. Lazy-load the external file 3. Write a cookie
  30. Inline vs. external •  Later visits: 1. Read cookie 2. Refer to the external file
  31. The Waterfall 1.  Less stuff ✔ 2.  Smaller stuff 3.  Out of the way 4.  Start early
  32. The Waterfall 1.  Less stuff 2.  Smaller stuff 3.  Out of the way 4.  Start early
  33. Gzip Source: Bill Scott, Netflix
  34. Minify •  Before /**   * The dom module provides helper methods for    *    manipulating Dom elements.   * @module dom   *   */  (function() {      var Y = YAHOO.util,     // internal shorthand          getStyle,           // for load time browser branching          setStyle,           // ditto          propertyCache = {}, // for faster hyphen converts          reClassNameCache = {},          // cache regexes for className          document = window.document;     // cache for faster lookups      YAHOO.env._id_counter = YAHOO.env._id_counter || 0; 
  35. Minify •  After (function(){var  B=YAHOO.util,K,I,J={},F={},M=window.document;YAHOO.env._id_counter=YAHOO.en v._id_counter||0; 
  36. Minify •  YUI Compressor •  Minifies JS and CSS •  Tolerates * and _ hacks •  More than minification
  37. Minify •  Minify inline code too
  38. Gzip or minification? •  62,885 bytes - original jQuery (back in Aug 2007) •  31,822 - minified with the YUI Compressor •  19,758 - original gzipped •  10,818 - minified and gzipped FTW http://www.julienlecomte.net/blog/2007/08/13/
  39. 204 •  The world’s smallest component? •  204 No Content <?php  header(quot;HTTP/1.0 204 No Contentquot;);  // .... do your job, e.g. logging  ?>  http://www.phpied.com/204-no-content/
  40. The Waterfall 1.  Less stuff ✔ 2.  Smaller stuff ✔ 3.  Out of the way 4.  Start early
  41. The Waterfall 1.  Less stuff 2.  Smaller stuff 3.  Out of the way 4.  Start early
  42. Free-falling waterfalls •  Less DNS lookups – fetch components from not more than 2-4 domains •  Less redirects •  Blocking JavaScript
  43. Not free-falling
  44. JavaScript rocks! •  But also blocks html js png png
  45. Non-blocking JavaScript •  Include via DOM html js png png var js = document.createElement('script');  js.src = 'myscript.js';  var h = document.getElementsByTagName('head')[0];  h.appendChild(js); 
  46. Non-blocking JavaScript •  And what about my inline scripts? •  Setup a collection (registry) of inline scripts
  47. Step 1 •  Inline in the <head>: var myapp = {    stuff: []  }; 
  48. Step 2 •  Add to the registry Instead of:   <script>alert('boo!');</script>  Do:   <script>      myapp.stuff.push(function(){     alert('boo!');      });    </script> 
  49. Step 3 •  Execute all var l = myapp.stuff.length;   for(var i = 0, i < l; i++) {    myapp.stuff[i]();  } 
  50. Blocking CSS? But they do block: •  In FF2 •  When followed by a script
  51. The Waterfall 1.  Less stuff ✔ 2.  Smaller stuff ✔ 3.  Out of the way ✔ 4.  Start early
  52. The Waterfall 1.  Less stuff 2.  Smaller stuff 3.  Out of the way 4.  Start early
  53. flush() early html png js  css html js png ✔ css
  54. flush() <html>  <head>    <script src=quot;my.jsquot;    type=quot;text/javascriptquot;></script>    <link href=quot;my.cssquot;    type=quot;text/cssquot; rel=quot;stylesheetquot; />  </head>  <?php flush() ?>  <body>    .... 
  55. The Waterfall 1.  Less stuff ✔ 2.  Smaller stuff ✔ 3.  Out of the way ✔ 4.  Start early ✔
  56. Life after onload
  57. Life after onload 1.  Lazy-load 2.  Preload 3.  XHR 4.  JavaScript optimizations
  58. Lazy-load •  bells & whistles •  badges & widgets
  59. Preload •  to help next page’s waterfall •  img, CSS, JS, DNS lookups
  60. XHR (Ajax) •  small – gzip, JSON •  less – Expires  •  GET over POST
  61. GET vs. POST for XHR var url = 'test.php';  var request =  new XMLHttpRequest();  request.open(quot;POSTquot;, url, false);  // …  request.send('test=1'); 
  62. GET vs. POST for XHR
  63. JavaScript optimizations •  local vars •  DOM •  garbage collection •  init-time branching •  memoization •  threads
  64. Local variables •  globals are all sorts of bad •  use var  •  localize globals
  65. Local variables var a = 1;   (function(){    var a = 2;     function b(){      var a = 3;       alert(a);    }    b();   })(); // 3 
  66. Local variables var a = 1;   (function(){    var a = 2;     function b(){      // var a = 3;       alert(a);    }    b();   })(); // 2 
  67. Local variables var a = 1;   (function(){    // var a = 2;     function b(){      // var a = 3;       alert(a);    }    b();   })(); // 1 
  68. Local variables •  less crawling up the scope chain •  localize •  function pointers too •  help YUI compressor (it won’t rename globals) Wait! Isn’t that a micro-optimization?
  69. Localize DOM access function foo(){    for (var i = 0; i < 100000; i++) {      document.getElementsByTagName('head');    }  }  foo(); 
  70. Localize DOM access function foo(){    var get = document.getElementsByTagName;    for (var i = 0; i < 100000; i++) {      get('head');    }  }  4 foo();   times faster
  71. Touching the DOM function foo() {    for (var count = 0; count < 1000; count++) {      document.body.innerHTML += 1;    }  } 
  72. Touching the DOM function foo() {    var inner = '';    for (var count = 0; count < 1000; count++) {      inner += 1;    }    document.body.innerHTML += inner;  1000 }  times faster
  73. Cleaning up after yourself •  Properties you no longer need var myApp = {    prop: huge  };  // ...  delete myApp.prop; 
  74. Cleaning up after yourself •  DOM elements you no longer need var el = $('mydiv');  el.parentNode.removeChild(el); 
  75. Cleaning up after yourself •  DOM elements you no longer need var el = $('mydiv');  delete el.parentNode.removeChild(el); 
  76. Init-time branching •  Instead of… function myEvent(el, type, fn) {    if (window.addEventListener) {      el.addEventListener(type, fn, false);    } else if (window.attachEvent) {      el.attachEvent(quot;onquot; + type, fn);    } else {…  } 
  77. Init-time branching •  Do… if (window.addEventListener) {    var myEvent = function (el, type, fn) {      el.addEventListener(type, fn, false);    }  } else if (window.attachEvent) {    var myEvent = function (el, type, fn) {      el.attachEvent(quot;onquot; + type, fn);    }  } 
  78. Lazy definition function myEvent(el, type, fn) {    if (window.addEventListener) {      myEvent = function(el, type, fn) {        el.addEventListener(type, fn, false);      };    } else if (window.attachEvent) {      //...    }    return myEvent(el, type, fn);  } 
  79. Memoization •  for expensive, repeating tasks function myFunc(param){      if (!myFunc.cache) {          myFunc.cache = {};      }      if (!myFunc.cache[param]) {          var result = {}; // …          myFunc.cache[param] = result;      }      return myFunc.cache[param];  } 
  80. Threads •  Web Workers for modern browsers var myWorker = new Worker('my_worker.js');    myWorker.onmessage = function(event) {      alert(quot;Called back by the worker!quot;);    };   https://developer.mozilla.org/en/Using_DOM_workers
  81. Threads •  … or setTimeout() for the rest  1.  Do a chunk of work  2.  setTimeout(chunk, 1) and return/yield 
  82. Life after onload 1.  Lazy-load ✔ 2.  Preload ✔ 3.  XHR ✔ 4.  JavaScript optimizations ✔
  83. YUI3 http://developer.yahoo.com/yui/3  
  84. YUI3 •  Lighter less KB, modules, sub-modules •  Faster opportunity to refactor •  A la carte modules
  85. YUI3 a la carte •  Combo handler   http://yui.yahooapis.com/combo?oop‐min.js&event‐min.js •  Self-populating YUI().use(“anim”, function(Y) {    var a = new Y.Anim({...});    a.run();  }); 
  86. Thank you! Stoyan Stefanov @stoyanstefanov http://www.phpied.com
  87. Credits/Further reading •  http://looksgoodworkswell.blogspot.com/2008/06/velocity-conference-improving-netflix.html •  http://developer.yahoo.com/yui/compressor/ •  http://www.julienlecomte.net/blog/2007/12/39/ •  http://webo.in/articles/habrahabr/46-cross-browser-data-url/ •  http://yuiblog.com/blog/2008/07/22/non-blocking-scripts •  http://hitchhikers.wikia.com/wiki/Mostly_Harmless •  http://developer.yahoo.com/performance/ •  http://oreilly.com/catalog/9780596522308/ •  http://oreilly.com/catalog/9780596529307/ •  http://www.nczonline.net/blog/tag/performance/

×