Network Performance

The quantum of the internet transmission is not the bit or the byte, it is packet. A message of N bytes is chopped into ceil(N / 1460) packets. That means there is little to no difference between sending 1 byte or 1000 bytes. It also mean that sending 1,461 bytes is twice the work of sending 1460 (two packets have to be sent, received, reassembled, and acknowledged).

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. An extra packet can make a large difference in the responsiveness of your app when it is compounded by latency and narrow upstream connections.

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 is another that will randomly drop half of all DNS requests:

sudo ipfw pipe 2 config plr 0.5
sudo ipfw add pipe 2 dst-port 53

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

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 URL count 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=bar instead of /blah?foo=bar.
  • If your app sends large amount 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 are 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.
Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License