Performance Patterns Stoyan Stefanov JSConfEU Berlin, 2010 http://slideshare.net/stoyan/
+= or array.join() var res = '', count = 10000;while (count--) { res += 'a';}
var res = [], count = 10000;while (count--) { res.push('a');}
res = res.join('');
http://jsperf.com/join-concat/
Better…
var start = new Date();// ... go crazy ...setTimeout(function () { var took = new Date() – start; }, 0);
When benchmarking…
1. Long enough operations (much longer than 15ms)
2. 200 runs for statistical significance
3. Filtering outliers 4. 0-timeout end time
Benchmarks
• No need to benchmark browsers
• Time values not important • Question is: given A and B,
which way to go? Really? All the time?
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>
Non-blocking JavaScript
• Asynchronous JavaScript
var js = document.createElement('script'); js.src = 'myscript.js'; var h = document.getElementsByTagName('head')[0]; h.appendChild(js);
html
js
png
png
flush() <html><head> <script src="my.js"
type="text/javascript"></script> <link href="my.css"
type="text/css" rel="stylesheet" /></head>
<body> ....
<?php flush() ?>
<!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
• Google Instant • /*""*/ - delimited JSON
pieces, MXHR anyone? • Chunk #1 suggestions • Chunk #2 results
http://tinyurl.com/chunkview
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); };}
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
Localization
var mamodule = (function (g, d, om) {
g... // global d... // document reference om... // another common module
}(this, document, otherModule));
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];}
• 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
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
• 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 – 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 // bettervar count = 0, content = ''; for (; count < 15000; count += 1) {
content += 'a';
}document.getElementById('here').innerHTML += content;
// DOM access: get + set
How bad is A compared to B? IE
6
IE 7
IE 8
Fire
fox
3
Fire
fox
3.5
Safa
ri 3.
2
Safa
ri 4
Ch
rom
e 2
Ch
rom
e 3
Op
era
9.6
4
Op
era
10