<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss'><id>tag:blogger.com,1999:blog-2936626869504789093</id><updated>2010-02-17T12:26:04.666-08:00</updated><title type='text'>Carlos Bueno</title><subtitle type='html'></subtitle><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2936626869504789093/posts/default'/><link rel='alternate' type='text/html' href='http://carlos.bueno.org/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://carlos.bueno.org/atom.xml'/><author><name>bueno</name><email>noreply@blogger.com</email></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>22</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-2936626869504789093.post-570350541258605416</id><published>2010-02-10T15:12:00.000-08:00</published><updated>2010-02-17T12:26:04.692-08:00</updated><title type='text'>Measuring Javascript Parse and Load</title><content type='html'>&lt;style&gt;  table.chart {    cell-padding: 0;    cell-spacing: 0;    border-bottom: solid 1px #999;    border-right: solid 1px #999;    border-collapse: collapse;  }  table.chart td {    border-top: solid 1px #999;    border-left: solid 1px #999;    margin:0;    padding:0;    font-size: 10pt;    vertical-align: center;    text-align: center;    width: 60px;  }  table.chart th {    padding: 4px;    dddbackground: #dfdfdf;    border-right: solid 1px #999;    font-weight: bold;    font-size: 10pt;  }  table.chart td.v {    white-space: nowrap;    text-align: left;    border: none;    font-size: 10pt;    ddbackground: #dfdfdf;    border-bottom: solid 1px #999;    padding: 4px;  }  div.caption {    font-size: 10pt;    font-style: italic;    margin: 10px;    text-align:center;  }  div.figure {    text-align: center;    margin: 20px 0;    padding: 10px;  }  td.asterix {    color: #666;  }  th.asterix {    color: #666;  }  div.td-container {    position:relative;  }  div.barbar {    width: 80px;    height:30px;    position:relative;  }  div.bar {    background-color:#a0d3fe;    position: absolute;    top: 0px;    left: 0px;    bottom: 0px;  }  div.num {     position: absolute;     text-align:center;     top: 8px;     width:100%;     text-shadow: 0.1em 0.1em #eee;  }  td.asterix div.bar {    background-color: #ddd;  }  div.aside {    font-size: 10pt;    font-weight: normal;  }  .hidden {    display:none;  }  div.img {    display:inline-block;    float:right;    margin-right: 20px;  }  div.img * {    font-size: 8pt;  }&lt;/style&gt;&lt;p&gt;Any savvy web developer can tell you how many kilobytes their code consumes. They bundle, &lt;a href="http://developer.yahoo.com/performance/rules.html#minify"&gt;minify&lt;/a&gt;, &lt;a href="http://developer.yahoo.com/performance/rules.html#gzip"&gt;compress&lt;/a&gt; and tune the data sent out to within an inch of its life. Wire weight is easy to measure and has a direct impact on your application's launch time. But how many milliseconds does it take the user's computer to &lt;i&gt;parse and load&lt;/i&gt; your code once it's arrived? What differences are there between CPUs, operating systems, browsers and plugins? What speed leaks are we overlooking?&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="#the-test"&gt;The Test&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="#libs"&gt;The Libraries&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="#results"&gt;Results&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="#minifi"&gt;Minification&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="#conclusion"&gt;So What?&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="#try"&gt;Try this at home&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Appendix  &lt;ul&gt;    &lt;li&gt;&lt;a href="#chrome"&gt;The Curious Case of Chrome&lt;/a&gt;&lt;/li&gt;    &lt;li&gt;&lt;a href="#debugging"&gt;Debugging the Benchmark&lt;/a&gt;&lt;/li&gt;    &lt;li&gt;&lt;a href="#gc"&gt;Adventures in Garbage Collecting&lt;/a&gt;&lt;/li&gt;    &lt;li&gt;&lt;a href="#opera"&gt;A Tragic Opera&lt;/a&gt;&lt;/li&gt;  &lt;/ul&gt;&lt;/ul&gt;&lt;a name="the-test"&gt;&lt;h3&gt;The Test&lt;/h3&gt;&lt;/a&gt;&lt;p&gt;As you look at the data below, keep in mind four things:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Your code is not the only code running on the user's computer&lt;/li&gt;&lt;li&gt;Parse-n-load time comes down to available CPU cycles and RAM&lt;/li&gt;&lt;li&gt;The fastest CPUs aren't getting much faster&lt;/li&gt;&lt;li&gt;The average consumer CPU is getting &lt;i&gt;slower&lt;/i&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Even if you look at just sexy new hardware it's hard to ignore low-power clients: there are about 50 million netbooks and 43 million iPhones out there, alongside 10-15 million Android devices. Almost all of them are in the 600-1,600 MHz range and have less than 512MB of RAM. A juicy, growing slice of the market wants to use your software on the equivalent of a desktop from 1998. This includes rich Westerners as well as people in the fastest-growing international markets.&lt;/p&gt;&lt;p&gt;The test harness loads a given block of Javascript from a local file over and over and measures the setup time. My test subjects were the Yahoo User Interface (&lt;a href="http://developer.yahoo.com/yui/3"&gt;YUI&lt;/a&gt;) libraries, &lt;a href="http://script.aculo.us"&gt;Scriptaculous&lt;/a&gt;, and &lt;a href="http://jqueryui.com"&gt;jQuery UI&lt;/a&gt;. I've also included the main Javascript application code from &lt;a href="http://github.com"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;The core test is as simple as can be: record a start time, load the script, record the end time, and repeat over 1,000 iterations. The tests were run on recently-booted machines with no other programs running. You can &lt;a href="http://github.com/aristus/parse-n-load"&gt;check out the project on GitHub&lt;/a&gt; and play along at home.&lt;/p&gt;&lt;a name="libs"&gt;&lt;h3&gt;The Libraries&lt;/h3&gt;&lt;/a&gt;&lt;div class="img"&gt;  &lt;img src="/images/you-like-apples.jpg"/&gt;  &lt;div xmlns:cc="http://creativecommons.org/ns#" about="http://www.flickr.com/photos/charlietakesphotos/78511025/"&gt;&lt;a rel="cc:attributionURL" href="http://www.flickr.com/photos/charlietakesphotos/"&gt;Flickr: charlietakesphotos&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;ul&gt;  &lt;li&gt;&lt;b&gt;YUI 2.8.0r4&lt;/b&gt;, 390KB &lt;div class="aside"&gt;partial (dom, event, datasource, datatable, layout, tabview, treeview, menu)&lt;/div&gt;&lt;/li&gt;  &lt;li&gt;&lt;b&gt;YUI 3.0 build 1549&lt;/b&gt;, 311KB &lt;div class="aside"&gt;"kitchen sink"&lt;/div&gt;&lt;/li&gt;  &lt;li&gt;&lt;b&gt;Scriptaculous 1.8.3&lt;/b&gt;, 159KB  &lt;div class="aside"&gt;"kitchen sink"&lt;/div&gt;&lt;/li&gt;  &lt;li&gt;&lt;b&gt;jQuery UI 1.7.2&lt;/b&gt;, 359KB  &lt;div class="aside"&gt;"kitchen sink", minus translations&lt;/div&gt;&lt;/li&gt;  &lt;li&gt;&lt;b&gt;GitHub.com 09 Feb 2010&lt;/b&gt;, 211KB &lt;div class="aside"&gt;main application code, including jQuery 1.4&lt;/div&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;The point of this benchmark is to compare browsers and CPUs. Comparing the parse-n-load of different &lt;i&gt;libraries&lt;/i&gt; puts you on shaky ground. For example, the YUI2 libraries are as of this writing more comprehensive than, say, YUI3 or  Scriptaculous. On the other hand it's rare for an application to load every module as we're doing here. Also, each library has a diffferent approach to initialization. YUI2 does a lot of work up-front while YUI3 does things more lazily. From there you get into a complex question about what benefits each library buys you. &lt;i&gt;Do not base the choice of library on this benchmark.&lt;/i&gt;&lt;/p&gt;&lt;a name="results"&gt;&lt;h3&gt;MacBook Pro 2.26GHz, OSX 10.5.8&lt;/h3&gt;&lt;br/&gt;&lt;/a&gt;&lt;table class="chart" cellspacing="0"&gt;  &lt;tr&gt;  &lt;th style="background:none"&gt;&lt;/th&gt;  &lt;th title="Google Chrome 5.0.307.7"&gt;Chrome&lt;/th&gt;  &lt;th title="Apple Safari 4.0.3"&gt;Safari 4&lt;/th&gt;  &lt;th title="Mozilla Firefox 3.0.14"&gt;Firefox 3&lt;/th&gt;  &lt;th title="Mozilla Firefox 3.5.3"&gt;Firefox 3.5&lt;/th&gt;  &lt;th title="Mozilla Firefox 3.6.0"&gt;Firefox 3.6&lt;/th&gt;  &lt;th class="asterix"&gt;Opera 10*&lt;/th&gt;  &lt;th class="asterix"&gt;Safari 3*&lt;/th&gt;  &lt;/tr&gt;  &lt;tr class="hidden"&gt;    &lt;td class="v"&gt;&lt;b&gt;YUI3&lt;/b&gt; (raw)&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:12px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;62&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:19px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;98&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:22px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;114&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:25px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;128&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:23px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;115&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="asterix"&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:22px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;112&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="asterix"&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:25px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;129&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;  &lt;/tr&gt;  &lt;tr&gt;    &lt;td class="v"&gt;&lt;b&gt;YUI3&lt;/b&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:6px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;31&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:9px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;47&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:15px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;78&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:18px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;94&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:16px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;83&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="asterix"&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:12px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;62&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="asterix"&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:22px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;111&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;  &lt;/tr&gt;  &lt;tr class="hidden"&gt;    &lt;td class="v"&gt;&lt;b&gt;YUI2&lt;/b&gt; (raw)&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:4px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;23&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:30px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;153&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:26px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;131&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:28px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;142&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:27px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;137&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="asterix"&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:23px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;116&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="asterix"&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:30px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;150&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;  &lt;/tr&gt;  &lt;tr&gt;    &lt;td class="v"&gt;&lt;b&gt;YUI2&lt;/b&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:2px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;11&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:22px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;110&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:15px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;77&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:21px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;105&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:20px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;102&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="asterix"&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:14px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;74&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="asterix"&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:25px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;125&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;  &lt;/tr&gt;  &lt;tr&gt;    &lt;td class="v"&gt;&lt;b&gt;Scriptaculous&lt;/b&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:1px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;7&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="asterix"&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:10px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;50*&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:12px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;63&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:18px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;91&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:14px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;71&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;    &lt;td class="empty"&gt;&lt;/td&gt;    &lt;td class="empty"&gt;&lt;/td&gt;  &lt;/tr&gt;  &lt;tr&gt;    &lt;td class="v"&gt;&lt;b&gt;jQuery UI&lt;/b&gt; &lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:0px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;3&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:8px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;40&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:16px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;84&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:19px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;95&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:16px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;84&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;    &lt;td class="empty"&gt;&lt;/td&gt;    &lt;td class="empty"&gt;&lt;/td&gt;  &lt;/tr&gt;  &lt;tr&gt;    &lt;td class="v"&gt;&lt;b&gt;GitHub&lt;/b&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:0px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;4&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:12px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;63&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:13px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;67&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:16px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;80&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:13px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;66&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;    &lt;td class="empty"&gt;&lt;/td&gt;    &lt;td class="empty"&gt;&lt;/td&gt;  &lt;/tr&gt;&lt;/table&gt;&lt;div class="caption"&gt;  &lt;p&gt;Table 0: Parse-and-load times in milliseconds for various Javascript libraries and browsers, 95th percentile mean. MacBook Pro 2.26 GHz Intel Core 2 Duo with 4GB of 1GHz DDR3 RAM.    &lt;br/&gt;* Incomplete test, only 100 iterations. See "Tragic Opera" below.  &lt;/p&gt;&lt;/div&gt;&lt;h3&gt;Presario R3000 1.6GHz, WinXP SP3&lt;/h3&gt;&lt;br/&gt;&lt;table class="chart" cellspacing="0"&gt;  &lt;tr&gt;  &lt;th style="background:none"&gt;&lt;/th&gt;  &lt;th title="Google Chrome 4.0.249.89"&gt;Chrome&lt;/th&gt;  &lt;th title="Mozilla Firefox 3.0.11"&gt;Firefox 3&lt;/th&gt;  &lt;th title="Mozilla Firefox 3.5.2"&gt;Firefox 3.5&lt;/th&gt;  &lt;th title="Mozilla Firefox 3.6.0"&gt;Firefox 3.6&lt;/th&gt;  &lt;th title="Microsoft Internet Explorer 6.00.2900"&gt;IE 6&lt;/th&gt;  &lt;th title="Microsoft Internet Explorer 7.00.5730"&gt;IE 7&lt;/th&gt;  &lt;th title="Microsoft Internet Explorer 8.00.6001"&gt;IE 8&lt;/th&gt;  &lt;/tr&gt;  &lt;tr class="hidden"&gt;    &lt;td class="v"&gt;&lt;b&gt;YUI3&lt;/b&gt; (raw)&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:7px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;38&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:32px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;163&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:37px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;189&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:35px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;178&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:23px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;118&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:21px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;106&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:20px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;101&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;  &lt;/tr&gt;  &lt;tr&gt;    &lt;td class="v"&gt;&lt;b&gt;YUI3&lt;/b&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:3px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;19&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:23px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;119&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:28px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;143&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:26px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;134&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:13px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;65&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:12px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;61&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:10px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;51&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;  &lt;/tr&gt;  &lt;tr class="hidden"&gt;    &lt;td class="v" class="hidden"&gt;&lt;b&gt;YUI2&lt;/b&gt; (raw)&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:7px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;37&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:37px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;187&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:44px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;220&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:31px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;158&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:41px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;209&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:26px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;132&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:24px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;122&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;  &lt;/tr&gt;  &lt;tr&gt;    &lt;td class="v"&gt;&lt;b&gt;YUI2&lt;/b&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:3px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;18&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:28px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;140&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:33px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;165&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:29px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;148&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:17px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;89&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:16px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;83&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:13px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;69&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;  &lt;/tr&gt;  &lt;tr&gt;    &lt;td class="v"&gt;&lt;b&gt;Scriptaculous&lt;/b&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:2px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;14&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:17px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;89&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:27px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;135&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:23px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;116&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:16px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;82&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:16px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;84&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:8px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;44&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;  &lt;/tr&gt;  &lt;tr&gt;    &lt;td class="v"&gt;&lt;b&gt;jQuery UI&lt;/b&gt; &lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:2px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;11&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:23px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;119&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:30px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;151&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:27px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;136&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:12px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;62&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:12px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;62&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:10px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;53&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;  &lt;/tr&gt;  &lt;tr&gt;    &lt;td class="v"&gt;&lt;b&gt;GitHub&lt;/b&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:1px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;9&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:17px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;89&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:22px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;110&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:21px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;107&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:11px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;56&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:10px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;54&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:8px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;41&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;  &lt;/tr&gt;&lt;/table&gt;&lt;div class="caption"&gt;  &lt;p&gt;Table 1: Parse-and-load, 95th percentile mean. Compaq Presario R3000 at 1.6GHz, Windows XP SP3 and 512MB RAM.&lt;/p&gt;&lt;/div&gt;&lt;h3&gt;MacBook Air 1.6GHz, OSX 10.5.8&lt;/h3&gt;&lt;br/&gt;&lt;table class="chart" cellspacing="0"&gt;  &lt;tr&gt;  &lt;th style="background:none"&gt;&lt;/th&gt;  &lt;th title="Google Chrome 5.0.307.7"&gt;Chrome&lt;/th&gt;  &lt;th title="Apple Safari 4.0.2"&gt;Safari 4&lt;/th&gt;  &lt;th title="Mozilla Firefox 3.0.12"&gt;Firefox 3&lt;/th&gt;  &lt;th title="Mozilla Firefox 3.5.2"&gt;Firefox 3.5&lt;/th&gt;  &lt;th title="Mozilla Firefox 3.6.0"&gt;Firefox 3.6&lt;/th&gt;  &lt;/tr&gt;  &lt;tr class="hidden"&gt;    &lt;td class="v"&gt;&lt;b&gt;YUI3&lt;/b&gt; (raw)&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:3px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;19&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:18px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;92&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:43px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;219&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:46px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;234&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:43px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;216&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;  &lt;/tr&gt;  &lt;tr&gt;    &lt;td class="v"&gt;&lt;b&gt;YUI3&lt;/b&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:1px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;8&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:14px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;72&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:35px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;178&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:34px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;170&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:31px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;157&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;  &lt;/tr&gt;  &lt;tr class="hidden"&gt;    &lt;td class="v"&gt;&lt;b&gt;YUI2&lt;/b&gt; (raw)&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:4px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;24&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:50px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;251&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:60px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;301&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:53px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;268&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:49px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;249&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;  &lt;/tr&gt;  &lt;tr&gt;    &lt;td class="v"&gt;&lt;b&gt;YUI2&lt;/b&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:3px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;15&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:36px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;182&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:35px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;176&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:38px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;190&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:35px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;177&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;  &lt;/tr&gt;  &lt;tr&gt;    &lt;td class="v"&gt;&lt;b&gt;Scriptaculous&lt;/b&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:2px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;10&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:11px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;56&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:19px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;97&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:32px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;163&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:28px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;141&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;  &lt;/tr&gt;  &lt;tr&gt;    &lt;td class="v"&gt;&lt;b&gt;jQuery UI&lt;/b&gt; &lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:0px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;4&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:12px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;61&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:30px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;150&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:33px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;167&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:30px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;151&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;  &lt;/tr&gt;  &lt;tr&gt;    &lt;td class="v"&gt;&lt;b&gt;GitHub&lt;/b&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:1px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;5&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:15px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;77&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:26px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;131&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:26px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;130&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:23px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;117&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;  &lt;/tr&gt;&lt;/table&gt;&lt;div class="caption"&gt;  &lt;p&gt;Table 2: Parse-and-load, 95th percentile mean. MacBook Air 1.6 GHz Intel Core 2 Duo with 2GB of 1GHz DDR2 RAM.&lt;/p&gt;&lt;/div&gt;&lt;p&gt;NB: the MacBook Air had overheating problems so some of these numbers in this table may be skewed upwards. On the other hand, that's precisely the kind of crap your users have to deal with.&lt;/p&gt;&lt;p&gt;There is a noticeable spread between different browsers on the same hardware and OS. Firefox 3.5 is a few points slower than 3.0, but 3.6 improved on that. Internet Explorer is surprisingly fast at parse-n-load across all tested versions. I didn't include standard deviations because aside from some pathological cases they were small. If you run the benchmark for yourself you will get mean average, stddev, and a time series graph for your enjoyment.&lt;/p&gt;&lt;a name="minifi"&gt;&lt;h3&gt;Minification FTW&lt;/h3&gt;&lt;/a&gt;&lt;p&gt;Here is a comparison of the YUI libraries in "raw" form (with comments, whitespace, etc) and the same code minified using &lt;a href="http://developer.yahoo.com/yui/compressor"&gt;YUI Compressor&lt;/a&gt;. As expected, minification helps parse-n-load in addition to network transmission time. This is probably due to the absence of comments and extra whitespace.&lt;/p&gt;&lt;table class="chart" cellspacing="0"&gt;  &lt;tr&gt;  &lt;th style="background:none"&gt;&lt;/th&gt;  &lt;th title="Google Chrome 4.0.249.89"&gt;Chrome&lt;/th&gt;  &lt;th title="Mozilla Firefox 3.0.11"&gt;Firefox 3&lt;/th&gt;  &lt;th title="Mozilla Firefox 3.5.2"&gt;Firefox 3.5&lt;/th&gt;  &lt;th title="Mozilla Firefox 3.6.0"&gt;Firefox 3.6&lt;/th&gt;  &lt;th title="Microsoft Internet Explorer 6.00.2900"&gt;IE 6&lt;/th&gt;  &lt;th title="Microsoft Internet Explorer 7.00.5730"&gt;IE 7&lt;/th&gt;  &lt;th title="Microsoft Internet Explorer 8.00.6001"&gt;IE 8&lt;/th&gt;  &lt;/tr&gt;  &lt;tr&gt;    &lt;td class="v"&gt;YUI3 (&lt;b&gt;raw&lt;/b&gt;)&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:7px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;38&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:32px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;163&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:37px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;189&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:35px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;178&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:23px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;118&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:21px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;106&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:20px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;101&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;  &lt;/tr&gt;  &lt;tr&gt;    &lt;td class="v"&gt;YUI3 (&lt;b&gt;minified&lt;/b&gt;)&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:3px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;19&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:23px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;119&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:28px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;143&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:26px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;134&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:13px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;65&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:12px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;61&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:10px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;51&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;  &lt;/tr&gt;  &lt;tr class="clearall"&gt;    &lt;td style="border-left:none;" colspan="8"&gt;&amp;nbsp;&lt;/td&gt;  &lt;/tr&gt;  &lt;tr&gt;    &lt;td class="v"&gt;YUI2 (&lt;b&gt;raw&lt;/b&gt;)&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:7px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;37&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:37px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;187&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:44px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;220&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:31px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;158&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:41px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;209&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:26px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;132&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:24px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;122&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;  &lt;/tr&gt;  &lt;tr&gt;    &lt;td class="v"&gt;YUI2 (&lt;b&gt;minified&lt;/b&gt;)&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:3px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;18&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:28px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;140&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:33px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;165&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:29px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;148&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:17px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;89&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:16px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;83&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;td&gt; &lt;div class="barbar"&gt;&lt;div class="bar" style="width:13px"&gt;&amp;nbsp;&lt;/div&gt;&lt;div class="num"&gt;69&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;  &lt;/tr&gt;&lt;/table&gt;&lt;div class="caption"&gt;  &lt;p&gt;Table 3: Code minified with YUI Compressor parses faster. &lt;/div&gt;&lt;a name="conclusion"&gt;&lt;h3&gt;So What?&lt;/h3&gt;&lt;/a&gt;&lt;p&gt;If you have a large amount of Javascript in your application it's natural to bundle it all up into one file to save on network transit time and increase cacheability. But if certain parts of your application only use certain parts of the bundle, you might cause the user to unecessarily parse unused code on every page load. The ideal would be to decouple three things that are now tightly wound together: network transit, parse-n-load, and cacheability. There is a lot of work going on in this space but so far there is no silver bullet.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Alexander Limi from Mozilla &lt;a href="http://limi.net/articles/resource-packages/"&gt;has a proposal&lt;/a&gt; to use zip files for bundling.&lt;/li&gt;&lt;li&gt;The folks at 280North have &lt;a href="http://cappuccino.org/discuss/2009/11/11/just-one-file-with-cappuccino-0-8/"&gt;found a neat way&lt;/a&gt; to do multi-file bundling in their Cappuccino framework, using existing technology.&lt;/li&gt;&lt;li&gt;Google is &lt;a href="http://dev.chromium.org/spdy/spdy-whitepaper"&gt;proposing an extension&lt;/a&gt; to HTTPS that allows multiple concurrent streams over a single TCP connection.&lt;/li&gt;&lt;li&gt;&lt;a href="http://blog.sproutcore.com/post/272853740/cut-your-javascript-load-time-90-with-deferred"&gt;SproutCore&lt;/a&gt; and the &lt;a href="http://googlecode.blogspot.com/2009/09/gmail-for-mobile-html5-series-reducing.html"&gt;Google Mobile Team&lt;/a&gt; recently demonstrated ways to load Javascript code as dumb strings that is evaluated at a time of the programmer's choosing.&lt;/li&gt;&lt;/ul&gt;&lt;a name="try"&gt;&lt;h3&gt;Try this at home&lt;/h3&gt;&lt;/a&gt;&lt;p&gt;The &lt;a href="http://github.com/aristus/parse-n-load"&gt;Parse-N-Load benchmark&lt;/a&gt; is open source and free for use. It's early and I'm sure there are bugs. If you have other kinds of hardware (Netbooks! Windows! Linux!), please try it out and let me know what you find.&lt;/p&gt;&lt;h2&gt;&lt;i&gt;Appendix&lt;/i&gt;&lt;/h2&gt;&lt;a name="chrome"&gt;&lt;h3&gt;The Curious Case of Chrome&lt;/h3&gt;&lt;/a&gt;&lt;p&gt;While Google Chrome appears to be an order of magnitude faster at parse-n-load, the truth may be a little more complex. Running this benchmark in Chrome sometimes produces sharp cliffs in the time series graph, especially on slower CPUs. That might be the V8 engine's &lt;a href="http://en.wikipedia.org/wiki/Inline_caching"&gt;inline caching&lt;/a&gt; kicking in. I also suspect it could be caching the machine code it compiles on the first pass. Or this could be something silly like the CPU coming out of low-power mode. If anyone who knows more about what's going on can speak up, please do.&lt;/p&gt;&lt;div class="figure"&gt;  &lt;img src="/images/chrome-cliff.png" /&gt;  &lt;div class="caption"&gt;Figure 0: Chrome's V8 Javascript engine has two speeds: fast and &lt;b&gt;very&lt;/b&gt; fast.&lt;/div&gt;&lt;/div&gt;&lt;a name="debugging"&gt;&lt;h3&gt;Debugging the Benchmark&lt;/h3&gt;&lt;/a&gt;&lt;p&gt;The first problem that came up was different blocking behavior between browsers. In Safari 4 (but not 3) if you create a script tag that points to an external file, that action will block, ie, wait until that file is completely parsed and loaded. This makes timing it very easy. In Firefox, however, this action asynchronous: the statement that creates the script element returns immediately and the file is loaded in a separate thread. This means you have to set up a callback in the separate thread to both measure elapsed time and kick off the next iteration of the test. You have to be &lt;a href="http://paulbarry.com/articles/2009/08/30/tail-call-optimization"&gt;careful not the blow the stack&lt;/a&gt; with too many nested function calls. Google Chrome is also asynchronous and has an altogether different stack behavior. If all that wasn't enough, browsers have very different memory allocation behavior, of which more (oh, much more) below.&lt;/p&gt;&lt;a name="gc"&gt;&lt;h3&gt;Adventures in Garbage Collecting&lt;/h3&gt;&lt;/a&gt;&lt;p&gt;Every browser seems to have a different system for allocating memory while parsing Javascript code. When you graph the results from Safari 4 this is what I saw initially:&lt;/p&gt;&lt;div class="figure"&gt;  &lt;img src="/images/spiky-garbage.png" /&gt;  &lt;div class="caption"&gt;Figure 1: the effects of GC halts&lt;/div&gt;&lt;/div&gt;&lt;p&gt;Interesting. All of the source files in the benchmark are local so that's not I/O wait. The regularity of huge spikes suggests that the browser is pausing every so often to free up memory via garbage collection. When you remove those spikes another interesting pattern shows up. Here is the same graph with the top 5% of datapoints removed:&lt;/p&gt;&lt;div class="figure"&gt;  &lt;img src="/images/sawtooth.png" /&gt;  &lt;div class="caption"&gt;Figure 2: 95th percentile graph of Javascript load times in Safari 4&lt;/div&gt;&lt;/div&gt;&lt;p&gt;It appears that the parse-n-load time of a given piece of Javascript in Safari 4 will increase linearly with the amount of garbage. The load time can grow as much as 3X longer than normal before GC kicks in. I'm not certain whether this is an artifact of the benchmark or if it actually happens a lot during real-world use. When I added code to explicitly delete the previous script tag before creating a new one, the sawtooth elongated but did not go away. Other browsers exhibit similar halt-the-world GC behavior but only Safari 4 and Opera 10.5alpha have this sawtooth. Firefox's graph stays fairly horizontal but has many more small spikes:&lt;/p&gt;&lt;div class="figure"&gt;  &lt;img src="/images/firefox-35.png" /&gt;  &lt;div class="caption"&gt;Figure 3: 95th percentile graph of Javascript load times in Firefox 3.5.3&lt;/div&gt;&lt;/div&gt;&lt;a name="opera"&gt;&lt;h3&gt;A Tragic Opera&lt;/h3&gt;&lt;/a&gt;&lt;p&gt;The results for Opera 10.0 and Safari 3 have asterisks because I couldn't get them to run the complete test. Opera got steadily slower up to 250 iterations after which it started serious thrashing and had to be killed. Opera 10.5alpha reportedly has a new Javascript engine, and some quick tests show more sawtooth-like behavior, but it still is not able to complete the full 1,000 iterations before spinning out of control.&lt;/p&gt;&lt;div class="figure"&gt;  &lt;img src="/images/oh-opera.png" /&gt;  &lt;div class="caption"&gt;Figure 4: Opera 10 choking on the parse-n-load benchmark&lt;/div&gt;&lt;/div&gt;&lt;p&gt;Safari 3 hit the wall even earlier, triggering an Out-of-Memory error after a couple hundred iterations. For some reason I had similar problems with Safari 4 on the Scriptaculous test, which is why that cell has an asterisk.&lt;p&gt;&lt;div class="figure"&gt;  &lt;img src="/images/safari-3.png" /&gt;  &lt;div class="caption"&gt;Figure 5: Safari 3 got to this point, then threw an OOM&lt;/div&gt;&lt;/div&gt;&lt;p&gt;&lt;i&gt;Many thanks to Adrien Arculeo, Ryan Tomayko of GitHub, Robert Halliday, Eric Miraglia, Lucas Smith, and random internet heroes for their help, ideas and corrections.&lt;/i&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2936626869504789093-570350541258605416?l=carlos.bueno.org' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2936626869504789093/posts/default/570350541258605416'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2936626869504789093/posts/default/570350541258605416'/><link rel='alternate' type='text/html' href='http://carlos.bueno.org/2010/02/measuring-javascript-parse-and-load.html' title='Measuring Javascript Parse and Load'/><author><name>bueno</name><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='02278308746331037541'/></author></entry><entry><id>tag:blogger.com,1999:blog-2936626869504789093.post-4587721024699812665</id><published>2009-12-18T10:49:00.000-08:00</published><updated>2009-12-18T10:50:55.732-08:00</updated><title type='text'>The Soul of a Portable Machine</title><content type='html'>&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://carlos.bueno.org/uploaded_images/osx-on-osx-773812.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"&gt;&lt;img border="0" src="http://carlos.bueno.org/uploaded_images/osx-on-osx-773662.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;Instead of lugging a laptop around, why not carry just the bits? I don't mean keeping your files on a USB key, or even booting from an external drive. I mean storing the entire "soul": the applications, data, OS &lt;b&gt;and RAM image&lt;/b&gt; as a virtual machine you carry around on a portable drive. You could plug it into any sufficiently powerful host and resume your work without interruption [0].&lt;br /&gt;&lt;br /&gt;So, I tried it. My primary "computer" is a $25 USB flash drive that I plug into $2,500 dumb terminals.&lt;br /&gt;&lt;h3&gt;Setting it up&lt;/h3&gt;&lt;div style="display: inline-block; float: right; margin: 0 0.5em 0.5em 1em; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;i&gt;Note to OSX users: &lt;/i&gt;Apple does not want people running their operating system under virtualization. An OSX install disc will not normally boot under VMWare or Parallels. This policy is stupid and overbearing but I don't expect Apple to change, any more than I expect the control-freak leader of a hippie commune to loosen his grip on the bag of peyote buttons [1].&lt;br /&gt;&lt;br /&gt;I started off with a 4GB stick I had lying around and an Ubuntu Linux image running under VMWare. It was fine for browsing, code editing, etc, but was too space-constrained for real work. The total size of your virtual machine files will be the disk size + RAM size plus a few percent overhead. VMWare requires additional free space equal to your RAM size, presumably in order to write the contents of RAM to disk atomically during a "suspend". So a 2GB virtual machine image with 1GB of RAM allocated will require more than 4GB of space. This turned out to be a nice excuse to buy myself a 16GB stick.&lt;br /&gt;&lt;br /&gt;Setting up corporate effluent pipes (email, calendaring, VPN) on the VM would have taken more time than I wanted to spend, so I run them directly on the hosts.&lt;br /&gt;&lt;br /&gt;Make sure your desktop background, window themes, terminal colors, etc are different between your host and virtual machines so you don't get confused. Turn off the VM's screensaver.&lt;br /&gt;&lt;br /&gt;Use a pre-allocated disk. I made a single-file disk image instead of multiple 2GB chunks, but I have no idea if it makes a difference to speed or error-resistance.&lt;br /&gt;&lt;h3&gt;It's like a laptop, except without the laptop&lt;/h3&gt;Running my Linux VM directly from a USB drive is smooth, contrary to expectations. I thought I would have to sync my machine image to the host machine's hard drive to get decent performance. I now put my computer in my pocket, commute to the office, plug in, and start working. Over lunch I hibernate the VM and make a backup to the host machine. If I need to work at home I plug it into my personal computer. &lt;br /&gt;&lt;h3&gt;Annoyances &amp;amp; Limitations&lt;/h3&gt;The VM is slower than running directly on the host machine, but not by much unless you are doing disk-intensive work like compiling MacPorts. Video performance feels sluggish when scrolling or moving windows.&lt;br /&gt;&lt;br /&gt;A usable OSX image is too large for my USB drive but a cheap external hard drive works well enough. Next year 64GB USB and SD drives will be in the sweet spot.&lt;br /&gt;&lt;br /&gt;My work often requires testing on Windows inside other virtual machines, so I can't use all available RAM for my primary VM. There are minor UI annoyances like the host menubar popping up on OSX. A bug with the virtual sound system in VMWare seems to trigger a long-standing bug in OSX/Flash: YouTube videos play for a second then halt.&lt;br /&gt;&lt;h3&gt;Next steps&lt;/h3&gt;It would be nice to transform an existing OSX system into a virtual one. I believe you could back up your machine using CarbonCopyCloner or SuperDuper to a .dmg file, then use qemu-img to convert to a .vmdk or other virtual disk format. But unless you find a way to shrink the image you'd need a portable drive with a capacity larger than your current hard drive.&lt;br /&gt;&lt;br /&gt;The next big leap would be to replace the USB key with a real portable computer, something like the &lt;a href="http://en.smartdevices.com.cn/Products/Smartq5/" rel="external nofollow"&gt;SmartQ5&lt;/a&gt;. The idea is to have one virtual machine that takes full advantage of whatever hardware it happens to be running on. Running a desktop-class virtual machine on a tiny device like would be extremely tricky if not impossible. Even leaving aside the different CPU, it's not possible (as far as I know) to change the amount of RAM or number of CPUs assigned to a VM without rebooting it. But I believe the hacks are out there, just waiting to be discovered.&lt;br /&gt;&lt;h3&gt;Notes&lt;/h3&gt;[0] See Tom Hughes-Croucher's &lt;a href="http://slideshare.net/sh1mmer/state-and-why-sync-is-dumb"&gt;state vs sync&lt;/a&gt; and also the &lt;a href="http://www.research.ibm.com/WearableComputing/SoulPad/soulpad.html" rel="external nofollow"&gt;IBM SoulPad&lt;/a&gt; project from 2005.&lt;br /&gt;&lt;br /&gt;[1] There are many ways to run OSX inside a VM, most of which are against the EULA in jurisdictions where the EULA is enforceable. The easiest way is to install a licensed generic copy of OSX (not the special model-specific copy that comes with Macs) on VMWare or Parallels, and then add some "special touches". &lt;br /&gt;&lt;br /&gt;XCode (and thus gcc/make, MacPorts and the rest of the toolchain) will not install on a virtual OSX machine with the "ServerVersion.plist" hack enabled, giving a "error while evaluating JavaScript for the package" error. Software updates also fail. The point of the hack is to misrepresent the type of OSX you are running. So make a pair of scripts to toggle it. &lt;a href="http://blog.rectalogic.com/2008/08/virtualizing-mac-os-x-leopard-client.html"&gt;Further, deponent sayeth not.&lt;/a&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2936626869504789093-4587721024699812665?l=carlos.bueno.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/2936626869504789093/4587721024699812665/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=2936626869504789093&amp;postID=4587721024699812665' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2936626869504789093/posts/default/4587721024699812665'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2936626869504789093/posts/default/4587721024699812665'/><link rel='alternate' type='text/html' href='http://carlos.bueno.org/2009/12/soul-of-portable-machine.html' title='The Soul of a Portable Machine'/><author><name>bueno</name><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='02278308746331037541'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2936626869504789093.post-7258220830621948532</id><published>2009-11-24T09:45:00.000-08:00</published><updated>2009-12-03T20:09:16.633-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='dismal guide'/><title type='text'>A Dismal Guide to DNS</title><content type='html'>&lt;p&gt;&lt;small&gt;&lt;i&gt;(Originally appeared on &lt;a href="http://developer.yahoo.net/blog/archives/2009/11/guide_to_dns.html"&gt;Yahoo's Developer Blog&lt;/a&gt;)&lt;/i&gt;&lt;/small&gt;&lt;/p&gt;&lt;p&gt;&lt;img border="0" height="200" src="http://carlos.bueno.org/uploaded_images/toronto-yp-728188.png" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;" width="169" /&gt;The Domain Name System (DNS) is part of the "dark matter" of the internet. It's hard to observe the DNS directly yet it exerts an obscure, pervasive influence without which everything would fly apart. Because it's so difficult to probe people tend to take it for granted, which I think is a mistake. DNS problems can hurt the speed and reliability of your applications without you even noticing. In this article we'll take a look at the behavior of the DNS and walk through some experiments you can run to gather valuable data about your users' network performance.&lt;/p&gt;&lt;h3&gt;A Clever Shambles&lt;/h3&gt;&lt;p&gt;Before two computers can talk to each other on the 'net, one of them has to know the numeric IP address of the other. Using the DNS is often compared to looking up a number in the phone book. But that can give the impression the information is in one place, close to hand.&lt;/p&gt;&lt;p&gt;Instead, imagine it's 1982. You live in Tucson and you want to call a hotel in Toronto. You don't have a Toronto phone book so you call your local library. They don't have one either. Life is boring in Tucson, so the librarian uses her New York phone book to call another library. The nice lady in New York looks up the hotel's number in her copy of the Toronto phone book, tells it to your local librarian, who then calls back to give it to you. Doing all this is a hassle, so everyone in the chain writes down the number just in case the question ever comes up again.&lt;/p&gt;&lt;p&gt;The DNS is even more complex because of the hierarchy of internet domains. Consider the host name &lt;code&gt;foo.bar.example.net&lt;/code&gt;. To look it up your computer will have to look up every part of the name, in reverse order. That means resolving ".", then "net.", then "example.net.", "bar.example.net.", and finally "foo.bar.example.net."[0]. It's not just a matter of finding the Toronto book. It's looking up someone who knows someone who has the Canada book and from there who has the Ontario book, then the Toronto book, and so on.&lt;/p&gt;&lt;p&gt;If this sounds ridiculously complex and fragile, &lt;a href="http://en.wikipedia.org/wiki/Domain_Name_System#Address_resolution_mechanism"&gt;that's because it is&lt;/a&gt;. Writing down the answer to common queries, aka caching, is the only reason we're able to get away with it. In practice the root domain "." is known to everyone. During normal operation "net." should be cached all levels including at your local librarian, aka your ISP. Anything beyond that requires some lookups unless the domain is already very well-known.&lt;/p&gt;&lt;h3&gt;How long does it take to look up a hostname?&lt;/h3&gt;&lt;p&gt;A single DNS lookup may involve several recursive lookups to machines all over the world. Because of this hassle, information is cached for short periods of time at every level, including on your computer. So "the time it takes to do a DNS lookup" can vary wildly depending on the state of affairs in many different places, and the quality of the network connections between them.&lt;/p&gt;&lt;p&gt;On Mac OSX the &lt;code&gt;&lt;a href="http://developer.apple.com/mac/library/documentation/Darwin/Reference/ManPages/man1/dscacheutil.1.html" title="dscacheutil"&gt;dscacheutil&lt;/a&gt;&lt;/code&gt; command will tell you about your computer's latency and cache hit ratio:&lt;/p&gt;&lt;pre&gt;&lt;br /&gt;$ dscacheutil -statistics&lt;br /&gt;Overall Statistics:&lt;br /&gt;    Average Call Time     - 0.118626&lt;br /&gt;    Cache Hits            - 236152&lt;br /&gt;    Cache Misses          - 231052&lt;br /&gt;    Total External Calls  - 279350&lt;br /&gt;&lt;br /&gt;Statistics by procedure:&lt;br /&gt;&lt;br /&gt;             Procedure   Cache Hits   Cache Misses   External Calls&lt;br /&gt;    ------------------   ----------   ------------   --------------&lt;br /&gt;         gethostbyname       161252          39952             6749&lt;br /&gt;         gethostbyaddr           60            151              211&lt;br /&gt;         ...&lt;br /&gt;&lt;/pre&gt;&lt;div style="text-align:center"&gt;&lt;em&gt;Listing 0: Mac OSX DNS statistics&lt;/em&gt;&lt;/div&gt;&lt;p&gt;These numbers are interesting but fairly useless for our purposes. It combines cached and uncached lookups into one "average". Also, &lt;a href="http://kb.mozillazine.org/Network.dnsCacheExpiration" title="Mozilla? Really?"&gt;browsers&lt;/a&gt; &lt;a href="http://list.opera.com/pipermail/opera-linux/2008-December/009846.html" title="Opera, tsk tsk"&gt;often&lt;/a&gt; &lt;a href="http://support.microsoft.com/kb/263558" title="Internet Explorer"&gt;cache&lt;/a&gt; and &lt;a href="https://lists.dns-oarc.net/pipermail/dns-operations/2008-September/003288.html" title="Ever wonder why Chrome is fast?"&gt;even&lt;/a&gt; &lt;a href="http://blog.chromium.org/2008/09/dns-prefetching-or-pre-resolving.html" title="It's resolving linked DNS records before you even click a link."&gt;precache&lt;/a&gt; DNS information, bypassing whatever the operating system is doing. So we can't rely on what the machine tells us. We need to do some experimenting on our own.&lt;/p&gt;&lt;p&gt;First, I ran long series of tests against Yahoo hostnames from the office, my house, and other locations. For 100 seconds I ran as many DNS lookups as I could and timed them. Each lookup was for a &lt;a href="http://en.wikipedia.org/wiki/Wildcard_DNS_record" title=""&gt;wildcard hostname&lt;/a&gt;. A wildcard like &lt;code&gt;*.dnstest.example.net&lt;/code&gt; means you can make up random new hostnames on the fly, eg &lt;code&gt;x9zzy.dnstest.example.net&lt;/code&gt;, that will resolve to a real IP address. This ensures that each test will be a full end-to-end DNS lookup without any caching to skew the numbers [1].&lt;/p&gt;&lt;p style="text-align:center"&gt;&lt;img src="http://farm3.static.flickr.com/2744/4131536246_8c5ae6e0a7_o.jpg" alt="" style="width: 500px; height: 226px"&gt;&lt;br&gt;&lt;em&gt;Figure 0: Average DNS latency at various locations&lt;/em&gt;&lt;/p&gt;&lt;p&gt;This graph is useful mostly to illustrate that it's possible for users on "broadband" connections to have invisible performance problems related to DNS. But it doesn't tell you which users or how many.&lt;/p&gt;&lt;p&gt;How can we figure out the response time distribution (&lt;em&gt;distribution&lt;/em&gt;, not average) for a wide range of users? How can we get a better idea of the role the DNS plays in the performance of web applications? Conditions on the internet change constantly. The tests would have to be large-scale and continuous to mean anything.&lt;/p&gt;&lt;p&gt;Let's scope things down a bit. We don't really care about how quickly users resolve any hostname. We care about how quickly &lt;em&gt;our&lt;/em&gt; users resolve &lt;em&gt;our&lt;/em&gt; hostnames. So maybe you can get the data you want by observing your users. Unfortunately DNS lookups happen mostly through computers we do not control. Worse, they happen over &lt;a id="dw8j" href="http://en.wikipedia.org/wiki/User_Datagram_Protocol" title="UDP"&gt;UDP&lt;/a&gt;, which doesn't expose performance data to the callee. The request and response packets are sent without any error correction or acknowledgement. So we can't just look at the usual logs we collect on our servers.&lt;/p&gt;&lt;p&gt;The librarian in New York will never know how long it took the librarian in Tucson to call you back. The hotel staffer in Toronto has no idea how you found their number. That is, unless you tell him. And that's what we'll do: run a special series of tests from the perspective of the caller, ie the users, and report back results.&lt;/p&gt;&lt;h3&gt;A DNS Observatory&lt;/h3&gt;&lt;p&gt;&lt;br /&gt;&lt;p&gt;It's tricky but not impossible to gather some statistics on user DNS latency without running benchmark software on their computers. One way works like this: &lt;/p&gt;&lt;/p&gt;&lt;ol&gt;&lt;li style="margin-bottom:1em"&gt;&lt;a href="http://xkcd.com/654/"&gt;&lt;img alt="" title="Everybody stand back. I'm going to try Science! (Thanks Xkcd)" src="http://farm3.static.flickr.com/2669/4130775309_b40334c1f5_o.jpg" style="width: 100px; height: 121px; float: right; margin-left: 1em; margin-right: 0px" border="0"&gt;&lt;/a&gt;Set up a wildcard hostname, perferably one that does not share cookies with your main site. Give it a low TTL, say, 60 seconds, so you don't pollute downstream caches.&lt;/li&gt;&lt;p&gt;&lt;li style="margin-bottom:1em"&gt;Set up a webserver for the wildcard hostname that serves zero-byte files as fast as possible. Make sure that &lt;a href="http://httpd.apache.org/docs/2.0/mod/core.html#keepalive" title="KeepAlive"&gt;KeepAlive&lt;/a&gt;, &lt;a href="http://redmine.lighttpd.net/issues/1239" title="Nagle"&gt;Nagle&lt;/a&gt;, and any &lt;a href="http://www.mnot.net/cache_docs/#CONTROL"&gt;caching headers&lt;/a&gt; are turned &lt;em&gt;off&lt;/em&gt;.&lt;/li&gt;&lt;/p&gt;&lt;p&gt;&lt;li style="margin-bottom:1em"&gt;In the footer of the pages in your main site, add a script similar to Listing 1. It performs two HTTP requests: &lt;code&gt;/A.gif&lt;/code&gt; and &lt;code&gt;/B.gif&lt;/code&gt;. The first image load, A, will require a full DNS lookup and an HTTP transaction. The second, B, should only involve an HTTP transaction.&lt;/li&gt;&lt;/p&gt;&lt;p&gt;&lt;li style="margin-bottom:1em"&gt;Subtract the time it takes to complete B from the time it takes to complete A, and you have a (very) rough idea of how long it took to perform just the DNS lookup. &lt;/li&gt;&lt;/p&gt;&lt;p&gt;&lt;li style="margin-bottom:1em"&gt;Send the DNS and HTTP statistics back to your server as part of another image request. You can extract the results later from your logs.&lt;/li&gt;&lt;/p&gt;&lt;p&gt;&lt;li style="margin-bottom:1em"&gt;Rinse and repeat over a large sample (&gt;10,000) of page views. Millions if you can.&lt;/li&gt;&lt;/p&gt;&lt;/ol&gt;&lt;p&gt;NB: You will get strange, even negative, numbers from this test. The deviation of individual data points can be greater than the phenomenon you are trying to measure. If you want to get accurate numbers for a specific user you'll need to run many tests over a period of time. But a single test per user works well enough in aggregate.&lt;/p&gt;&lt;pre class="code"&gt;&lt;br /&gt;&amp;lt;&lt;span style="color: #a020f0;"&gt;script&lt;/span&gt;&amp;gt;&lt;br /&gt;(&lt;span style="color: #0000ff;"&gt;function&lt;/span&gt;() {&lt;br /&gt;  &lt;span style="color: #0000ff;"&gt;function&lt;/span&gt; &lt;span style="color: #a020f0;"&gt;dns_test&lt;/span&gt;() {&lt;br /&gt;    &lt;span style="color: #0000ff;"&gt;var&lt;/span&gt; &lt;span style="color: #a52a2a;"&gt;random&lt;/span&gt; = Math.floor(Math.random()*(2147483647)).toString(36);&lt;br /&gt;    &lt;span style="color: #0000ff;"&gt;var&lt;/span&gt; &lt;span style="color: #a52a2a;"&gt;host&lt;/span&gt; = &lt;span style="color: #a52a2a;"&gt;'http://'&lt;/span&gt;+random+&lt;span style="color: #a52a2a;"&gt;".dnstest.example.net"&lt;/span&gt;;&lt;br /&gt;    &lt;span style="color: #0000ff;"&gt;var&lt;/span&gt; &lt;span style="color: #a52a2a;"&gt;img1&lt;/span&gt; = &lt;span style="color: #0000ff;"&gt;new&lt;/span&gt; Image();&lt;br /&gt;    &lt;span style="color: #0000ff;"&gt;var&lt;/span&gt; &lt;span style="color: #a52a2a;"&gt;img2&lt;/span&gt; = &lt;span style="color: #0000ff;"&gt;new&lt;/span&gt; Image();&lt;br /&gt;    &lt;span style="color: #0000ff;"&gt;var&lt;/span&gt; &lt;span style="color: #a52a2a;"&gt;img3&lt;/span&gt; = &lt;span style="color: #0000ff;"&gt;new&lt;/span&gt; Image();&lt;br /&gt;    &lt;span style="color: #0000ff;"&gt;var&lt;/span&gt; &lt;span style="color: #a52a2a;"&gt;ts&lt;/span&gt; = &lt;span style="color: #000099;"&gt;null&lt;/span&gt;;&lt;br /&gt;    &lt;span style="color: #0000ff;"&gt;var&lt;/span&gt; &lt;span style="color: #a52a2a;"&gt;stats&lt;/span&gt; = {};&lt;br /&gt;    img1.&lt;span style="color: #a020f0;"&gt;onload&lt;/span&gt; = &lt;span style="color: #0000ff;"&gt;function&lt;/span&gt;() {&lt;br /&gt;        stats[&lt;span style="color: #a52a2a;"&gt;'dns'&lt;/span&gt;] = (&lt;span style="color: #0000ff;"&gt;new&lt;/span&gt; Date()).getTime() - ts;&lt;br /&gt;        ts = (&lt;span style="color: #0000ff;"&gt;new&lt;/span&gt; Date()).getTime();&lt;br /&gt;        img2.src = host + &lt;span style="color: #a52a2a;"&gt;"/B.gif"&lt;/span&gt;;&lt;br /&gt;    };&lt;br /&gt;    img2.&lt;span style="color: #a020f0;"&gt;onload&lt;/span&gt; = &lt;span style="color: #0000ff;"&gt;function&lt;/span&gt;() {&lt;br /&gt;        stats[&lt;span style="color: #a52a2a;"&gt;'http'&lt;/span&gt;] = (&lt;span style="color: #0000ff;"&gt;new&lt;/span&gt; Date()).getTime() - ts;&lt;br /&gt;        stats.dns = stats.dns - stats.http; &lt;span style="color: #009900;"&gt;// the clever bit&lt;/span&gt;&lt;br /&gt;        img3.src = host + &lt;span style="color: #a52a2a;"&gt;'/dnstest.gif?dns='&lt;/span&gt;+stats.dns+&lt;span style="color: #a52a2a;"&gt;'&amp;amp;http='&lt;/span&gt;+stats.http;&lt;br /&gt;    };&lt;br /&gt;    ts = (&lt;span style="color: #0000ff;"&gt;new&lt;/span&gt; Date()).getTime();&lt;br /&gt;    img1.src = host + &lt;span style="color: #a52a2a;"&gt;"/A.gif"&lt;/span&gt;;&lt;br /&gt;  }&lt;br /&gt;  window.setTimeout(dns_test, 11337);&lt;br /&gt;})();&lt;br /&gt;&amp;lt;/&lt;span style="color: #a020f0;"&gt;script&lt;/span&gt;&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;div style="text-align: center;"&gt;&lt;em&gt;Listing 1: A poor man's DNS observatory&lt;/em&gt;&lt;/div&gt;&lt;p&gt;Below is a graph of the distribution of uncached DNS lookup times from real users in the wild, collected by this script over one week. The sample was heavily skewed towards US broadband connections. The median was 146 milliseconds and the &lt;a href="http://en.wikipedia.org/wiki/Geometric_mean"&gt;geometric mean&lt;/a&gt; was 163 milliseconds [2]. This is rather larger than the 20-120 milliseconds quoted in the &lt;a href="http://developer.yahoo.com/performance/rules.html#dns_lookups"&gt;Yahoo Performance Guidelines&lt;/a&gt; for a "typical" DNS lookup. Beware pithy numbers (even ours).&lt;/p&gt;&lt;p&gt;The distribution is even more interesting than the averages. &lt;em&gt;Twenty percent&lt;/em&gt; of users in our sample took more than 500 milliseconds just to resolve one hostname. Granted, these lookups were uncached. Assuming a 50% cache hit rate, that's still &lt;em&gt;one out of ten&lt;/em&gt; users in this dataset laboring under crappy DNS performance. As of this writing that's a market as large as Safari, Chrome and Opera combined.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;img src="http://farm3.static.flickr.com/2746/4131536662_890ede0323_o.jpg" alt="" style="width: 539px; height: 320px"&gt;&lt;/div&gt;&lt;/p&gt;&lt;div style="text-align: center;"&gt;&lt;em&gt;Figure 1: A histogram of uncached DNS request times&lt;/em&gt;&lt;/div&gt;&lt;p&gt;The cause is unclear. It's possible that user network quality is just that bad. It could be physical distance. It could also be the DNS resolvers of ISPs at fault. It could be your DNS server. Or it could be something else. Or all of the above.&lt;/p&gt;&lt;p&gt;Remember that your mileage may vary. Not every combination of site and userbase will have a similar graph. Also remember that a lot of caching is going on at every level of the system. There's not a simple fixed cost to using alternate hosts for your images and scripts. The best strategy may well be to have one and only one "asset host" or CDN that does not share cookies with your main site.&lt;/p&gt;&lt;p&gt; If you run a commercial website, consider setting up with a dedicated DNS hosting provider that has presence on several continents. The DNS hosting service typically thrown in for free by domain registrars is not very good. For most sites, solid DNS hosting costs about $USD 50 per year. It's worth the effort. Heck, set up with two different services for failover.&lt;/p&gt;&lt;h3&gt;Try this at home&lt;/h3&gt;&lt;p&gt;For privacy reasons we can't release the raw data we collected. But if you have a website with a fair amount of traffic, I strongly encourage you to run these DNS measurements for yourself. You can learn a lot by drilling down into the data.&lt;/p&gt;&lt;ul&gt;&lt;li style="margin-bottom:1em"&gt;Play with graphing the distributions of different subnets (eg 18.* for MIT or 12.* for AT&amp;amp;T). You might be surprised at who is fast and who is slow. &lt;/li&gt;&lt;p&gt;&lt;li style="margin-bottom:1em"&gt;&lt;img src="http://farm3.static.flickr.com/2493/4131537452_497efdc669_o.jpg" style="width: 176px; height: 82px; float: right; margin-left: 1em; margin-right: 0px" alt="" id="yw.q"&gt;In your webserver logs for /dnstest.gif there should be a User-Agent field as well. So you can look at correlations between DNS performance, browsers, and operating systems. For example, check out those little bumps at 1s and 3s in Figure 1. It turns out that the DNS resolver in Windows has &lt;a id="u85b" href="http://support.microsoft.com/kb/198550" title="aggressive timeouts" &gt;aggressive timeouts&lt;/a&gt;. Those bumps are caused by Windows clients timing out then succeeding on a retry.&lt;/li&gt;&lt;/p&gt;&lt;p&gt;&lt;li style="margin-bottom:1em"&gt;We're not just timing DNS latency, we're also timing how long it takes to perform a minimal TCP handshake + HTTP transaction. That gives you interesting information about user connection latencies, for free. But that's a whole 'nother subject.&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;/p&gt;&lt;p&gt;This article is the &lt;a href="http://developer.yahoo.net/blog/archives/2009/10/a_engineers_gui.html" title="second in a series"&gt;second in a series&lt;/a&gt; and part of ongoing research on web app performance. If you have any suggestions or ideas to help improve the experiments, please leave a note in the comments. Next we hope to dig into more detail about user network performance data and how you can use it to improve your websites and applications.&lt;/p&gt;&lt;h4&gt;Notes&lt;/h4&gt;&lt;p&gt;[0] The dot "." at the end is not a typo. Though "com" and "net" are called "top-level" domains there is actually one more layer behind them called the root domain, designated by that trailing dot. The root domain is managed as a global public utility by dozens of internet service providers all over the world.&lt;/p&gt;&lt;p&gt;Fun fact: &lt;a href="http://www.iis.se/en/om-se/incidenten-12-okober-2009/"&gt;the entire country of Sweden&lt;/a&gt; dropped off the 'net in October 2009 because a network operator forgot to include that last dot in a configuration file.&lt;/p&gt;&lt;p&gt;[1] I'm fudging here a bit. It's possible that during this test, everything up to .dnstest.example.net will be cached at the user's ISP. This is by design, to reduce load on the root and top-level domain servers. But the lookup should always at least do a request to the ISP's resolver and a request in turn to example.net's authoritative DNS server.&lt;/p&gt;&lt;p&gt;[2] These kinds of datasets tend to be log-normal, with long thin tails trailing from a large central spike. The "average" value, or arithmetic mean, would be misleading in this case so we won't discuss it.&lt;/p&gt;&lt;p&gt;[&amp;infin;] Bonus footnote! Here is the code to generate a table from your webserver logs:&lt;/p&gt;&lt;pre class="code"&gt;&lt;br /&gt;&lt;span style="color: #009900;"&gt;# &lt;/span&gt;&lt;span style="color: #009900;"&gt;run a grep for "/dnstest.gif" and save to a file&lt;br /&gt;&lt;/span&gt;bzcat /your/apache/logs/access_log.*.txt.bz2 | grep dnstest.gif &amp;gt; /tmp/dnstest.log&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #009900;"&gt;# &lt;/span&gt;&lt;span style="color: #009900;"&gt;perl magic to split the data into columns&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #da70d6;"&gt;echo&lt;/span&gt; &lt;span style="color: #a52a2a;"&gt;"A,B,C,D,dns,http"&lt;/span&gt; &amp;gt; latency.csv&lt;br /&gt;perl -lane &lt;span style="color: #a52a2a;"&gt;'if (/^([\d]+)\.([\d]+)\.([\d]+)\.([\d]+).+dns=(\d+)\&amp;amp;http=(\d+)/) { print "$1,$2,$3,$4,$5,$6" }'&lt;/span&gt; dnstest.log &amp;gt;&amp;gt; latency.csv&lt;br /&gt;&lt;/pre&gt;Here are the &lt;a href="http://www.r-project.org"&gt;R commands&lt;/a&gt; to generate graphs and poke around at the data. If you've never tried R, it is a wonderful open-source statistics suite. The best introduction on how to use it &lt;a href="http://cran.r-project.org/doc/manuals/R-intro.pdf"&gt;is here&lt;/a&gt;. (PDF)&lt;pre class="code"&gt;&lt;br /&gt;&lt;span style="color: #009900;"&gt;## &lt;/span&gt;&lt;span style="color: #009900;"&gt;R script for generating the histogram&lt;br /&gt;&lt;/span&gt;x &lt;span style="color: #000099;"&gt;&amp;lt;-&lt;/span&gt; read.csv(&lt;span style="color: #a52a2a;"&gt;"latency.csv"&lt;/span&gt;, header=&lt;span style="color: #228b22;"&gt;TRUE&lt;/span&gt;)&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #009900;"&gt;# &lt;/span&gt;&lt;span style="color: #009900;"&gt;take only results greater than 0 and less than 4,000&lt;br /&gt;&lt;/span&gt;y &lt;span style="color: #000099;"&gt;&amp;lt;-&lt;/span&gt; subset(x, (x$dns &amp;gt; 0 &amp;amp; x$dns &amp;lt; 4000))&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #009900;"&gt;# &lt;/span&gt;&lt;span style="color: #009900;"&gt;draw the histogram &lt;br /&gt;&lt;/span&gt;hist(y$dns, xlab=&lt;span style="color: #a52a2a;"&gt;"Milliseconds"&lt;/span&gt;, main=&lt;span style="color: #228b22;"&gt;NA&lt;/span&gt;, breaks=200, col=&lt;span style="color: #a52a2a;"&gt;"red"&lt;/span&gt;, border=&lt;span style="color: #a52a2a;"&gt;"red"&lt;/span&gt;, prob=&lt;span style="color: #228b22;"&gt;FALSE&lt;/span&gt;)&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #009900;"&gt;# &lt;/span&gt;&lt;span style="color: #009900;"&gt;rug() adds "tassles" to the bottom of the graph to show data point density.&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #009900;"&gt;# &lt;/span&gt;&lt;span style="color: #009900;"&gt;rug() makes tassles. Get it? Yeah.&lt;br /&gt;&lt;/span&gt;rug(sample(y$dns, 5000))&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #009900;"&gt;## &lt;/span&gt;&lt;span style="color: #009900;"&gt;bonus bonus: more interesting stats&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #009900;"&gt;# &lt;/span&gt;&lt;span style="color: #009900;"&gt;Show only requests from Brazil. this filter is not strictly true&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #009900;"&gt;# &lt;/span&gt;&lt;span style="color: #009900;"&gt;(ie, they have more subnets than 200/8) but it's true enough to play with.&lt;br /&gt;&lt;/span&gt;brazil = subset(y, (y$A==200))&lt;br /&gt;hist(brazil$dns, xlab=&lt;span style="color: #a52a2a;"&gt;"Milliseconds"&lt;/span&gt;, main=&lt;span style="color: #a52a2a;"&gt;"Brazil (200.*)"&lt;/span&gt;, breaks=200, col=&lt;span style="color: #a52a2a;"&gt;"green"&lt;/span&gt;, border=&lt;span style="color: #a52a2a;"&gt;"green"&lt;/span&gt;, prob=&lt;span style="color: #228b22;"&gt;FALSE&lt;/span&gt;)&lt;br /&gt;rug(sample(brazil$dns, 5000))&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #009900;"&gt;## &lt;/span&gt;&lt;span style="color: #009900;"&gt;other interesting subnets&lt;br /&gt;&lt;/span&gt;mit = subset(y, (y$A==18))&lt;br /&gt;att = subset(y, (y$A==12))&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #009900;"&gt;# &lt;/span&gt;&lt;span style="color: #009900;"&gt;the "average" is not very useful with log-normal datasets&lt;br /&gt;&lt;/span&gt;&lt;span style="color: #009900;"&gt;#&lt;/span&gt;&lt;span style="color: #009900;"&gt;mean(y$dns)&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #009900;"&gt;# &lt;/span&gt;&lt;span style="color: #009900;"&gt;median and geometric mean are more informative&lt;br /&gt;&lt;/span&gt;median(y$dns)&lt;br /&gt;exp(mean(log(y$dns)))&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #009900;"&gt;# &lt;/span&gt;&lt;span style="color: #009900;"&gt;percentage of users over / under a specific point&lt;br /&gt;&lt;/span&gt;table(y$dns &amp;gt; 250) / length(y$dns) &lt;br /&gt;table(y$dns &amp;gt; 500) / length(y$dns) &lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2936626869504789093-7258220830621948532?l=carlos.bueno.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/2936626869504789093/7258220830621948532/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=2936626869504789093&amp;postID=7258220830621948532' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2936626869504789093/posts/default/7258220830621948532'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2936626869504789093/posts/default/7258220830621948532'/><link rel='alternate' type='text/html' href='http://carlos.bueno.org/2009/11/dismal-guide-to-dns.html' title='A Dismal Guide to DNS'/><author><name>bueno</name><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='02278308746331037541'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2936626869504789093.post-5335344635181842869</id><published>2009-11-21T11:21:00.000-08:00</published><updated>2009-11-29T23:14:19.606-08:00</updated><title type='text'>YCombinator's RFS #5: An Accidental Case Study</title><content type='html'>&lt;p&gt;&lt;i&gt;Update: check out &lt;a href="http://ddotdash.com"&gt;ddotdash&lt;/a&gt;.&lt;/i&gt;&lt;/p&gt;&lt;p&gt;&lt;img src="http://carlos.bueno.org/uploaded_images/eee-mac-793772.jpg" style="float: right; margin-bottom: 8px; margin-left: 10px; margin-right: 0px; margin-top: 0px;" width="250" /&gt;I pricked my ears up at the latest &lt;a href="http://ycombinator.com/rfs5.html"&gt;Request For Startups&lt;/a&gt; from YCombinator. Last year I wrote the first 6,000 lines of &lt;a href="http://www.archivd.com/"&gt;my last startup&lt;/a&gt; on an eeePC 901, which is about half the size of a MacBook Air. It's cheap and solid-state so I treated it more like a largish book than a smallish computer. I toted it around to many more places, even parties. Losing that thread of worry about damage made it feel an extra half-kilo lighter. I sometimes wished it had a cell phone built in.&lt;/p&gt;&lt;p&gt;In the end the BabyBook was too cramped for long sessions. Both my code output and my hands suffered. The RFS's &lt;a href="http://paulgraham.com/apple.html"&gt;accompanying essay&lt;/a&gt; is correct that whatever platform hackers use to hack on eventually wins in the larger market. But it doesn't necessarily follow that because hackers have smartphones the next step is hacking directly on them. An equally plausible path is that our phones become portable homedirs that plug into any computer when we need to do real work. &lt;/p&gt;&lt;p&gt;Any human input surface smaller than a sheet of paper starts to bump against physical limitations that Moore's law can't fix. To do development on a mobile you have to be willing to give up both touch-typing and large screens. That's a &lt;i&gt;big&lt;/i&gt; sacrifice, and it's hard to say what benefits I'd trade for them.&lt;/p&gt;&lt;p&gt;Any innovation that improves the speed of reading or writing code on a small device should also work on a large device, unless it takes advantage of features or use cases unique to the small one. Otherwise it's not a net advantage to mobile hacking. I giggle at the thought of using the accelerometer to indent, smacking my code into line. But the list of things an iPhone can do that a laptop can't is short while the inverse is very long.&lt;/p&gt;&lt;h3&gt;Was the OQO early, or wrong?&lt;/h3&gt;&lt;p&gt;&lt;img src="http://carlos.bueno.org/uploaded_images/palm-kbd-776400.jpg" style="float: left; margin-bottom: 8px; margin-left: 0px; margin-right: 10px; margin-top: 0px;" width="150" /&gt; Let's start fresh. Imagine you have a device the size of a thick passport or Moleskine. It is a phone plus a computer as powerful as a laptop from three years ago. You can hack on it directly in a pinch. It has enough storage for all of your working projects. You can plug it into any keyboard and screen (or computer) and start hacking.&lt;/p&gt;&lt;p&gt;You know what? That sounds pretty neat. And we'll probably get there soon. Perhaps someone will make a dongle that does this for iPhone or Android. Or it could be the accessory that saves the &lt;a href="http://wiki.openmoko.org/wiki/Neo_FreeRunner"&gt;FreeRunner&lt;/a&gt;. But this is not very different from today, except for the reduced utility of your computer when you are not at a properly-equipped desk.&lt;/p&gt;&lt;p&gt;So let's go back to code input and output. We use keyboards because we have 100 years of experience with them as speed-of-thought input devices. We use ever-larger screens in order to view ever-larger amounts of code and data in one glance, partly because moving our eyes is faster than moving our hands. Random access matters a lot.&lt;/p&gt;&lt;p&gt;Multitouch gestures (pinch, zoom, scroll, etc) are interesting and so far underused. Using the "mouse" is less of a productivity hit on a phone because your fingers are already in position. Instead of representing code as long lists we spread out like so much wallpaper, maybe we can represent and navigate it like the directed multigraph it actually is. The Canon Cat and &lt;a href="http://en.wikipedia.org/wiki/The_Humane_Interface"&gt;Archy&lt;/a&gt; might point the way here.&lt;/p&gt;&lt;p&gt;That leaves speed of input. Hinting and autocomplete might bring the mobile programmer back up to an acceptable wpm. Terser languages and WYSIWYG UI builders can help as well. But still I worry about fatigue. It's hard enough to work comfortably on the hardware we have. On the other hand, there's no reason we need to have a keyboard per se. Experienced telegraph operators could type up to &lt;a href="http://en.wikipedia.org/wiki/Morse_code#Speed_records"&gt;40 wpm&lt;/a&gt; with one finger using Morse code. Typing on virtual keyboards is slow because unlike keyboards or game controllers you have to carefully coordinate vision with motion. Instead, I wonder what &lt;a href="http://ddotdash.com"&gt;one could do&lt;/a&gt; with two thumbs, an updated Morse, and a little practice.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2936626869504789093-5335344635181842869?l=carlos.bueno.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/2936626869504789093/5335344635181842869/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=2936626869504789093&amp;postID=5335344635181842869' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2936626869504789093/posts/default/5335344635181842869'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2936626869504789093/posts/default/5335344635181842869'/><link rel='alternate' type='text/html' href='http://carlos.bueno.org/2009/11/ycombinators-rfs-5-accidental-case.html' title='YCombinator&apos;s RFS #5: An Accidental Case Study'/><author><name>bueno</name><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='02278308746331037541'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2936626869504789093.post-9086356859056895644</id><published>2009-10-17T14:09:00.000-07:00</published><updated>2009-10-17T14:11:22.832-07:00</updated><title type='text'>YUI Tip: Using DataTable and TreeView together</title><content type='html'>For this tip we will make a browser for web server logs. The TreeView will display file and folder paths, and the DataTable will display individual log lines. Clicking on a file or folder in the Tree will cause the DataTable to filter out all but that path.&lt;br /&gt;&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 500px; height: 104px;" src="http://www.yuiblog.com/blog/wp-content/uploads/2009/10/datatable-treeview2.png" border="0" alt="" /&gt;&lt;br /&gt;&lt;br /&gt;Read the rest at &lt;a href="http://yuiblog.com/blog/2009/10/12/using-yui-treeview-and-datatable-together/"&gt;Yahoo's UI blog&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2936626869504789093-9086356859056895644?l=carlos.bueno.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/2936626869504789093/9086356859056895644/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=2936626869504789093&amp;postID=9086356859056895644' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2936626869504789093/posts/default/9086356859056895644'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2936626869504789093/posts/default/9086356859056895644'/><link rel='alternate' type='text/html' href='http://carlos.bueno.org/2009/10/yui-tip-using-datatable-and-treeview.html' title='YUI Tip: Using DataTable and TreeView together'/><author><name>bueno</name><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='02278308746331037541'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2936626869504789093.post-4092138695913740717</id><published>2009-10-07T17:41:00.000-07:00</published><updated>2009-11-28T14:04:42.855-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='dismal guide'/><title type='text'>A Dismal Guide to Bandwidth</title><content type='html'>&lt;small&gt;&lt;em&gt;(Originally appeared on &lt;a href="http://developer.yahoo.net/blog/archives/2009/10/a_engineers_gui.html"&gt;Yahoo's Developer Blog&lt;/a&gt;)&lt;/em&gt;&lt;/small&gt;&lt;br /&gt;&lt;img alt="" height="240" src="http://farm2.static.flickr.com/1053/525734621_06674aaf7e_m.jpg" style="float: right; margin: 10px;" width="161" /&gt;Web app developers spend most of our time not thinking about how data is actually transmitted through the bowels of the network stack. Abstractions at the application layer let us pretend that networks read and write whole messages as smooth streams of bytes. Generally this is a good thing. But knowing what's going underneath is crucial to performance tuning and application design. The character of our users' internet connections is changing and some of the rules of thumb we rely on may need to be revised.&lt;br /&gt;&lt;a name='more'&gt;&lt;/a&gt;In reality, the Internet is more like a giant cascading multiplayer game of &lt;a href="http://en.wikipedia.org/wiki/Pachinko"&gt;pachinko&lt;/a&gt;. You pour some balls in, they bounce around, lights flash and —usually— they come out in the right order on the other side of the world.&lt;br /&gt;&lt;h3&gt;What we talk about, when we talk about bandwidth&lt;/h3&gt;It's common to talk about network connections solely in terms of "bandwidth". Users are segmented into the high-bandwidth who get the best experience, and low-bandwidth users in the backwoods. We hope some day everyone will be high-bandwidth and we won't have to worry about it anymore.&lt;br /&gt;That mental shorthand served when users had reasonably consistent wired connections and their computers ran one application at a time. But it's like talking only about the top speed of a car or the MHz of a computer. Latency and asymmetry matter at least as much as the notional bits-per-second and I argue that they are becoming even more important. The quality of the "last mile" of network between users and the backbone is in some ways getting worse as people ditch their copper wires for shared wifi and mobile towers, and clog their uplinks with video chat.&lt;br /&gt;It's a rough world out there, and we need to to a better job of thinking about and testing under realistic network conditions. A better mental model of bandwidth should include:&lt;br /&gt;&lt;ul&gt;&lt;li class="bullist"&gt;packets-per-second&lt;/li&gt;&lt;li class="bullist"&gt;packet latency&lt;/li&gt;&lt;li class="bullist"&gt;upstream vs downstream&lt;/li&gt;&lt;/ul&gt;&lt;h3&gt;Packets, not bytes&lt;/h3&gt;The quantum of internet transmission is not the bit or the byte, it's the &lt;em&gt;packet&lt;/em&gt;. Everything that happens on the 'net happens as discrete pachinko balls of regular sizes. A message of N bytes is chopped into &lt;code&gt;ceil(N / 1460)&lt;/code&gt; packets [&lt;a href="http://www.blogger.com/post-edit.g?blogID=2936626869504789093&amp;amp;postID=4092138695913740717#note1" title="Note 1"&gt;1&lt;/a&gt;] which are then sent willy-nilly. That means there is little to no difference between sending 1 byte or 1,000. It also means that sending 1,461 bytes is &lt;em&gt;twice&lt;/em&gt; the work of sending 1,460: two packets have to be sent, received, reassembled, and acknowledged.&lt;br /&gt;&lt;pre&gt;    Packet #1 Payload&lt;br /&gt;    &lt;br /&gt;    .....................................................................&lt;br /&gt;    .....................................................................&lt;br /&gt;    .....................................................................&lt;br /&gt;    .....................................................................&lt;br /&gt;    .....................................................................&lt;br /&gt;    .....................................................................&lt;br /&gt;    .....................................................................&lt;br /&gt;    .....................................................................&lt;br /&gt;    .....................................................................&lt;br /&gt;    .....................................................................&lt;br /&gt;    .....................................................................&lt;br /&gt;    .....................................................................&lt;br /&gt;    .....................................................................&lt;br /&gt;    .....................................................................&lt;br /&gt;    .....................................................................&lt;br /&gt;    .....................................................................&lt;br /&gt;    .....................................................................&lt;br /&gt;    .....................................................................&lt;br /&gt;    .....................................................................&lt;br /&gt;    .....................................................................&lt;br /&gt;    ...........................................................&lt;br /&gt;&lt;br /&gt;    Packet #2 Payload&lt;br /&gt;    .&lt;br /&gt;&lt;/pre&gt;&lt;em&gt;Listing 0: Byte 1,461 aka The Byte of Doom&lt;/em&gt;&lt;br /&gt;Crossing the packet line in HTTP is very easy to do without knowing it. Suppose your application uses a third-party web analytics library which, like most analytics libraries, stores a big hunk of data about the user inside long-lived cookie tied to your domain. Suppose you also stuff a little bit of data into the cookie too. This cookie data is thereafter echoed back to your web server upon each request. The boilerplate HTTP headers (Accept, User-agent, etc) sent by every modern browser take up a few hundred more bytes. Add in the actual URL, Referer header, query parameters... and you're dead. There is also the &lt;a href="http://josephscott.org/archives/2009/08/xmlhttprequest-xhr-uses-multiple-packets-for-http-post/"&gt;little-known fact&lt;/a&gt; that browsers &lt;a href="http://yuiblog.com/blog/2007/03/01/performance-research-part-3/#comment-59531"&gt;split certain POST requests&lt;/a&gt; into at least two packets regardless of the size of the message.&lt;br /&gt;One packet, more or less, who cares? For one, none of your fancy caching and CDNs can help the client send data upstream. &lt;a href="http://en.wikipedia.org/wiki/Slow-start"&gt;TCP slow-start&lt;/a&gt; means that the client will wait for acknowledgement of the first packets before sending the rest. And as we'll see below, that extra packet can make a large difference in the responsiveness of your app when it's compounded by latency and narrow upstream connections.&lt;br /&gt;&lt;h3&gt;Packet Latency&lt;/h3&gt;Packet latency is the time it takes a packet to wind through the wires and hops between points A and B. It is roughly a function of the physical distance (at &lt;a href="http://en.wikipedia.org/wiki/Optical_fiber#Index_of_refraction"&gt;2/3 of the speed of light&lt;/a&gt;) plus the time the packet spends queued up inside various network devices along the way. A typical packet sent on the 'net backbone between San Francisco and New York will take about 60 milliseconds. But the latency of a user's last-mile internet connection can vary enormously [&lt;a href="http://www.blogger.com/post-edit.g?blogID=2936626869504789093&amp;amp;postID=4092138695913740717#note2"&gt;2&lt;/a&gt;]. Maybe it's a hot day and their router is running slowly. The EDGE mobile network has a best-case latency of 150msec and a real-world average of 500msec. There is a semi-famous &lt;a href="http://rescomp.stanford.edu/%7Echeshire/rants/Latency.html"&gt;rant from 1996&lt;/a&gt; complaining about &lt;em&gt;100&lt;/em&gt;msec latency from substandard telephone modems. If only.&lt;br /&gt;&lt;h3&gt;Packet loss&lt;/h3&gt;Packet loss manifests as packet latency. The odds are decent that a couple packets that made up the copy of this article you are reading got lost along the way. Maybe they had a collision, maybe they stopped to have a beer and forgot. The sending end then has to notice that a packet has not been acknowledged and re-transmit.&lt;br /&gt;Wireless home networks are becoming the norm and they are unfortunately very susceptible to interference from devices sitting on the 2.4GHz band, like microwaves and baby monitors. They are also notorious for cross-vendor incompatibilities. Another dirty secret is that consumer-grade wifi devices you'll find in cafés and small offices don't do &lt;a href="http://en.wikipedia.org/wiki/Traffic_shaping"&gt;traffic shaping&lt;/a&gt;. All it takes is one user watching a video to flood the uplink.&lt;br /&gt;&lt;h3&gt;Upstream &amp;lt; Downstream&lt;/h3&gt;Internet providers lie. That "6 Megabit" cable internet connection is actually 6mbps down and 1mbps up. The bandwidth reserved for upstream transmission is often 20% or less of the total available. This was an almost defensible thing to do until users started file sharing, VOIPing, video chatting, etc en masse. Even though users still pull more information down than they send up, the asymmetry of their connections means that the upstream is a chokepoint that will probably get worse for a long time.&lt;br /&gt;&lt;h3&gt;A Dismal Testing Harness&lt;/h3&gt;&lt;div class="figure"&gt;&lt;img alt="A laptop sitting on top of a microwave with a cup of tea inside. The microwave is labeled 'packet loss generator'. The laptop is labeled 'packet loss measurement device'." height="500" src="http://farm3.static.flickr.com/2551/3951382520_b75c61362f.jpg" width="350" /&gt;&lt;br /&gt;&lt;em&gt;Figure 0: It's popcorn for dinner tonight, my love. I'm doing science!&lt;/em&gt;&lt;br /&gt;&lt;/div&gt;We need a way to simulate high latency, variable latency, limited packet rate, and packet loss. In the olden days a good way to test the performance of a system through a bad connection was to configure the switch port to run at half-duplex. Sometimes we even did such testing on purpose. :) &lt;a href="http://www.torproject.org/"&gt;Tor&lt;/a&gt; is pretty good for simulating a crappy connection but it only works for publicly-accessible sites. Microwave ovens consistently cause packet loss (my parents' old monster kills wifi at 20 paces) but it's a waste of electricity.&lt;br /&gt;The &lt;a href="http://developer.apple.com/mac/library/documentation/Darwin/Reference/ManPages/man8/ipfw.8.html"&gt;ipfw&lt;/a&gt; on Mac and FreeBSD comes in handy for local testing. The command below will approximate an iPhone on the EDGE network with a 350kbit/sec throttle, 5% packet loss rate and 500msecs latency. Use &lt;code&gt;sudo ipfw flush&lt;/code&gt; to deactivate the rules when you are done.&lt;br /&gt;&lt;pre&gt;    $ sudo ipfw pipe 1 config bw 350kbit/s plr 0.05 delay 500ms&lt;br /&gt;    $ sudo ipfw add pipe 1 dst-port http&lt;br /&gt;&lt;/pre&gt;Here's another that will randomly drop half of all DNS requests. Have fun with that one.&lt;br /&gt;&lt;pre&gt;    $ sudo ipfw pipe 2 config plr 0.5&lt;br /&gt;    $ sudo ipfw add pipe 2 dst-port 53&lt;br /&gt;&lt;/pre&gt;To measure the effects of latency and packet loss I chose a &lt;a href="http://yui.yahooapis.com/2.8.0r4/build/datatable/datatable-min.js"&gt;highly-cached 130KB file&lt;/a&gt; from Yahoo's servers. I ran a script to download it as many times as possible in 5 minutes under various ipfw rules [&lt;a href="http://www.blogger.com/post-edit.g?blogID=2936626869504789093&amp;amp;postID=4092138695913740717#note3"&gt;3&lt;/a&gt;]. The "baseline" runs were the control with no ipfw restrictions or interference.&lt;br /&gt;&lt;div class="figure"&gt;&lt;img alt="A graph showing number of request in 300 seconds on the X axis and packet latency on the Y axis. With increasing packet latency the number of requests decreases" height="301" src="http://farm3.static.flickr.com/2639/3967843800_a613e9c8ce.jpg" width="500" /&gt;&lt;br /&gt;&lt;em&gt;Figure 1: The effect of packet latency on download speed&lt;/em&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="figure"&gt;&lt;img alt="A graph showing number of request in 300 seconds on the X axis and packet loss on the Y axis. With increasing packet loss the number of requests decreases." height="300" src="http://farm3.static.flickr.com/2506/3967842708_f143eca6db.jpg" width="500" /&gt;&lt;br /&gt;&lt;em&gt;Figure 2: Effect of packet loss on download speed&lt;/em&gt;&lt;br /&gt;&lt;/div&gt;Just 100 milliseconds of packet latency is enough to cause a smallish file to download in an average of 1500 milliseconds instead of 350 milliseconds. And that's not the worst part: the individual download times ranged from 1,000 to 3,000 milliseconds. Software that's consistently slow can be endured. Software that halts for no obvious reason is maddening.&lt;br /&gt;&lt;div class="figure"&gt;&lt;img alt="A graph showing the response time in seconds from 0 to 3 on the Y axis with time on the X axis. The response times fluctuate between 0.25 and 3 seconds until a point in time labeled 'tea is done' when they become consistent between 0.25 and 0.75s." height="193" src="http://farm3.static.flickr.com/2461/3950605017_2fee97f5de.jpg" width="500" /&gt;&lt;br /&gt;&lt;em&gt;Figure 3: Extreme volatility of response times during packet loss.&lt;/em&gt;&lt;br /&gt;&lt;/div&gt;&lt;h3&gt;So, latency sucks. Now what?&lt;/h3&gt;Yahoo's &lt;a href="http://developer.yahoo.com/performance/rules.html"&gt;web performance guidelines&lt;/a&gt; are still the most complete resource around, and backed up by real-world data. The key advice is to reduce the number of HTTP requests, reduce the amount of data sent, and to order requests in ways that use the observed behavior of browsers to best effect. However there is a simplification which buckets users into high/low/mobile categories. This doesn't necessarily address poor-quality bandwidth across all classes of user. The user's connection quality is often very bad and getting worse, which changes the calculus of what techniques to employ. In particular we should also take into account that:&lt;br /&gt;&lt;ul&gt;&lt;li class="bullist"&gt;Upstream packets are almost always expensive.&lt;/li&gt;&lt;li class="bullist"&gt;Any client can have high or low overall bandwidth.&lt;/li&gt;&lt;li class="bullist"&gt;High latency is not an error condition, it's a fact of life.&lt;/li&gt;&lt;li class="bullist"&gt;TCP connections and DNS lookups are expensive under high latency.&lt;/li&gt;&lt;li class="bullist"&gt;Variable latency is in some ways worse than low bandwidth.&lt;/li&gt;&lt;/ul&gt;Assuming that a large but unknown percentage of your users labor under adverse network conditions, here are some things you can do:&lt;br /&gt;&lt;ul&gt;&lt;li class="bullist"&gt;To keep your user's HTTP requests down to one packet, stay within a budget of about 800 bytes for cookies and URLs. Note that every byte of the URL counts twice: once for the URL and once for the Referer header on subsequent clicks. An interesting technique is to store app state that doesn't need to go to the server in fragment identifiers instead of query string parameters, e.g. &lt;code&gt;/blah#foo=bar&lt;/code&gt; instead of &lt;code&gt;/blah?foo=bar&lt;/code&gt;. Nothing after the # mark is sent to the server.&lt;/li&gt;&lt;li class="bullist"&gt;If your app sends largish amounts of data upstream (excluding images, which are already compressed), consider implementing client-side compression. It's possible to get 1.5:1 compression with a simple LZW+Base64 function; if you're willing to monkey with ActionScript you could probably do &lt;a href="http://probertson.com/projects/gzipencoder/"&gt;real gzip compression&lt;/a&gt;.&lt;/li&gt;&lt;li class="bullist"&gt;&lt;br /&gt;YSlow says you should &lt;a href="http://developer.yahoo.com/performance/rules.html#flush"&gt;flush() early&lt;/a&gt; and put &lt;a href="http://developer.yahoo.com/performance/rules.html#js_bottom"&gt;Javascript at the bottom&lt;/a&gt;. The reasoning is sound: get the HTML &amp;lt;head&amp;gt; portion out as quickly as possible so the browser can start downloading any referenced stylesheets and images. On the other hand, JS is supposed to go on the bottom because script tags halt parallel downloads. The trouble comes when your page arrives in pieces over a long period of time: the HTML and CSS are mostly there, maybe some images, but the JS is lost in the ether. That means the application may look like it's ready to go but actually isn't — the click handlers and logic and ajax includes haven't arrived yet.&lt;br /&gt;&lt;div class="figure"&gt;&lt;img alt="Partly loaded images on Google docs view of this article" src="http://farm4.static.flickr.com/3532/3951381234_f55f000299_o.jpg" /&gt;&lt;br /&gt;&lt;em&gt;Figure 4: docs is loading slowly... dare I click?&lt;/em&gt;&lt;br /&gt;&lt;/div&gt;Maybe in addition to the CSS/HTML/Javascript sandwich you could stuff a minimal version of the UI into the first 1-3KB, which gets replaced by the full version. Google Docs presents document contents as quickly as possible but disables the buttons until its sanity checks pass. Yahoo's home page does something similar.&lt;br /&gt;This won't do for heavier applications, or those that don't have a lot of passive text to distract the user with while frantic work happens offstage. Gmail compromises with a loading screen which times out after X seconds. On timeout it asks the user to choose whether to reload or use their lite version.&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;&lt;li class="bullist"&gt;Have a plan for disaster: what should happen when one of your scripts or styles or data blobs never arrives? Worse, what if the user's cached copy is corrupted? How do you detect it? Do you retry or fail? A quick win might be to add a checksum/eval step to your javascript and stylesheets.&lt;/li&gt;&lt;li class="bullist"&gt;We also recommend that you should make as much CSS and Javascript as possible external and to &lt;a href="http://developer.yahoo.com/performance/rules.html#cdn"&gt;parallelize HTTP requests&lt;/a&gt;. But is it wise to do more DNS lookups and open new TCP connections under very high latency? If each new connection takes a couple seconds to establish, it may be better to inline as much as possible.&lt;/li&gt;&lt;li class="bullist"&gt;The trick is how to decide that an arbitrary user is suffering high latency. For mobile users you can pretty much take high latency as a given [&lt;a href="http://www.blogger.com/post-edit.g?blogID=2936626869504789093&amp;amp;postID=4092138695913740717#note4"&gt;4&lt;/a&gt;]. Armed with per-IP statistics on client network latency from bullet #4 above, you can build a lookup table of high-latency subnets and handle requests from those subnets differently. For example if your servers are in Seattle it's a good bet that clients in the 200.0.0.0/8 subnet will be slow. 200.* is for Brasil but the point is that you don't need to know it's for Brasil or iPhone or whatever — you're just acting on observed behavior. Handling individual users from "fast" subnets who happen to have high latency is a taller order. It may be possible to get information from the socket layer about how long it took to establish the initial connection. I don't know the answer yet but there is promising research here and there.&lt;/li&gt;&lt;li class="bullist"&gt;A good technique that seems to go in and out of fashion is KeepAlive. Modern high-end load balancers will try to keep the TCP connection alive between themselves and the client, no matter what, while also honoring whatever KeepAlive behavior the webserver asks for. This saves expensive TCP connection setup and teardown without tying up expensive webserver processes (the reason why some people turn it off). There's no reason why you couldn't do the same with a software load balancer / proxy like Varnish.&lt;/li&gt;&lt;/ul&gt;This article is the first in a series and part of ongoing research on bandwidth and web app performance. It's still early in our research, but we chose to share what we've found early so you can join us on our journey of discovery. Next, we will dig deeper into some of the open questions we've posed, examine real-world performance in the face of high latency and packet loss, and suggest more techniques on how to make your apps work better in adverse conditions based on the data we collect.&lt;br /&gt;&lt;h3&gt;Notes&lt;/h3&gt;&lt;div class="note" id="note1"&gt;[1] ceil(N / 1460) is the same algorithm you use to figure out how many trips it takes to carry your laundry down the stairs. (ceil is geekspeak for rounding up.) Say you have 50 pounds of clothes and the basket holds 13 pounds. 50 / 13 = 3 remainder 11, so you need to make 4 trips. The bigger the basket the fewer the trips. So why not use huge packets? On private networks you might see configurations for "Jumbo frames". But in the wild you have to consider the cost of packet loss, typical message sizes, old or incompatible routers, etc.&lt;br /&gt;&lt;/div&gt;&lt;div class="note"&gt;That specific number (aka Maximum Segment Size) comes from the maximum packet size (aka Maximum Transmission Unit) of 1,500 octets (aka bytes) set in &lt;a href="http://www.faqs.org/rfcs/rfc1191.html"&gt;RFC 1191 (aka Ethernet v2)&lt;/a&gt;, minus the space reserved for the source and destination addresses, flags, etc. IPv6, which has been coming any day now since the Clinton administration, will probably converge on an MSS of 1,220 or 1,440 in the wild. Point being, we're stuck with tiny packets for the rest of our lifetimes.&lt;br /&gt;&lt;/div&gt;&lt;div class="note" id="note2"&gt;[2] DNS can also cause latency. We tend to take hostname lookups for granted, but an ISP's DNS resolvers are often unloved. It once took me several years to convince BellSouth's customer service that one of their DNS resolvers was actually off the network. User DNS problems are doubly nasty because we as application developers can't control or even detect them.&lt;br /&gt;&lt;/div&gt;&lt;div class="note" id="note3"&gt;[3] The script was single-threaded and used a new TCP connection for each request. A single restriction was used per run, ie X milliseconds latency or Y% packet loss. The wifi was a Linksys WRT54g at a distance of 5 meters, with standard firmware in 802.11g mode and WPA2 encryption. The uplink was a "6mbps" home cable connection about 50 miles and ten network hops away from the nearest Yahoo caching server, during off-peak hours.&lt;br /&gt;&lt;/div&gt;&lt;div class="note" id="note4"&gt;[4] The Google mobile team recently put out an &lt;a href="http://ajaxian.com/archives/gmail-mobile-latency"&gt;interesting fact&lt;/a&gt;: "On an iPhone 2.2 device, 200k of JavaScript held within a block comment adds 240ms during page load, whereas 200k of JavaScript that is parsed during page load added 2600 ms."&lt;br /&gt;&lt;/div&gt;&lt;div class="note"&gt;Image Credit: &lt;a href="http://www.flickr.com/photos/toestubber/525734621/"&gt;Pachinko&lt;/a&gt; by the_toe_stubber on Flickr.&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2936626869504789093-4092138695913740717?l=carlos.bueno.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/2936626869504789093/4092138695913740717/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=2936626869504789093&amp;postID=4092138695913740717' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2936626869504789093/posts/default/4092138695913740717'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2936626869504789093/posts/default/4092138695913740717'/><link rel='alternate' type='text/html' href='http://carlos.bueno.org/2009/10/dismal-guide-to-bandwidth.html' title='A Dismal Guide to Bandwidth'/><author><name>bueno</name><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='02278308746331037541'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2936626869504789093.post-6150628811142190184</id><published>2009-02-26T19:02:00.000-08:00</published><updated>2009-11-14T14:27:22.420-08:00</updated><title type='text'>Install a DNS resolver on your laptop</title><content type='html'>&lt;b&gt;Updates:&lt;/b&gt; &lt;br /&gt;&lt;ol&gt;&lt;li&gt;I have since been lectured sternly (and correctly) that running your own resolver is a rude thing to do. If you want an alternative, &lt;a href="http://www.handcoding.com/archives/2005/04/15/alternate-dns-servers/"&gt;try using 4.2.2.1-4.2.2.6 as your DNS servers&lt;/a&gt; instead.&lt;/li&gt;&lt;li&gt;a Meraki employee pointed out that they are at the mercy of whatever resolvers the ISPs of their volunteers use. I remain unconvinced as they seem to add an extra layer of indirection. Also, they run their own hardware and software. There is no reason why they couldn't use alternate DNS servers or even run their own.&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;Free hotspot internet providers (eg Meraki) can have pretty good bandwidth but still feel slow because their DNS resolvers suck and they don't know it. You'll have great response from an SSH session or webmail but clicking a link to a new site will pause or fail. &lt;br /&gt;&lt;br /&gt;Even large ISPs get this wrong. I tried for &lt;b&gt;several years&lt;/b&gt; to convince BellSouth that one of their DNS resolvers was down: &lt;br /&gt;&lt;br /&gt;&lt;i&gt;"No, my internet is not down. The DNS server is down. I can ping. DNS. Dee Enn Ess. Pee Eye Enn Gee. Do you understand I'm trying to tell you about a bad problem with your system? One of your DNS servers is down. It's been down since 2003 but it's still in rotation. Yes, I restarted my router. Yes, my connection is now working but that's not the poi--".&lt;/i&gt; &lt;b&gt;Click&lt;/b&gt;. Good times.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Solution: install your own damned resolver.&lt;/b&gt; I recommend Dr Berstein's excellent &lt;b&gt;dnscache&lt;/b&gt;, part of &lt;s&gt;daemontools&lt;/s&gt; djbdns (which itself runs under daemontools). Incidentally, this is also a good idea for your servers if you do any crawling, image fetching, etc. You'd be surprised how much it can help.&lt;br /&gt;&lt;br /&gt;Excellent installation instructions here:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://matt.simerson.net/computing/dns/djbdns-macosx.shtml"&gt;http://matt.simerson.net/computing/dns/djbdns-macosx.shtml&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://matt.simerson.net/computing/dns/djbdns-freebsd.shtml"&gt;http://matt.simerson.net/computing/dns/djbdns-freebsd.shtml&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Credit to tlack, who taught me this trick back when I was still figuring out bash.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2936626869504789093-6150628811142190184?l=carlos.bueno.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/2936626869504789093/6150628811142190184/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=2936626869504789093&amp;postID=6150628811142190184' title='9 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2936626869504789093/posts/default/6150628811142190184'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2936626869504789093/posts/default/6150628811142190184'/><link rel='alternate' type='text/html' href='http://carlos.bueno.org/2009/02/protip-install-dns-resolver-on-your.html' title='Install a DNS resolver on your laptop'/><author><name>bueno</name><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='02278308746331037541'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>9</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2936626869504789093.post-6026839745311630270</id><published>2008-08-25T23:29:00.030-07:00</published><updated>2009-12-09T10:34:40.539-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='archives'/><category scheme='http://www.blogger.com/atom/ns#' term='dowser'/><title type='text'>How To Save The Web</title><content type='html'>&lt;p&gt;A strange as it may sound, public record does not exist on the internet. Consider this: it would be impossible for, say, the New York Times to change something it printed in 1997 -- there are hardcopies all over the world. But for nytimes.com it's as simple as a mouse click. So the internet we have today is &lt;i&gt;public&lt;/i&gt; but it's not really a &lt;i&gt;record&lt;/i&gt;. Healthy public record is the foundation of a free and literate society.&lt;/p&gt;&lt;p&gt;I propose that a loosely-connected network of independent archives, running on personal computers, under the care of self-interested individuals, and sharing common data formats, can in time self-assemble into something that fits the bill.&lt;/p&gt;&lt;h3&gt;How we got here&lt;/h3&gt;&lt;p&gt;&lt;a href="http://en.wikipedia.org/wiki/Dewey_Defeats_Truman" rel="external nofollow"&gt;&lt;img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;" src="http://carlos.bueno.org/uploaded_images/dewey-truman-undo-740041.jpg" border="0" alt="" /&gt;&lt;/a&gt; For historical and technical reasons, stuff published on the web lives primarily on servers controlled by the original publisher. It is "distributed" at the time it is asked for, one copy at a time, and that copy is usually discarded. This means that if the original publisher goes away the authoritative source goes too. It also means the publisher, be it a company, government agency, small club or individual, gets to choose how the data is accessed now and in the future. This new power has become very tempting &lt;a name="n0" href="#f0"&gt;[0]&lt;/a&gt;. The mainstream is slowly realizing that stuff frequently disappears by accident and by design &lt;a name="n1" href="#f1"&gt;[1]&lt;/a&gt;: &lt;/p&gt;&lt;ol style="list-style-type: lower-alpha;"&gt;&lt;li&gt;&lt;a href="http://sports.yahoo.com/olympics/beijing/blog/fourth_place_medal/post/IOC-orders-investigation-into-He-Kexin-s-age?urn=oly,102564" rel="external nofollow"&gt;sports.yahoo.com&lt;/a&gt; &lt;cite&gt;"...a New York computer security expert who found official Chinese documents that list He's age as 14 years and 220 days... The spreadsheets were taken down off the site recently and He's name had been removed..."&lt;/cite&gt;  &lt;/li&gt;&lt;br /&gt; &lt;li&gt;&lt;a href="http://www.talkingpointsmemo.com/archives/007536.php" rel="external nofollow"&gt;www.talkingpointsmemo.com&lt;/a&gt; &lt;cite&gt;"When we went to the page for the photograph of President Bush and Abramoff, the page in question had disappeared from the site..."&lt;/cite&gt;  &lt;/li&gt;&lt;br /&gt; &lt;li&gt;&lt;a href="http://www.nytimes.com/2008/08/09/opinion/09collins.html"&gt;www.nytimes.com&lt;/a&gt; &lt;cite&gt;...Said videos were posted, then mysteriously disappeared from the Edwards Web site, with officials muttering something about campaign finance rules...&lt;/cite&gt;&lt;/li&gt;&lt;br /&gt; &lt;li&gt;&lt;a href="http://perezhilton.com/2008-08-24-more-on-the-dr-drew-rehab-facility-scandal" rel="external nofollow"&gt;perezhilton.com&lt;/a&gt;&lt;cite&gt; "Pinksky's photo disappeared from the hospital's site on Friday, as the scandal story started to get more legs..."&lt;/cite&gt; &lt;/li&gt; &lt;/ol&gt;&lt;h3&gt;Wouldn't people notice if things went away?&lt;/h3&gt;&lt;p&gt;Most often they don't, and if they do notice, so what? Revisionism and neglect is low-risk. "Orphan" works are sometimes rescued by their fans. But it's easy to forget that these cases are the exception. Intelligent, influential people say things like this: &lt;b&gt;&lt;cite&gt;"Once the Internet knows something, it never forgets. This material just doesn't disappear from the Internet if it's sufficiently interesting."&lt;/cite&gt;&lt;/b&gt; &lt;a name="n2" href="#f2"&gt;[2]&lt;/a&gt;&lt;/p&gt;&lt;p&gt;There are serious problems with this idea. The Rosetta Stone didn't survive thousands of years in the desert because of some intrinsic cultural value. It survived because it's &lt;i&gt;made of stone&lt;/i&gt;. The UNIX crowd learned this lesson a few years ago to their lasting regret: there are no digital copies of the first four versions of UNIX, only some printouts.&lt;a name="n3" href="#f3"&gt;[3]&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Worse is the implication is that if something is not "sufficiently interesting", if it's not part of the story a society wants to tell about itself, it's worthless. The future disagrees about what is and is not important, and why. That's the defining characteristic of the future. No one today cares what the Rosetta Stone actually says &lt;a name="n4" href="#f4"&gt;[4]&lt;/a&gt;, yet it is more important to us (as the key to hieroglyphics) than it was to the society that made it.&lt;/p&gt;&lt;h3&gt;The current situation&lt;/h3&gt;&lt;p&gt;The current situation in archives is much like the web: uncoordinated, conflicting, changing. The most widespread problem is a paradoxical attitude: most people understand that a centralized web would be unsustainable, but few seem to carry that logic over to archiving.&lt;/p&gt;&lt;p&gt;There are a few public archives. Many national libraries have set up consortia to study the problem. There are search engine caches like Google's. There is the remarkable and far-sighted archive.org. All of them are welcome --in this game, the more the merrier-- but I believe they have various flaws. Google's cache exists for Google's purposes, and is not designed for the long term &lt;a name="n5" href="#f5"&gt;[5]&lt;/a&gt;. The LOCKSS project &lt;a name="n6" href="#f6"&gt;[6]&lt;/a&gt; is commendable in sprit and clever in design, but access to it appears to be limited to select universities and libraries. &lt;/p&gt;&lt;p&gt;Archive.org has two handicaps. It's not actually possible for one organization to curate the web. Second, being a non-profit sitting target, they are forced to take down stuff they do save &lt;a name="n7" href="#f7"&gt;[7]&lt;/a&gt; &lt;a name="n8" href="#f8"&gt;[8]&lt;/a&gt;&lt;/p&gt;&lt;p&gt;In April 2004 The public editor of the The New York Times spelled out the paper's position in an article titled &lt;cite&gt;"Paper of Record? No Way, No Reason, No Thanks"&lt;/cite&gt;. He was speaking more against the obligation to print government notices than the idea of public record per se. But all through it he assumes that (a) someone else will do it, and (b) this information (not to mention copies of his newspaper) will always somehow be available to future historians. At the same time his newspaper was telling archive.org to remove nytimes.com from the collection &lt;a name="n9" href="#f9"&gt;[9]&lt;/a&gt;.&lt;/p&gt;&lt;h3&gt;What the Archive should be&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;i&gt;Decentralized &amp;amp; Redundant&lt;/i&gt;&lt;br /&gt;     Centralized is too expensive and too fragile. Redundancy increases the odds of survival.&lt;br /&gt;   &lt;/li&gt;&lt;br /&gt;   &lt;li&gt;&lt;i&gt;Long-Term&lt;/i&gt;&lt;br /&gt;     If it's not long-term there's not much point. Open, stable formats.&lt;br /&gt;   &lt;/li&gt;&lt;br /&gt;   &lt;li&gt;&lt;i&gt;Locally curated&lt;/i&gt;&lt;br /&gt;     There should be at least as many opinions about what should go into the archive as people using it.&lt;br /&gt;   &lt;/li&gt;&lt;br /&gt;   &lt;li&gt;&lt;i&gt;Public &amp;amp; Coherent&lt;/i&gt;&lt;br /&gt;     A cache is useless if no one can get to it. It should also contain only things that are already public.&lt;br /&gt;   &lt;/li&gt;&lt;br /&gt;   &lt;li&gt;&lt;i&gt;Verifiable&lt;/i&gt;&lt;br /&gt;     Whether by digital signatures or by comparing copies, or both, the archive must be resistant to tampering.&lt;br /&gt;   &lt;/li&gt;&lt;br /&gt;   &lt;li&gt;&lt;i&gt;Respectful of privacy&lt;/i&gt;&lt;br /&gt;     A lot can be revealed by someone's reading list, and the need to anonymize may conflict with the needs of tamper-proofing.&lt;br /&gt;   &lt;/li&gt;&lt;br /&gt;   &lt;li&gt;&lt;i&gt;Useful for User 0&lt;/i&gt;&lt;br /&gt;     It has to be useful even if you are the only user, otherwise there is much less incentive to use and contribute to it.&lt;br /&gt;   &lt;/li&gt;   &lt;/ul&gt;&lt;p&gt;In short, we need something akin to the web itself: something that can grow without limit, yet does not require much centralized organization. It can be pulled into pieces and operate independently and merge back together. Once it reaches a certain size it, or at least the idea, will be impossible to kill.&lt;/p&gt;&lt;p&gt;In a sense the archive already exists, though in a low-energy state. Part of it lives in the browser cache of everyone's computer. These caches are not coherent, organized, searchable, or public. They also have a lot of stuff in there that is better left private. We have to work around that. But it's a start.&lt;/p&gt;&lt;h3&gt;Dowser's approach&lt;/h3&gt;&lt;p&gt;&lt;cite&gt;"The lost cannot be recovered; but let us save what remains: not by vaults and locks which fence them from the public eye and use in consigning them to the waste of time, but by such a multiplication of copies, as shall place them beyond the reach of accident."&lt;/cite&gt;&lt;br /&gt;Thomas Jefferson, 18 February 1791&lt;/p&gt;&lt;p&gt;Dowser is a program designed to run on a normal personal computer but operate exactly like a website. Its user interface is a fully-functioning website that is hosted by and only accessible to the local machine &lt;a name="n10" href="#f10"&gt;[10]&lt;/a&gt;. This website enables the user to add pages to their local archive, search many search engines at once (aka "metasearch"), search the local archive, add organizational tags, take notes, export and share, etc. It is not intended as a professional tool for research specialists, but as a helpmeet for "power users": journalists, students, writers, etc, who have a demonstrated need for powerful research tools but do not have the time or inclination to train on an academic-grade tool.&lt;/p&gt;&lt;p&gt;When a user adds a URL to the archive, Dowser will attempt to retrieve the content of this URL by itself, without reference to any private authentication or "cookies" held by the user. This helps to ensure that private data stays private. Optionally, Dowser will download linked images, videos and such, and any linked pages up to a user-defined "link depth". As time goes on, Dowser will occasionally "ping" the URLs it knows about to determine if they have changed. If so it will download the new copy and thus build up a change history.&lt;/p&gt;&lt;p&gt;The program will not allow the user to &lt;i&gt;alter&lt;/i&gt; any data in his archive, though it is possible to &lt;i&gt;delete&lt;/i&gt; anything. Since the formats are open and simple, a determined user can of course alter the archive data with other tools. We can't stop that, and should not try, but will be an interesting obstacle.&lt;/p&gt;&lt;h3&gt;Pulling it together&lt;/h3&gt;&lt;p&gt;So far we have only a local archive. What User 0 reads is saved and available only to User 0. So how do we share it worldwide without inviting spam or violating privacy or running afoul of the law, etc?&lt;/p&gt;&lt;p&gt;The seven qualities above are somewhat in conflict with each other. Privacy may conflict with the need to verify copies, decentralization conflicts with system performance conflicts with public access, etc. Centralized archives solve it by doing it all themselves and building up a reputation for trustworthiness. LOCKSS, which is a network of caching servers installed in many universities, depends on the mutual trust of those institutions for verification and limits public access to ward of lawsuits by copyright owners.&lt;/p&gt;&lt;p&gt;You and I aren't smart enough to solve the sharing problem. We should start small and listen to the users, to use the much greater imagination of the group. Make tools to allow User 0 to share with User 1, then User 2, etc, but make them orthogonal to the rest. Allow people to extend Dowser and use it in ways we don't expect.&lt;/p&gt;&lt;p&gt;The common feature of all of the current archiving schemes is that they are hard for a mere mortal to set up. In theory anyone can download archive.org's software and use it to build their own archive. In practice it's very tricky, and not worth the effort for most people to learn the ancillary skills like programming, systems administration, etc. You can't join LOCKSS without being a major university.&lt;/p&gt;&lt;p&gt;Archiving today is a luxury good: complex, balky, expensive, like cars before 1908. When something is a luxury and you want more of it, what do you do? You turn it into a commodity. Make the hard parts easy, do what you can for the harder parts, get something workable into the hands of a much larger group. They will take it from there.&lt;/p&gt;&lt;h3&gt;Notes&lt;/h3&gt;&lt;div class="foot"&gt;&lt;ul&gt;&lt;li class="foot"&gt;&lt;a name="f0" href="#n0"&gt;[0]&lt;/a&gt; Even for me. This is the second version of this essay. The first version is here:&lt;br /&gt;&lt;a href="http://carlos.bueno.org/save-the-web-v1.html"&gt;http://carlos.bueno.org/save-the-web-v1.html&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;li class="foot"&gt;&lt;a name="f1" href="#n1"&gt;[1]&lt;/a&gt; It is, unfortunately, easy to find cases where data was disappeared. In 30 minutes I compiled this list of instances where something that went missing was important enough to be written about.&lt;br /&gt;&lt;a href="http://carlos.bueno.org/disappeared.html"&gt;http://carlos.bueno.org/disappeared.html&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;li class="foot"&gt;&lt;p&gt;&lt;a name="f2" href="#f2"&gt;[2]&lt;/a&gt;&lt;cite&gt; "Paris Hilton's genitals have joined the undead - they will live forever, stalking the Internet until the last plug is pulled on the last network router." &lt;/cite&gt;&lt;br /&gt; &lt;a href="http://craphound.com/cambridge_biz_lectures.txt"&gt;http://craphound.com/cambridge_biz_lectures.txt&lt;/a&gt;&lt;/p&gt;&lt;p&gt;For a few weeks in 2007, the film industry fought a futile a battle to contain the dissemination of a short string of numbers.&lt;br /&gt;&lt;a href="http://en.wikipedia.org/wiki/AACS_encryption_key_controversy"&gt;http://en.wikipedia.org/wiki/AACS_encryption_key_controversy&lt;/a&gt;&lt;/p&gt;  &lt;/li&gt;&lt;li class="foot"&gt;&lt;p&gt;&lt;a name="f3" href="#n3"&gt;[3]&lt;span style="font-style: italic;"&gt;&lt;/span&gt;&lt;/a&gt;&lt;cite&gt; "Although most of the UNIX source code from before 5th Edition has disappeared, we are fortunate that paper copies of all the research editions still exist..."&lt;/cite&gt;&lt;br /&gt; &lt;a href="http://minnie.tuhs.org/Seminars/Saving_Unix"&gt;http://minnie.tuhs.org/Seminars/Saving_Unix&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;li class="foot"&gt;&lt;p&gt;&lt;a name="f4" href="#n4"&gt;[4]&lt;/a&gt; The Rosetta Stone is a political ad, the equivalent of "your tax dinarae at work": &lt;cite&gt;"...he caused the canals which supplied water to that fortress to be dammed off, although the previous kings could not have done likewise, and much money was expended on them; he assigned a force of footsoldiers and horsemen to the mouths of those canals, in order to watch over them and to protect them, because of the [rising] of the water, which was great in Year 8...&lt;/cite&gt;"&lt;br /&gt;&lt;a href="http://www.britishmuseum.org/explore/highlights/article_index/r/the_rosetta_stone_translation.aspx"&gt;http://www.britishmuseum.org/explore/highlights/article_index/r/&lt;wbr/&gt;the_rosetta_stone_translation.aspx&lt;/a&gt;&lt;br /&gt;&lt;/p&gt;&lt;/li&gt;&lt;li class="foot"&gt;&lt;a name="f5" href="#n5"&gt;[5]&lt;/a&gt;&lt;cite&gt; "Google takes a snapshot of each page examined as it crawls the web and caches these as a back-up in case the original page is unavailable... The Cached link will be missing for sites that have not been indexed, as well as for sites whose owners have requested we not cache their content."&lt;/cite&gt;&lt;br /&gt;&lt;a href="http://www.google.com/intl/en/help/features_list.html#cached"&gt;http://www.google.com/intl/en/help/features_list.html#cached&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;&lt;li class="foot"&gt;&lt;a name="f6" href="#n6"&gt;[6]&lt;/a&gt; The LOCKSS project, "Lots Of Copies Keeps Stuff Safe"&lt;br /&gt;&lt;a href="http://www.lockss.org/"&gt;http://www.lockss.org&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;li class="foot"&gt;&lt;p&gt;&lt;a name="f7" href="#n7"&gt;[7]&lt;/a&gt; People forget that curating the web was Yahoo's original failed idea.&lt;/p&gt;&lt;p&gt;As of this writing chillingeffects.org documents more than 90 "takedown" letters sent to archive.org alone.&lt;br /&gt;&lt;a href="http://www.chillingeffects.org/dmca512/notice.cgi?NoticeID=1899"&gt;http://www.chillingeffects.org/dmca512/notice.cgi?NoticeID=1899&lt;/a&gt;&lt;/p&gt;&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;li class="foot"&gt;&lt;p&gt;&lt;a name="f8" href="#n8"&gt;[8]&lt;/a&gt; &lt;cite&gt;"The Internet Archive is not interested in offering access to Web sites or other Internet documents whose authors do not want their materials in the collection."&lt;/cite&gt;&lt;br /&gt;&lt;a href="http://www.archive.org/about/exclude.php"&gt;http://www.archive.org/about/exclude.php&lt;/a&gt;&lt;/p&gt;&lt;br /&gt;&lt;/li&gt;&lt;li class="foot"&gt;&lt;p&gt;&lt;a name="f9" href="#n9"&gt;[9]&lt;/a&gt; &lt;cite&gt;"Paper of Record? No Way, No Reason, No Thanks", Daniel Orkent, public editor, The New York Times, 25 April 2004&lt;/cite&gt;&lt;br /&gt;&lt;a href="http://query.nytimes.com/gst/fullpage.html?res=9D02E1D8123AF936A15757C0A9629C8B63"&gt;http://query.nytimes.com/gst/fullpage.html?&lt;wbr/&gt;res=9D02E1D8123AF936A15757C0A9629C8B63&lt;/a&gt;&lt;/p&gt;   &lt;p&gt;There are about 3,400 pages for 2005, 1,800 for 2006, 550 for 2007, and only 30 pages for 2008 from nytimes.com in archive.org's public collection.&lt;br /&gt; &lt;a href="http://web.archive.org/web/*/www.nytimes.com"&gt;http://web.archive.org/web/*/www.nytimes.com&lt;/a&gt; &lt;/p&gt;&lt;p&gt;I don't mean to pick on the NYT, but in the last few years they have gone to a lot of trouble to delete themselves.&lt;/p&gt;&lt;/li&gt;&lt;li class="foot"&gt;&lt;a name="f10" href="#n10"&gt;[10]&lt;/a&gt; This design is similar to Google's Desktop search application and several others. In addition to having seamless integration with external websites, this design avoids the tricky &amp;amp; tedious work of writing custom "plugins" for many combinations of operating system and web browser.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2936626869504789093-6026839745311630270?l=carlos.bueno.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/2936626869504789093/6026839745311630270/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=2936626869504789093&amp;postID=6026839745311630270' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2936626869504789093/posts/default/6026839745311630270'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2936626869504789093/posts/default/6026839745311630270'/><link rel='alternate' type='text/html' href='http://carlos.bueno.org/2008/08/save-web.html' title='How To Save The Web'/><author><name>bueno</name><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='02278308746331037541'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2936626869504789093.post-3421443460321135961</id><published>2008-06-09T14:04:00.009-07:00</published><updated>2009-11-28T14:04:56.201-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='dismal guide'/><title type='text'>Network Distance</title><content type='html'>&lt;span style="font-weight:bold;"&gt;When Sydney is closer than Sao Paulo&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;It's quite possible to make your living from the internet without really considering how it's constructed. I came across this talking with my friend Aaron. He's a bright guy but like many people he assumes the net is done "with satellites or whatever", or never think about it.  &lt;br /&gt;&lt;br /&gt;The general structure of the internet pretty similar to the global air network. Picture those glowing arcs connecting cities on the back of airline magazines. Then recall the hour you spent in line before boarding, the hour spent driving to the airport, the two hours from the big hub airport to the smaller city you really want to go to, and how relieved you are to be traveling now instead of last week when a storm in Chicago somehow messed up flights to Los Angeles. That's basically how your data feels, too.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;The long-haul internet is, in fact, a series of tubes. Very fragile tubes. &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The long-haul cables that run the global economy are less than 2 inches in diameter, buried under railroad tracks, highways, or ocean sediment. Within densely-settled areas the network is relatively redundant. But between countries and across oceans and mountains, data flows through an uncomfortably small set of bottlenecks. &lt;br /&gt;&lt;br /&gt;Any minor disaster can damage large portions of the 'net. In 2004 the Miami/Sao Paulo traffic was suddenly re-routed through Washington DC, then New York, then across the ocean to Brussels(!), then back to SP. As late as 1998, a train wreck or wildfire in northern Florida could cut off large parts of Latin America for days. This year Suez suffered multiple failures and re-routing flooded the already overloaded Europe-Asia network for several weeks.&lt;br /&gt;&lt;br /&gt;Even on a good day you can see the problem. Look at how a packet of data might travel from San Francisco to Hong Kong:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;(start)&lt;br /&gt;Folsom St, San Francisco (1 mile)&lt;br /&gt;Pine St, San Francisco (2 miles)&lt;br /&gt;Pine St, San Francisco&lt;br /&gt;Oakland, CA (10 miles)&lt;br /&gt;Sacramento, CA (80 miles)&lt;br /&gt;San Jose, CA (120 miles)&lt;br /&gt;Oakland, CA (40 miles)&lt;br /&gt;San Jose, CA (40 miles)&lt;br /&gt;Hong Kong (7,000 miles)&lt;br /&gt;(finish)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;That poor little packet of data rattled all around California, looking for an uncongested cable over the Pacific. For each hop, a decision is made to send it on to some other place that may have better luck. The system works pretty well under stress which is good because stress is there all of the time.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold; margin:4px;"&gt;The long-haul internet is a map of trade volume between cities, that lags up to 20 years behind reality.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;This is actually true for all forms of high-volume transport, so there is a lot of history to learn from. Infrastructure is insanely expensive and slow to build even though it almost always pays off in the long run. Short hops between financial/military/political/industrial hubs tend to get built up first. Just look at how many ways there are to travel between New York, Boston, and DC, for example.&lt;br /&gt;&lt;br /&gt;Or look at area codes. At the time the precursor to the modern phone system was built, dialing a 1 took 1/9th the time of dialing a 9. Silly, but true. So there was a premium on lower numbers. New York's area code was 212, DC 202, Los Angeles 310, Chicago 312. El Paso, Texas? 915. Anchorage, Alaska? 907. Miami was definitely not a hub at the time, but it was important to the Navy and Air Force. Miami's code is 305.&lt;br /&gt;&lt;br /&gt;&lt;img style="float:right; margin:8px;" src="http://carlos.bueno.org/uploaded_images/2585887836_eb2ce57f87-775904.jpg" border="0" alt="Finally" /&gt;So if several factors of demography, geography, and politics align, there may form a route of sufficient capacity between two points. If not, too bad. It takes years to build up demand, more years to begin the project, and more and more years to finish it. There are bribes, labor riots, sabotage, political chicanery, etc. The first trans-continental rail link in the US was completed in 1869. Twenty years earlier, people had been hijacking ships in Louisiana to sail around South America and crash-land ashore at San Francisco.&lt;br /&gt;&lt;br /&gt;The same dramas play out when cables are planned and laid. The connection between Seattle and Tokyo is excellent. Ciudad Mexico and Dallas? Fairly new and really fast. New York to London? World-class. But try to get an email from Barcelona to Bangalore, and often you'll find that it routes through America. Companies are scrambling to build up Europe-Asia links. They've been scrambling since the late 1980's, and it will be some time before they get there.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;The long-haul internet is not a magic leprechaun. &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Even if there weren't these human problems there are still fundamental ones. Let's imagine a perfectly balanced world-wide network. You have an internet business based in San Francisco. Your people are in SF, your technology suppliers are in SF, most of your customers are in the United States. You have a small but growing customer base in Hong Kong and China. We have a perfect 'net, so there are no silly congestion problems and there is just as much bandwidth across oceans as between cities. Rack space in HK is twice the price as in SF. &lt;br /&gt;&lt;br /&gt;So what is the no-brainer place to put your next server farm, hire people to maintain it, set up office space, pay property and business taxes, etc? Correct. Hong Kong. &lt;br /&gt;&lt;br /&gt;No matter how good the internet gets it will never be faster than a small fraction of the speed of light.&lt;br /&gt;&lt;br /&gt;Recently I was looking at the server logs of a site located in the US. The response times had a very high variance, which indicates a severe bottleneck somewhere. After a lot of poking around, trying to find the problem within the server farm, I had the bright idea to segment the logs by source country. And there it was: the response times were all over the map &lt;span style="font-style:italic;"&gt;because the users were all over the map&lt;/span&gt;. From the perspective of the 'net, Stockholm and Singapore are just down the block while India and Sao Paulo are past the moon.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Rules of thumb:&lt;/span&gt;&lt;br /&gt;  - Light takes about 100 milliseconds to travel 10,000 miles and back.&lt;br /&gt;&lt;br /&gt;  - A network packet on a good route may take 3 to 10 times that long.&lt;br /&gt;&lt;br /&gt;  - A network packet has not "arrived" until a return acknowledgment has been sent all the way back.&lt;br /&gt;&lt;br /&gt;  - The longer and more complicated the route from here to there, the higher the chance (often more than 10%!) that a packet will get lost.&lt;br /&gt;&lt;br /&gt;  - The larger the file you send, the more packets it has to be chopped up into, the more likely one will be lost and have to be re-transmitted. All else being equal the transit time of a file increases more than linearly to its size.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2936626869504789093-3421443460321135961?l=carlos.bueno.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/2936626869504789093/3421443460321135961/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=2936626869504789093&amp;postID=3421443460321135961' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2936626869504789093/posts/default/3421443460321135961'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2936626869504789093/posts/default/3421443460321135961'/><link rel='alternate' type='text/html' href='http://carlos.bueno.org/2008/06/network-distance.html' title='Network Distance'/><author><name>bueno</name><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='02278308746331037541'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2936626869504789093.post-2890902383868981529</id><published>2008-02-01T17:51:00.001-08:00</published><updated>2008-09-24T15:32:01.113-07:00</updated><title type='text'>Evolved Code vs Enterprise Code</title><content type='html'>Once upon a time, Adrian Thompson accidentally &lt;a href="http://archive.bcs.org/bulletin/jan98/leading.htm" id="ae.e" title="created a magical artifact"&gt;created a magical artifact&lt;/a&gt;. He used a genetic algorithm to evolve a FPGA circuit that would distinguish between signals of two frequencies. The winning specimen was pretty unlikely: it worked on that chip only, and only at certain temperatures. It contained circuits that were not connected to the others, but when he turned those off the damned thing stopped working. The neighboring gates depended on that extra bit of inductance, which varied with a slight flaw in another gate... maybe. No one really knows.&lt;br /&gt;&lt;br /&gt;Once upon a time, a small group of hackers evolved a piece of software. They focused so much on doing more with less that they themselves became crucial parts of the implementation: Leo took over ops because he stayed late to avoid traffic anyway. These support emails routed to Bianca because she spoke Spanish and Portuguese. Tatiana had a nicer touch with angry customers. Carlos had the pager because he's good at troubleshooting. Tom made the changes to the template parser because Tom's the genius who wrote it. The bug tracking system was a whiteboard. Every little detail seemed to depend on some idiosyncrasy of the people or their environment. It worked -- somehow. Every startup is a freak accident.&lt;br /&gt;&lt;br /&gt;This is why I think &lt;a href="http://discovermagazine.com/2007/dec/long-live-closed-source-software/" id="xo-1" title="Jaron Lanier's bomb-throwing"&gt;Jaron Lanier's bomb-throwing&lt;/a&gt; about closed- versus open-source is off-target. When you're talking about the potential for "radical creativity" in a program, it doesn't matter how many people are allowed to &lt;i&gt;read&lt;/i&gt; the source code, but how many are &lt;a href="http://www.catb.org/jargon/html/Y/You-are-not-expected-to-understand-this.html" rel="external nofollow"&gt;required to understand it&lt;/a&gt;. Having too many junior hackers on your source tree is just as bad as too many customers pestering you about compatibility.&lt;br /&gt;&lt;br /&gt;The fundamental difference between the first two systems and the ones that come out of large organizations is that there was no difference between design and implementation. Enterprise code is designed, vetted, reviewed, implemented, deployed, life-cycled, etc. The purpose is to have no irreplaceable parts. On the other hand, the magic chip and the startup were evolved as fast as possible in a way that could never be exactly repeated.&lt;br /&gt;&lt;br /&gt;A funny thing: when an enterprizey programmer and an alpha-hacker look at each other's code, the same thought occurs for different reasons: "&lt;i&gt;How do they get away with this crap?&lt;/i&gt;" &lt;br /&gt;&lt;br /&gt;The documentation is the code, alpha-hackers say. Don't you see that you're writing your programs in PowerPoint? The process gurus grumble darkly about heroics, maintainability, knowledge transfer, bus accidents. The enterprizey people regard design as something that must be as separate as possible from the expression-as-code. How else can it be maintained by anyone? Take away Tom and the thing no longer works. True, but if enterprizey was always better, BigCo wouldn't get their lunch eaten by wittle bitty startups.&lt;br /&gt;&lt;br /&gt;This schism is most sharp when a big company eats a little one. Most startup acquisitions play out like a spider dissecting a fly: gruesomely impersonal and tedious. You just can't get over the mental image of that fat spider pick pick picking away at a violated husk. &lt;a href="http://www.paulgraham.com/icad.html" id="t-hh" title="Yahoo's epic rewrite of RTML"&gt;Yahoo's epic rewrite of RTML&lt;/a&gt;, its painful digestion of my &lt;a href="http://www.terespondo.com" id="b861" title="company's"&gt;company's&lt;/a&gt; system, and that poor bastard trying to understand his magical chip are all the same phenomenon. &lt;br /&gt;&lt;br /&gt;There are bits that work only there &amp;amp; then, bits that may be impossible for an outsider to grok completely, bits that even the same people would do differently now.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2936626869504789093-2890902383868981529?l=carlos.bueno.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/2936626869504789093/2890902383868981529/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=2936626869504789093&amp;postID=2890902383868981529' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2936626869504789093/posts/default/2890902383868981529'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2936626869504789093/posts/default/2890902383868981529'/><link rel='alternate' type='text/html' href='http://carlos.bueno.org/2008/02/evolved-code-vs-enterprise-code.html' title='Evolved Code vs Enterprise Code'/><author><name>bueno</name><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='02278308746331037541'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2936626869504789093.post-3232140043019028390</id><published>2007-12-03T09:11:00.000-08:00</published><updated>2007-12-03T09:21:52.807-08:00</updated><title type='text'>It's the Economy, Stupid.</title><content type='html'>At least 50.7% of the voters in Venezuela would rather not give their president dictatorial powers and a shot at life in office.&lt;br /&gt;&lt;br /&gt;The "socialist paradise" bribe in the constitutional referendum was transparent: a 6-hour day and some worker's benefits in exchange for astonishing new powers for Chavez, including no term limit and an extension of the term from 6 to 7 years. The 7-year thing really bothered me. The only reason I can think for it is that, being a prime number, a 7-year term would put him out of step. At the times most other leaders would be sweating re-election he would have lots of elbow room.&lt;br /&gt;&lt;br /&gt;But Chavez was out of step on the economy. The bribes were to be funded by oil exports, which are strongly linked to the dollar. With the dollar going down, there is enormous inflation pressure in oil-producing countries. Chavez' answer to that was price and currency controls. The official rate is 2,200-odd Bolivares to 1 Dollar, with tight limits on how much you can buy or sell. As of last week the black-market rate is over 5,000:1. To wish inflation away he put in price controls on basic foodstuffs, with the unsurprising result that there is less and less stuff on the shelves.&lt;br /&gt;&lt;br /&gt;Just as Solidarity in Poland was partly fueled by the price of sausage, the NO campaign got boost from the scarcity of &lt;a href="http://en.wikipedia.org/wiki/Harina_P.A.N."&gt;Doña Arepa&lt;/a&gt;. You can shut down the TV stations, you can shoot the protesters are arrest the "traitors", but you can't monkey with the price of milk.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2936626869504789093-3232140043019028390?l=carlos.bueno.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/2936626869504789093/3232140043019028390/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=2936626869504789093&amp;postID=3232140043019028390' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2936626869504789093/posts/default/3232140043019028390'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2936626869504789093/posts/default/3232140043019028390'/><link rel='alternate' type='text/html' href='http://carlos.bueno.org/2007/12/its-economy-stupid.html' title='It&apos;s the Economy, Stupid.'/><author><name>bueno</name><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='02278308746331037541'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2936626869504789093.post-5802091723972859727</id><published>2007-11-01T15:09:00.000-07:00</published><updated>2007-11-01T15:38:54.313-07:00</updated><title type='text'>Almost Famous</title><content type='html'>&lt;a href="http://www.spock.com/Linda_Goodwin-Nichols_Mayor/"&gt;&lt;img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;" src="http://carlos.bueno.org/uploaded_images/Picture-32-797671.png" border="0" alt="Linda Goodwin-Nichols on Spock" title="Linda Goodwin-Nichols on Spock" /&gt;&lt;/a&gt;Check out this cutie: &lt;a href="http://www.spock.com/Linda_Goodwin-Nichols_Mayor/"&gt;Linda Goodwin-Nichols&lt;/a&gt; &lt;br /&gt;&lt;br /&gt;Ms. Nichols is the Mayor of Kissimee, Florida, a real estate agent and a local philanthropist. She is what we at &lt;a href="http://www.spock.com/q/%22spock-team%22"&gt;Spock&lt;/a&gt; call a semi-famous person. Her data was &lt;b&gt;automatically&lt;/b&gt; picked out from thousands of "Linda Nichols" pages and assembled from over 25 separate sources on the web. Pictures, co-workers, political contributions, fax number, mailing address, press articles, etc etc and so forth. A trained researcher at a newspaper would take hours to gather this much information.&lt;br /&gt;&lt;br /&gt;Forget about trawling social networks and Wikipedia. Tagging your friends is fun but not very useful outside of your own circle. Famous people are easy because there is so much unambiguous information about them. Regular joes like you and I are easy because there is so little. The &lt;span style="font-weight:bold;"&gt;real&lt;/span&gt; challenge and value of "people search" is getting this level of depth for the several million people who are somewhere in between.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2936626869504789093-5802091723972859727?l=carlos.bueno.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/2936626869504789093/5802091723972859727/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=2936626869504789093&amp;postID=5802091723972859727' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2936626869504789093/posts/default/5802091723972859727'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2936626869504789093/posts/default/5802091723972859727'/><link rel='alternate' type='text/html' href='http://carlos.bueno.org/2007/11/almost-famous.html' title='Almost Famous'/><author><name>bueno</name><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='02278308746331037541'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2936626869504789093.post-1395488164015746791</id><published>2007-09-02T23:41:00.000-07:00</published><updated>2007-09-03T21:28:03.352-07:00</updated><title type='text'>Persai: failed in the cradle</title><content type='html'>&lt;img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;" src="http://carlos.bueno.org/uploaded_images/kitty-rpe-761646.png" border="0" alt="" /&gt; &lt;a href="http://www.persai.com"&gt;Persai&lt;/a&gt; is &lt;a href="http://www.uncov.com/2007/8/6/valleywag-tries-investigative-journalism-fails"&gt;Ted D's new startup&lt;/a&gt;. Details are still sketchy, and will remain so until these e-tards finally wither away. The basic idea is that Persai will suck on everyone's RSS feed pipe, swish it around and then spit it out. The  killer feature is "recommendations", probably based on a classifying algorithm they cribbed from an old IR paper. So not only will it suck for user #1, it will suck for every other user until their personal filter is trained.&lt;br /&gt;&lt;br /&gt;A shitty idea deserves a shitty implementation. The whole thing depends on Rails plus a wobbly collection of Java search-engine-y stuff that keeps changing its name and is nowhere near as efficient or reliable as the Google software it tries to imitate. Bonus: Persai will be hosted on Amazon's EC2 for maximum falability. Read my lips, guys: $73/month plus bandwidth for a lamed webserver with no N-O S-L-A. This is what happens when math nerds-turned-Google interns design a server farm.&lt;br /&gt;&lt;br /&gt;Congratulations! Your company is pursuing a&lt;br /&gt;&lt;br /&gt;(X) pseudo-technical ( ) web 2.0 ( ) ad-based ( ) acquisition-based &lt;br /&gt;&lt;br /&gt;approach to making money. Your idea fails. Here is why it fails. (One or more of the following may apply to your particular idea, and it may have other flaws which we couldn't be arsed to point out.)&lt;br /&gt;&lt;br /&gt;(X) 80% of blog pages are robot-generated rips and spam&lt;br /&gt;(X) of the remaining 20%, 95% are metoo posts&lt;br /&gt;(X) "Authority" (i.e. "groupthink") is easily gamed and useless in any event&lt;br /&gt;(X) storage and bandwidth are cheap, but not that cheap&lt;br /&gt;( ) moving data to the CPU is still not cheap&lt;br /&gt;(X) No one will pay to use this&lt;br /&gt;( ) Google will not buy you&lt;br /&gt;(X) Nicely-organized shit is still shit&lt;br /&gt;( ) A dating site that actually works means no repeat customers, dummy.&lt;br /&gt;&lt;br /&gt;Specifically, your plan fails to account for:&lt;br /&gt;&lt;br /&gt;( ) Lack of central controlling authority for blogs&lt;br /&gt;(X) Asshats&lt;br /&gt;( ) Fakesters&lt;br /&gt;(X) Providing actual value&lt;br /&gt;(X) DMCA takedown notices&lt;br /&gt;(X) Criminal investigations &amp; liability &lt;br /&gt;( ) Thundering Herds (not that you have any users)&lt;br /&gt;(X) Scalability&lt;br /&gt;( ) Hyperventilation about paedophiles/kiddie porn/bomb instructions&lt;br /&gt;(X) Undocumented, alpha-quality, Open Source knock-offs of Google infrastructure&lt;br /&gt;(X) Continued availability of Amazon compute services&lt;br /&gt;( ) Languages other than English (you provincial e-tard)&lt;br /&gt;(X) Extremely low marginal cost of blogspam&lt;br /&gt;(X) Clickfraud&lt;br /&gt;( ) Inherent failure of geocoding IP addresses&lt;br /&gt;( ) Armies of worm-riddled, broadband-connected computers&lt;br /&gt;(X) Eternal arms race involved in all filtering approaches&lt;br /&gt;(X) Unpopularity of weird new search engines&lt;br /&gt;(X) User Fatigue And Internet Login Syndrome (UFAILS)&lt;br /&gt;( ) No one wants to be on the same social network as their mom&lt;br /&gt;( ) Extreme stupidity of the internet public&lt;br /&gt;( ) Extreme cupidity of the internet public&lt;br /&gt;(X) Badly-coded blog readers&lt;br /&gt;(X) Badly-coded feeds&lt;br /&gt;(X) False positives that result in the replication of spammy data at your expense. (Thanks, asshole!)&lt;br /&gt;(X) Search Engine payola&lt;br /&gt;(X) Facebook&lt;br /&gt;&lt;br /&gt;and the following philosophical objections may also apply:&lt;br /&gt;&lt;br /&gt;(X) Ideas similar to yours are easy to come up with, yet all have failed&lt;br /&gt;( ) Blacklists suck&lt;br /&gt;( ) Whitelists suck&lt;br /&gt;(X) PageRank-style ideas are patented, and also suck&lt;br /&gt;(X) Clustering algorithms run in k^2 time or worse, and, furthermore, suck&lt;br /&gt;( ) Other people should be able to talk about Ron Paul without us having to hear it&lt;br /&gt;( ) Profitability should not involve AdSense&lt;br /&gt;( ) Profitability should not involve banner ads or affiliate programs&lt;br /&gt;( ) Userbase growth should not involve spamming address books&lt;br /&gt;( ) Searching should be free&lt;br /&gt;(X) Why should we have to trust you and your servers?&lt;br /&gt;(X) Why should I listen to your sucky algorithm instead of my friends?&lt;br /&gt;&lt;br /&gt;Furthermore, this is what I think about you:&lt;br /&gt;&lt;br /&gt;( ) Sorry dude, but I don’t think it would work.&lt;br /&gt;( ) This is a stupid idea, and you’re stupid for suggesting it.&lt;br /&gt;(X) Nice try, assh0le! Go back to wanking on 4chan.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2936626869504789093-1395488164015746791?l=carlos.bueno.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/2936626869504789093/1395488164015746791/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=2936626869504789093&amp;postID=1395488164015746791' title='8 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2936626869504789093/posts/default/1395488164015746791'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2936626869504789093/posts/default/1395488164015746791'/><link rel='alternate' type='text/html' href='http://carlos.bueno.org/2007/09/persai-your-company-advocates-x-pseudo.html' title='Persai: failed in the cradle'/><author><name>bueno</name><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='02278308746331037541'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2936626869504789093.post-1196312825613364647</id><published>2007-06-01T09:11:00.000-07:00</published><updated>2007-06-01T09:27:27.215-07:00</updated><title type='text'>Think Twitter is frivolous? Ask the protesters in Venezuela.</title><content type='html'>Twitting your bathroom breaks is frivolous. Like the user who used reddit to report on &lt;a href="http://reddit.com/info/j30d/comments"&gt;last year's coup in Thailand&lt;/a&gt;, the kids on the streets of Caracas who are protesting Hugo Chavez have a real use for it. They are using Twitter, SMS, camera phones, etc to coordinate, keep people safe, and get the word out as things happen on the scene.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.periodismodepaz.org/index.php/2007/05/30/dias-de-guardar/"&gt;Periodismo De Paz (Spanish)&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Update: Sis reminded me about the Usenet posts from "&lt;a href="http://en.wikipedia.org/wiki/Kremvax"&gt;kremvax&lt;/a&gt;" during the Russian coup in 1991.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2936626869504789093-1196312825613364647?l=carlos.bueno.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/2936626869504789093/1196312825613364647/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=2936626869504789093&amp;postID=1196312825613364647' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2936626869504789093/posts/default/1196312825613364647'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2936626869504789093/posts/default/1196312825613364647'/><link rel='alternate' type='text/html' href='http://carlos.bueno.org/2007/06/think-twitter-is-frivolous-ask.html' title='Think Twitter is frivolous? Ask the protesters in Venezuela.'/><author><name>bueno</name><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='02278308746331037541'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2936626869504789093.post-6540371906461735820</id><published>2007-05-22T08:54:00.000-07:00</published><updated>2007-05-22T10:07:57.793-07:00</updated><title type='text'>Helprin Wants Eternal Copyright</title><content type='html'>I have to respond to this. Mark Helprin, an author and occasional pundit, has really jumped the shark in &lt;a href="http://www.nytimes.com/2007/05/20/opinion/20helprin.html?ei=5090&amp;en=4187cd8cddc05eaf&amp;ex=1337313600&amp;partner=rssuserland&amp;emc=rss&amp;pagewanted=all"&gt;his New York Times Op-Ed about copyrights&lt;/a&gt; &lt;small&gt;(&lt;a href="/mark.helprin.txt"&gt;cached&lt;/a&gt;)&lt;/small&gt;. He is seriously (though not too intelligently) arguing for eternal copyright protection.&lt;br /&gt;&lt;br /&gt;Let's skip over the emotional appeals in the essay, the strange images of a copyleft junta that steals yachts from the mouths of children yet unborn. Helprin has made two errors in his thinking. One is that real property can be equated with other kinds. The other is that ideas can be easily separated from works.&lt;br /&gt;&lt;br /&gt;The term "intellectual property" has no legal basis. There is only real property, trademarks, patents, and copyrights. Trademarks are already treated almost the same as real property (with good reason), and he does not seem to be too interested in patents, so let's stick to copyright. &lt;br /&gt;&lt;br /&gt;A story is demonstrably different from an office building: I can copy a story with no material loss to the original copy. It is impossible to "own" or "steal" a copyrighted work, in the sense of denying others the use of it by possession. Thus most real property laws simply do not apply, and that is why they are treated differently under the law. A story cannot be contained, moved, hidden, or destroyed. Pretending otherwise is as silly as charging Xerox or Sony with grand theft. (Which, by the way, was actually tried by &lt;a href="http://en.wikipedia.org/wiki/Jack_Valenti"&gt;numbskulls&lt;/a&gt; such as the late Jack Valenti.)&lt;br /&gt;&lt;br /&gt;Helprin claims that this is unfair. Ford can keep his car factory for as long as it stands. Ay, but there's the rub. Ford owns real things that must be staffed and amortized and upgraded for them to produce more value. A story has no such pesky problems -- it is indeed forever. By his own logic, wouldn't eternal ownership of maintenance-free, non-material, value-producing things be unfair to owners of material ones? &lt;br /&gt;&lt;br /&gt;On to the second assertion: "ideas are immaterial to the question of copyright." Pardon? An entire phylum of lawyers lives off of derivative-work litigation. The separation of "idea" from "work" is a matter of judgement (ie expensive trials), not law. I invite Helprin to invest his hard-earned cash in a cartoon featuring a happy-go-lucky mouse in red shorts. I think it'll be a &lt;a href="http://en.wikipedia.org/wiki/Derivative_work"&gt;winner&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;His conclusion that "no good case can exist for treating with special disfavor the work of the spirit and the mind" is insupportable and false-to-fact. Copyrights and patents already have extraordinary monopoly protection --enjoyed by no other kind of work-- as compensation for their immateriality. Simply by breathing an author gets exclusive right to exploit the industry called copyright, backed up by the full might of every Berne Convention government. To balance this enormous grant of power against the needs of our shared culture, the culture that enriches all of our lives, it is limited in time.&lt;br /&gt;&lt;br /&gt;Now we have some &lt;a href="http://writ.news.findlaw.com/commentary/20020305_sprigman.html"&gt;deathless, rapacious corporations&lt;/a&gt; that would like to have this protection forever, to hold a veto on our culture unto the seventh generation. These same corporations take the lion's share of profit from authors, and then enlist them in their cause! The arrogance of Ozymandias was a rabbit fart in comparison.&lt;br /&gt;&lt;br /&gt;The Financial Times &lt;a href="http://www.ft.com/cms/s/25843b52-07b1-11dc-9541-000b5df10621.html"&gt;weighs in savagely&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Lawrence Lessig's &lt;a href="http://wiki.lessig.org/index.php/Against_perpetual_copyright"&gt;copyleft hordes&lt;/a&gt; do too.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2936626869504789093-6540371906461735820?l=carlos.bueno.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/2936626869504789093/6540371906461735820/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=2936626869504789093&amp;postID=6540371906461735820' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2936626869504789093/posts/default/6540371906461735820'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2936626869504789093/posts/default/6540371906461735820'/><link rel='alternate' type='text/html' href='http://carlos.bueno.org/2007/05/helprin-wants-eternal-copyright.html' title='Helprin Wants Eternal Copyright'/><author><name>bueno</name><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='02278308746331037541'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2936626869504789093.post-7699187318658518445</id><published>2007-04-06T10:05:00.001-07:00</published><updated>2007-04-06T17:21:10.630-07:00</updated><title type='text'>Software for the World</title><content type='html'>Q: How many Californians does it take to change a light bulb?&lt;br /&gt;A: &lt;i&gt;Just one. He holds it up and the world rotates around him.&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;In the US border states there used to be a phenomenon called "sombrero politics". During election season the gringo politicians would go to the Mexican parts of town, put on a gaudy sombrero, eat a burrito and give a short speech (in English). Then they would leave until the next election season. That's about where we are now with software. We take a bit of time out of our busy lives, code some functions, test the Unicode support, and go back to the important work.&lt;br /&gt;&lt;br /&gt;When a typical US programmer thinks of "internationalizing" software, he does it superficially. Porting your software to another culture is much more complex than porting to another OS or browser. Like the plates of the Earth's crust, language and mythology sit unseen underneath, shaping the mental landscape. &lt;br /&gt;&lt;br /&gt;Language indicates profound differences in how people think. For example, there is no satisfying equivalent for the Spanish word "&lt;a href="http://en.wikipedia.org/wiki/Dennis_the_Menace_(US)"&gt;travieso&lt;/a&gt;". It denotes a young, puckish, clever, likable little bastard. The trickster archetype is an important part of that culture; in English there is only a fuzzy cloud of adjectives &lt;sup&gt;&lt;a href="#note1" name="sup1"&gt;[1]&lt;/a&gt;&lt;/sup&gt;. There are emotional states that would take a dozen words to translate. Time is expressed as a quantity; the Anglophone time-as-distance is slightly weird. Compass directions (i.e., "turn North") are deeply weird and complex. The 12-hour clock and mm/dd/yy format are as archaic as feet and pounds. Why are they the default in so much software? Cultural bias in the programmers.&lt;br /&gt;&lt;br /&gt;Bias seeps in everywhere. I grew up in the desert. I don't think much about seafood. If I were creating a recipe site I would lump all seafood into one category and put beef, lamb, etc into their own categories. To me, fish is fish. To someone from the coast that's backwards: meat is meat. Fish is one type, shellfish another. There are mussels, lake fish, river fish, seawater and briny. That is a cultural disconnect at work. If only desert-people wrote software, their view would appear to dominate even though there are many more people who live on coasts. &lt;br /&gt;&lt;br /&gt;The &lt;a href="likebetter.com"&gt;LikeBetter&lt;/a&gt; guys have a &lt;a href="http://www.techcrunch.com/2007/03/30/are-you-a-y-combinator-founder/"&gt;test&lt;/a&gt; that compares you against all the YCombinator startup founder's scores. From a series of pictures, it inferred a lot of things about me: I am a female who doesn't like spicy food, is uninterested in politics and who doesn't like pranks. Wrong, wrong, wrong and &lt;a href="http://farm1.static.flickr.com/170/439422722_78e1cf0f93_o.png"&gt;wrong&lt;/a&gt;. What I really don't like is snow, and 7 or 8 of the pictures featured brain-damaged people up to their hips in that stuff. Without feedback likebetter is just shouting in an echo chamber.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://carlos.bueno.org/uploaded_images/likebetter-741997.gif"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://carlos.bueno.org/uploaded_images/likebetter-741985.gif" border="0" alt="" /&gt;&lt;/a&gt;&lt;small style="display:block; text-align:center;"&gt;They got my arrogance right, at least.&lt;/small&gt;&lt;br /&gt;&lt;br /&gt;Add all these differences up and you get a kind of cultural accent. As with verbal accents, the thicker it is the less people take you seriously. You might still succeed internationally but only by chance or lack of choice. The most popular chat software in South America and much of Europe is MSN. Do you know why? MSN doesn't either. Google's social network, Orkut, was flooded by Brasilians in 2004, even though the site was in English. Even the Brasilians don't agree on why. But consider that Orkut was invite-only, that three big signs of cultural status in Brasil are sexuality, popularity, and connections with the United States... and there are a lot of brasileiros working for Google &lt;sup&gt;&lt;a href="#note2" name="sup2"&gt;[2]&lt;/a&gt;&lt;/sup&gt;.&lt;br /&gt;&lt;br /&gt;US programmers often reply that it's the features that matter, not the funny squiggles over the vowels. Business folk will come back with the size of the US market. But that's short-term thinking. Treating entire continents as rounding errors only works when you are the only game in town.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;sup&gt;&lt;a href="#sup1" name="note1"&gt;[1]&lt;/a&gt;&lt;/sup&gt; A reader suggested "rascal", which captures the flavor very well. Try to remember the last time you've heard that word in casual speech.&lt;br /&gt;&lt;br /&gt;&lt;sup&gt;&lt;a href="#sup2" name="note2"&gt;[2]&lt;/a&gt;&lt;/sup&gt; Do you know who is big in Japan? &lt;a href="http://en.wikipedia.org/wiki/Pat_Metheny"&gt;Pat Metheney&lt;/a&gt;, &lt;a href="http://en.wikipedia.org/wiki/Frank_Zappa"&gt;Frank Zappa&lt;/a&gt;, and all the outré jazz composers you would only know if you are a hardcore music geek. In this case it's not US cachet. Experimental jazz has never been widely popular in the States. The conventional wisdom in music schools is that average ear doesn't get it, that Anglophones focus on lyrics over melody. But the average Asian ear is trained from birth on tonal languages in which a slight inflection changes meaning.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Also: &lt;a href="http://carlos.bueno.org/2007/04/globalization-test.html"&gt;Globalization Test&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2936626869504789093-7699187318658518445?l=carlos.bueno.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/2936626869504789093/7699187318658518445/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=2936626869504789093&amp;postID=7699187318658518445' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2936626869504789093/posts/default/7699187318658518445'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2936626869504789093/posts/default/7699187318658518445'/><link rel='alternate' type='text/html' href='http://carlos.bueno.org/2007/04/software-for-world_06.html' title='Software for the World'/><author><name>bueno</name><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='02278308746331037541'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2936626869504789093.post-9223345479032874881</id><published>2007-04-02T07:00:00.000-07:00</published><updated>2007-04-02T08:37:50.049-07:00</updated><title type='text'>Globalization Test</title><content type='html'>&lt;img src="http://carlos.bueno.org/uploaded_images/haz-clic-aqui-783207.gif" alt="" border="0" align="top"  /&gt;  &amp;nbsp; &amp;nbsp; &lt;img src="http://carlos.bueno.org/uploaded_images/brasileros-725315.gif" alt="" border="0" align="top"  /&gt;&lt;br /&gt;&lt;br /&gt;If you write software assuming any of the following:&lt;ul&gt;&lt;li&gt; English&lt;/li&gt;&lt;li&gt;Acronyms &lt;/li&gt;&lt;li&gt; Fixed timezone offsets (pssst... it's summer in Brasil and it's tomorrow in Australia)&lt;/li&gt;&lt;li&gt; "AM" and "PM"&lt;/li&gt;&lt;li&gt; Knowledge of US geography (Quick -- name all of the Canadian "states")&lt;/li&gt;&lt;li&gt; Latin-1 or ASCII-7 (We'll just add the funny things over the vowels later)&lt;/li&gt;&lt;li&gt; US-centric telephone &amp;amp; address formats (You mean it's not called a Zip code?)&lt;/li&gt;&lt;li&gt; US-centric methods of payment&lt;/li&gt;&lt;li&gt; US-centric legal frameworks (You can drink at 16 in England?)&lt;/li&gt;&lt;li&gt; Feet, Pounds, Fahrenheit (How's that Mars probe doing?)&lt;/li&gt;&lt;li&gt; Commas for decimals and periods for thousands-markers&lt;/li&gt;&lt;li&gt; "Soccer"&lt;/li&gt;&lt;li&gt; "Foreign" or "International" anything&lt;br /&gt;&lt;br /&gt;...report to my cube immediately for re-education with this baseball bat.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2936626869504789093-9223345479032874881?l=carlos.bueno.org' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2936626869504789093/posts/default/9223345479032874881'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2936626869504789093/posts/default/9223345479032874881'/><link rel='alternate' type='text/html' href='http://carlos.bueno.org/2007/04/globalization-test.html' title='Globalization Test'/><author><name>bueno</name><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='02278308746331037541'/></author></entry><entry><id>tag:blogger.com,1999:blog-2936626869504789093.post-6119175993167814608</id><published>2007-03-22T15:25:00.000-07:00</published><updated>2007-03-22T16:12:43.707-07:00</updated><title type='text'>Why Popular Sites Are "Ugly"</title><content type='html'>It seems as though popular websites, books, songs, movies, etc offend a lot of principles of quality and taste. What's more, it seems to be by design. It is. The funny thing is that this is exactly as it should be. It can't be any other way.&lt;br /&gt;&lt;br /&gt;The reason is that people are more different than you usually like to think. The form of a message has to be tuned to the intended effect, to the semantic receptors of the audience. When your audience is large and the common thread between them so thin, the form of a successful message becomes self-parody, an appeal to the basest desires. Nothing escapes this rule. Just look at the highest of highbrow-but-popular stuff (say, Shakespeare or Cervantes) and count the profound ideas shoved next to diarrhea jokes.&lt;br /&gt;&lt;br /&gt;I spent a few years designing ads for the phone book and direct mail. It's a no-nonesense business. Personal aesthetic takes a backseat to performance. In a way it's great: you have a single test of effectiveness (i.e., the phone rings or does not ring), reinforced by fast iteration and feedback. Direct Marketing is a real-world example of design by genetic algorithm.&lt;br /&gt;&lt;br /&gt;This kind of design does not always produce ugly results. It seems that way because, by definition, you see more wide-audience-targeted material than other kinds. Yachting and Spa ads are pretty posh but odds are good you're not on the list.&lt;br /&gt;&lt;br /&gt;Nor is popularity a good measure of quality. The books &lt;a href="http://www.gutenberg.org/etext/74"&gt;Tom Sawyer&lt;/a&gt; and &lt;a href="http://www.gutenberg.org/etext/76"&gt;Huckleberry Finn&lt;/a&gt; have the same characters, same kinds of silly scenes, pirates, farce. Yet &lt;span style="font-style:italic;"&gt;Sawyer&lt;/span&gt; is a kid's book while &lt;span style="font-style:italic;"&gt;Finn&lt;/span&gt; is a masterpiece. The difference is the stuff Mark Twain wove in between the crowd-pleasers.&lt;br /&gt;&lt;br /&gt;I sympathize with esthetes. I am am one. But if you are trying to make a popular website, remember that you are a commercial artist. Commercial art is the most demanding because of its constraints: the worst being that you do not have final say. The audience does.  Would Monty Python be as popular as it is without the flying sheep or the road-testing they did on each sketch? &lt;br /&gt;&lt;br /&gt;Also: An essay on &lt;a href="http://alistapart.com/articles/whitespace"&gt;Whitespace&lt;/a&gt; by Mark Boulton&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2936626869504789093-6119175993167814608?l=carlos.bueno.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/2936626869504789093/6119175993167814608/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=2936626869504789093&amp;postID=6119175993167814608' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2936626869504789093/posts/default/6119175993167814608'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2936626869504789093/posts/default/6119175993167814608'/><link rel='alternate' type='text/html' href='http://carlos.bueno.org/2007/03/why-popular-sites-are-ugly.html' title='Why Popular Sites Are &quot;Ugly&quot;'/><author><name>bueno</name><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='02278308746331037541'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2936626869504789093.post-2234436720319179603</id><published>2007-03-22T06:41:00.000-07:00</published><updated>2007-12-09T15:48:38.162-08:00</updated><title type='text'>Weird things you must believe in to be an applied mathematician:</title><content type='html'>1)  Any theorem that is disproved by contradiction must be thrown out.&lt;br /&gt;&lt;br /&gt;2)  ...except for &lt;a href="http://en.wikipedia.org/wiki/Bivalence"&gt;Bivalence&lt;/a&gt; and &lt;a href="http://en.wikipedia.org/wiki/Excluded_middle"&gt;LEM&lt;/a&gt; even though Russel &amp;amp; Gödel &lt;a href="http://foldoc.org/?Russell%27s+Paradox"&gt;kicked&lt;/a&gt; the &lt;a href="http://www.math.hawaii.edu/%7Edale/godel/godel.html#SecondIncompleteness"&gt;bottom&lt;/a&gt; out of them three generations ago. It's just too scary.&lt;br /&gt;&lt;br /&gt;3)  Probability, that &lt;a href="http://edge.org/q2005/q05_8.html#susskind"&gt;fluffy blue blankie&lt;/a&gt; we use where bivalence breaks down, is axiomatic even though it produces no significant results.&lt;br /&gt;&lt;br /&gt;4)  A logical tautology cannot also be a factual truth.&lt;br /&gt;&lt;br /&gt;5)  ...but the &lt;a href="http://www.unlv.edu/faculty/beisecker/Courses/Phi-101/Induction.htm"&gt;Principle of Induction&lt;/a&gt; always holds, except when it doesn't, and when it doesn't it means there is something new to learn. Why? &lt;span style="font-style:italic;"&gt;Because that's the way it has always happened in the past.&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2936626869504789093-2234436720319179603?l=carlos.bueno.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/2936626869504789093/2234436720319179603/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=2936626869504789093&amp;postID=2234436720319179603' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2936626869504789093/posts/default/2234436720319179603'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2936626869504789093/posts/default/2234436720319179603'/><link rel='alternate' type='text/html' href='http://carlos.bueno.org/2007/03/weird-things-you-must-believe-in-to-be.html' title='Weird things you must believe in to be an applied mathematician:'/><author><name>bueno</name><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='02278308746331037541'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2936626869504789093.post-6046438669046334338</id><published>2007-03-19T12:48:00.000-07:00</published><updated>2007-03-19T13:10:43.869-07:00</updated><title type='text'>Bueno's Law Of Geocoding:</title><content type='html'>&lt;a href="http://travel.yahoo.com/p-travelguide-482021-wrong_lake_mb_vacations-i"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://carlos.bueno.org/uploaded_images/wrong-lake-767021.png" border="0" alt="" /&gt;&lt;/a&gt;&lt;br /&gt;The accuracy of a given company's mapping application is 100% for the company headquarters, greater than 75% for the homes and hometowns of company employees, and undefined everywhere else.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2936626869504789093-6046438669046334338?l=carlos.bueno.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/2936626869504789093/6046438669046334338/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=2936626869504789093&amp;postID=6046438669046334338' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2936626869504789093/posts/default/6046438669046334338'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2936626869504789093/posts/default/6046438669046334338'/><link rel='alternate' type='text/html' href='http://carlos.bueno.org/2007/03/buenos-law-of-geocoding.html' title='Bueno&apos;s Law Of Geocoding:'/><author><name>bueno</name><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='02278308746331037541'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2936626869504789093.post-2206913385440312872</id><published>2007-03-09T07:39:00.000-08:00</published><updated>2007-03-09T08:27:00.377-08:00</updated><title type='text'>Startup School &amp; Startup Culture</title><content type='html'>Well, I've made it into &lt;a href="http://startupschool.org"&gt;Startup School&lt;/a&gt; this year. I'm genuinely excited, not just for the speakers but to meet the other 649 hackers.&lt;br /&gt;&lt;br /&gt;I'm curious to meet Paul Graham in particular. Over email he's always gracious &amp; thoughtful. But --and this is a little seed of a thought that's been growing for a couple of years-- I wonder about the effect he's having on the industry: whether YCombinator is doing good for the &lt;b&gt;art&lt;/b&gt; of software as it is for the business. &lt;br /&gt;&lt;br /&gt;Take music, the other creative pursuit linked with garages. People learn it on their own or at expensive schools. Groups condense out of the social soup of young people, and it tends to clump in certain cities. They have goofy names. They do it because they love it. The best are able to work magic. A lot of them fail, some make a decent living, a very few become rock stars.&lt;br /&gt;&lt;br /&gt;That's the myth. But that's not what actually happens. In the background are competing factions of promoters, managers, coaches, ghost writers, hired experts, financial backers. Without the cooperation of this network your chances of making it are thin enough to cut your wrist. Without them the Beatles would have just been a really good bar band. Generations of jazz and blues geniuses died in poverty because their looks were not marketable. Talent is not closely correlated with success.&lt;br /&gt;&lt;br /&gt;So ask yourself: Who makes money in this regime? The backers, the promoters, the hired guns, the music schools, and a pinch of the talents. How does this regime further the art of music? Do you have a radio? Have you used it lately? The internet is a great end-run around the music cartels, but your startup is &lt;i&gt;already&lt;/i&gt; on the internet.&lt;br /&gt;&lt;br /&gt;YC is backed by serious &amp; smart people. The pitch is soft, the benefits are compelling. They didn't create this situation. They are doing a lot of good: directly to the kids coming to Boston and as inspiration to everyone else. But if you love the art of software, keep your eyes open. Don't assume that what's best for your success is best for your art, or what's best for them is best for you.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2936626869504789093-2206913385440312872?l=carlos.bueno.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/2936626869504789093/2206913385440312872/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=2936626869504789093&amp;postID=2206913385440312872' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2936626869504789093/posts/default/2206913385440312872'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2936626869504789093/posts/default/2206913385440312872'/><link rel='alternate' type='text/html' href='http://carlos.bueno.org/2007/03/startup-school-startup-culture.html' title='Startup School &amp; Startup Culture'/><author><name>bueno</name><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='02278308746331037541'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2936626869504789093.post-5422317811602105352</id><published>2007-02-26T19:23:00.000-08:00</published><updated>2009-12-08T22:30:21.769-08:00</updated><title type='text'>Our Discontented Bones</title><content type='html'>Today the Miami sun is a hungover showgirl, leaving pink and purple smears on her pillow-clouds. She washes with lazy rain. It drips on Mediterranean collonades and quirky Arabian facades: memories ancient Persia by way of Spain. It drizzles into your Calle Ocho cafe con leche. It puddles the driveways of a hundred thousand SUVs and a dozen kinds of palm trees -- all of them, like most of us, recent imports.&lt;br /&gt;&lt;br /&gt;The lure of Miami is how it embraces the international and mocks the cosmopolitan. Another block is another dimesion: Little Haiti, Little Havana, pockets of Europe and Argentina and Hong Kong, people trading driving, praying, building, teeming in and out of one of the Grand Caravanserais of the New World.&lt;br /&gt;&lt;br /&gt;On occasion some 4th-generation cracker will read too much into my complexion. He will confide the unmoored feeling that comes every time he sees a billboard written in another language. I will drop into an accent that is comfortable to my tounge yet alien to my ears. It's an unconcious habit. I will share with him in the Brotherhood of Expatriates: a club with no rites or roster but which calls itself into session wherever beleaguered men find the familiar in a strange place. We will sit and sip and I will listen to how is it's a shame to have to taste the Brotherhood while on our native soil.&lt;br /&gt;&lt;br /&gt;The first time it happened I threw the ignorant prick out of my house. But that was during the golden time between callow youth and heavy drinking, before I realized that the unmoored feeling was with me always. &lt;br /&gt;&lt;br /&gt;People of recently mixed heritage have no homeland in this sense: there is no place, however distant in space and time, peopled with those who share every one of your values. Is the answer a permanent Wanderjahr?&lt;br /&gt;&lt;br /&gt;Quoth Nathaniel Hawthorne, a man who got paid by the comma:&lt;br /&gt;&lt;br /&gt;"...the years, after all, have a kind of emptiness, when we spend too many of them on a foreign shore. We defer the reality of life, in such cases, until a future moment, when we shall again breathe our native air; but, by and by, there are no future moments; or, if we do return, we find that the native air has lost its invigorating quality, and that life has shifted its reality to the spot where we have deemed ourselves only temporary residents. Thus, between two countries, we have none at all, or only that little space of either, in which we finally lay down our discontented bones."&lt;br /&gt;&lt;br /&gt;I didn't come to love this place until I had left, seen what was around, and came back. I needed distance to appreciate this mad soup that seems to be self-organizing into a cultural capital.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2936626869504789093-5422317811602105352?l=carlos.bueno.org' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2936626869504789093/posts/default/5422317811602105352'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2936626869504789093/posts/default/5422317811602105352'/><link rel='alternate' type='text/html' href='http://carlos.bueno.org/2007/02/our-discontented-bones.html' title='Our Discontented Bones'/><author><name>bueno</name><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='02278308746331037541'/></author></entry></feed>
