Building Faster Websites

Preview:

DESCRIPTION

This is my latest version of my client side performance presentations. This has been presented at TechEd NZ 2009 & to a couple of .NET user groups around NZ. This presentation focuses on the basics of client-side performance tuning.

Citation preview

Building Faster Websites

Craig Walker, Chief Technology Officer

What is Xero?

http://www.xero.com/signup/

Performance === Usability

Performance === Money

• Bing: +2s in response time = 4.3% LOSS of revenue/user

• Google: +500ms in response time = 20% LESS traffic

• Amazon: +100ms in response time = 1% LESS sales

• Shopzilla: +5s response time led to 12% increase in revenue & 25% increase in page views

ReceiveLast Byte

Send Last Byte

Send Data

The HTTP Request

Server

Browser

Establish Connection

InitialConnection

Open Socket

Initial HTTPRequest

First Byte

ReceiveFirst Byte

Send First Byte

Content Download

ISP

Get IP

DNSLookup

5%

95%

25%

75%

Empty Cache Primed Cache

Focus on the front-end

• 75-95% of the end-user response time for Xero customers was spent on the front end

• Much easier to optimise than server side performance

• Greater potential for improvement – especially from a front-end users point of view

• MySpace's Performance Tracker msfast.myspace.com

• MS Visual Round Trip Analyzer

Lotsa tools!

• HTTPWatch www.httpwatch.com

• AOL PageTest www.webpagetest.org

The ones we use at Xero …

• Fiddler www.fiddlertool.com

• Firebug www.getfirebug.com

• YSlow developer.yahoo.com/yslow

We don't all run Windows 7 & IE8 …

Browsers

6616

29275

15596

5318

27233

Chrome IE7 IE8Safari 4 Firefox 3

Operating Systems

728

10891

20717

53065

1383

Linux MacOSXVista XPWindows 7

Fiddlerdemo

The Rules

Zip It!

• Significantly reduces download size – 60-80% saving on text based content

• 90% of browsers support compression

• Benefits users & you too:– Reduces traffic costs

– Doesn’t require code change

• Zip everything you can– html, aspx, js, css, xml, txt, json,

ashx …

Fly’s down Zipped

Turning on HTTP Compression

demo

Things to be wary of with compression …• Make sure you test – don't assume compression

• Approximately 5% of users get uncompressed responses

• Browsers (& other applications) indicate compression support by sending Accept-Encoding: gzip, deflate and responding with Content-Encoding: gzip

• Some client software (anti-virus, anti-phishing) and proxy servers can strip Accept-Encoding

• If Accept-Encoding is missing let your users know!

• Use Advanced Logging for IIS7 to track request & response headers http://www.iis.net/extensions/advancedlogging

Minify all static content

• CSS, JavaScript, XML, JSON, HTML can all be minified

