Saturday, March 29, 2008

Website Performance Talk

This is a really enlightening talk from Steve Souders, author of High Performance Websites, and Chief Performance Yahoo!.  Below is a summary of his talk, but they also have a page detailing their rules.

  1. Make fewer HTTP requests: combing CSS and Javascript files, CSS sprites*, use image maps instead of multiple images where possible, inline images*
  2. Use a Content Distribution Network.  Akamai, Panther Express, Limelight, Mirror Image, SAVVIS.  Distribute your static content before creating a distributed architecture for your dynamic content.
  3. Add an Expires header.  If you aren't already, you should be changing the URL of your resources when you version them, to ensure that all users will get updates and fixes.  Since you then won't be updating a given resource, you should give it a far future expires header.
  4. Gzip all content: html, css, and javascript.
  5. Put stylesheets at the top.  IE will not begin rendering until all CSS files are downloaded.  Use the link HTML tag to pull in stylesheets, not the @import CSS directive, as IE will defer the download of the stylesheet.
  6. Put javascripts at the bottom.  HTTP 1.1 allows for two parallel server connections per hostname, but all downloads are blocked until the script is downloaded.  The defer attribute of the script block is not supported in Firefox and doesn't really defer in IE.
  7. Avoid CSS expressions.  The speaker doesn't cover this, but I gather from my own experience that this can seriously delay rendering as the expression will not be evaluated until the page is fully loaded.
  8. Make CSS and JS external.  This rule can be bent in situations where page views are low and users don't revisit often.  In this situation in-lining is suggested.
  9. Reduce DNS lookups.  He didn't cover this either, but it follows from rule #1 that additional network requests negatively impact performance.  He does mention later in the talk that splitting your requests across domains can drastically increase response time.  This is a consequence of the two connections per domain limit in the HTTP 1.1 specification.  It is important to remember that JavaScripts from one domain cannot affect JavaScripts or pages from another domain, due to browser security restrictions.  Also, this is a case of diminishing returns, as beyond four domains causes negative returns from, presumably DNS lookups and resource contention in the browser itself.
  10. "Minify" Javascript.  JSMin written by Doug Crockford is the most popular tool.  This is just removing whitespace, generally.  YUI compressor may be preferred and also does CSS.
  11. Avoid redirects.
  12. Remove duplicate scripts.
  13. Configure ETags.  These are used to uniquely identify a resource in space and time, such that if the ETag header received is identical to the cached result from a previous request for that resource, the browser can choose to use the local cache without need to download the remainder of the response stream.  The speaker doesn't go into this subject, so questions about how this improves caching performance over caching headers remain unanswered until you read his book.
  14. Make AJAX cacheable.  If you embed a last modified token into the URL of your AJAX request, you can cause the browser to pull from cache when possible.

A great tool to analyze your site's conformance to these rules is YSlow.  It's an add-on for Firebug.  During development your YSlow grade can give you a very good indication of what is happening to the response time of your application.  This metric, the YSlow grade, seems to me to be a much better quality gate for iterative development than does something as variable and volatile measured response time.

Some additional rules from the next version of the book:

  • As mentioned in my commentary in rule #9, split dominant content domains.
  • Reduce cookie weight.
  • Make static content cookie free.
  • Minify CSS (see rule #10 comments above).
  • Use iframes wisely.  The are a very expensive DOM operation.  Think about it--it's an entirely new page, but linked to another.
  • Optimize images.  Should your GIFs be PNGs?  Should your PNGs be JPGs?  Can you shrink your color palette?

Wednesday, March 19, 2008

Erlang News

Robert Virding's First Rule: "Any sufficiently complicated concurrent program in another language contains an ad hoc, informally-specified, bug-ridden, slow implementation of half of Erlang."

"I now know based on hard-won experience that you could replace "concurrent program" with "distributed system" in Robert's rule and it would still be just as accurate." --Steve Vinosky

Yariv wrote a cogent response to "What Sucks About Erlang".

Erlang Flavored Lisp anyone?

Monday, March 10, 2008

Programming Erlang: Chapter 8 Problem 2

'Tis an odd title, I know.  My puppy is prancing around the kitchen trying to get me interested in his chew toy, dropping it near me so I can see that it bounces and wiggles.

Do you want to see Erlang bounce and wiggle? The code below creates a circular linked list of Erlang N processes, then sends a Message around it M times. Pretty cool, huh? Well, it was meant to be used by readers of "Programming Erlang" to compare their favorite language to Erlang. I didn't bother doing this with C# threads, though it would be interesting to try it out with the concurrency and co-ordination runtime.

Here's the out put of the code (time is in milliseconds):

Sent 30000000 messages in 27453 / 29186


start(N, M, Message) when is_integer(N), is_integer(M) ->
FirstPid = spawn(?MODULE,loop,[[]]),
LastPid = create(1, N, FirstPid),
FirstPid ! LastPid, %% the ring is closed
send(0, M, FirstPid, Message),
{_, Time1} = statistics(runtime),
{_, Time2} = statistics(wall_clock),
io:format("Sent ~p messages in ~p / ~p~n", [N * M, Time1, Time2]).

send(C, C, _Pid, _Message) -> void;
send(I, C, Pid, Message) ->
Pid ! {self(), Message},
finished -> void
send(I+1, C, Pid, Message).

create(C, C, Pid) -> Pid;
create(I, C, Pid) ->
create(I+1, C, spawn(?MODULE, loop, [Pid])).

loop(Next) ->
{Caller, FirstPid, Message} ->
FirstPid =:= self() -> Caller ! finished;
true -> Next ! {Caller, FirstPid, Message}
{Caller, Message} ->
Next ! {Caller, self(), Message},
NewNext when Next =:= [] ->