KEMBAR78
Performance patterns | PDF
Performance Patterns




Stoyan Stefanov
JSConfEU
Berlin, 2010
http://slideshare.net/stoyan/
About me
           YSlow 2.0
Lies,
      damn lies,
and performance advise
On the agenda
1.  Understanding the problem
2.  Some pointers/patterns
Moving targets
Moving targets
•  Browsers
•  Libraries


•  e.g. IE string concatenation
+= or array.join()
var res = '',	        var res = [],	
    count = 10000;	       count = 10000;	
while (count--) {	    while (count--) {	
 res += 'a';	          res.push('a');	
}	                    }	

                      res = res.join('');	



              http://jsperf.com/join-concat/
“The Pen is mightier
       than the Sword” *

* only if the Sword is very small
   and the Pen very sharp
Benchmarks
Commonly…
var start = new Date();	
// ... go crazy ...	
var took = new Date() – start;
But…
•  Releasing the thread?
•  Updating the page?
•  In-memory DOM
  operations?
Better…
var start = new Date();	
// ... go crazy ...	
setTimeout(function () {	
    var took = new Date() – start;   	
}, 0);
Zero timeout
•  15 ms in IE
•  10 in FF, Safari
•  4 in Chrome
When benchmarking…
1.  Long enough operations
    (much longer than 15ms)
2.  200 runs for statistical
    significance
3.  Filtering outliers
4.  0-timeout end time
Results
•  Average
•  Median
•  Throw < 25% and >75%
Quarters


   ¼       ¼          ¼    ¼

 Garbage       Data       Garbage
Benchmarks
•  No need to benchmark
   browsers
•  Time values not important
•  Question is: given A and B,
   which way to go? Really?
   All the time?
JSPerf.com
•  Type in a form
•  Test, compare browsers
•  Community
How about real life?
Picking the battles
Speeding Up
Speeding Up
1.  Loading
2.  Running
Speeding Up
1.  Loading
2.  Running
Loading – the basics
•  Reducing the # scripts
•  Gzip
•  Minification
•  Expires
•  CDN
Loading asynchronously
•  Async === Good
•  Progress
•  Perception
JavaScript blocks



   html
          js
               png

               png
JavaScript at the bottom



   html
          png

          png

                js
Non-blocking JavaScript
•  defer and async	
•  Defer: IE innovation, ok to
   delay, but keep order
•  Async: HTML5, whatever
<script async src="my.js" onload="doIt()"></script> 
<script defer src="my.js" onload="doIt()"></script> 
defer and async timeline

               async
                   	


     defer
         	



         DOMContentLoaded
                        	   load
Non-blocking JavaScript
•    Asynchronous JavaScript
     html
                  js

            png

            png


var js = document.createElement('script'); 
js.src = 'myscript.js'; 
var h = document.getElementsByTagName('head')[0]; 
h.appendChild(js); 
flush() early
 html
                         png

                    js               
                               css



 html



   js
        png
                                     ✔
              css
flush()
<html>	
<head>	
  <script src="my.js" 	
   	type="text/javascript"></script>	
  <link href="my.css" 	
   	type="text/css" rel="stylesheet" />	
</head>	

<?php flush() ?>
<body>	
  ....
Progressive rendering
                         Chunk
                         #1




                         Chunk
                         #2




x.appendChild(script)	   Chunk
                         #3
<!doctype html>	
<html>	
<head><title>My App</title></head>	
<body>	
  <div id="header">	
     <img src="logo.png" />	
     ...	
  </div>                   <!-- end of chunk #1 -->	

  ... The full body of the page ...	
                           <!-- end of chunk #2 -->	



<script src="all_20100925.js"></script>	
</body>	
</html>                    <!-- end of chunk #3 -->
Script injection patterns
document.getElementsByTagName("head")[0]	
    .appendChild(script);	

document.documentElement.firstChild	
   	.appendChild(script);	

document.body.appendChild(script);	

var first_script = document	
    .getElementsByTagName('script')[0];	
first_script.parentNode	
    .insertBefore(script, first_script);
HTTP chunking: not only HTML
HTTP chunking: not only HTML
•  Google Instant
•  /*""*/ - delimited JSON
   pieces, MXHR anyone?
•  Chunk #1 suggestions
•  Chunk #2 results

                    http://tinyurl.com/chunkview
