<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>David Calhoun&#039;s Blog &#187; performance</title>
	<atom:link href="http://davidbcalhoun.com/category/performance/feed" rel="self" type="application/rss+xml" />
	<link>http://davidbcalhoun.com</link>
	<description>Just another blog</description>
	<lastBuildDate>Mon, 07 May 2012 02:06:20 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.2</generator>
		<item>
		<title>iOS5 SunSpider: iPhone 4S vs iPhone 4 vs iPhone 3GS</title>
		<link>http://davidbcalhoun.com/2011/ios5-sunspider-iphone-4s-vs-iphone-4-vs-iphone-3gs</link>
		<comments>http://davidbcalhoun.com/2011/ios5-sunspider-iphone-4s-vs-iphone-4-vs-iphone-3gs#comments</comments>
		<pubDate>Sat, 15 Oct 2011 01:39:14 +0000</pubDate>
		<dc:creator>David</dc:creator>
				<category><![CDATA[performance]]></category>

		<guid isPermaLink="false">http://davidbcalhoun.com/?p=831</guid>
		<description><![CDATA[Result table (SunSpider 0.9.1, all devices running iOS 5) Test iPhone 4S iPhone 4 iPhone 3GS Total 2270ms 3483ms 4903ms 3D 281ms 486ms 672ms Access 279ms 410ms 614ms Bitops 177ms 200ms 270ms Controlflow 20ms 26ms 34ms Crypto 164ms 211ms 315ms Date 332ms 528ms 770ms Math 219ms 433ms 553ms Regexp 88ms 117ms 151ms String 711ms 1072ms [...]]]></description>
			<content:encoded><![CDATA[<h3>Result table (SunSpider 0.9.1, all devices running iOS 5)</h3>
<table>
<thead>
<tr>
<td>Test</td>
<td>iPhone 4S</td>
<td>iPhone 4</td>
<td>iPhone 3GS</td>
</tr>
</thead>
<tfoot></tfoot>
<tbody>
<tr>
<td>Total</td>
<td class="positive">2270ms</td>
<td>3483ms</td>
<td>4903ms</td>
</tr>
<tr>
<td>3D</td>
<td class="positive">281ms</td>
<td>486ms</td>
<td>672ms</td>
</tr>
<tr>
<td>Access</td>
<td class="positive">279ms</td>
</td>
<td>410ms</td>
<td>614ms</td>
</tr>
<tr>
<td>Bitops</td>
<td class="positive">177ms</td>
<td>200ms</td>
<td>270ms</td>
</tr>
<tr>
<td>Controlflow</td>
<td class="positive">20ms</td>
<td>26ms</td>
</td>
<td>34ms</td>
</tr>
<tr>
<td>Crypto</td>
<td class="positive">164ms</td>
<td>211ms</td>
<td>315ms</td>
</tr>
<tr>
<td>Date</td>
<td class="positive">332ms</td>
<td>528ms</td>
<td>770ms</td>
</tr>
<tr>
<td>Math</td>
<td class="positive">219ms</td>
<td>433ms</td>
<td>553ms</td>
</tr>
<tr>
<td>Regexp</td>
<td class="positive">88ms</td>
<td>117ms</td>
<td>151ms</td>
</tr>
<tr>
<td>String</td>
<td class="positive">711ms</td>
<td>1072ms</td>
<td>1525ms</td>
</tr>
</tbody>
</table>
<h3>Raw results</h3>
<p><a href="http://www.webkit.org/perf/sunspider-0.9.1/sunspider-0.9.1/results.html?%7B%22v%22:%20%22sunspider-0.9.1%22,%20%223d-cube%22:%5B98,100,97,101,98,98,99,99,100,105%5D,%223d-morph%22:%5B72,75,72,74,72,74,72,72,72,73%5D,%223d-raytrace%22:%5B107,107,108,111,108,108,108,109,110,108%5D,%22access-binary-trees%22:%5B48,48,53,51,47,49,51,54,52,47%5D,%22access-fannkuch%22:%5B121,120,124,120,121,121,120,120,125,120%5D,%22access-nbody%22:%5B70,72,71,71,71,72,70,70,69,70%5D,%22access-nsieve%22:%5B35,40,38,38,37,35,35,36,38,37%5D,%22bitops-3bit-bits-in-byte%22:%5B29,29,29,28,29,29,29,29,29,29%5D,%22bitops-bits-in-byte%22:%5B39,36,37,39,37,36,36,37,38,37%5D,%22bitops-bitwise-and%22:%5B47,48,48,46,49,48,49,51,48,47%5D,%22bitops-nsieve-bits%22:%5B65,63,62,62,62,62,63,63,63,62%5D,%22controlflow-recursive%22:%5B19,20,20,20,19,20,20,19,20,20%5D,%22crypto-aes%22:%5B94,96,96,100,103,102,96,96,96,99%5D,%22crypto-md5%22:%5B36,37,37,38,37,36,39,36,38,39%5D,%22crypto-sha1%22:%5B29,28,30,28,30,29,29,28,29,29%5D,%22date-format-tofte%22:%5B171,182,167,168,170,172,167,170,181,172%5D,%22date-format-xparb%22:%5B160,159,161,158,160,168,162,156,159,158%5D,%22math-cordic%22:%5B81,79,76,77,77,77,78,77,76,77%5D,%22math-partial-sums%22:%5B92,92,92,92,94,92,92,91,92,94%5D,%22math-spectral-norm%22:%5B49,49,49,49,51,49,49,50,48,52%5D,%22regexp-dna%22:%5B91,87,88,87,88,88,88,87,88,87%5D,%22string-base64%22:%5B89,88,86,92,87,91,96,87,87,87%5D,%22string-fasta%22:%5B91,92,92,91,94,94,95,95,89,89%5D,%22string-tagcloud%22:%5B144,147,147,145,144,145,144,144,148,150%5D,%22string-unpack-code%22:%5B246,243,238,244,254,249,243,249,240,238%5D,%22string-validate-input%22:%5B145,134,136,137,140,137,137,137,141,149%5D%7D">iPhone 4S (iOS5)</a></p>
<p><a href="http://www.webkit.org/perf/sunspider-0.9.1/sunspider-0.9.1/results.html?%7B%22v%22:%20%22sunspider-0.9.1%22,%20%223d-cube%22:%5B175,179,192,175,180,174,181,174,173,182%5D,%223d-morph%22:%5B138,137,136,135,136,135,136,137,144,135%5D,%223d-raytrace%22:%5B166,171,174,174,166,178,168,167,169,168%5D,%22access-binary-trees%22:%5B61,61,62,58,57,56,57,56,60,57%5D,%22access-fannkuch%22:%5B141,142,143,146,141,142,148,142,146,148%5D,%22access-nbody%22:%5B154,152,147,146,144,148,146,154,146,149%5D,%22access-nsieve%22:%5B59,59,57,67,58,60,57,58,59,59%5D,%22bitops-3bit-bits-in-byte%22:%5B29,29,29,28,34,29,28,28,29,28%5D,%22bitops-bits-in-byte%22:%5B40,40,41,40,41,40,42,41,41,41%5D,%22bitops-bitwise-and%22:%5B58,59,60,58,60,58,59,62,58,58%5D,%22bitops-nsieve-bits%22:%5B71,76,70,75,71,69,72,71,70,70%5D,%22controlflow-recursive%22:%5B26,25,25,26,25,26,25,25,26,27%5D,%22crypto-aes%22:%5B124,132,124,124,126,123,123,125,122,127%5D,%22crypto-md5%22:%5B48,48,48,50,50,50,50,50,50,50%5D,%22crypto-sha1%22:%5B36,37,36,35,36,37,36,37,36,36%5D,%22date-format-tofte%22:%5B265,267,265,314,259,260,257,266,263,260%5D,%22date-format-xparb%22:%5B258,265,258,259,265,259,259,264,259,260%5D,%22math-cordic%22:%5B291,141,141,141,147,140,140,141,178,140%5D,%22math-partial-sums%22:%5B174,173,179,180,171,174,171,179,177,171%5D,%22math-spectral-norm%22:%5B102,98,97,98,97,97,99,100,99,98%5D,%22regexp-dna%22:%5B115,114,114,116,115,119,122,112,120,122%5D,%22string-base64%22:%5B120,123,116,121,114,118,116,115,117,117%5D,%22string-fasta%22:%5B128,125,122,129,122,130,129,131,123,128%5D,%22string-tagcloud%22:%5B208,209,209,206,215,210,210,209,211,219%5D,%22string-unpack-code%22:%5B401,412,410,413,406,422,412,413,410,414%5D,%22string-validate-input%22:%5B205,208,199,200,205,207,206,212,206,211%5D%7D">iPhone 4 (iOS5)</a></p>
<p><a href="http://www.webkit.org/perf/sunspider-0.9.1/sunspider-0.9.1/results.html?%7B%22v%22:%20%22sunspider-0.9.1%22,%20%223d-cube%22:%5B245,251,245,317,247,244,250,249,250,250%5D,%223d-morph%22:%5B191,184,186,189,186,190,202,190,194,188%5D,%223d-raytrace%22:%5B222,226,223,222,229,227,234,229,227,231%5D,%22access-binary-trees%22:%5B86,87,87,93,89,89,92,92,94,85%5D,%22access-fannkuch%22:%5B189,188,190,189,350,187,188,189,188,190%5D,%22access-nbody%22:%5B195,194,193,204,585,196,198,199,194,194%5D,%22access-nsieve%22:%5B83,94,86,82,84,83,83,83,82,84%5D,%22bitops-3bit-bits-in-byte%22:%5B38,40,37,38,38,43,40,38,38,40%5D,%22bitops-bits-in-byte%22:%5B70,54,53,58,62,55,59,55,54,54%5D,%22bitops-bitwise-and%22:%5B78,78,79,79,82,79,77,76,77,79%5D,%22bitops-nsieve-bits%22:%5B93,94,94,95,93,94,93,93,101,99%5D,%22controlflow-recursive%22:%5B34,34,32,34,33,34,33,33,36,35%5D,%22crypto-aes%22:%5B365,173,173,174,174,184,177,172,170,171%5D,%22crypto-md5%22:%5B101,65,64,68,67,69,65,66,65,67%5D,%22crypto-sha1%22:%5B75,49,48,50,51,48,49,54,49,49%5D,%22date-format-tofte%22:%5B618,358,357,361,373,365,367,482,379,372%5D,%22date-format-xparb%22:%5B408,360,349,354,371,365,371,369,359,363%5D,%22math-cordic%22:%5B199,191,192,188,188,188,190,188,190,191%5D,%22math-partial-sums%22:%5B228,227,228,225,229,232,227,232,229,227%5D,%22math-spectral-norm%22:%5B160,132,131,131,130,131,131,130,133,130%5D,%22regexp-dna%22:%5B158,149,148,151,150,149,152,148,153,148%5D,%22string-base64%22:%5B164,163,176,167,171,172,175,172,181,174%5D,%22string-fasta%22:%5B227,187,176,179,167,175,176,189,176,172%5D,%22string-tagcloud%22:%5B303,288,270,277,284,289,282,284,281,284%5D,%22string-unpack-code%22:%5B1029,531,527,523,543,545,553,545,538,541%5D,%22string-validate-input%22:%5B296,300,297,299,299,293,298,320,296,296%5D%7D">iPhone 3GS (iOS5)</a></p>
<h3>Bonus: <a href="http://ie.microsoft.com/testdrive/Performance/FishIETank/">FishIETank</a> (10 fish) (Canvas test)</h3>
<table>
<thead>
<tr>
<td>Test</td>
<td>iPhone 4S</td>
<td>iPhone 4</td>
<td>iPhone 3GS</td>
</tr>
</thead>
<tfoot></tfoot>
<tbody>
<tr>
<td>Total</td>
<td class="positive">50fps</td>
<td>35fps</td>
<td>25fps</td>
</tr>
</tbody>
</table>
<p>One thing to note here: the 3GS has a bit of an advantage because of a non-retina screen.  However, it&#8217;s still outperformed easily by the iPhone 4 and 4S.</p>
<h3>Related</h3>
<p><a href="http://davidbcalhoun.com/2011/javascript-sunspider-benchmark-ios-4-3-vs-ios-4-0">JavaScript SunSpider benchmark: iOS 4.3 vs iOS 4.0</a><br />
<a href="http://davidbcalhoun.com/2010/blackberry-torch-sunspider-results-javascript-benchmark">BlackBerry Torch SunSpider results (JavaScript benchmark)</a><br />
<a href="http://davidbcalhoun.com/2010/iphone-4-sunspider-test-results">iPhone 4 SunSpider test results (22% faster than iPhone 3GS)</a><br />
<a href="http://davidbcalhoun.com/2010/sunspider-ios-3-1-3-versus-ios-4-gm">JavaScript SunSpider test: iOS 3.1.3 versus iOS 4 GM</a><br />
<a href="http://davidbcalhoun.com/2010/javascript-sunspider-htc-evo-versus-htc-incredible-versus-nexus-one">JavaScript SunSpider: HTC Evo versus HTC Incredible versus Nexus One</a></p>
]]></content:encoded>
			<wfw:commentRss>http://davidbcalhoun.com/2011/ios5-sunspider-iphone-4s-vs-iphone-4-vs-iphone-3gs/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Mobile Performance Manifesto</title>
		<link>http://davidbcalhoun.com/2011/mobile-performance-manifesto</link>
		<comments>http://davidbcalhoun.com/2011/mobile-performance-manifesto#comments</comments>
		<pubDate>Tue, 11 Oct 2011 07:44:45 +0000</pubDate>
		<dc:creator>David</dc:creator>
				<category><![CDATA[mobile]]></category>
		<category><![CDATA[performance]]></category>

		<guid isPermaLink="false">http://davidbcalhoun.com/?p=768</guid>
		<description><![CDATA[Earlier this year I gave a talk (slides) outlining the latest and greatest in mobile performance, including a bit of my own unscientific research into carrier latency and bandwidth thanks to boomerang.js. I realized that interest in mobile performance has exploded recently, especially with Steve Souders announcing his focus on mobile, and I thought it [...]]]></description>
			<content:encoded><![CDATA[<p><img src="http://davidbcalhoun.com/wp-content/uploads/2011/10/odometer.jpg" alt="" title="Dashboard with odometer" width="800" height="299" class="aligncenter size-full wp-image-806" /></p>
<p>Earlier this year I gave a <a href="http://www.meetup.com/SF-Web-Performance-Group/events/16116764">talk</a> (<a href="http://davidbcalhoun.com/present/mobile-performance/">slides</a>) outlining the latest and greatest in mobile performance, including a bit of my own unscientific research into carrier latency and bandwidth thanks to <a href="https://github.com/yahoo/boomerang">boomerang.js</a>.</p>
<p>I realized that interest in mobile performance has exploded recently, especially with <a href="http://www.stevesouders.com/blog/2011/01/10/announcing-my-focus-on-mobile/">Steve Souders announcing his focus on mobile</a>, and I thought it was time for an update, this time in blog form.  Also, my old <a href="http://davidbcalhoun.com/present/mobile-performance/">slides</a> have been somewhat embarrassing.  For some strange reason, at the time I wanted to give <a href="http://meyerweb.com/eric/tools/s5/">S5</a> a try &#8211; that outdated, ancient, not-performant slideshow framework.  The result is a slideshow on performance that loads slowly&#8230; doh!  (incidentally, I recommend <a href="http://imakewebthings.github.com/deck.js/">deck.js</a> as an alternative).</p>
<p>In any case, it was time for a roundup of mobile performance best practices, in blog form.  I&#8217;m not sure if it&#8217;s properly called a manifesto, but it is what it is!  Onward!</p>
<h3>For fun: Latency and bandwidth tests</h3>
<p>Before we start.. just a little fun data!  It&#8217;s always been a pleasure to fiddle around with <a href="https://github.com/yahoo/boomerang">boomerang.js</a> and compare results.  With my latest trip to Japan, I had the opportunity to run Boomerang on Japan&#8217;s <a href="http://en.wikipedia.org/wiki/EMOBILE_Limited">e-mobile</a> 3G network in the remotest of places, up in Hakuba/Nagano in the Japanese alps.</p>
<p>When I got back, I ran the same tests from downtown San Francisco, which was closer to my California-based test server, and should&#8217;ve been faster, right?  Theoretically&#8230;</p>
<h4>Boomerang Tests (running on a California-hosted server)</h4>
<table>
<th>
<td>Verizon 3G (SF)</td>
<td>ATT 3G (SF)</td>
<td>e-mobile 3G (Hakuba)</td>
</th>
<tr>
<td>Latency</td>
<td class="positive">251ms</td>
<td>901ms</td>
<td>401ms</td>
</tr>
<tr>
<td>Bandwidth</td>
<td>62kbps</td>
<td>3kbps</td>
<td class="positive">95kbps</td>
</tr>
</table>
<p>Yes, e-mobile didn&#8217;t have the greatest ping, but it handily beat ATT, even though it was going across the entire Pacific Ocean!  Note that it came out the best in the bandwidth tests however&#8230;  </p>
<p>(Note: it&#8217;s unknown if or how <a href="http://stevesouders.com/ms/">Steve Souders&#8217;s latest research</a> affects these findings)</p>
<p>There&#8217;s a lot of factors that could&#8217;ve been involved, so look to something like <a href="http://opensignalmaps.com/">OpenSignalMaps</a> for more data.</p>
<p>Ok, onto the tips!</p>
<h3>Page Organization</h3>
<p><img src="http://davidbcalhoun.com/wp-content/uploads/2011/10/mobile-site-organization.png" alt="" title="Smart phone versus feature phone website organization" width="645" height="633" class="aligncenter size-full wp-image-793" /></p>
<p>For feature phones that have little to no caching, aggressively combine requests (deliver HTML/CSS/JS all in one package).  For smart phones, take advantage of caching by mirroring desktop frontend best practices: separate HTML, CSS, and JS so they can be cached (per-session and across sessions).</p>
<p>There&#8217;s old research about <a href="http://www.yuiblog.com/blog/2008/02/06/iphone-cacheability/">extremely small cache sizes on iOS in particular</a>, but this research has been followed up on by more recent research by <a href="http://www.yuiblog.com/blog/2010/07/12/mobile-browser-cache-limits-revisited/">Ryan Grove (Yahoo!)</a> and <a href="http://www.stevesouders.com/blog/2010/07/12/mobile-cache-file-sizes/">Steve Souders (Google)</a>, which shows that we shouldn&#8217;t be so paranoid, since caching is pretty decent across all the major mobile browsers.</p>
<p>Of particular interest is a browser&#8217;s capability to cache files in a current session, browsing from page-to-page, which is what most users will end up doing (caching of the page across sessions is another matter).  What is the maximum file size a browser will cache during a session?  The <a href="http://www.browserscope.org/user/tests/table/agt1YS1wcm9maWxlcnINCxIEVGVzdBj_1OsBDA?v=3&#038;layout=simple&#038;f=Max%20js%20Cache%20Size%20(kB)">results (via Browserscope)</a> end up being encouraging:</p>
<table>
<th>
<td>Maximum Cache Size (MB)</td>
</th>
<tr>
<td>Android 2.1</td>
<td>4+</td>
</tr>
<tr>
<td>Android 2.2</td>
<td>2</td>
</tr>
<tr>
<td>Android 2.3</td>
<td>2</td>
</tr>
<tr>
<td>Android 3.0</td>
<td>4+</td>
</tr>
<tr>
<td>Blackberry 6.0.0</td>
<td>4+</td>
</tr>
<tr>
<td>iPad 4.3.5</td>
<td>4+</td>
</tr>
<tr>
<td>iPad 5.0</td>
<td>4+</td>
</tr>
<tr>
<td>iPhone 4.3.5</td>
<td>4+</td>
</tr>
<tr>
<td>iPhone 5.0</td>
<td>4+</td>
</tr>
<tr>
<td>Opera Mini 4</td>
<td>4+</td>
</tr>
<tr>
<td>Opera Mini 5</td>
<td>4+</td>
</tr>
<tr>
<td>Opera Mini 6</td>
<td>4+</td>
</tr>
<tr>
<td>webOS 2.0</td>
<td>1</td>
</tr>
</table>
<h3>Avoid redirects (foo.com -> m.foo.com)</h3>
<p>If possible, perform the redirection behind the scenes on the server, which should be transparent to the user.  When a user performs a Google search and clicks on your page, they&#8217;re already getting redirected once by Google (check it yourself).  Your own redirects are adding a second redirect where there need not be one.</p>
<h3>Optimize Images</h3>
<p><a href="http://filamentgroup.com/examples/responsive-images-new/demos/A-Default/demo.html"><img src="http://davidbcalhoun.com/wp-content/uploads/2011/10/responsive-images.jpg" alt="" title="Responsive images" width="600" height="423" class="aligncenter size-full wp-image-796" /></a><br />
Deliver appropriately-sized images to devices.  The philosophy of responsive design makes it easy to simply downscale images to fit the screen, but avoid this where possible, as this means wasted bandwidth.</p>
<p>You can optimize images through CSS media queries or in JavaScript (see below).  Though you want to reduce your dependence on cookies, it may be a good idea to store these width/height values into a cookie (or localStorage if you are fetching images in nontraditional ways) so the values can be read by the server, which can deliver appropriately-sized images.  This technique has been <a href="http://filamentgroup.com/lab/responsive_images_experimenting_with_context_aware_image_sizing/">implemented by Filament Group</a>, so you should probably read about their experiences before trying to roll your own.</p>
<p>Also note that where applicable, at the expense of performance you may want to serve higher resolution images for better screens, such as for Retina displays (which can be detected with JavaScript or CSS).</p>
<h4>JavaScript examples</h4>
<pre name="code" class="JScript">
window.innerHeight;       // max height actually available
window.innerWidth;        // max width actually available
window.devicePixelRatio;  // pixel density (standard is 1, high resolution is generally > 1)
</pre>
<p>(note that <code>screen.width</code> and <code>screen.height</code> are also available to tell you the dimensions of the entire screen, but this isn&#8217;t all available due to the space taken by the OS and browser chrome)</p>
<h4>Media query examples</h4>
<pre name="code" class="css">
@media only screen and (max-width: 480px) {
  /* small screen styles */
}

@media only screen and (min-width: 481px) {
  /* large screen styles */
}

@media (-webkit-min-device-pixel-ratio: 1.5),
       (-o-min-device-pixel-ratio: 3/2),
       (min--moz-device-pixel-ratio: 1.5),
       (min-device-pixel-ratio: 1.5) {
  /* high resolution styles */
}
</pre>
<h3>navigator.connection (Android only)</h3>
<p>Use <a href="http://davidbcalhoun.com/2010/using-navigator-connection-android">navigator.connection</a> if it&#8217;s available to serve different assets based on connection speed (3G vs WIFI, for instance).</p>
<p>Here&#8217;s the contents of the <code>navigator.connection</code> object (with a phone running on a 3G connection):</p>
<pre name="code" class="JScript">
{
  type: 4,
  UNKNOWN: 0,
  ETHERNET: 1,
  WIFI: 2,
  CELL_2G: 3,
  CELL_3G: 4
}
</pre>
<p>And an example of how you could write code for each type:</p>
<pre name="code" class="JScript">
(function(){  // sandbox our code
  if(!navigator.connection) navigator.connection = {type:0, UNKNOWN: 0};  // polyfill
  var connection = navigator.connection;

  if(connection.type === connection.WIFI || connection.type === connection.ETHERNET) {
    // high bandwidth
  } else {
    // normal bandwidth
  }
})();
</pre>
<p>Here&#8217;s how we might be able to use <a href="http://modernizr.com/">Modernizr</a> to help us out:</p>
<pre name="code" class="JScript">
(function(){  // sandbox our code
  if(!navigator.connection) navigator.connection = {type:0, UNKNOWN: 0};  // polyfill
  var connection = navigator.connection;

  // add a custom test to Modernizr
  Modernizr.addTest('highbandwidth', function(){
    return connection.type === connection.WIFI || connection.type === connection.ETHERNET;
  });

})();
</pre>
<p>We can now target special high-bandwidth assets with CSS:</p>
<pre name="code" class="css">
.highbandwidth .logo {background-image:url('logo-high.jpg');}
.no-highbandwidth .logo {background-image:url('logo.jpg');}
</pre>
<h3>base64 encode small UI images</h3>
<p>You can base64 encode binary image data in HTML and CSS:</p>
<h4>HTML</h4>
<pre name="code" class="html">
&lt;img src=&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUA
AAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO
9TXL0Y4OHwAAAABJRU5ErkJggg==&quot; alt=&quot;Red dot&quot;&gt;
</pre>
<h4>CSS</h4>
<pre name="code" class="css">
.dot {
  background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUA
AAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO
9TXL0Y4OHwAAAABJRU5ErkJggg==');
}
</pre>
<p>There seems to be the mistaken belief floating around that base64 images aren&#8217;t cacheable.  However, because you can embed them in your CSS, base64 encoded images will be cached along with the rest of your CSS.</p>
<p>You&#8217;ll probably just want to limit this to small UI icons however.  I wrote a <a href="http://davidbcalhoun.com/2011/when-to-base64-encode-images-and-when-not-to">quick post on the drawbacks, and also a comparison with sprites</a>.</p>
<p>If you&#8217;re using Compass/SASS, base64 encoding images is pretty trivial and easy to maintain with the <a href="http://compass-style.org/reference/compass/helpers/inline-data/">inline-image helper</a>.</p>
<h3>Unicode and Emoji</h3>
<p>&#9734; (HTML entity: #9734)<br />
<img src="http://davidbcalhoun.com/wp-content/uploads/2011/10/emoji3.png" alt="" title="Emoji" width="18" height="18" /> (HTML entity: <code>#x1f468</code>)</p>
<p>Before even considering base64 images, take advantage of icons that are already made for you: unicode characters and <a href="http://pukupi.com/post/1964">emoji</a> (where supported).  Keep in mind that these will look different across browsers, so this might not be an option for everyone.</p>
<p>Because of inconsistent implementations of Emoji character codes between Asian carriers, representatives from Apple and Google made a <a href="http://en.wikipedia.org/wiki/Emoji#Emoji_in_the_Unicode_standard">proposal to add Emoji to the Unicode standard</a>.  Unfortunately it seems that only iPhone supports them for now.  Here&#8217;s a <a href="http://www.unicode.org/charts/PDF/U1F300.pdf">handy chart of all the Unicode emoji characters available</a>.</p>
<h3>Take advantage of CSS3</h3>
<p>CSS3 offers many replacements for things we needed images for previously.  RGBA values replace the need for a semitransparent image for overlays and such.  Likewise, border-radius, box shadow, linear-gradients, radial-gradients all reduce the need for images.</p>
<p>However, do be aware that though phones support these new features, it doesn&#8217;t mean they&#8217;re necessarily ready for primetime.  Something I&#8217;ve encountered recently to remind me of this fact is <a href="http://code.google.com/p/android/issues/detail?id=767">severe color banding issues when using a CSS radial gradient</a>.  The solution was to fake the radial gradient using two linear gradients, or to (unfortunately) use an old-fashioned image.</p>
<h3>Avoid using cookies</h3>
<p>Cookies get thrown into <em>every</em> request on a per-domain basis, so limit your usage of them.  Use localStorage/sessionStorage instead, where possible.</p>
<h3>App cache</h3>
<p>Take advantage of the HTML5 app cache, though it&#8217;s an unwieldy beast to tame.  This will mean you will need to make your site work offline, which might be tricky.</p>
<p>Assets that are traditionally cached get rechecked on page refresh (the server sends back a 304 response if your cache is still up-to-date).  Whereas with the app cache, only one file (your manifest) is rechecked on page load.</p>
<h3>Deferred JavaScript execution</h3>
<p><img src="http://davidbcalhoun.com/wp-content/uploads/2011/10/gmail-deferred-javascript-execution.png" alt="" title="Gmail deferred JavaScript execution" width="497" height="450" class="aligncenter size-full wp-image-810" /></p>
<p>We know that deferring downloading of scripts is advantageous (by placing scripts at the bottom of a page or by using the <code>async</code> attribute), but deferred execution of JavaScript is even more important.  Sure, we can use the <code>defer</code> attribute, but that&#8217;s only really relevant for when the page is loading (<code>defer</code> lets the browser know that the UI doesn&#8217;t depend on the JavaScript, so it can be safely deferred).</p>
<p>But what about JavaScript that runs after the page loads, such as XHR and JSONP requests?  What does this mean for the user?  It means that the UI freezes up unexpectedly when JavaScript is being downloaded and executed in the background.  But this doesn&#8217;t mean that you want to completely avoid background downloading of JavaScript.  The Gmail mobile team came up with a <a href="http://googlecode.blogspot.com/2009/09/gmail-for-mobile-html5-series-reducing.html">clever solution</a>: by commenting all of their code and dynamically eval&#8217;ing it when needed, they split up the JavaScript downloading from its execution.</p>
<h3>Perceived performance</h3>
<p>Do whatever you have to do to let the user know that the UI is still responding.  This sometimes means faking that something is happening.  Communication is key!  If they clicked on a button, give some indication that they clicked on it.  If they clicked on something that requires a request to the network, show a spinner right away, even if it means you haven&#8217;t even sent out a request.  The user doesn&#8217;t need to know the nitty-gritty details &#8211; they just want to know that their intent was communicated.</p>
<h3>Onclick delay</h3>
<p><iframe width="853" height="480" src="http://www.youtube.com/embed/k1M4L3TAyEU" frameborder="0" allowfullscreen></iframe></p>
<p>On several major mobile operating systems there&#8217;s a several hundred millisecond delay on the onclick event.  This is because of the double-tap-to-zoom functionality.  When a user first taps on the screen, there&#8217;s a hard-coded delay that waits for the second tap.  If there&#8217;s no second tap, the onclick event is then fired.  Unfortunately the only way to get around the delay is to tap into touch events instead, which is a bit more complicated than it appears on first glance.</p>
<p>For an overview of some of the challenges of implementing the workaround, see <a href="http://code.google.com/mobile/articles/fast_buttons.html">this article by Google&#8217;s Ryan Fioravanti</a>.</p>
<h3>Take advantage of hardware acceleration</h3>
<p>Use hardware-accelerated CSS transforms where possible (translate3d, translateZ, rotate3d, and scale3d).  An element that is hardware accelerated is turned into a graphic, which is perfect for the GPU to manipulate, taking away the burden from the CPU.</p>
<p>However, the GPU isn&#8217;t all-powerful, so don&#8217;t try to apply hardware accelerations to everything.  Also, these elements still need to be refreshed periodically, and it turns out that you can make some good optimizations here.  First, you&#8217;ll want to debug the composited layers on your desktop browser:</p>
<h4>Debugging hardware acceleration</h4>
<p><a href="http://www.webkit.org/blog-files/3d-transforms/poster-circle.html"><img src="http://davidbcalhoun.com/wp-content/uploads/2011/10/debug-composited-layers-hardware-acceleration.png" alt="" title="WebKit&#039;s poster circle demo with and without the debug information" width="767" height="399" class="aligncenter size-full wp-image-803" /></a><br />
<em>Chrome</em></p>
<ol>
<li>Type the following in the address bar: about:flags</li>
<li>&#8220;Composited render layer borders&#8221; -> Enable</li>
</ol>
<p><em>Safari</em></p>
<ol>
<li>Open a terminal</li>
<li>$ defaults write com.apple.Safari IncludeInternalDebugMenu 1</li>
<li>$ defaults write com.apple.Safari CA_COLOR_OPAQUE 1</li>
<li>Open (or restart) Safari</li>
<li>Debug -> Show Compositing Borders</li>
</ol>
<p>(to turn these off, run the same commands with a boolean FALSE: i.e. <code>defaults write com.apple.Safari CA_COLOR_OPAQUE FALSE</code>)</p>
<p>Keep in mind that each composited layer has a limited width and height.  For instance, if you&#8217;re creating an image carousel, chances are the dimensions of the element will be too big to fit the layer into memory as one piece.  This means that when the element is animated, the GPU has to break up the layer manually into several manageable chunks.  It&#8217;s much better to chunk it yourself.  To do this, you trigger hardware acceleration on each chunk.</p>
<p>So you will change this:</p>
<pre name="code" class="css">
.carousel {
  -webkit-transform: translate3d(0,0,0);  /* or translateZ(0); */
}
</pre>
<p>To this:</p>
<pre name="code" class="css">
.carousel {
  -webkit-transform: translate3d(0,0,0);  /* or translateZ(0); */
}

.carousel-pane {
  -webkit-transform: translate3d(0,0,0);  /* or translateZ(0); */
}
</pre>
<p>Where carousel-pane represents each child element of the carousel.</p>
<p>More info: <a href="http://dl.dropbox.com/u/5618867/mseeley-2011-09-27-html5devconf.pdf">(slides) WebKit in Your Living Room (Matt Seeley, Netflix)</a></p>
<p>Also see <a href="http://www.html5rocks.com/en/mobile/optimization-and-performance.html">HTML5 Techniques for Optimizing Mobile Performance</a>.</p>
<h3>HTTP Pipelining</h3>
<p><a href="http://www.blaze.io/mobile/http-pipelining-big-in-mobile/"><img src="http://davidbcalhoun.com/wp-content/uploads/2011/10/pipelining.png" alt="HTTP Pipelining diagram" title="HTTP Pipelining (via Blaze.io)" width="640" height="445" class="aligncenter size-full wp-image-783" /></a></p>
<p>Take advantage of <a href="http://www.blaze.io/mobile/http-pipelining-big-in-mobile/">HTTP Pipelining</a>, which is often overlooked, but has broad support on mobile.  This virtually eliminates round trip times, for all but the first request.</p>
<p>Opera and Android support pipelining, and the newly released iOS 5 has added support for it.</p>
<p>Great! So how do you make sure your server is taking advantage of pipelining?</p>
<blockquote><p>The first request to every server is sent by itself (only one request on the connection), and the browser looks for two properties in the response:</p>
<ol>
<li>Use of HTTP/1.1</li>
<li>An explicit “Connection: Keep-Alive” header (required by Android)</li>
</ol>
<p><a href="http://www.blaze.io/mobile/http-pipelining-big-in-mobile/">-Blaze.io</a>
</p></blockquote>
<p>If these criteria are met, subsequent requests will be pipelined.  Sweet!</p>
<h3>DNS Prefetching</h3>
<p>Take advantage of <a href="https://developer.mozilla.org/En/Controlling_DNS_prefetching">DNS Prefetching</a>.  This theoretically speeds up load times, but there have been <a href="http://www.mydigitallife.info/turn-off-dns-prefetching-in-google-chrome-to-fix-resolving-host-and-cannot-load-page-error/">some issues</a>, so be sure to test it.</p>
<p>To turn off DNS Prefetching, serve this meta tag:</p>
<pre name="code" class="html">
&lt;meta http-equiv=&quot;x-dns-prefetch-control&quot; content=&quot;off&quot;&gt;
</pre>
<p>You can also explicitly force a DNS lookup:</p>
<pre name="code" class="html">
&lt;link rel=&quot;dns-prefetch&quot; href=&quot;http://www.example.com/&quot;&gt;
</pre>
<h3>Avoid library code bloat</h3>
<p>If you&#8217;re developing for smart phones, you may be able to dramatically reduce the data over the wire by simply using new JavaScript APIs instead of a full-blown library or framework such as jQuery Mobile (which is rather a UI framework and depends on the desktop version of jQuery).</p>
<p>Among the things offered by newer browsers, which should reduce your dependence on a library:</p>
<ul>
<li><a href="https://developer.mozilla.org/En/DOM/Document.querySelector">querySelector</a>/<a href="https://developer.mozilla.org/en/DOM/document.querySelectorAll">querySelectorAll</a>: CSS selectors that replace the need for a selector engine</li>
<li><a href="https://developer.mozilla.org/en/DOM/document.getElementsByClassName">getElementsByClassName</a></li>
<li><a href="https://developer.mozilla.org/en/DOM/element.classList">classList</a> &#8211; replaces the need for hasClass, addClass, removeClass helpers (available on iOS5, but not sure what else)</li>
<li><a href="https://developer.mozilla.org/En/XMLHttpRequest/Using_XMLHttpRequest">XMLHttpRequest</a> &#8211; it&#8217;s probably time to learn how to do this natively instead of using a wrapper that takes care of  IE&#8217;s old implementation that requests an ActiveX object.  <a href="http://hacks.mozilla.org/2009/07/cross-site-xmlhttprequest-with-cors/">Cross-Origin Resource Sharing</a> also means we can easily share resources across domains.</li>
<li><a href="https://developer.mozilla.org/en/DOM/Manipulating_the_browser_history">HTML5 history (pushState, etc)</a> &#8211; though an <a href="https://bugs.webkit.org/show_bug.cgi?id=42940">older bug</a> means you should maybe hold off for now (note: the fix is present in iOS 5, which is based on a newer WebKit)</li>
<li><a href="http://www.the-art-of-web.com/html/html5-form-validation/">HTML5 Form Validation</a> reduces our need for JavaScript-based validation</li>
<li>HTML5 input types that reduce our need for custom UI controls (date, time, slider [range], etc)</li>
</ul>
<p>You&#8217;ll want to consider using either a library that was optimized for the browsers you&#8217;re targeting (such as <a href="http://zeptojs.com/">Zepto.js</a>) or simply bring in <a href="http://microjs.com/">a microlibrary for specific tasks you want to perform</a>.</p>
<h3>Clientside databases</h3>
<p>Most mobile browsers currently support WebSQL, which is being phased out in support of indexedDB, which is not widely implemented.  However, if you use a wrapper such as <a href="http://westcoastlogic.com/lawnchair/">Lawnchair</a>, the transition from one to the other is relatively painless.</p>
<h3>TODO: testing</h3>
<h3>More</h3>
<p>(to be expanded on later)</p>
<ul>
<li>use standard desktop best practices (for smart phones)</li>
<li>requestAnimationFrame instead of setTimeout</li>
<li>setImmediate (where available) instead of setTimeout(fn(){},0)</li>
<li>Ajax parsing times: you might want to use Multipart XHR so you can yield the process as it&#8217;s going through big responses (to prevent the UI locking up)</li>
<li>avoid rgba, box shadows, text shadow, etc, as this greatly degrades performance (especially on animations)</li>
<li>high resolution screens may suffer from animation/transition slowness &#8211; it&#8217;s been shown that manipulating the viewport tag to scale the page <a href="http://29a.ch/2011/5/27/fast-html5-canvas-on-iphone-mobile-safari-performance">will speed up canvas rendering</a>, and <a href="http://paulbakaus.com/2011/10/10/scroller-vs-scrollability-deathmatch/">high-dpi scaling concerns are also evident in CSS transforms</a></li>
<li><a href="http://joehewitt.com/2011/10/05/fast-animation-with-ios-webkit">WebKit animations are faster than animations done via JavaScript</a></li>
<li>if you do use JavaScript-based animations, limit the UI updates to ~17ms, which is equivalent to 60fps, which is the fastest your display will refresh anyway, so it&#8217;s pointless to try to do anything faster than that.  Keep track of the time since the last UI update and don&#8217;t do anything if 17ms haven&#8217;t passed.</li>
</ul>
<h3>On the horizon&#8230;</h3>
<ul>
<li><a href="http://www.chromium.org/spdy">SPDY</a>: already implemented in Amazon&#8217;s Silk browser for Kindle.  Will possibly be on Android phones <a href="http://androidandme.com/2011/10/news/google-chrome-is-finally-coming-to-an-android-device-near-you/">when the browser is powered by Chrome</a></li>
<li>indexedDB replacing WebSQL</li>
</ul>
<h3>Tools</h3>
<p><a href="http://stevesouders.com/mobileperf/mobileperfbkm.php">Mobile Perf bookmarklet</a><br />
<a href="http://calendar.perfplanet.com/2010/mobile-performance-analysis-using-pcapperf/">pcapperf (Packet Capture Web Performance Analyzer)</a><br />
<a href="http://www.blaze.io/mobile/">Blaze Mobitest</a> &#8211; tests the loading time of your site on actual phones</p>
<h3>More Resources</h3>
<p><a href="http://www.w3.org/TR/mwabp/">Mobile Web Application Best Practices</a><br />
<a href="http://stevesouders.com/mobileperf/">Mobile Performance (Steve Souders)</a><br />
<a href="http://www.strangeloopnetworks.com/web-performance-optimization-hub/topics/mobile/">Strangeloop Web Performance Hub / Mobile</a></p>
<p>(odometer image via <a href="http://www.flickr.com/photos/henrybloomfield/4442900025/">henrybloomfield on Flickr</a>)</p>
]]></content:encoded>
			<wfw:commentRss>http://davidbcalhoun.com/2011/mobile-performance-manifesto/feed</wfw:commentRss>
		<slash:comments>18</slash:comments>
		</item>
		<item>
		<title>When to base64 encode images (and when not to)</title>
		<link>http://davidbcalhoun.com/2011/when-to-base64-encode-images-and-when-not-to</link>
		<comments>http://davidbcalhoun.com/2011/when-to-base64-encode-images-and-when-not-to#comments</comments>
		<pubDate>Sun, 28 Aug 2011 09:19:58 +0000</pubDate>
		<dc:creator>David</dc:creator>
				<category><![CDATA[performance]]></category>

		<guid isPermaLink="false">http://davidbcalhoun.com/?p=737</guid>
		<description><![CDATA[Introduction Ever since Steve Souders started evangelizing web performance, it&#8217;s been pounded into our heads that extra HTTP requests add a lot of additional overhead, and that we should combine them if possible to dramatically decrease the load time of our web pages. The practical implication of this has been to combine our JavaScript and [...]]]></description>
			<content:encoded><![CDATA[<h3>Introduction</h3>
<p>Ever since Steve Souders started evangelizing web performance, it&#8217;s been pounded into our heads that extra HTTP requests add a lot of additional overhead, and that we should combine them if possible to dramatically decrease the load time of our web pages.</p>
<p>The practical implication of this has been to combine our JavaScript and CSS files, which is relatively easy and straightforward, but the harder question has been what to do with images.</p>
<h4>Sprites</h4>
<p>Image sprites are a concept taken from video games: the idea is to cram a ton of image assets into one file, and rearrange a &#8220;viewport&#8221; of sorts to view only specific pieces of that file at a time.  For instance, a simple sprite that holds two images might have one viewport that only looks at the top half of the sprite (image #1), and another viewport that only looks at the bottom half (image #2).</p>
<p>On the web side of things, this means that those multiple requests have now been combined into one request.  This is nice because it saves both the overhead of additional HTTP requests as well as the overhead of setting up an image&#8217;s <a href="http://en.wikipedia.org/wiki/Portable_Network_Graphics#File_header">file header</a> each time.</p>
<p>But there&#8217;s a few drawbacks with using image sprites:</p>
<ul>
<li>hard to maintain and update: without some tool to help, manually editing and putting together image sprites is quite a chore</li>
<li><a href="http://blog.vlad1.com/2009/06/22/to-sprite-or-not-to-sprite/">increased memory consumption</a> (possibly very dramatic): this is often overlooked.  The time to deliver the images is decreased at the expense of a bigger memory and CPU footprint, especially for large sprites and sprites with a lot of whitespace.  </li>
<li>bleedthrough: for sprites that don&#8217;t have much whitespace to separate images, there&#8217;s an increased chance of nearby images visibly bleeding through other elements</li>
</ul>
<h4>Data URIs and Base64 encoding</h4>
<p>Data URIs (see <a href="http://www.phpied.com/data-urls-what-are-they-and-how-to-use/">this</a>, <a href="http://css-tricks.com/5970-data-uris/">this</a>, and <a href="http://www.nczonline.net/blog/2010/07/06/data-uris-make-css-sprites-obsolete/">this</a>) and Base64 encoding goes hand-in-hand.  This method allows you to embed images right in your HTML, CSS, or JavaScript.</p>
<p>Just like sprites, you save HTTP requests, but there&#8217;s also some drawbacks:</p>
<ul>
<li>base64 encoding makes file sizes <a href="http://en.wikipedia.org/wiki/Base64">roughly 33% larger</a> than their original binary representations, which means more data down the wire (this might be exceptionally painful on mobile networks)</li>
<li>data URIs aren&#8217;t supported on IE6 or IE7</li>
<li>base64 encoded data may possibly take longer to process than binary data (anyone want to do a study on this?) (again, this might be exceptionally painful for mobile devices, which have more limited CPU and memory) (side note: <a href="http://twitter.com/#!/stoyanstefanov/status/106605257265655809">CSS background-images seem to actually be faster than img tags</a>)</li>
</ul>
<p>The &#8220;33% larger&#8221; claim is generally accepted truth now, despite the fact that the figure varies wildly depending on the type of content.  This is exactly what I wanted to test, albeit in a pretty limited and nonscientific way.</p>
<p>Before I tested, I wanted to keep in mind a few <em>unverified intuitions</em> (which aren&#8217;t entirely my own, but seem to be ideas that are floating around out there).  Here&#8217;s a few questions I had before going to test:</p>
<ul>
<li>Is base64 encoding with gzipping roughly equal to the original filesize of the binary file?</li>
<li>Is base64 encoding best for small images?</li>
<li>Is base64 encoding best for small and simple icons and not good for pictures and photos?</li>
<li>Is base64 encoding best when multiple files are merged together?</li>
</ul>
<p>There&#8217;s something else I wanted to test: whether Gzipping binary image data made much difference.  I know text compresses well, but is it even worth compressing JPEG files with Gzip, for instance?</p>
<p>I ran three tests: one with a set of small UI icons, one with a set of small photographs, and one with a set of the same photographs in a larger size.  Though my tests were by no means extensive, they do show that care should be taken in making assumptions about base64.</p>
<p>Just a note about the tables: they are comparing the binary form (original png or jpeg) with the base64 form as it would appear in a CSS stylesheet, and comparing each of those with their gzipped form, which is most likely how they would be sent down the wire.  The CSS representation has a few practical declarations and looks something like this:</p>
<pre name="code" class="css">
.address-book--arrow {
	background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAp1JREFUeNqEU21IU1EYfu7unW5Ty6aBszYs6MeUjGVYokHYyH5E1B9rZWFEFPQnAwmy6Hc/oqhfJsRKSSZGH1JIIX3MNCsqLTD9o1Oj6ebnnDfvvefezrnbdCHhCw/n433P8z7nPe/hBEEAtX0U7hc164uwuvVSXKwZLoOmaRDim+7m9vZa0WiEKSUFFpNpCWlmMyypqTDRuYn6t3k8vmQ2gRDCxs0t9fW45F52aBTROJLtZl7nEZad2m+KtoQCQ0FBARyOCGRZ/q92I1WgqqXlfdd95VsrK8/pChIEqqpCkiQsiCII0aBQZZoWl8lzFDwsFjMl0DBLY8Lj41hBwK4jSQrWOIphL6xYyhwJDWGo6wFSaH1Y3PTCAsITE1oyAa8flhWkbSiCLX8vun11eiGIpiJ/z2nYdx5HqLdVV7elrOzsuqysL3rmBIGiKPizKCHHWY4PLVeQbnXAdegqdhy+hu8dDTBnbqQJZJ1A7u+vz7RaiymWCZgCRSF6Edk8b9cx+B/W6WuVxPaZnyiqXoPpyUmVYvkKTIFClHigEieKjYuSvETUllaF4GAUM1NT6ooaJDKx+aDfC9fByxj90REb+9ppmIoAscH/6leg8MS9DJXPAM9xHCM443K57C6biMjcHDaVVCHw9RmCA2/RGC5C00AqXk/m4p20HZK4CM/J3Zk9n0ecMBhDQnJHcrTisyMfdQXOilrdMfxcwoHq/fg5R59TiQV3hYGKo6X2J/c7LyQIjOx9GXhOw/zoJ8wEevRGyp53o/lGMNYsBgPtEwLecwov7/jGDKa1twT6o3KpL4MdZgGsWZLtfPr7f1q58k1JNHy7YYaM+J+K3Y2PmAIbRavX66229hrGVvvL5uzsHDEUvUu+NT1my78CDAAMK1a8/QaZCgAAAABJRU5ErkJggg==);
	width: 16px;
	height: 16px;
	background-repeat: no-repeat;
}
</pre>
<p>Ok, onto the tests!</p>
<h3>Test #1: Five 16&#215;16 icons from the <a href="http://p.yusukekamiyamane.com/">Fugue Icon set</a> (PNG)</h3>
<table>
<thead>
<tr>
<td>File</td>
<td>Binary</td>
<td>Binary Gzipped</td>
<td>CSS + Base64</td>
<td>CSS + Base64 Gzipped</td>
</tr>
</thead>
<tbody>
<tr>
<td>abacus</td>
<td>1443</td>
<td class="positive">1179</td>
<td>2043</td>
<td>1395</td>
</tr>
<tr>
<td>acorn</td>
<td>1770</td>
<td class="positive">1522</td>
<td>2478</td>
<td>1728</td>
</tr>
<tr>
<td>address-book&#8211;arrow</td>
<td class="positive">763</td>
<td>810</td>
<td>1153</td>
<td>948</td>
</tr>
<tr>
<td>address-book&#8211;exclamation</td>
<td class="positive">795</td>
<td>848</td>
<td>1199</td>
<td>988</td>
</tr>
<tr>
<td>address-book&#8211;minus</td>
<td class="positive">734</td>
<td>781</td>
<td>1113</td>
<td>919</td>
</tr>
<tr>
<td>Total</td>
<td>5,505</td>
<td class="positive">5,140</td>
<td>7,986</td>
<td>5,978</td>
</tr>
<tr>
<td>Combined file</td>
<td>(5,505)</td>
<td class="positive">(4,128)</td>
<td>7,986</td>
<td>4,423</td>
</tr>
</tbody>
</table>
<p>* All numbers are byte sizes<br />
** Numbers in parenthesis represent actual but impractical data.  Unfortunately, images cannot be combined and delivered together in their binary form.</p>
<p>Takeaways:</p>
<ul>
<li>The binaries are always smaller.</li>
<li>Sometimes Gzipping makes the files larger.</li>
<li>Gzipping the base64 version brings the filesize close to the size of the original binary, but this ignores the fact that the binaries get Gzipped as well.  The Gzipped binaries (how they would be delivered to the client) are always smaller than the Gzipped base64 images</li>
<li>Combining files together dramatically reduces filesizes.</li>
</ul>
<p>Practically, the developer has two options: deliver 5,140 bytes to the user in 5 separate HTTP requests, or 4,423 bytes in one HTTP request (CSS with base64 encoded image data).  Base64 is the clear winner here, and seems to confirm that small icons compress extremely well.</p>
<h3>Test #2: Five Flickr 75&#215;75 Pictures (JPEG)</h3>
<table>
<thead>
<tr>
<td>File</td>
<td>Binary</td>
<td>Binary Gzipped</td>
<td>CSS + Base64</td>
<td>CSS + Base64 Gzipped</td>
</tr>
</thead>
<tbody>
<tr>
<td><a href="http://www.flickr.com/photos/franksvalli/6064665766/sizes/sq/in/photostream/">1</a></td>
<td>6734</td>
<td class="positive">5557</td>
<td>9095</td>
<td>6010</td>
</tr>
<tr>
<td><a href="http://www.flickr.com/photos/franksvalli/6064638202/sizes/sq/in/photostream/">2</a></td>
<td>5379</td>
<td class="positive">4417</td>
<td>7287</td>
<td>4781</td>
</tr>
<tr>
<td><a href="http://www.flickr.com/photos/franksvalli/6063380824/sizes/sq/in/photostream/">3</a></td>
<td>25626</td>
<td class="positive">18387</td>
<td>34283</td>
<td>20103</td>
</tr>
<tr>
<td><a href="http://www.flickr.com/photos/franksvalli/6062803159/sizes/sq/in/photostream/">4</a></td>
<td>7031</td>
<td class="positive">6399</td>
<td>9491</td>
<td>6702</td>
</tr>
<tr>
<td><a href="http://www.flickr.com/photos/franksvalli/6017796233/sizes/sq/in/photostream/">5</a></td>
<td>5847</td>
<td class="positive">4655</td>
<td>7911</td>
<td>5077</td>
</tr>
<tr>
<td>Total</td>
<td>50,617</td>
<td class="positive">39,415</td>
<td>68,067</td>
<td>42,673</td>
</tr>
<tr>
<td>Combined file</td>
<td>(50,617)</td>
<td class="positive">(36,838)</td>
<td>68,067</td>
<td>40,312</td>
</tr>
</tbody>
</table>
<p>Takeaways:</p>
<ul>
<li>(some of the same takeaways as Test #1)</li>
<li>Separately, photos aren&#8217;t too much bigger when base64 encoded and Gzipped.  It&#8217;s very much within reason.</li>
</ul>
<p>Practically, the developer can deliver 39,415 bytes in 5 separate requests, or 40,312 in 1 request.  Not much filesize difference here, but 1 request seems preferable when we&#8217;re talking about 40kb.</p>
<h3>Test #3: Five Flickr 240&#215;160 Pictures (JPEG)</h3>
<table>
<thead>
<tr>
<td>File</td>
<td>Binary</td>
<td>Binary Gzipped</td>
<td>CSS + Base64</td>
<td>CSS + Base64 Gzipped</td>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>24502</td>
<td class="positive">23403</td>
<td>32789</td>
<td>23982</td>
</tr>
<tr>
<td>2</td>
<td>20410</td>
<td class="positive">19466</td>
<td>27333</td>
<td>19954</td>
</tr>
<tr>
<td>3</td>
<td>43833</td>
<td class="positive">36729</td>
<td>58561</td>
<td>38539</td>
</tr>
<tr>
<td>4</td>
<td>31776</td>
<td class="positive">31180</td>
<td>42485</td>
<td>31686</td>
</tr>
<tr>
<td>5</td>
<td>21348</td>
<td class="positive">20208</td>
<td>28581</td>
<td>20761</td>
</tr>
<tr>
<td>Total</td>
<td>141,869</td>
<td class="positive">130,986</td>
<td>189,749</td>
<td>134,922</td>
</tr>
<tr>
<td>Combined file</td>
<td>(141,869)</td>
<td class="positive">(129,307)</td>
<td>189,749</td>
<td>133,615</td>
</tr>
</tbody>
</table>
<p>Takeaways:</p>
<ul>
<li>(some of the same takeaways as Test #1)</li>
<li>Larger photos seem to bring the Gzipped binary and Gzipped base64 filesizes MUCH closer together, making the difference very minimal</li>
</ul>
<p>The developer must choose between delivering 130,986 bytes in 5 HTTP requests, or 133,615 bytes in one HTTP request.  Any good Souders follower would opt for the one request, BUT I would be careful here&#8230;</p>
<h4>Caution: things aren&#8217;t always as they seem</h4>
<p>There&#8217;s a huge caveat here: it may actually be more beneficial for perceived performance to deliver the images in 5 separate requests.</p>
<p>Why?  Because 133,615 bytes is a lot to deliver all in one package to an end user who will be staring at blank placeholders for the duration.  If the 5 base64 images all come in one request, that request will have to complete before ANYTHING is shown on the screen.  All 5 images go from blank placeholders to almost immediately decoded from base64 and displayed in place.</p>
<p>Compare this with 5 requests that are most likely made in parallel and actually give a visual indicator to the user that actual image content is being downloaded, by showing parts of the images as they&#8217;re downloaded (you can also try a throwback to progressive JPEGs &#8211; really anything will be better than just a blank screen).  That&#8217;s why it might actually be beneficial for <em>perceived</em> performance to just load images in the good old fashioned way.  They will most likely load in parallel anyway, so the extra HTTP requests may actually not really make a difference.  Not to mention it will be easier to let the browser manage the cache for each file instead of having to make your JavaScript manage your cache and prevent you from downloading an image that&#8217;s already stored away in localStorage or sessionStorage.</p>
<p>This being said, it&#8217;s generally advisable to put your common UI icons in base64 in your CSS, then let that whole chunk get cached by the browser.  Those are usually clean vector icons as well, which seem to get compressed quite well (see Test #1).</p>
<p>But for image content, where there is nothing to be saved but HTTP requests, you should definitely think twice about base64 encoding to save requests.  Yes, you will save a few HTTP requests, you won&#8217;t really be saving bytes, and the user might actually think the experience is slower because they can&#8217;t see the image content as it&#8217;s being downloaded.  Even if you shave off a few milliseconds of wait time, the <em>perceived</em> performance is what matters most.</p>
<p>(EDIT: changed the wording of the &#8220;unverified intuitions&#8221; section from &#8220;not verified&#8221; to actual questions, to make it clearer)</p>
]]></content:encoded>
			<wfw:commentRss>http://davidbcalhoun.com/2011/when-to-base64-encode-images-and-when-not-to/feed</wfw:commentRss>
		<slash:comments>14</slash:comments>
		</item>
		<item>
		<title>JavaScript SunSpider benchmark: iOS 4.3 vs iOS 4.0</title>
		<link>http://davidbcalhoun.com/2011/javascript-sunspider-benchmark-ios-4-3-vs-ios-4-0</link>
		<comments>http://davidbcalhoun.com/2011/javascript-sunspider-benchmark-ios-4-3-vs-ios-4-0#comments</comments>
		<pubDate>Sat, 22 Jan 2011 08:18:04 +0000</pubDate>
		<dc:creator>David</dc:creator>
				<category><![CDATA[performance]]></category>

		<guid isPermaLink="false">http://davidbcalhoun.com/?p=614</guid>
		<description><![CDATA[Result table (SunSpider 0.9.1) Test iOS 4 (3GS) iOS 4.3 beta 2 (3GS) % change Total 13787ms 5357ms -61.1% 3D 1917ms 737ms -61.6% Access 1893ms 617ms -67.4% Bitops 1239ms 289ms -76.7% Controlflow 221ms 35ms -84.2% Crypto 850ms 308ms -63.8% Date 1065ms 706ms -33.7% Math 1511ms 606ms -59.9% Regexp 1916ms 153ms -92.0% String 3175ms 1907ms -39.9% [...]]]></description>
			<content:encoded><![CDATA[<h3>Result table (SunSpider 0.9.1)</h3>
<table>
<thead>
<tr>
<td>Test</td>
<td>iOS 4 (3GS)</td>
<td>iOS 4.3 beta 2 (3GS)</td>
<td>% change</td>
</tr>
</thead>
<tfoot></tfoot>
<tbody>
<tr>
<td>Total</td>
<td>13787ms</td>
<td class="positive">5357ms</td>
<td class="positive">-61.1%</td>
</tr>
<tr>
<td>3D</td>
<td>1917ms</td>
<td class="positive">737ms</td>
<td class="positive">-61.6%</td>
</tr>
<tr>
<td>Access</td>
<td>1893ms</td>
<td class="positive">617ms</td>
<td class="positive">-67.4%</td>
</tr>
<tr>
<td>Bitops</td>
<td>1239ms</td>
<td class="positive">289ms</td>
<td class="positive">-76.7%</td>
</tr>
<tr>
<td>Controlflow</td>
<td>221ms</td>
<td class="positive">35ms</td>
<td class="positive">-84.2%</td>
</tr>
<tr>
<td>Crypto</td>
<td>850ms</td>
<td class="positive">308ms</td>
<td class="positive">-63.8%</td>
</tr>
<tr>
<td>Date</td>
<td>1065ms</td>
<td class="positive">706ms</td>
<td class="positive">-33.7%</td>
</tr>
<tr>
<td>Math</td>
<td>1511ms</td>
<td class="positive">606ms</td>
<td class="positive">-59.9%</td>
</tr>
<tr>
<td>Regexp</td>
<td>1916ms</td>
<td class="positive">153ms</td>
<td class="positive">-92.0%</td>
</tr>
<tr>
<td>String</td>
<td>3175ms</td>
<td class="positive">1907ms</td>
<td class="positive">-39.9%</td>
</tr>
</tbody>
</table>
<h3>Raw results (iOS 4.3 beta 2 running on my iPhone 3GS)</h3>
<pre>
============================================
RESULTS (means and 95% confidence intervals)
--------------------------------------------
Total:                  5357.3ms +/- 2.3%
--------------------------------------------

  3d:                    736.9ms +/- 7.5%
    cube:                260.1ms +/- 3.5%
    morph:               206.0ms +/- 23.2%
    raytrace:            270.8ms +/- 4.4%

  access:                617.4ms +/- 4.1%
    binary-trees:        118.8ms +/- 4.7%
    fannkuch:            180.4ms +/- 0.7%
    nbody:               222.0ms +/- 11.7%
    nsieve:               96.2ms +/- 0.8%

  bitops:                288.7ms +/- 9.6%
    3bit-bits-in-byte:    38.0ms +/- 4.3%
    bits-in-byte:         70.5ms +/- 39.0%
    bitwise-and:          85.1ms +/- 1.9%
    nsieve-bits:          95.1ms +/- 2.1%

  controlflow:            34.7ms +/- 3.2%
    recursive:            34.7ms +/- 3.2%

  crypto:                308.3ms +/- 1.1%
    aes:                 175.2ms +/- 0.5%
    md5:                  79.3ms +/- 3.9%
    sha1:                 53.8ms +/- 0.8%

  date:                  706.2ms +/- 5.5%
    format-tofte:        341.3ms +/- 11.0%
    format-xparb:        364.9ms +/- 1.1%

  math:                  605.9ms +/- 1.1%
    cordic:              224.5ms +/- 1.0%
    partial-sums:        263.4ms +/- 2.3%
    spectral-norm:       118.0ms +/- 1.2%

  regexp:                152.5ms +/- 5.5%
    dna:                 152.5ms +/- 5.5%

  string:               1906.7ms +/- 0.7%
    base64:              247.9ms +/- 2.4%
    fasta:               268.5ms +/- 0.6%
    tagcloud:            370.4ms +/- 3.0%
    unpack-code:         676.2ms +/- 0.3%
    validate-input:      343.7ms +/- 1.4%
</pre>
<p><a href="http://davidbcalhoun.com/wp-content/uploads/2010/iOS-4-GM.html">Raw results for iOS 4.0</a></p>
<h3>Related</h3>
<p><a href="http://davidbcalhoun.com/2010/blackberry-torch-sunspider-results-javascript-benchmark">BlackBerry Torch SunSpider results (JavaScript benchmark)</a><br />
<a href="http://davidbcalhoun.com/2010/iphone-4-sunspider-test-results">iPhone 4 SunSpider test results (22% faster than iPhone 3GS)</a><br />
<a href="http://davidbcalhoun.com/2010/sunspider-ios-3-1-3-versus-ios-4-gm">JavaScript SunSpider test: iOS 3.1.3 versus iOS 4 GM</a><br />
<a href="http://davidbcalhoun.com/2010/javascript-sunspider-htc-evo-versus-htc-incredible-versus-nexus-one">JavaScript SunSpider: HTC Evo versus HTC Incredible versus Nexus One</a></p>
]]></content:encoded>
			<wfw:commentRss>http://davidbcalhoun.com/2011/javascript-sunspider-benchmark-ios-4-3-vs-ios-4-0/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>BlackBerry Torch SunSpider results (JavaScript benchmark)</title>
		<link>http://davidbcalhoun.com/2010/blackberry-torch-sunspider-results-javascript-benchmark</link>
		<comments>http://davidbcalhoun.com/2010/blackberry-torch-sunspider-results-javascript-benchmark#comments</comments>
		<pubDate>Wed, 18 Aug 2010 21:54:26 +0000</pubDate>
		<dc:creator>David</dc:creator>
				<category><![CDATA[performance]]></category>
		<category><![CDATA[blackberry]]></category>
		<category><![CDATA[sunspider]]></category>
		<category><![CDATA[webkit]]></category>

		<guid isPermaLink="false">http://davidbcalhoun.com/?p=402</guid>
		<description><![CDATA[Results ============================================ RESULTS (means and 95% confidence intervals) -------------------------------------------- Total: 322.2ms +/- 4.9% -------------------------------------------- 3d: 55.0ms +/- 15.3% cube: 19.8ms +/- 12.1% morph: 16.6ms +/- 35.8% raytrace: 18.6ms +/- 22.5% access: 32.8ms +/- 12.4% binary-trees: 1.8ms +/- 30.9% fannkuch: 14.2ms +/- 7.3% nbody: 12.6ms +/- 26.7% nsieve: 4.2ms +/- 24.8% bitops: 29.4ms +/- 10.2% 3bit-bits-in-byte: [...]]]></description>
			<content:encoded><![CDATA[<h3>Results</h3>
<pre>
============================================
RESULTS (means and 95% confidence intervals)
--------------------------------------------
Total:                 322.2ms +/- 4.9%
--------------------------------------------

  3d:                   55.0ms +/- 15.3%
    cube:               19.8ms +/- 12.1%
    morph:              16.6ms +/- 35.8%
    raytrace:           18.6ms +/- 22.5%

  access:               32.8ms +/- 12.4%
    binary-trees:        1.8ms +/- 30.9%
    fannkuch:           14.2ms +/- 7.3%
    nbody:              12.6ms +/- 26.7%
    nsieve:              4.2ms +/- 24.8%

  bitops:               29.4ms +/- 10.2%
    3bit-bits-in-byte:   2.4ms +/- 28.4%
    bits-in-byte:        8.0ms +/- 15.5%
    bitwise-and:         8.6ms +/- 21.9%
    nsieve-bits:        10.4ms +/- 21.7%

  controlflow:           2.4ms +/- 28.4%
    recursive:           2.4ms +/- 28.4%

  crypto:               22.0ms +/- 10.6%
    aes:                 9.8ms +/- 27.5%
    md5:                 6.2ms +/- 22.0%
    sha1:                6.0ms +/- 20.7%

  date:                 33.2ms +/- 11.7%
    format-tofte:       16.6ms +/- 20.2%
    format-xparb:       16.6ms +/- 13.6%

  math:                 32.6ms +/- 15.2%
    cordic:             12.0ms +/- 29.3%
    partial-sums:       15.2ms +/- 20.4%
    spectral-norm:       5.4ms +/- 20.6%

  regexp:               15.6ms +/- 7.1%
    dna:                15.6ms +/- 7.1%

  string:               99.2ms +/- 14.0%
    base64:              9.2ms +/- 14.8%
    fasta:              14.4ms +/- 27.7%
    tagcloud:           27.2ms +/- 23.2%
    unpack-code:        31.8ms +/- 14.5%
    validate-input:     16.6ms +/- 18.0%
</pre>
<h3>Related articles</h3>
<p><a href="http://davidbcalhoun.com/2010/iphone-4-sunspider-test-results">iPhone 4 SunSpider test results (22% faster than iPhone 3GS)</a><br />
<a href="http://davidbcalhoun.com/2010/sunspider-ios-3-1-3-versus-ios-4-gm">JavaScript SunSpider test: iOS 3.1.3 versus iOS 4 GM</a><br />
<a href="http://davidbcalhoun.com/2010/javascript-sunspider-htc-evo-versus-htc-incredible-versus-nexus-one">JavaScript SunSpider: HTC Evo versus HTC Incredible versus Nexus One</a><br />
<a href="http://www.sencha.com/blog/2010/08/18/blackberry-torch-the-html5-developer-scorecard/">Sencha: BlackBerry Torch: The HTML5 Developer Scorecard</a></p>
]]></content:encoded>
			<wfw:commentRss>http://davidbcalhoun.com/2010/blackberry-torch-sunspider-results-javascript-benchmark/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Is a hash faster than a Switch in JavaScript?</title>
		<link>http://davidbcalhoun.com/2010/is-hash-faster-than-switch-in-javascript</link>
		<comments>http://davidbcalhoun.com/2010/is-hash-faster-than-switch-in-javascript#comments</comments>
		<pubDate>Tue, 17 Aug 2010 08:32:02 +0000</pubDate>
		<dc:creator>David</dc:creator>
				<category><![CDATA[performance]]></category>
		<category><![CDATA[android]]></category>
		<category><![CDATA[hash]]></category>
		<category><![CDATA[jsperf]]></category>
		<category><![CDATA[mobile safari]]></category>
		<category><![CDATA[switch]]></category>

		<guid isPermaLink="false">http://davidbcalhoun.com/?p=392</guid>
		<description><![CDATA[I stumbled across this concept recently and I thought I&#8217;d share it, because I don&#8217;t generally see this pattern being used. More importantly, I also share test results that show that maybe it&#8217;s not always a good idea to use this pattern&#8230; The problem with Switch statements The basic switch statement in JavaScript looks something [...]]]></description>
			<content:encoded><![CDATA[<p>I stumbled across this concept recently and I thought I&#8217;d share it, because I don&#8217;t generally see this pattern being used.  More importantly, I also share test results that show that maybe it&#8217;s not always a good idea to use this pattern&#8230;</p>
<h3>The problem with Switch statements</h3>
<p>The basic switch statement in JavaScript looks something like this:</p>
<pre name="code" class="JScript">
var foo = 'c';

switch(foo) {
  case 'a':
  break;

  case 'b':
  break;

  case 'c':
  break;

  default:
}
</pre>
<p>So what&#8217;s wrong with this?  The JS engine has to examine a bunch of unrelated cases until it finds the relevant one, executes the code, then breaks out of the switch because the job is done (this is why it&#8217;s important to break!).  In the above example we had to go through case A and case B until finally reaching case C.  What&#8217;s worse is that if it didn&#8217;t match any of these cases, the JS engine has to jump through ALL of the cases before it reaches Default, the fall-through case.</p>
<p>Actually it&#8217;s not so bad, as long as there are a limited number of cases.  It&#8217;s probably no big deal if you only have a few cases to jump through.  The problem gets bigger as your number of cases increases (some of you may know this as <a href="http://en.wikipedia.org/wiki/Big_O_notation">O(n)</a>).  What happens when there&#8217;s 10 cases?  Then there&#8217;s potentially 10 checks on cases (assuming what ended up being executed was the default).  100 cases?  Then potentially 100 checks.</p>
<p>What would be better is if there were a way to reduce the number of checks.  One way would be to put the most frequently used cases at the top.  This would alleviate some of the pain, but you still end up with extra processing while the JS checks each case.  It would be ideal to avoid this extra processing altogether.</p>
<h3>An alternative: The hash table</h3>
<p>There is a way to avoid this extra processing!  It&#8217;s by leading the code directly where it needs to go, without unnecessary checking of unrelated cases.</p>
<p>You can do this using a hash.  In JavaScript we accomplish this with an object:</p>
<pre name="code" class="JScript">
var foo = 'c';
var cases = {};
cases['a'] = function() {
  alert('I am A!');
};
cases['b'] = function() {
  alert('I am B!');
};
cases['c'] = function() {
  alert('I am C!');
}

if(typeof cases[foo] == 'function') {
  // only executes if we've defined it above
  cases[foo]();  // I am C!
} else {
  // default (the fallthrough)
}
</pre>
<p>There we go!  No extra case checking here.  We&#8217;ve led the JS straight to the code we want to execute!</p>
<h3>Performance improvement&#8230;?</h3>
<p>So.. this hash lookup seems faster in theory, but what about in practice?  Unfortunately I ended up with some mixed results&#8230;</p>
<p>I created a <a href="http://jsperf.com/switch-vs-hash/3">simple performance test on jsperf.com</a> and got these results:</p>
<table>
<thead>
<tr>
<td>Browser</td>
<td>Switch ops/sec</td>
<td>Hash table ops/sec</td>
<td>% Difference</td>
</tr>
</thead>
<tfoot></tfoot>
<tbody>
<tr>
<td>Chrome 6.0.490.1 dev</td>
<td class="negative">34,606,469</td>
<td class="positive">43,329,587</td>
<td class="positive">25% faster</td>
</tr>
<tr>
<td>Safari 5.0</td>
<td class="positive">16,777,216</td>
<td class="negative">10,854,824</td>
<td class="negative">35% slower</td>
</tr>
<tr>
<td>Opera 10.61</td>
<td class="positive">4,405,782</td>
<td class="negative">2,719,336</td>
<td class="negative">38% slower</td>
</tr>
<tr>
<td>Firefox 3.6.3</td>
<td class="positive">2,785,802</td>
<td class="negative">2,400,586</td>
<td class="negative">14% slower</td>
</tr>
<tr>
<td>IE6</td>
<td class="negative">147,870</td>
<td class="positive">206,869</td>
<td class="positive">40% faster</td>
</tr>
<tr>
<td>IE7</td>
<td class="negative">144,735</td>
<td class="positive">191,179</td>
<td class="positive">32% faster</td>
</tr>
<tr>
<td>IE8</td>
<td class="negative">350,085</td>
<td class="positive">472,417</td>
<td class="positive">35% faster</td>
</tr>
<tr>
<td>Mobile Safari (iOS4 on iPhone 3GS)</td>
<td class="positive">668,053</td>
<td class="negative">416,366</td>
<td class="negative">37% slower</td>
</tr>
<tr>
<td>Android (2.2 on Nexus One)</td>
<td class="negative">605,693</td>
<td class="positive">864,591</td>
<td class="positive">42% faster</td>
</tr>
</tbody>
</table>
<p>* Ops/sec = Operations per second.  Higher is better<br />
** Chrome, Safari, Opera, and Firefox were tested on Mac OSX 10.6.4 2.53GHz Intel Core i5.  IE tests were run on Windows 7 64bit 2.4GHz Quad Core</p>
<h3>The Results</h3>
<p>From the results, it looks like the hash optimization is only a benefit for Chrome, IE6-IE8, and Android.  That&#8217;s quite a specific sampling.  My guess is that the other browsers have implemented some sort of Switch statement optimizations that actually turn the hash optimization into an antipattern.</p>
<h3>More info</h3>
<p>Although I first read about this online, by no surprise this trick also appears in <a href="http://www.nczonline.net/">Nicholas Zakas&#8217;s</a> <a href="http://www.amazon.com/Performance-JavaScript-Faster-Application-Interfaces/dp/059680279X">High Performance JavaScript</a> in a section on &#8220;Lookup Tables&#8221; (p. 72).</p>
]]></content:encoded>
			<wfw:commentRss>http://davidbcalhoun.com/2010/is-hash-faster-than-switch-in-javascript/feed</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>JavaScript SunSpider: HTC Evo versus HTC Incredible versus Nexus One</title>
		<link>http://davidbcalhoun.com/2010/javascript-sunspider-htc-evo-versus-htc-incredible-versus-nexus-one</link>
		<comments>http://davidbcalhoun.com/2010/javascript-sunspider-htc-evo-versus-htc-incredible-versus-nexus-one#comments</comments>
		<pubDate>Fri, 11 Jun 2010 05:07:11 +0000</pubDate>
		<dc:creator>David</dc:creator>
				<category><![CDATA[performance]]></category>
		<category><![CDATA[htc evo]]></category>
		<category><![CDATA[htc incredible]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[mobile]]></category>
		<category><![CDATA[nexus one]]></category>
		<category><![CDATA[sunspider]]></category>

		<guid isPermaLink="false">http://davidbcalhoun.com/?p=292</guid>
		<description><![CDATA[Result table Test Evo (2.1) Incredible (2.1) Nexus One (2.2) Total 16167ms 15237ms 5452ms 3D 2014ms 1835ms 946ms Access 2126ms 1892ms 463ms Bitops 1519ms 1591ms 360ms Controlflow 243ms 206ms 20ms Crypto 1033ms 1003ms 344ms Date 1849ms 1896ms 639ms Math 1684ms 1419ms 602ms Regexp 1779ms 1673ms 155ms String 3920ms 3722ms 1923ms Thoughts The Incredible is just [...]]]></description>
			<content:encoded><![CDATA[<h3>Result table</h3>
<table>
<thead>
<tr>
<td>Test</td>
<td>Evo (2.1)</td>
<td>Incredible (2.1)</td>
<td>Nexus One (2.2)</td>
</tr>
</thead>
<tfoot></tfoot>
<tbody>
<tr>
<td>Total</td>
<td>16167ms</td>
<td>15237ms</td>
<td class="positive">5452ms</td>
</tr>
<tr>
<td>3D</td>
<td>2014ms</td>
<td>1835ms</td>
<td class="positive">946ms</td>
</tr>
<tr>
<td>Access</td>
<td>2126ms</td>
<td>1892ms</td>
<td class="positive">463ms</td>
</tr>
<tr>
<td>Bitops</td>
<td>1519ms</td>
<td>1591ms</td>
<td class="positive">360ms</td>
</tr>
<tr>
<td>Controlflow</td>
<td>243ms</td>
<td>206ms</td>
<td class="positive">20ms</td>
</tr>
<tr>
<td>Crypto</td>
<td>1033ms</td>
<td>1003ms</td>
<td class="positive">344ms</td>
</tr>
<tr>
<td>Date</td>
<td>1849ms</td>
<td>1896ms</td>
<td class="positive">639ms</td>
</tr>
<tr>
<td>Math</td>
<td>1684ms</td>
<td>1419ms</td>
<td class="positive">602ms</td>
</tr>
<tr>
<td>Regexp</td>
<td>1779ms</td>
<td>1673ms</td>
<td class="positive">155ms</td>
</tr>
<tr>
<td>String</td>
<td>3920ms</td>
<td>3722ms</td>
<td class="positive">1923ms</td>
</tr>
</tbody>
</table>
<h3>Thoughts</h3>
<p>The Incredible is just slightly faster than the Evo, to the point where it&#8217;s probably negligible or within a margin of error.  Both of these phones run on Android 2.1 with HTC&#8217;s Sense UI modifications, and represent the latest and greatest in Android phones available on the market today.  Both run on the same 1GHz Snapdragon processor (QSD8650). The Nexus One is a bit older, and runs with an older version of the Snapdragon processor (QSD8250), however it still runs at 1GHz just like the other two phones.</p>
<p>As you can see the Nexus One blows away all the competition because it&#8217;s running Android 2.2 Froyo.  These results were quite a shock to me and are quite impressive.  These results even blow away Apple&#8217;s new iOS 4 running on my iPhone 3GS, which clocked in at a total time of 13787ms compared to the Nexus One&#8217;s startling 5452ms.</p>
<h3>Testing methodology</h3>
<p>Test: <a href="http://www2.webkit.org/perf/sunspider-0.9.1/sunspider-0.9.1/driver.html">SunSpider 0.9.1</a></p>
<p>Devices: HTC Evo (Android 2.1), HTC Incredible (Android 2.1), HTC Nexus One (Android 2.2)</p>
<p>The SunSpider test was run five times on each phone.  The phone was completely turned off and on before each test.  The most extreme values of the five tests were thrown out, and the resulting four tests were averaged (sometimes from three tests when the values were very close together).</p>
<p>Raw results:</p>
<p><a href="http://davidbcalhoun.com/wp-content/uploads/2010/sunspider-htc-evo.html">SunSpider HTC Evo results (5 tests)</a></p>
<p><a href="http://davidbcalhoun.com/wp-content/uploads/2010/sunspider-htc-incredible.html">SunSpider HTC Incredible results (5 tests)</a></p>
<p><a href="http://davidbcalhoun.com/wp-content/uploads/2010/sunspider-nexus-one.html">SunSpider HTC Nexus One results (5 tests)</a></p>
<h3>Related links</h3>
<p><a href="http://davidbcalhoun.com/2010/sunspider-ios-3-1-3-versus-ios-4-gm">JavaScript SunSpider test: iOS 3.1.3 versus iOS 4 GM</a></p>
]]></content:encoded>
			<wfw:commentRss>http://davidbcalhoun.com/2010/javascript-sunspider-htc-evo-versus-htc-incredible-versus-nexus-one/feed</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>JavaScript SunSpider test: iOS 3.1.3 versus iOS 4 GM</title>
		<link>http://davidbcalhoun.com/2010/sunspider-ios-3-1-3-versus-ios-4-gm</link>
		<comments>http://davidbcalhoun.com/2010/sunspider-ios-3-1-3-versus-ios-4-gm#comments</comments>
		<pubDate>Thu, 10 Jun 2010 09:50:38 +0000</pubDate>
		<dc:creator>David</dc:creator>
				<category><![CDATA[performance]]></category>
		<category><![CDATA[ios]]></category>
		<category><![CDATA[iphone]]></category>
		<category><![CDATA[sunspider]]></category>

		<guid isPermaLink="false">http://davidbcalhoun.com/?p=281</guid>
		<description><![CDATA[Result table Test iOS 3.1.3 (3GS) iOS 4 GM (3GS) % change Total 15396ms 13787ms -10.5% 3D 2411ms 1917ms -20.5% Access 1884ms 1893ms +0.5% Bitops 1044ms 1239ms +18.7% Controlflow 143ms 221ms +54.5% Crypto 982ms 850ms -13.4% Date 1355ms 1065ms -21.4% Math 2053ms 1511ms -26.4% Regexp 1616ms 1916ms +18.6% String 3908ms 3175ms -18.8% Thoughts After running [...]]]></description>
			<content:encoded><![CDATA[<h3>Result table</h3>
<table>
<thead>
<tr>
<td>Test</td>
<td>iOS 3.1.3 (3GS)</td>
<td>iOS 4 GM (3GS)</td>
<td>% change</td>
</tr>
</thead>
<tfoot></tfoot>
<tbody>
<tr>
<td>Total</td>
<td>15396ms</td>
<td>13787ms</td>
<td class="positive">-10.5%</td>
</tr>
<tr>
<td>3D</td>
<td>2411ms</td>
<td>1917ms</td>
<td class="positive">-20.5%</td>
</tr>
<tr>
<td>Access</td>
<td>1884ms</td>
<td>1893ms</td>
<td class="negative">+0.5%</td>
</tr>
<tr>
<td>Bitops</td>
<td>1044ms</td>
<td>1239ms</td>
<td class="negative">+18.7%</td>
</tr>
<tr>
<td>Controlflow</td>
<td>143ms</td>
<td>221ms</td>
<td class="negative">+54.5%</td>
</tr>
<tr>
<td>Crypto</td>
<td>982ms</td>
<td>850ms</td>
<td class="positive">-13.4%</td>
</tr>
<tr>
<td>Date</td>
<td>1355ms</td>
<td>1065ms</td>
<td class="positive">-21.4%</td>
</tr>
<tr>
<td>Math</td>
<td>2053ms</td>
<td>1511ms</td>
<td class="positive">-26.4%</td>
</tr>
<tr>
<td>Regexp</td>
<td>1616ms</td>
<td>1916ms</td>
<td class="negative">+18.6%</td>
</tr>
<tr>
<td>String</td>
<td>3908ms</td>
<td>3175ms</td>
<td class="positive">-18.8%</td>
</tr>
</tbody>
</table>
<h3>Thoughts</h3>
<p>After running these SunSpider tests, it looks like overall there&#8217;s significant speed gains between iOS 3.1.3 and iOS 4 GM.  However, it&#8217;s concerning from these tests there were some things that actually ran <em>slower</em> on iOS 4.  This either represents a real speed loss between the versions, a margin of error, or some flaw or inconsistency while testing.  Or maybe I possibly have some wrong setting on my phone?  Any input would be appreciated.</p>
<h3>Testing methodology</h3>
<p>Test: <a href="http://www2.webkit.org/perf/sunspider-0.9.1/sunspider-0.9.1/driver.html">SunSpider 0.9.1</a></p>
<p>Device: iPhone 3GS</p>
<p>The test was run five separate times on the same phone for each version of the OS.  The phone was completely turned off and on before each test.</p>
<p>The most extreme values of the five tests were thrown out, and the resulting four tests were averaged (sometimes from three tests when the values were very close together).  I&#8217;m no statistics expert, so if you&#8217;d like to work it out for yourself, here are my raw test results:</p>
<p><a href="http://davidbcalhoun.com/wp-content/uploads/2010/iOS-3.1.3.html">SunSpider iOS 3.1.3 results (5 tests)</a></p>
<p><a href="http://davidbcalhoun.com/wp-content/uploads/2010/iOS-4-GM.html">SunSpider iOS 4 results (5 tests)</a></p>
<h3>Related links</h3>
<p><a href="http://www.medialets.com/blog/2009/06/24/speed-test-iphone-3gs-even-faster-than-apple-claims/">Speed Test: iPhone 3GS Even Faster than Apple Claims</a></p>
]]></content:encoded>
			<wfw:commentRss>http://davidbcalhoun.com/2010/sunspider-ios-3-1-3-versus-ios-4-gm/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Video: John Resig &#8211; Testing, Performance Analysis, and jQuery 1.4</title>
		<link>http://davidbcalhoun.com/2009/video-john-resig-testing-performance-analysis-and-jquery-1-4</link>
		<comments>http://davidbcalhoun.com/2009/video-john-resig-testing-performance-analysis-and-jquery-1-4#comments</comments>
		<pubDate>Tue, 22 Dec 2009 03:54:01 +0000</pubDate>
		<dc:creator>David</dc:creator>
				<category><![CDATA[javascript]]></category>
		<category><![CDATA[performance]]></category>
		<category><![CDATA[testing]]></category>
		<category><![CDATA[video]]></category>
		<category><![CDATA[john resig]]></category>
		<category><![CDATA[yahoo]]></category>

		<guid isPermaLink="false">http://davidbcalhoun.com/?p=78</guid>
		<description><![CDATA[In case you hadn&#8217;t seen it yet, John Resig was kind enough to stop by Yahoo! for the December Bayjax meetup. Here&#8217;s the video: Usually developers are more interested in just getting the dang code to work, and as a result actual the testing and maintenance of JavaScript isn&#8217;t talked about too much, so I&#8217;m [...]]]></description>
			<content:encoded><![CDATA[<p>In case you hadn&#8217;t seen it yet, <a href="http://ejohn.org/">John Resig</a> was kind enough to stop by Yahoo! for the December <a href="http://www.meetup.com/BayJax/">Bayjax</a> meetup.  Here&#8217;s the <a href="http://developer.yahoo.com/yui/theater/video.php?v=resig-testing">video</a>:</p>
<div class="video" style="width:576px; height: 324px;"><object width="576" height="324"><param name="movie" value="http://d.yimg.com/m/up/ypp/default/player.swf"></param><param name="flashVars" value="vid=17156941&#038;"></param><param name="allowfullscreen" value="true"></param><param name="wmode" value="transparent"></param><embed width="576" height="324" allowFullScreen="true" src="http://d.yimg.com/m/up/ypp/default/player.swf" type="application/x-shockwave-flash" flashvars="vid=17156941&#038;"></embed></object></div>
<p>Usually developers are more interested in just getting the dang code to work, and as a result actual the testing and maintenance of JavaScript isn&#8217;t talked about too much, so I&#8217;m sure this will be new territory for many developers.  And since it&#8217;s John Resig speaking, there was of course a bit about using <a href="http://testswarm.com/">TestSwarm</a>, a testing distributed framework-agnostic automated testing tool (that&#8217;s a mouthful!).</p>
<p>Included in the talk are good things to note while testing, such as the fact unless you&#8217;re running Firefox or Chrome on Windows, all test times have a margin of error of up to 15ms (not to be confused with <a href="http://www.quirksmode.org/blog/archives/2009/08/when_to_read_ou.html">PPK&#8217;s observation of the delay between JavaScript computation and browser rendering</a>).</p>
<p>(via <a href="http://www.yuiblog.com/blog/2009/12/16/video-resig-testing-and-jquery/">YUIBlog</a>)</p>
]]></content:encoded>
			<wfw:commentRss>http://davidbcalhoun.com/2009/video-john-resig-testing-performance-analysis-and-jquery-1-4/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

