82
Performance Patterns Stoyan Stefanov JSConfEU Berlin, 2010 http://slideshare.net/stoyan/

Performance patterns

Embed Size (px)

Citation preview

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 = '', count = 10000;while (count--) { res += 'a';}

var res = [], count = 10000;while (count--) { 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

js

png

png

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

DOMContentLoaded load

async

defer

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() early

html

png

js

css

html

png

js

css

flush() <html><head> <script src="my.js"

type="text/javascript"></script> <link href="my.css"

type="text/css" rel="stylesheet" /></head>

<body> ....

<?php flush() ?>

Progressive rendering Chunk #1

Chunk #2

Chunk #3

x.appendChild(script)

<!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

•  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

•  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 // 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

DOMland ECMAland

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