Moar HTML5 attributes
•    ping="http://stats…"	
•    prefetch="http://later…"
Preload sans execute
var preload; 	
if (/*@cc_on!@*/false) { // IE 	
    preload = function (file) {	
        new Image().src = file;	
    };	
} else {	
    preload = function (file) {	
        var obj = document.createElement('object'),	
            body = document.body;	
        obj.width = 0;	
        obj.height = 0;	
        obj.data = file;	
        body.appendChild(obj);	
    };	
}
Speeding Up
1.  Loading
2.  Running
Runtime performance
1.  Going local
2.  Reusing
       aka caching, aka DRY
Runtime performance
1.  Going local
2.  Reusing
Local variables

•  globals are all sorts of bad
•  use var 
•  localize globals
Local variables
var a = 1; 	
(function (){	
  var a = 2; 	
  function b(){	
     var a = 3; 	
     alert(a);	
  }	
  b(); 	
})(); // 3
Local variables
var a = 1; 	
(function (){	
  var a = 2; 	
  function b(){	
     // var a = 3; 	
     alert(a);	
  }	
  b(); 	
})(); // 2
Local variables
var a = 1; 	
(function (){	
  // var a = 2; 	
  function b(){	
     // var a = 3; 	
     alert(a);	
  }	
  b(); 	
})(); // 1
Local variables

•  less crawling up the scope
chain
•  helps minification
Localization
var mamodule = (function () {	

  window...	
  document...	
  otherModule...   	

}());
Localization
var mamodule = (function (g, d, om)
{	

  g... // global	
  d... // document reference	
  om... // another common module	

}(this, document, otherModule));
Runtime performance
1.  Going local
2.  Reusing, aka caching
Init-time branching
•  Before… 
function myEvent(el, type, fn) {	
     if (window.addEventListener) {	
       el.addEventListener(type, fn, false);	
  } else if (window.attachEvent) {	
       el.attachEvent("on" + type, fn);	
  } else {...	
}
Init-time branching
•  After… 
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("on" + type, fn);	
  }	
}
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];	
}
Memoization – other options
•  in a closure
•  offline
Classical       inheritance
•  Before…

var inherit = function (C, P) {	
  var F = function () {};	
  F.prototype = P.prototype;	
  C.prototype = new F();	
  C.uber = P.prototype;	
  C.prototype.constructor = C;	
};
Classical        inheritance
•  After…

var inherit = (function () {	
  var F = function () {};	
  return function (C, P) {	
     F.prototype = P.prototype;	
     C.prototype = new F();	
     C.uber = P.prototype;	
     C.prototype.constructor = C;	
  }	
}());	
                 http://jsperf.com/holy-grail-classical-reuse
Regex loops
•  Before…

var i = 1000,	
    dude;	

while (i--) {	
    dude = /[a-z]/.test('duderino');	
};
Regex loops
•  After…

var i = 1000,	
    dude, 	
    re = /[a-z]/;	

while (i--) {	
    dude = re.test('duderino');	
}	



                          http://jsperf.com/regexp-loops
Cashing lengths in loops
•  before…

var a = [];	

for (var i = 0; i < a.length; i++) {	
    console.log(a[i]);	
}
Cashing lengths in loops
•  later…

var a = [], 	
    i = 0;	

for (; i < a.length; i += 1) {	
    console.log(a[i]);	
}
Cashing lengths in loops
•  after…

var a = [], 	
    i = 0,	
    len = a.length;	

for (; i < len; i += 1) {	
    console.log(a[i]);	
}
Cashing lengths in loops
•  alternatively…

var a = [], 	
   i = a.length;	

while (i--) {	
    console.log(a[i]);	
}
DOM
DOM – case A
// bad 	
var count = 0;	
for (; count < 15000; count += 1) {	

     document.getElementById('here').innerHTML += 'a'; 	

}	

// DOM access = (1 get + 1 set) * 15000
DOM – case B
// better	
var count = 0, content = ''; 	
for (; count < 15000; count += 1) {	

    content += 'a'; 	

}	
document.getElementById('here').innerHTML += content;	

// DOM access: get + set
IE 6


        IE 7


        IE 8


  Firefox 3


 Firefox 3.5


 Safari 3.2


   Safari 4


 Chrome 2


 Chrome 3


Opera 9.64
               How bad is A compared to B?




 Opera 10
ECMAland   DOMland
DOM is slow
Proxy Design Pattern
Proxy pattern
•  One object acts as an
  interface to another
Proxy pattern
Proxy pattern
Proxy pattern
Proxy pattern
Words of Wisdom
•  Ask why? How?
•  Load quickly and async
•  Don’t touch the DOM
•  Go local
Thank you!


Stoyan Stefanov
@stoyanstefanov
http://www.phpied.com

Performance patterns