Yahoo! Developer Network Blog
« Previous | Main | Next »
October 1, 2009
An Engineer's Guide to Bandwidth
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.
In reality, the Internet is more like a giant cascading multiplayer game of pachinko. 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.
What we talk about, when we talk about bandwidth
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.
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.
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:
- packets-per-second
- packet latency
- upstream vs downstream
Packets, not bytes
The quantum of internet transmission is not the bit or the byte, it's the packet. Everything that happens on the 'net happens as discrete pachinko balls of regular sizes. A message of N bytes is chopped into ceil(N / 1460) packets [1] 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 twice the work of sending 1,460: two packets have to be sent, received, reassembled, and acknowledged.
Packet #1 Payload
.....................................................................
.....................................................................
.....................................................................
.....................................................................
.....................................................................
.....................................................................
.....................................................................
.....................................................................
.....................................................................
.....................................................................
.....................................................................
.....................................................................
.....................................................................
.....................................................................
.....................................................................
.....................................................................
.....................................................................
.....................................................................
.....................................................................
.....................................................................
...........................................................
Packet #2 Payload
.
Listing 0: Byte 1,461 aka The Byte of Doom
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 little-known fact that browsers split certain POST requests into at least two packets regardless of the size of the message.
One packet, more or less, who cares? For one, none of your fancy caching and CDNs can help the client send data upstream. TCP slow-start means that the client will wait for acknowledgement of the first packet before sending the second. 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.
Packet Latency
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 2/3 of the speed of light) 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 [2]. 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 rant from 1996 complaining about 100msec latency from substandard telephone modems. If only.
Packet loss
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.
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 traffic shaping. All it takes is one user watching a video to flood the uplink.
Upstream < Downstream
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.
A Dismal Testing Harness

Figure 0: It's popcorn for dinner tonight, my love. I'm doing science!
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. :) Tor 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.
The ipfw 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 sudo ipfw flush to deactivate the rules when you are done.
$ sudo ipfw pipe 1 config bw 350kbit/s plr 0.05 delay 500ms
$ sudo ipfw add pipe 1 dst-port http
Here's another that will randomly drop half of all DNS requests. Have fun with that one.
$ sudo ipfw pipe 2 config plr 0.5
$ sudo ipfw add pipe 2 dst-port 53
To measure the effects of latency and packet loss I chose a highly-cached 130KB file from Yahoo's servers. I ran a script to download it as many times as possible in 5 minutes under various ipfw rules [3]. The "baseline" runs were the control with no ipfw restrictions or interference.

Figure 1: The effect of packet latency on download speed

Figure 2: Effect of packet loss on download speed
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.

Figure 3: Extreme volatility of response times during packet loss.
So, latency sucks. Now what?
Yahoo's web performance guidelines 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:
- Upstream packets are almost always expensive.
- Any client can have high or low overall bandwidth.
- High latency is not an error condition, it's a fact of life.
- TCP connections and DNS lookups are expensive under high latency.
- Variable latency is in some ways worse than low bandwidth.
Assuming that a large but unknown percentage of your users labor under adverse network conditions, here are some things you can do:
- 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.
/blah#foo=barinstead of/blah?foo=bar. Nothing after the # mark is sent to the server. - 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 real gzip compression.
YSlow says you should flush() early and put Javascript at the bottom. The reasoning is sound: get the HTML <head> 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.

