Post on 26-Mar-2015
Steve Souderssouders@google.com
http://stevesouders.com/docs/sxsw-20090314.ppt
Even Faster Web Sites
Disclaimer: This content does not necessarily reflect the opinions of my employer.
17%
83%
iGoogle, primed cache
the importance of frontend performance
9% 91%
iGoogle, empty cache
time spent on the frontend
Empty Cache
Primed Cache
www.aol.com 97% 97%
www.ebay.com 95% 81%
www.facebook.com 95% 81%
www.google.com/search
47% 0%
search.live.com/results 67% 0%
www.msn.com 98% 94%
www.myspace.com 98% 98%
en.wikipedia.org/wiki 94% 91%
www.yahoo.com 97% 96%
www.youtube.com 98% 97%April 2008
14 RULES
1. MAKE FEWER HTTP REQUESTS
2. USE A CDN3. ADD AN EXPIRES HEADER4. GZIP COMPONENTS5. PUT STYLESHEETS AT THE
TOP6. PUT SCRIPTS AT THE
BOTTOM7. AVOID CSS EXPRESSIONS8. MAKE JS AND CSS
EXTERNAL9. REDUCE DNS LOOKUPS10.MINIFY JS11.AVOID REDIRECTS12.REMOVE DUPLICATE
SCRIPTS13.CONFIGURE ETAGS14.MAKE AJAX CACHEABLE
25% discount code: "ssouders25"
Sept 2007
June 2009
Even Faster Websites
Split the initial payloadLoad scripts without
blockingCouple asynchronous scripts
Don't scatter inline scriptsSplit the dominant
domainFlush the document early
Use iframes sparingly
Simplify CSS Selectors
Ajax performance (Doug Crockford)
Writing efficient JavaScript (Nicholas Zakas)
Creating responsive web apps (Ben Galbraith, Dion Almaer)
Comet (Dylan Schiemann)Beyond Gzipping (Tony
Gentilcore)Optimize Images (Stoyan
Stefanov, Nicole Sullivan)
AOLeBayFacebookMySpaceWikipediaYahoo!
why focus on JavaScript?
YouTube
scripts block
<script src="A.js"> blocks parallel downloads and rendering
http://stevesouders.com/cuzillion/?ex=10008
MSNScripts and other resources downloaded in parallel! How? Secret sauce?!var p= g.getElementsByTagName("HEAD")[0];var c=g.createElement("script");c.type="text/javascript";c.onreadystatechange=n;c.onerror=c.onload=k;c.src=e;p.appendChild(c)
MSN.com: parallel scripts
asynchronous script loading
XHR Eval
XHR Injection
Script in Iframe
Script DOM Element
Script Defer
document.write Script Tag
XHR Eval
script must have same domain as main page
must refactor script
var xhrObj = getXHRObject();xhrObj.onreadystatechange = function() { if ( xhrObj.readyState != 4 ) return; eval(xhrObj.responseText); };xhrObj.open('GET', 'A.js', true);xhrObj.send('');
http://stevesouders.com/cuzillion/?ex=10009
XHR Injectionvar xhrObj = getXHRObject();xhrObj.onreadystatechange = function() { if ( xhrObj.readyState != 4 ) return; var se=document.createElement('script'); document.getElementsByTagName('head') [0].appendChild(se); se.text = xhrObj.responseText; };xhrObj.open('GET', 'A.js', true);xhrObj.send('');
script must have same domain as main pagehttp://stevesouders.com/cuzillion/?ex=10015
Script in Iframe<iframe src='A.html' width=0 height=0 frameborder=0 id=frame1></iframe>
iframe must have same domain as main page
must refactor script:// access iframe from main pagewindow.frames[0].createNewDiv();
// access main page from iframeparent.document.createElement('div');
http://stevesouders.com/cuzillion/?ex=10012
Script DOM Elementvar se = document.createElement('script');se.src = 'http://anydomain.com/A.js';document.getElementsByTagName('head')[0] .appendChild(se);
script and main page domains can differ
no need to refactor JavaScript
http://stevesouders.com/cuzillion/?ex=10010
Script Defer<script defer src='A.js'></script>
only supported in IE (just landed in FF 3.1)
script and main page domains can differ
no need to refactor JavaScript
http://stevesouders.com/cuzillion/?ex=10013
document.write Script Tagdocument.write("<scr" + "ipt type='text/javascript' src='A.js'>" + "</scr" + "ipt>");
parallelization only works in IE
parallel downloads for scripts, nothing else
all document.writes must be in same script block
http://stevesouders.com/cuzillion/?ex=10014
load scripts without blocking
*Only other document.write scripts are downloaded in parallel (in the same script block).
asynchronous JS example: menu.js
<script type="text/javascript">var domscript = document.createElement('script');domscript.src = "menu.js"; document.getElementsByTagName('head')
[0].appendChild(domscript);
var aExamples = [ ['couple-normal.php', 'Normal Script Src'], ['couple-xhr-eval.php', 'XHR Eval'], ... ['managed-xhr.php', 'Managed XHR'] ];
function init() { EFWS.Menu.createMenu('examplesbtn', aExamples);}
init();</script>
script DOM element approach
before
after
load scripts without blocking
*Only other document.write scripts are downloaded in parallel (in the same script block).
!IE
what about
inlined code that depends on the script?
coupling techniques
hardcoded callback
window onload
timer
degrading script tags
script onload
technique 5: script onload<script type="text/javascript">var aExamples = [['couple-normal.php', 'Normal Script Src'], ...];
function init() { EFWS.Menu.createMenu('examplesbtn', aExamples);}
var domscript = document.createElement('script');domscript.src = "menu.js";
domscript.onloadDone = false;domscript.onload = function() { if ( ! domscript.onloadDone ) { init(); } domscript.onloadDone = true; };domscript.onreadystatechange = function() { if ( "loaded" === domscript.readyState ) { if ( ! domscript.onloadDone ) { init(); } domscript.onloadDone = true; }}
document.getElementsByTagName('head')[0].appendChild(domscript);</script>
pretty nice, medium complexity
case study: Google Analytics
recommended pattern:1
<script type="text/javascript">var gaJsHost = (("https:" == document.location.protocol) ?
"https://ssl." : "http://www.");
document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
</script>
<script type="text/javascript">var pageTracker = _gat._getTracker("UA-xxxxxx-x");pageTracker._trackPageview();</script>
document.write Script Tag approach blocks other resources
1http://www.google.com/support/analytics/bin/answer.py?hl=en&answer=55488
case study: dojox.analytics.Urchin1
_loadGA: function(){ var gaHost = ("https:" == document.location.protocol) ? "https://ssl." : "http://www."; dojo.create('script', { src: gaHost + "google-analytics.com/ga.js" }, dojo.doc.getElementsByTagName("head")[0]); setTimeout(dojo.hitch(this, "_checkGA"), this.loadInterval);},
_checkGA: function(){ setTimeout(dojo.hitch(this, !window["_gat"] ? "_checkGA" :
"_gotGA"), this.loadInterval);},
_gotGA: function(){ this.tracker = _gat._getTracker(this.acct); ...}
Script DOM Element approach"timer" coupling technique (script onload
better)1http://docs.dojocampus.org/dojox/analytics/Urchin
asynchronous loading & coupling
async technique: Script DOM Element– easy, cross-browser– doesn't ensure script order
coupling technique: script onload– fairly easy, cross-browser– ensures execution order for external
script and inlined code
bad: stylesheet followed by inline script
browsers download stylesheets in parallel with other resources that follow...
...unless the stylesheet is followed by an inline script
http://stevesouders.com/cuzillion/?ex=10021
best to move inline scripts above stylesheets or below other resources
use Link, not @import
eBayMSNMySpaceWikipedia
don't scatter inline scripts
iframes: most expensive DOM element
load 100 empty elements of each type
tested in all major browsers1
1IE 6, 7, 8; FF 2, 3.0, 3.1b2; Safari 3.2, 4; Opera 9.63, 10; Chrome 1.0, 2.0
iframes block onload
parent's onload doesn't fire until iframe and all its components are downloaded
workaround for Safari and Chrome: set iframe src in JavaScript
<iframe id=iframe1 src=""></iframe><script type="text/javascript">document.getElementById('iframe1').src="url";</script>
scripts block iframe
no surprise – scripts in the parent block the iframe from loading
IE
Firefox
Safari Chrome
Opera
script
script
script
stylesheets block iframe (IE, FF)
surprise – stylesheets in the parent block the iframe or its resources in IE & Firefox
IE
Firefox
Safari Chrome
Opera
stylesheet
stylesheet
stylesheet
stylesheets after iframe still block (FF)
surprise – even moving the stylesheet after the iframe still causes the iframe's resources to be blocked in Firefox
IE
Firefox
Safari Chrome
Opera
stylesheet
stylesheet
stylesheet
iframes: no free connections
iframe shares connection pool with parent (here – 2 connections per server in IE 7)
iframe
parent
flush the document early
gotchas:– PHP output_buffering – ob_flush()– Transfer-Encoding: chunked– gzip – Apache's DeflateBufferSize before
2.2.8– proxies and anti-virus software– browsers – Safari (1K), Chrome (2K)
other languages: $| or FileHandle autoflush (Perl), flush
(Python), ios.flush (Ruby)
htmlimageimagescript
htmlimageimagescript call PHP's flush()
flushing and domain blocking
you might need to move flushed resources to a domain different from the HTML dochtml
imageimagescript
htmlimageimagescript
googleimageimagescriptimage204
case study: Google search
blocked by HTML document
different domains
takeaways
focus on the frontend
run YSlow: http://developer.yahoo.com/yslow
this year's focus: JavaScript
speed matters
impact on revenue
Google:
Yahoo:
Amazon:
1 http://home.blarg.net/~glinden/StanfordDataMining.2006-11-29.ppt2 http://www.slideshare.net/stoyan/yslow-20-presentation
+500 ms -20% traffic1
+400 ms -5-9% full-page traffic2
+100 ms -1% sales1
cost savings
hardware – reduced load
bandwidth – reduced response size
http://billwscott.com/share/presentations/2008/stanford/HPWP-RealWorld.pdf
if you want better user experience more revenue reduced operating expenses
the strategy is clear
Even Faster Web Sites
Steve Souderssouders@google.com
http://stevesouders.com/docs/sxsw-20090314.ppt