How we improved performance on Google Code
By Jacob
Moon, Google Developer ProgramsIf you're a frequent
visitor to
code.google.com for product updates and reference materials for
Google APIs
you're working with, you might have noticed that the page loading time (or page rendering time
depending on how you see it) has reduced in varying degrees in the past several weeks.
As you'll see below, we've made several changes to help reduce user-perceived
latency. This is not an exhaustive list of all improvements we've made recently, but these are
the major ones we've made.
As Steve Souders emphasizes as the
"Performance Golden Rule" in his book
High Performance Web Sites,
"only 10-20% of the end user response time is spent downloading the HTML document.
The other 80-90% is spent downloading all the components in the page (p.5)".
We agree. That's why we focused our effort on reducing the number and size of
downloads (HTTP requests) for the "components" throughout Google Code.
- Combined and minimized JavaScript and CSS files used
throughout the site
Downloading JavaScript and CSS files blocks rendering of the rest of the page. Thus,
to reduce the number of HTTP requests made on the initial page load, we combined
frequently-used JavaScript and CSS files into one file each. This technique has brought down
20 HTTP requests down to just 2. We also minimized the files by stripping out unnecessary
whitespace and shortening function/variable names whenever possible.
- Implemented CSS sprites for frequently-used
images
There are 7 images
prominently used throughout Google Code, including the Google Code logo, the googley balls at
the bottom of every page, the plus and minus signs as well as the subscribe icon inside each
blog gadget.
Although browsers usually download several images in
parallel, we concatenated these images into one image so only one HTTP request would be made.
Of course, concatenating several images into one required us to make several changes in
HTML/CSS. For example, instead of having:
<img
src="/images/plus.gif" />
We had to change it to:
<div
style="background-image:url(/images/sprites.gif); background-position:-28px -246px; width:9px;
height:9px">&</div></span>
where sprites.gif is the concatenated
image and background-position and width/height carefully calculated.
- Implemented lazy loading of Google AJAX APIs
loader module (google.load)
We like to eat our own
dogfood.
Among other APIs, we use our very own AJAX Feed API on product homepages inside the blog
gadgets and the AJAX Search API on the
search
page. These Google AJAX APIs require the Google loader module (google.load) to be
loaded first before any of the specific AJAX APIs (i.e. AJAX Feed API, AJAX Search API, Maps
API) can be initialized and used. Traditionally, the Google AJAX APIs loader module would be
loaded by including the following <script> tag in the <head>
section:
<script type="text/javascript" src="http://www.google.com/jsapi"></script>
This
works well in most cases, but when optimizing for the display of static content, this blocks
the browser from rendering the rest of the page until it's finished loading that script, thus
impacting the user-perceived latency. So instead of loading the Google AJAX APIs loader module
upfront, we are now loading it lazily only on the pages where it's required. This is made
possible as follows (please note that this is a stripped-down version of what we have on
Google Code):
First, in the <head> section, we load the
Google AJAX APIs loader module via DOM scripting only on the pages where it's
required:
if
(needToLoadGoogleAjaxApisLoaderModule) {
// Load Google AJAX APIs loader module
(google.load)
var script = document.createElement('script');
script.src = 'http://www.google.com/jsapi?callback=googleLoadCallback';
script.type = 'text/javascript';
document.getElementsByTagName('head')[0].appendChild(script);
}
It's important to add the 'callback' parameter in the src attribute,
'callback=googleLoadCallback'. This
callback handler will then be called whenever the Google loader module is finished
loading.
Then, in the Google loader callback handler (googleLoadCallback()), we initialize the
AJAX Feed API and provide the function name that utilizes the AJAX Feed API (startUsingAjaxFeedAPI):
function
googleLoadCallback() {
// Initialize AJAX Feed API
google.load('feeds', '1', {callback: startUsingAjaxFeedAPI});
}
function startUsingAjaxFeedAPI() {
// Start using AJAX Feed API
var feed = new google.feeds.Feed(someFeedUrl);
...
}
In effect,
we're loading the AJAX Feed API on-demand through the use of two consecutive callback
handlers, first to load the Google AJAX APIs loader module (google.load) and then to
initialize the AJAX Feed API before it's used. Similar technique can be used for the Maps API
and the AJAX Search API.
By now you're probably wondering
just how much of an impact did these changes have on Google Code anyways? According to our
latency measurement stats, the user-perceived latency on Google Code dropped quite a bit,
anywhere between 30% and 70% depending on the page. This is a huge return for relatively small
investments we've made along the way, and we hope you'll find these techniques useful for your
own web development as well.