Figure 4: docs is loading slowly... dare I click?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.
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.
- 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.
- We also recommend that you should make as much CSS and Javascript as possible external and to parallelize HTTP requests. 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.
- 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 [4]. 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.
- 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.
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.
Carlos Bueno
Software Engineer, Yahoo! Mail
Read more about how to optimize your web site performance with the Yahoo! performance guidelines.
Notes
[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.
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 RFC 1191 (aka Ethernet v2), 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.
[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.
[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 WRT45g 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.
[4] The Google mobile team recently put out an interesting fact: "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."
Image Credit: Pachinko by the_toe_stubber on Flickr.
Posted at October 1, 2009 8:00 AM | Permalink
Comments
Linux commands to add and remove 20ms delay to loopback device for local testing:
tc qdisc add dev lo root handle 1:0 netem delay 20msec
tc qdisc del dev lo root
Posted by: Pádraig Brady at October 1, 2009 9:08 AM
There is also the little-known fact that browsers split certain POST requests into at least two packets regardless of the size of the message.
It might be an even lesser known fact that every browser EXCEPT Firefox uses multiple packets. My post mentions that every browser I tested used multiple packets, except for Firefox.
Posted by: Joseph Scott at October 1, 2009 10:21 AM
Excellent article, looking forward to more!
Posted by: Rob at October 1, 2009 12:50 PM
Keep in mind that 1500-ish bytes is sort of a maximum. Most TCP's do path MTU discovery, to establish what the maximum segment size is, and would wind up at that value most of the time, but it is not hard at all to wind up with a 500-ish byte segment size or even a 250-ish byte segment size. (Anybody remember PPP? SLIP?)
Posted by: Tommy McGuire at October 1, 2009 4:16 PM
Using sudo is anti-unix. It's lame. If you want to run a program that needs to be root, then "su". You have a problem with that? OK, go run WIndows.
If you don't understand the unix philosophy, you should not be running unix.
(I can imagine a whole list of "...do" commands, e.g., "rmdo" (almost does rm, but really doesn't), "lsdo" (well, might list you're files if they're "not special"), "tardo" (this isn't tape, are you sure you want to do this?) oh gag me on and on.)
Man up. Use su !
Posted by: Ninja at October 1, 2009 5:34 PM
Ninja, you need to get off your high horse, it's lame to be so cocky.
Sudo is in fact the default way for OS X users to run stuff as root from a terminal session, so it's no surprise the OP provided that syntax. Further, if you directly login as root, may folks would consider you lame indeed.
Rather than having a shared root password, required by "su" the sudo command also allows for only certain programs to be run as root. Of course, if you really want to run bunches of commands in sequence as root, you can just do "sudo su -" or something similar and you'll have plenty of rope to hang yourself with.
Posted by: thankgoodnessimnotaninjajerk at October 1, 2009 9:08 PM
While your dialogue may prove insightful to a network technician (i.e. what you might refer to as a "network engineer") please don't confuse this with engineering. Scientists discover, we create and you assemble.
I came here expecting a simplified introduction to Shannon's theorem, Nyquist's rule or maybe even elementary coding theory; not, like I've found, a review of basic internet protocol.
Also, FYI: "bandwidth" has _nothing_ to do with packets/sec or latency; rather the _width_ of the _band_ being used to transmit information. How would you measure the bandwidth of (analog) FM radio with packets/sec or latency? I assure you FM does, indeed, consume bandwidth albeit in a form you may not understand. Another example might be ethernet, where frames are used instead of packets.
Finally, it is inappropriate to use the word engineering for technician work. This is analogous to confusing a nurse with a doctor or a mechanic with a mechanical engineer. In fact, the MCSE (Microsoft Certified Systems Engineer - a designation common amongst computer network techs) is illegal in Canada because of this.
I believe a more apt title for your article might be: "A Technician's Guide to Internet Protocol".
Posted by: tw0thr33 at October 1, 2009 11:04 PM
@ninja
Even the newest noob would know that sudo doesn't replace or compete with su. sudo inherits your environment while su does not. I use both. For example, if I must root then I su; however, if I need only override some settings, I'll use sudo. This allows me to continue with my custom vi, bash, etc. while being more secure. i.e. Alice might sudo to Bob's compromised account with (relatively) little risk. OTH, if Alice used su to access ~bob she might open herself up to attack.
Posted by: tw0thr33 at October 1, 2009 11:19 PM
@tw0thr33
I actually read Shannon in college. So I have a grasp of your issue.
However, I think it's pretty derisive to call the people that build the complex systems we use to power the web "technicians". At Yahoo! we employee "Software Engineers", of which I am one.
I'm sorry you don't like our nomenclature, but I don't feel that reaction to it added anything at all to the discussion.
Tom Hughes-Croucher,
Yahoo! Developer Network
Posted by: Tom Hughes-Croucher at October 1, 2009 11:48 PM
@tw0thr33
Wow, you had a serious reaction to the use of the word engineer there (everything okay?) In grad school I learned that titles and degrees don't mean so much (some of my classmates were 'interesting')... in fact, some of the most effective people I have ever worked with have the least formal education. I have the degrees and licenses sure, but they are not as essential as my natural aptitudes.
Carlos, great article thanks.
Anyone feel like building a webapp to count packets of a site? (@tw0thr33 wanna see if you have what it takes to build this... and slum it with us technician web repair maintenance men types?)
// insert @tw0thr33 code here
Posted by: Web Technician (who happens to have a PhD in computer science) at October 2, 2009 2:59 AM
Cheers - that was fascinating.
Posted by: Andrew Ducker at October 2, 2009 5:23 AM
Well that very valuable info.
Posted by: Burglar Alarms at October 2, 2009 5:58 AM
Oh Well
Posted by: Test at October 2, 2009 6:56 AM
Wow... someone offers up a decent article, and the douchebag brigade shows up to make sure that we're all straight on stupid shit. Bully for you, douchebags.
This is a good article, right up to where the comments start, including my own. I wish they could invent a separate internet for the douchebags (d-net?) where they could gratify their need to be trivial morons without intruding upon the rest of the civilized world. But I guess that's what being a self-preoccupied douchebag is all about, right ninja? right tw0thr33?
Keep up the good work OP -- you are appreciated by the less provincial of us out here.
Posted by: SickOfTheIgnorant at October 2, 2009 7:27 AM
Thanks for writing this. Neat.
I'd like to comment on css/javascript parallelism. Many companies and 'intelligent' web-designers are intent on improving the performance of their sites/applications while delivering all the gizmos that have become standard today.
One practice is to use as few link-embedded files as possible, and to cache configurations for the dynamic parts of a web page within a client-side data store. This is a reaction to emerging problems you describe here, and in some cases can be quite effective.
Posted by: relativityboy at October 2, 2009 8:38 AM
This article was very informative, well document and useful.
I really don't mind the post's title, but
I could also be considered a nomenclature douchebag. Engineering is serious business and ought to be.
I'm a Canadian working for an American company in Canada.
Until last year my title was Software Test Engineer. The company had to change it for Senior Software Test Specialist and I am glad of it.
In some Canadian provinces (3 that I know), "Engineer" is an academic title as much as "Doctor" or "Professor". Degrees are required and you need to be part of and accountable to a professional order.
Posted by: some canuck at October 2, 2009 8:55 AM
@canuck
So when an employee with little to no formal education and twice the knowledge, ability, and responsibility of their "engineer" peers is denied the same title, would you still support such logic?
There is certainly a need to differentiate a technician from an engineer, but having a specific degree has absolutely no place in that determination.
Posted by: Josh L at October 2, 2009 1:02 PM
Great article!
I also don't mind the "I have more bandwidth in my pee stream than you" comments either. At least they are close to being on topic.....
So whats better? Building a bigger water pipe or making water travel faster through a smaller one....
Posted by: ZuLuTrafficShapper at October 2, 2009 6:17 PM
Yes I know I spelled "Shapper" in my name wrong... I am intentionally increasing load on this site with my extra "P"
Posted by: ZuLuTrafficShapper at October 2, 2009 6:20 PM
I don’t know what to say other than - thanks for the kind words and your post is extremely relevant.Keep Posting
Posted by: smart lighting at October 2, 2009 11:35 PM
Thank you for all the wonderful comments and suggestions of analytics tools. stimulating and informative, but would make something more on this topic?
Posted by: hvac control at October 3, 2009 1:17 AM
It appears tw0thr33's elitism (or is that ignorance?) is showing.
Per the silly argument about the meaning of bandwidth, obviously the author knows what he is talking about, and is absolutely correct. As Miriam-Webster's defines it:
band·width
1 : a range within a band of wavelengths, frequencies, or energies; especially : a range of radio frequencies which is occupied by a modulated carrier wave, which is assigned to a service, or over which a device can operate
2 : the capacity for data transfer of an electronic communications system ; especially : the maximum data transfer rate of such a system
While using the term "throughput" corrects any misunderstandings, anyone but the most self-important of douche bags knows what the author was talking about in his explanation of bandwidth.
Beyond that, the US Bureau of Labor defines the role of an engineer as follows:
"Engineers apply the principles of science and mathematics to develop economical solutions to technical problems. Their work is the link between scientific discoveries and the commercial applications that meet societal and consumer needs."
Judging by the definition above, surely the topics discussed are in the realm of software engineering.
----
Back on topic, I wanted to say thanks for sharing your findings. I'm interested in seeing the real-world numbers that are coming up in future posts.
Posted by: elitism isn't impressive at October 3, 2009 4:38 AM
This is a good article, don't mind the naysayers. I wish there were more guys like you around!
Posted by: Widebandcowboy at October 3, 2009 4:45 AM
PRetty interesting read! electronic cigarette
Posted by: Jien at October 3, 2009 10:18 AM
Just so everyone knows, tw0thr33 is right with his comments about Engineer vs. Technician (technologist) in Canada. I'm a Computer Engineer Technologist and can never call myself a full Engineer. I can't be a P.Eng (professional engineer) unless I get a degree, my diploma isn't enough. Whether or not employers see it that way is a different story. I do know that I have as much knowledge as any Engineering degree student but didn't feel like spending 4+ years in school for book smarts when I could spend 2 1/2 years learning real world, gain the same amount of knowledge and be working sooner.
The difference, tw0thr33 (if they're a professional engineer) is legally bound by the advice he gives. If he even gives a friend advice saying that he can safely hook a switch to a hub and that person's house burns down because the cat5 cable caught on fire, his friend can sue him (I know, a stretch). As a technologist, we're taught ethics in our field but the consequences aren't as bad if we were Professional Engineers.
Either way, great article so far for web developers but do keep in mind that bandwidth (as pointed out) has at least two meanings. tw0thr33 pointed out one where the frequency swing at a certain band determines the bandwidth, and the other being how much information can be put down a pipe. Both are related to each other in a way but both are different in the technical aspect.
Posted by: chris at October 3, 2009 3:00 PM
When I read the title of this article, I thought this must have some very useful information for as web developer but when I opened it to read, I found that more than 50% of the article is too electronic rather than being software.
I hope that your next article is more for the web developers who really need to know these facts but in a bit more software specific language rather than more hardware based language.
Thanks so much... Looking forward to your second article.
Posted by: rocky singh at October 8, 2009 10:44 AM
> If your app sends largish amounts of data upstream ...
>consider implementing client-side compression.
Do browser uploads not routinely use Content-Encoding: gzip ?
Also: ceil(N / 1460)
Current versions of Windows, MacOS, and Linux all implement TCP window scaling, which adds 12 bytes of TCP headers to the first frame in a new TCP connection. Is window scaling is routinely enabled in the connections to a website like yahoo.com? If it is, then fitting a POST request into one packet has slightly less room to work with.
Posted by: DGentry at October 13, 2009 6:05 AM
This is a helpful article. Please don't listen to all these trolls giving you a hard time.
Posted by: Pete at October 16, 2009 7:26 AM
Carlos - really enjoyed reading this, thanks for posting such a detailed and interesting article. Looking forward to the next one! :)
Posted by: Andrew at October 16, 2009 6:23 PM
Is there any tools with the help of that I can know what is the bandwidth used by the end user in our network.
Posted by: Nikhil at February 5, 2010 10:40 PM
Post a comment
Comment Policy: We encourage comments and look forward to hearing from you. Please note that Yahoo! may, in our sole discretion, remove comments if they are off topic, inappropriate, or otherwise violate our Terms of Service. Fields marked with asterisk '*' are required.
Subscribe
Recent Blog Articles
view all
YQL Open Table for Google Buzz now live
Tue, 09 Feb 2010
INSERT INTO twitter.status ...
Mon, 08 Feb 2010
Announcing the Yahoo! Brasil Open Hack Day 2010, 20-21 March
Mon, 08 Feb 2010
Marketing hacks, linchpins, and tech women of valor
Sun, 07 Feb 2010
Yahoo! India invites you to join the first India Hadoop Summit
Thu, 04 Feb 2010
Recent Links
Appcelerator Titanium + Yahoo YQL on Vimeo
Mon, 08 Feb 2010
Tue, 02 Feb 2010
PhoneGap | Cross platform mobile framework
Sat, 30 Jan 2010
Web developers can rule the iPad - O'Reilly Radar
Sat, 30 Jan 2010
rc3.org - Is the iPad the harbinger of doom for personal computing?
Thu, 28 Jan 2010
Archives
2010
2009
2008
2007
2006
2005
Recent Readers