• Not a replacement for gzipping but is a perfect accompaniment to it (we've seen up to 50% extra savings)

• No shortage of tools:– JSMin

– CSSTidy

– YUI Compressor

• JavaScript obfuscation can also be useful – just test that your app still works afterwards

You don't really want people to read your code do you?

Ext.DomHelper = function(){ var tempTableEl = null; var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i; var tableRe = /^table|tbody|tr|td$/i;

var createHtml = function(o){ if(typeof o == 'string'){ return o; } var b = ""; if (Ext.isArray(o)) { for (var i = 0, l = o.length; i < l; i++) { b += createHtml(o[i]); } return b; } if(!o.tag){ o.tag = "div"; } b += "<" + o.tag; for(var attr in o){ if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue; if(attr == "style"){ var s = o["style"]; if(typeof s == "function"){ s = s.call(); } if(typeof s == "string"){ b += ' style="' + s + '"'; }else if(typeof s == "object"){

Ext.DomHelper=function(){var n=null;var g=/^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;var b=/^table|tbody|tr|td$/i;var a=function(w){if(typeof w=="string"){return w;}var q="";if(Ext.isArray(w)){for(var u=0,r=w.length;u<r;u++){q+=a(w[u]);}return q;}if(!w.tag){w.tag="div";}q+="<"+w.tag;for(var p in w){if(p=="tag"||p=="children"||p=="cn"||p=="html"||typeof w[p]=="function"){continue;}if(p=="style"){var v=w["style"];if(typeof v=="function"){v=v.call();}if(typeof v=="string"){q+=' style="'+v+'"';}else{if(typeof v=="object"){q+=' style="';for(var t in v){if(typeof v[t]!="function"){q+=t+":"+v[t]+";";}}q+='"';}}}else{if(p=="cls"){q+=' class="'+w["cls"]+'"';}else{if(p=="htmlFor"){q+=' for="'+w["htmlFor"]+'"';}else{q+=" "+p+'="'+w[p]+'"';}}}}if(g.test(w.tag)){q+="/>";}else{q+=">";var x=w.children||w.cn;if(x){q+=a(x);}else{if(w.html){q+=w.html;}}q+="</"+w.tag+">";}return q;};var o=function(v,q){var u;if(Ext.isArray(v)){u=document.createDocumentFragment();for(var t=0,r=v.length;t<r;t++){o(v[t],u);}}else{if(typeof v=="string"){u=document.createTextNode(v);}else{u=document.createElement(v.tag||"div");var s=!!u.setAttribute;for(var p in v){if(p=="tag"||p=="children"||p=="cn"||p=="html"||p=="style"||typeof v[p]=="function"){continue;}if(p=="cls"){u.className=v["cls"];}else{if(s){u.setAttribute(p,v[p]);}else{u[p]=v[p];}}}Ext.DomHelper.applyStyles(u,v.style);var w=v.children||v.cn;if(w){o(w,u);}else{if(v.html){u.innerHTML=v.html;}}}}if(q){q.appendChild(u);}return u;};var k=function(v,t,r,u){n.innerHTML=[t,r,u].join("");var p=-1,q=n;while(++p<v){q=q.firstChild;}return q;};var l="<table>",e="</table>",c=l+"<tbody>",m="</tbody>"+e,i=c+"<tr>",d="</tr>"+m;var h=function(p,q,s,r){if(!n){n=document.createElement("div");}var t;var u=null;if(p=="td"){if(q=="afterbegin"||q=="beforeend"){return;}if(q=="beforebegin"){u=s;s=s.parentNode;}else{u=s.nextSibling;s=s.parentNode;}t=k(4,i,r,d);}else{if(p=="tr"){if(q=="beforebegin"){u=s;s=s.parentNode;t=k(3,c,r,m);}else{if(q=="afterend"){u=s.nextSibling;s=s.parentNode;t=k(3,c,r,m);}else{if(q=="afterbegin"){u=s.firstChild;}t=k(4,i,r,d);}}}else{if(p=="tbody"){if(q=="beforebegin"){u=s;s=s.parentNode;t=k(2,l,r,e);}else{if(q=="afterend"){u=s.nextSibling;s=s.parentNode;t=k(2,l,r,e);}else{if(q=="afterbegin"){u=s.firstChild;}t=k(3,c,r,m);}}}else{if(q=="beforebegin"||q=="afterend"){return;}if(q=="afterbegin"){u=s.firstChild;}t=k(2,l,r,e);}}}s.insertBefore(t,u);return

955KB -> 265KB

539KB -> 141KB

Reduce HTTP Requests

• Less components means a faster page

• Every request is an overhead

• Combine scripts

• Combine CSS

• Combine images into CSS Sprites

• Don’t rely on cache: 304’s are still requests

CSS Sprites

• Combine all small images into one large image

• Use CSS to control the displaying of each image

The designers want what?

11 images11 HTTP Requests3.3 KB total size

Obey your thirst®

1 image1 HTTP Request1.6 KB total size

And the code?<div class="buttons">

<span class="large green button"> <button type="button"> <span class="checkbox icon"> Approve </span> </button> </span> <span class="large blue button"> <button type="button"> <span> Save </span> </button> </span> <span class="large red button"> <button type="button"> <span class="delete icon"> Delete </span> </button> </span> <span class="medium gray button"> <button type="button"> <span class="delete icon"> Cancel </span> </button> </span> </div>

.buttons span.button { background:transparent url(buttons.png) no-repeat 0 0;}

.buttons span.button button, .buttons span.button a { background:transparent url(buttons.png) no-repeat 100% -120px;}

.buttons span.blue { background-position: 0 -30px;}

.buttons span.blue button, .buttons span.blue a { background-position: 100% -150px;}

.buttons span.red { background-position: 0 -60px;}

.buttons span.red button, .buttons span.red a { background-position: 100% -180px;}

.buttons span.green { background-position: 0 -90px;}

.buttons span.green button, .buttons span.green a { background-position: 100% -210px;}

SpriteMedemo

Optimise images• Use PNGs instead of GIFs

• Avoid alpha filters – can block rendering and freeze the browser

• PNG8 is best and supported by IE6 (yes – even with transparency

• Optimise further with tools like PNGOUT

• Make sure you have a favicon.ico:• Every browser will request it• Best not to respond with a 404• Make it small and cacheable

12KB15KB

Maximise Parallel Downloads

• Most browsers are limited to 6 connections total and 2 connections per hostname

Browser Parallel Downloads

Firefox 3.x 6

Internet Explorer 7 2

Internet Explorer 8 6

Safari 3.x 4

Safari 4 4

Chrome 4

• Increase the number of hostnames to increase the number of parallel downloads

• Don't overdo it! (DNS lookups are expensive so limit to 2-4 domains)

JavaScript external and on the bottom• Move scripts to external files for both reuse and

caching

• Promotes better script design

• Push scripts as low as possible

– Often difficult with document.write or with inline calls requiring loaded JavaScript

– Be pragmatic – think about splitting JavaScript into “must be loaded” and “can be loaded on demand”

• Scripts will block both downloading and rendering until parsed

• Remove duplicate scripts (IE has a habit of downloading them again)

Maximise the cache

• Understand the ratio of users with cached vs uncached

• Add an Expires header– Not just for images – should be used on all static content

– Set a “Never expire” or far future expires policy if you can

– Reduces HTTP requests – once component is served, the browser never asks for it again

– Date stamping in file names makes it easier

• Remove ETags– ETags are another caching mechanism – sent with every

request

– Uniquely created per web server – not good in web farms

– Just turn them off and use Expires headers instead

Use a CDN

• Content Delivery Network

• Distributes content closer to the last mile

• Distribute your static content before distributing your dynamic content

• Akamai most popular but expensive for small sites

• Xero utilises a rudimentary CDN using IP lookup to determine location

GETRequest

Response with HTML document

ImagesJSCSS

How it works:RegisterCSS("/common/style/xero.css", "screen")RegisterJavascript("/common/scripts/xero.js")

<link rel="stylesheet" type="text/css" media="screen" href="https://nzs1.xero.com/common/style/xero.css" />

<script type="text/javascript" src="https://nzs2.xero.com/common/scripts/xero.js">

</script>

Get location from IP

Reduce cookie weight

• Cookies are sent back with every request

• Keep cookie size small and only store what's required – use server-side storage for everything else

• Consider cookie free domains for static content

• And while we're at it – reduce ViewState too!

Preloading …

• Preload components you’ll need in the future

• Unconditional preload – Xero login page

preloads all core components so that the dashboard experience is better

• Conditional preload– Often based on where

you think a user might go to next

YSlow

• Firebug extension

• Grades performance – not about response times but about how well a site has adopted the techniques suggested

• Response time inversely proportional to YSlow score – get as close to A as possible to get the maximum performance gain

YSlowdemo

Aptimize

• Plugin that works with both IIS & Apache

• Merges CSS & JSS files

• Reduces & optimises images with CSS sprites & CSS inlining

• Compresses content with minification

• Improves caching

• Used by over 300 websites & intranets

• And it does all this in real-time!

www.aptimize.com

Aptimizedemo

Aptimizing Microsoft

• Microsoft used Aptimize to speed up sharepoint.microsoft.com

• HTTP requests reduced from 96 to 35

• Sped up first view, repeat view and start render by over 50%

• YSlow went from an E to a B

Things to take away

• Focus on the front-end

• Be an advocate for your users – isn't it nice to have happy users?

• Faster web sites lead to:

– Better user experience

– Reduced operating expenses

– Increase revenue

Questions?

www.xero.com