<?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>Code: Flickr Developer Blog &#187; frontend</title>
	<atom:link href="http://code.flickr.com/blog/tag/frontend/feed/" rel="self" type="application/rss+xml" />
	<link>http://code.flickr.com/blog</link>
	<description>Just another WordPress weblog</description>
	<lastBuildDate>Thu, 19 Nov 2009 15:59:29 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8.6</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Building Fast Client-side Searches</title>
		<link>http://code.flickr.com/blog/2009/03/18/building-fast-client-side-searches/</link>
		<comments>http://code.flickr.com/blog/2009/03/18/building-fast-client-side-searches/#comments</comments>
		<pubDate>Wed, 18 Mar 2009 15:59:42 +0000</pubDate>
		<dc:creator>ross</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[frontend]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[optimization]]></category>
		<category><![CDATA[yui]]></category>

		<guid isPermaLink="false">http://code.flickr.com/blog/?p=703</guid>
		<description><![CDATA[Yesterday we released a new people selector widget (which we’ve been calling Bo Selecta internally). This widget downloads a list of all of your contacts, in JavaScript, in under 200ms (this is true even for members with 10,000+ contacts). In order to get this level of performance, we had to completely rethink how we send [...]]]></description>
			<content:encoded><![CDATA[<p>Yesterday we released <a href="http://blog.flickr.net/en/2009/03/17/find-people-faster/">a new people selector widget</a> (which we’ve been calling Bo Selecta internally). This widget downloads a list of all of your contacts, in JavaScript, in under 200ms (this is true even for members with 10,000+ contacts). In order to get this level of performance, we had to completely rethink how we send data from the server to the client.</p>
<h2 style="margin-bottom: 12px;">Server Side: Cache Everything</h2>
<p>To make this data available quickly from the server, we maintain and update a per-member cache in our database, where we store each member&#8217;s contact list in a text blob — this way it’s a single quick DB query to retrieve it. We can format this blob in any way we want: XML, JSON, etc. Whenever a member updates their information, we update the cache for all of their contacts. Since a single member who changes their contact information can require updating the contacts cache for hundreds or even thousands of other members, we rely upon prioritized tasks in <a href="http://code.flickr.com/blog/2008/09/26/flickr-engineers-do-it-offline/">our offline queue system</a>.</p>
<h2 style="margin-bottom: 12px;">Testing the Performance of Different Data Formats</h2>
<p>Despite the fact that our backend system can deliver the contact list data very quickly, we still don’t want to unnecessarily fetch it for each page load. This means that we need to defer loading until it&#8217;s needed, and that we have to be able to request, download, and parse the contact list in the amount of time it takes a member to go from hovering over a text field to typing a name.</p>
<p>With this goal in mind, we started testing various data formats, and recording the average amount of time it took to download and parse each one. We started with Ajax and XML; this proved to be the slowest by far, so much so that the larger test cases wouldn&#8217;t even run to completion (the tags used to create the XML structure also added a lot of weight to the filesize). It appeared that using XML was out of the question. </p>
<h2 style="margin-bottom: 12px;">BoSelectaJsonGoodFunTimes: eval() is Slow</h2>
<p><a href="http://www.flickr.com/photos/dchousegrooves/2539003507/" title="DJ Bo Selecta on the decks by dchousegrooves, on Flickr"><img src="http://farm4.static.flickr.com/3020/2539003507_02728b3e80.jpg" width="500" height="375" alt="DJ Bo Selecta on the decks" /></a></p>
<p>Next we tried using Ajax to fetch the list in the JSON format (and having eval() parse it). This was a major improvement, both in terms of filesize across the wire and parse time.</p>
<p>While all of our tests ran to completion (even the 10,000 contacts case), parse time per contact was not the same for each case; it geometrically increased as we increased the number of contacts, up to the point where the 10,000 contact case took over 80 seconds to parse — 400 times slower than our goal of 200ms. It seemed that JavaScript had a problem manipulating and eval()ing very large strings, so this approach wasn’t going to work either.</p>
<table class="data-table">
<tbody>
<tr class="header">
<th>Contacts</th>
<th>File Size (KB)</th>
<th>Parse Time (ms)</th>
<th>File Size per Contact (KB)</th>
<th>Parse Time per Contact (ms)</th>
</tr>
<tr>
<td class="s4">10,617</td>
<td class="s5">1536</td>
<td class="s5">81312</td>
<td class="s6">0.14</td>
<td class="s7">7.66</td>
</tr>
<tr class="odd">
<td class="s8">4,878</td>
<td class="s9">681</td>
<td class="s9">18842</td>
<td class="s10">0.14</td>
<td class="s11">3.86</td>
</tr>
<tr>
<td class="s8">2,979</td>
<td class="s9">393</td>
<td class="s9">6987</td>
<td class="s10">0.13</td>
<td class="s11">2.35</td>
</tr>
<tr class="odd">
<td class="s8">1,914</td>
<td class="s9">263</td>
<td class="s9">3381</td>
<td class="s10">0.14</td>
<td class="s11">1.77</td>
</tr>
<tr>
<td class="s8">1,363</td>
<td class="s9">177</td>
<td class="s9">1837</td>
<td class="s10">0.13</td>
<td class="s11">1.35</td>
</tr>
<tr class="odd">
<td class="s8">798</td>
<td class="s9">109</td>
<td class="s9">852</td>
<td class="s10">0.14</td>
<td class="s11">1.07</td>
</tr>
<tr>
<td class="s8">644</td>
<td class="s9">86</td>
<td class="s9">611</td>
<td class="s10">0.13</td>
<td class="s11">0.95</td>
</tr>
<tr class="odd">
<td class="s8">325</td>
<td class="s9">44</td>
<td class="s9">252</td>
<td class="s10">0.14</td>
<td class="s11">0.78</td>
</tr>
<tr>
<td class="s8">260</td>
<td class="s9">36</td>
<td class="s9">205</td>
<td class="s10">0.14</td>
<td class="s11">0.79</td>
</tr>
<tr class="odd">
<td class="s8">165</td>
<td class="s9">24</td>
<td class="s9">111</td>
<td class="s10">0.15</td>
<td class="s11">0.67</td>
</tr>
</tbody>
</table>
<h2 style="margin-bottom: 12px;">JSON and Dynamic Script Tags: Fast but Insecure</h2>
<p>Working with the theory that large string manipulation was the problem with the last approach, we switched from using Ajax to instead fetching the data using a dynamically generated script tag. This means that the contact data was never treated as string, and was instead executed as soon as it was downloaded, just like any other JavaScript file. The difference in performance was shocking: 89ms to parse 10,000 contacts (a reduction of 3 orders of magnitude), while the smallest case of 172 contacts only took 6ms. The parse time per contact actually <i>decreased</i> the larger the list became. This approach looked perfect, except for one thing:  in order for this JSON to be executed, we had to wrap it in a callback method. Since it’s executable code, any website in the world could use the same approach to download a Flickr member&#8217;s contact list.  This was a deal breaker.</p>
<table class="data-table">
<tbody>
<tr class="header">
<th>Contacts</th>
<th>File Size (KB)</th>
<th>Parse Time (ms)</th>
<th>File Size per Contact (KB)</th>
<th>Parse Time per Contact (ms)</th>
</tr>
<tr>
<td class="s4">10,709</td>
<td class="s5">1105</td>
<td class="s5">89</td>
<td class="s6">0.10</td>
<td class="s7">0.01</td>
</tr>
<tr class="odd">
<td class="s8">4,877</td>
<td class="s9">508</td>
<td class="s9">41</td>
<td class="s10">0.10</td>
<td class="s11">0.01</td>
</tr>
<tr>
<td class="s8">2,979</td>
<td class="s9">308</td>
<td class="s9">26</td>
<td class="s10">0.10</td>
<td class="s11">0.01</td>
</tr>
<tr class="odd">
<td class="s8">1,915</td>
<td class="s9">197</td>
<td class="s9">19</td>
<td class="s10">0.10</td>
<td class="s11">0.01</td>
</tr>
<tr>
<td class="s8">1,363</td>
<td class="s9">140</td>
<td class="s9">15</td>
<td class="s10">0.10</td>
<td class="s11">0.01</td>
</tr>
<tr class="odd">
<td class="s8">800</td>
<td class="s9">83</td>
<td class="s9">11</td>
<td class="s10">0.10</td>
<td class="s11">0.01</td>
</tr>
<tr>
<td class="s8">644</td>
<td class="s9">67</td>
<td class="s9">9</td>
<td class="s10">0.10</td>
<td class="s11">0.01</td>
</tr>
<tr class="odd">
<td class="s8">325</td>
<td class="s9">35</td>
<td class="s9">8</td>
<td class="s10">0.11</td>
<td class="s11">0.02</td>
</tr>
<tr>
<td class="s8">260</td>
<td class="s9">27</td>
<td class="s9">7</td>
<td class="s10">0.10</td>
<td class="s11">0.03</td>
</tr>
<tr class="odd">
<td class="s8">172</td>
<td class="s9">18</td>
<td class="s9">6</td>
<td class="s10">0.10</td>
<td class="s11">0.03</td>
</tr>
</tbody>
</table>
<h2 style="margin-bottom: 12px;">Going Custom</h2>
<p><a href="http://www.flickr.com/photos/brianauer/2722866656/" title="Custom Ride by Brian Auer, on Flickr"><img src="http://farm4.static.flickr.com/3108/2722866656_5d5a9163e4.jpg" width="500" height="333" alt="Custom Ride" /></a></p>
<p>Having set the performance bar pretty high with the last approach, we dove into custom data formats. The challenge would be to create a format that we could parse ourselves, using JavaScript’s String and RegExp methods, that would also match the speed of JSON executed natively.  This would allow us to use Ajax again, but keep the data restricted to our domain. </p>
<p>Since we had already discovered that some methods of string manipulation didn’t perform well on large strings, we restricted ourselves to a method that we knew to be fast: split(). We used control characters to delimit each contact, and a different control character to delimit the fields within each contact. This allowed us to parse the string into contact objects with one split, then loop through that array and split again on each string. </p>
<pre style="margin-bottom: 14px;">
that.contacts = o.responseText.split("\\c");

for (var n = 0, len = that.contacts.length, contactSplit; n < len; n++) {

	contactSplit = that.contacts[n].split("\\a");

	that.contacts[n] = {};
	that.contacts[n].n = contactSplit[0];
	that.contacts[n].e = contactSplit[1];
	that.contacts[n].u = contactSplit[2];
	that.contacts[n].r = contactSplit[3];
	that.contacts[n].s = contactSplit[4];
	that.contacts[n].f = contactSplit[5];
	that.contacts[n].a = contactSplit[6];
	that.contacts[n].d = contactSplit[7];
	that.contacts[n].y = contactSplit[8];
}
</pre>
<p> Though this technique sounds like it would be slow, it actually performed on par with native JSON parsing (it was a little faster for cases containing less than 1000 contacts, and a little slower for those over 1000). It also had the smallest filesize: 80% the size of the JSON data for the same number of contacts. This is the format that we ended up using.</p>
<table class="data-table">
<tbody>
<tr class="header">
<th>Contacts</th>
<th>File Size (KB)</th>
<th>Parse Time (ms)</th>
<th>File Size per Contact (KB)</th>
<th>Parse Time per Contact (ms)</th>
</tr>
<tr>
<td class="s4">10,741</td>
<td class="s5">818</td>
<td class="s5">173</td>
<td class="s6">0.08</td>
<td class="s7">0.02</td>
</tr>
<tr class="odd">
<td class="s8">4,877</td>
<td class="s9">375</td>
<td class="s9">50</td>
<td class="s10">0.08</td>
<td class="s11">0.01</td>
</tr>
<tr>
<td class="s8">2,979</td>
<td class="s9">208</td>
<td class="s9">34</td>
<td class="s10">0.07</td>
<td class="s11">0.01</td>
</tr>
<tr class="odd">
<td class="s8">1,916</td>
<td class="s9">144</td>
<td class="s9">21</td>
<td class="s10">0.08</td>
<td class="s11">0.01</td>
</tr>
<tr>
<td class="s8">1,363</td>
<td class="s9">93</td>
<td class="s9">16</td>
<td class="s10">0.07</td>
<td class="s11">0.01</td>
</tr>
<tr class="odd">
<td class="s8">800</td>
<td class="s9">58</td>
<td class="s9">10</td>
<td class="s10">0.07</td>
<td class="s11">0.01</td>
</tr>
<tr>
<td class="s8">644</td>
<td class="s9">46</td>
<td class="s9">8</td>
<td class="s10">0.07</td>
<td class="s11">0.01</td>
</tr>
<tr class="odd">
<td class="s8">325</td>
<td class="s9">24</td>
<td class="s9">4</td>
<td class="s10">0.07</td>
<td class="s11">0.01</td>
</tr>
<tr>
<td class="s8">260</td>
<td class="s9">14</td>
<td class="s9">3</td>
<td class="s10">0.05</td>
<td class="s11">0.01</td>
</tr>
<tr class="odd">
<td class="s8">160</td>
<td class="s9">13</td>
<td class="s9">3</td>
<td class="s10">0.08</td>
<td class="s11">0.02</td>
</tr>
</tbody>
</table>
<h2 style="margin-bottom: 12px;">Searching</h2>
<p><a href="http://www.flickr.com/photos/dafuriousd/2870299882/" title="Ben to the Rescue by dafuriousd, on Flickr"><img src="http://farm4.static.flickr.com/3037/2870299882_54f39775bd.jpg" width="500" height="332" alt="Ben to the Rescue" /></a></p>
<p>Now that we have a giant array of contacts in JavaScript, we needed a way to search through them and select one. For this, we used YUI’s excellent <a href="http://developer.yahoo.com/yui/autocomplete/">AutoComplete widget</a>. To get the data into the widget, we created a DataSource object that would execute a function to get results. This function simply looped through our contact array and matched the given query against four different properties of each contact, using a regular expression (RegExp objects turned out to be extremely well-suited for this, with the average search time for the 10,000 contacts case coming in under 38ms). After the results were collected, the AutoComplete widget took care of everything else, including caching the results.</p>
<p>There was one optimization we made to our AutoComplete configuration that was particularly effective. Regardless of how much we optimized our search method, we could never get results to return in less than 200ms (even for trivially small numbers of contacts). After a lot of profiling and hair pulling, we found the queryDelay setting. This is set to 200ms by default, and artificially delays every search in order to reduce UI flicker for quick typists. After setting that to 0, we found our search times improved dramatically. </p>
<h2 style="margin-bottom: 12px;">The End Result</h2>
<div style="margin: 0 auto; text-align: center;">
<object type="application/x-shockwave-flash" width="400" height="229" data="http://www.flickr.com/apps/video/stewart.swf?v=68975" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"><param name="flashvars" value="intl_lang=en-us&amp;photo_secret=7d60e13b0d&amp;photo_id=3328806159"></param><param name="movie" value="http://www.flickr.com/apps/video/stewart.swf?v=68975"></param><param name="bgcolor" value="#000000"></param><param name="allowFullScreen" value="true"></param><embed type="application/x-shockwave-flash" src="http://www.flickr.com/apps/video/stewart.swf?v=68975" bgcolor="#000000" allowfullscreen="true" flashvars="intl_lang=en-us&amp;photo_secret=7d60e13b0d&amp;photo_id=3328806159" height="229" width="400"></embed></object>
</div>
<p>Head over to your <a href="http://www.flickr.com/people/me/contacts/">Contact List</a> page and give it a whirl. We are also using the Bo Selecta with <a href="http://www.flickr.com/mail/write/">FlickrMail</a> and the Share This widget on each photo page. </p>
]]></content:encoded>
			<wfw:commentRss>http://code.flickr.com/blog/2009/03/18/building-fast-client-side-searches/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>YUI Blog: Improving The Flickr Upload Exprience With YUI Uploader</title>
		<link>http://code.flickr.com/blog/2009/02/26/yui-blog-improving-the-flickr-upload-exprience-with-yui-uploader/</link>
		<comments>http://code.flickr.com/blog/2009/02/26/yui-blog-improving-the-flickr-upload-exprience-with-yui-uploader/#comments</comments>
		<pubDate>Thu, 26 Feb 2009 21:15:28 +0000</pubDate>
		<dc:creator>schill</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[flash]]></category>
		<category><![CDATA[frontend]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[uploadr]]></category>
		<category><![CDATA[yui]]></category>

		<guid isPermaLink="false">http://code.flickr.com/blog/?p=638</guid>
		<description><![CDATA[
Visual analogy of simultaneous file uploading. Also, internet/pipe joke goes here.

As a site which has many nifty JavaScript-driven features, Flickr makes good use of the Yahoo! User Interface library for much of its JavaScript DOM, Event handling and Ajax functionality.
One of the fancier widgets we&#8217;ve implemented is a flashy browser-based Web Uploadr which uses the [...]]]></description>
			<content:encoded><![CDATA[<div style="float:right;display:inline;width:195px;margin-left:1em"><a href="http://www.flickr.com/photos/34001301@N00/220050655/" title="water pipe by chris_00815, on Flickr" style="background:#fff"><img src="http://farm1.static.flickr.com/63/220050655_4977f63b4c_m.jpg" width="180" height="240" alt="water pipe" style="margin:0.5em 0px 0px 0.5em;padding:0.25em;border:1px solid #ddeeff;background:#f0f9ff;"></a>
<p style="font-family:helvetica,arial,verdana;font-size:x-small;color:#666;padding-left:1em;padding-right:1em;padding-top:0.1em">Visual analogy of simultaneous file uploading. Also, internet/pipe joke goes here.</p>
</div>
<p>As a site which has many nifty JavaScript-driven features, Flickr makes good use of the <a href="http://developer.yahoo.com/yui">Yahoo! User Interface library</a> for much of its JavaScript DOM, Event handling and Ajax functionality.</p>
<p>One of the fancier widgets we&#8217;ve implemented is a flashy browser-based Web Uploadr which uses the <a href="http://developer.yahoo.com/yui/uploader/">YUI Uploader</a> component (a combination of JavaScript and Flash) which allows for faster batch uploads, progress reporting, a nicer UI and overall improved user experience.</p>
<p>Head over to the YUI Blog and check out <a href="http://yuiblog.com/blog/2009/02/26/flickr-uploadr/">how Flickr uses YUI Uploader</a> to provide a faster, shinier upload experience.</p>
]]></content:encoded>
			<wfw:commentRss>http://code.flickr.com/blog/2009/02/26/yui-blog-improving-the-flickr-upload-exprience-with-yui-uploader/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
