Introduction to web page performance

Orde Saunders' avatarby Orde Saunders

What do we mean by "web page performance"?

Web page performance refers to how fast a page loads in a user's browser, how long it takes between clicking a link and the new page showing up on screen.

We're not talking about how the page reacts once loaded, whilst that is important it's a separate subject, delivering content is the focus here.

Why does it matter?

Average page load time is 8.7 seconds

  • Reducing load time from 6s to 2s doubled engagement
  • 1 second delay lead to 2.8% drop in revenue
  • 200ms delay leads to 0.59% drop in engagement

There have been a number of studies into the impact of page performance.

To put these numbers in context, the average page load time is 8.7s.

Source: HTTP archive data for April 2014, onLoad metric.

A study by Torbit showed that by reducing the page load time from 6 seconds to 2 seconds reduced bounce rate by 10% and doubled the number of pages people visited.

A study by Bing[PPTX] showed that adding a delay of a second reduced revenue by 2.8%.

There's one from Google and Microsoft that added artificial delays to their search engine pages which showed that adding a delay of just 200ms reduced engagement by 0.59%.

Caveat

doge
  • very missing detail
  • such omissions
  • many oversimplification
  • wow

This is an introduction to the concepts involved, there is an awful lot more to this topic that what I'm going to cover today. As a result I've simplified a lot of things. In fact I've oversimplified quite a few things because the details would just confuse things at this level.

Waterfall diagrams

The most useful tool in understanding page load performance is the waterfall diagram.

A waterfall diagram has time on the horizontal axis and events on the vertical axis. This allows us to represent both timings and sequence.

Generic waterfall diagram

If you look in the network tab of browsers' developer tools you'll see that they'll generate a waterfall diagram for pages.

For this talk I'm going to be using illustrative diagrams like these, the waterfall diagrams in developer tools are much more detailed.

How is a page rendered in the browser?

In order to understand how we can make pages faster to load we need to understand how pages are rendered in the browser.

Request the page

The first and most obvious step is that the browser has to request the page over the network.

Network request shown as a waterfall daigram

There are many contributing factors to the and the low level specifics of how the network connection is made are beyond the scope of this talk but a useful working assumption is that there is a delay of around half a second between requesting a page and the data starting to arrive.

(It's worth bearing in mind that due to the way mobile networks attempt to preserve power it can be significantly longer, even on 4G network with a good signal.)

HTML

Once the browser has the HTML it can parse it and build the DOM tree.

The DOM tree is the data structure that the browser uses to represent a web page.

Only the DOM used for rendering

If the browser were to render the DOM tree at this point we would just see unstyled content.

Unstyled HTML

Given we don't normally see there must be something else going on.

CSS

That something else is CSS. Before it renders a page to screen the browser needs both the DOM and the CSS.

The DOM and CSS are required for rendering

What happens is the browser goes through the HTML looking for any references to CSS files and requests them. Once it has all the referenced CSS files it is then in a position to render the page.

Styled HTML

JavaScript

Except it's not that simple. We can - and usually do - use JavaScript to alter both the DOM and the CSS.

JavaScript can alter both the DOM and CSS which affects rendering

This means that, in addition to the CSS, the browser has to load and execute all the JavaScript included in a page before it can render it.

Page rendering

Lets have a look at this as a waterfall diagram:

HTML, CSS and JavaScript loading shown on a waterfall diagram

First the browser loads the HTML. Then it loads any CSS and JavaScript that it finds in the page before it can render the page.

Blocking and non-blocking requests

From this we can see that CSS and JavaScript block rendering of the page, the browser must resolve their effects before continuing.

Blocking and non-blocking requests shown on a waterfall diagram

Images, and other assets that - crucially - don't alter the DOM, are referred to as "non-blocking": the page can be rendered without the image having loaded and, once the image loads, it can be placed into the rendered document.

Key points

  1. Request start
  2. First paint
  3. Load complete

There are three key points we need to look at during page performance: request start, first paint and load complete.

  • Request start - this is when someone first requests the page (for example by clicking on a link). This is the start point for our timings.
  • First paint - this is the first point where the browser has all the information it needs to render the page. This is referred to as first paint as this is the first time the browser will paint an image of the rendered page on the screen.
  • Load complete - this is when all the requested assets have finished loading.

This is how they would fit on our waterfall diagram.

Key timing points shown on a waterfall diagram

Critical path

From the waterfall diagram we can identify which resources are key to the page load.

Critical path shown on a waterfall diagram

In this diagram we can see which resources are contributing to the critical path. We start with the HMTL - we can't do anything until we've got the HTML.

Next we load the CSS and the JavaScript so that we can render the page. As the browser knows it needs both of these it will request them at the same time. In this case the JavaScript is taking longer to load than the CSS so the images can't start loading until the JavaScript has finished.

The two images also start loading at the same time but, as the first one takes longer to load than the second, we can see that our overall timings only depend on the first image.

This critical path lets us identify where we need to focus our efforts at making the page load faster. In this case we should look at the JavaScript first - there's no point in making the CSS any faster because we'll still be waiting for the JavaScript.

Number of requests and data transferred

Page resources shown on a waterfall diagram

5 requests, 250KB, 2.11 seconds.

Whilst the critical path lets you identify the resources that are key to the page load you should also pay attention to the amount of data and how you are transferring it.

So in this hypothetical example we are transferring 250KB of data in five requests.

Obviously, the more data we transfer the longer it's going to take. But, as we saw at the start, each request we make over the network will incur a time overhead. So in this case we have three requests on the critical path so there is an overhead of approximately 1.5 seconds.

If we transfer a lot of data in one request that's going to take time. The usual culprit here is imagery - whilst those full bleed stock images might seem like a good idea are they really worth slowing your page down so much people don't bother to wait for them to load before going over to your competitor?

Similarly, whilst splitting your JavaScript and CSS up into smaller files does make them easier to work on, requesting a small amount of data spread over a dozen requests will add several seconds to your load time just in the time spent requesting each one individually. Anything that can be combined into one request should be.

Unblocking first paint

Up till now we've been looking at pages where the CSS and JavaScript have been blocking the rendering, and this is the behaviour we expect from web pages that have external CSS and JavaScript resources:

<link rel="stylesheet" href="styles.css"/>

<script src="behaviour.js"></script>

And this is how they are loaded:

Requests blocking first paint shown on a waterfall diagram

However, there are a couple of ways we can get round this.

HIC SVNT DRACONES

(Here be dragons)

These techniques can significantly improve page load performance but you need to understand what you're doing because you can break things if you get them wrong.

Asynchronous JavaScript

Whilst we have seen above that JavaScript is a blocking request it is possible to tell the browser that specific scripts are non-blocking by specifying the asynchronous flag:

<script src="behaviour.js" async></script>

This means they are no longer part of the critical rendering path:

Effect of asynchronous scripts shown on a waterfall diagram

This is supported in modern browsers and older browsers that don't understand it will still load the script in a blocking manner so it's a nice progressive enhancement.

There is trade-off involved in making scripts non-blocking, you can't just put the async attribute on your scripts and it will magically work, there are a number of things that can potentially go wrong. By using the async attribute you accept responsibility for the consequences of how your code executes.

Some of the things that can go wrong with asynchronous scripts include:

  • document.write will break things (not that anyone is using it anyway).
  • Scripts can execute in any order, there's no guarantee that any of a given script's dependencies will have loaded before it runs.
  • Events - and in particular DOMContentLoadedmight have been and gone before you try and bind to them.

Inlining CSS

So now with asynchronous scripts we've removed JavaScript from the critical path for first paint. We're left with CSS - the browser still has to load our external CSS file over the network and that takes time. So, instead of loading the CSS from an external file:

We include the CSS inline with the page in a style element:

<style>
  body{background-color:#1e163d;color:#32b2d7;font-family:sans-serif;font-size:100%;margin:1em 2.5%}@media all and (max-width:13.75em){body…
</style>

This now means that, whilst the HTML page is now slightly larger, the browser has everything it needs to render the page in that first request, we don't have to wait for CSS and JavaScript to load:

Effect of inline CSS shown on a waterfall diagram

First paint unblocked

With these two techniques we've gone from this:

Blocked rendering shown on a waterfall diagram

To this:

Unblocked rendering shown on a waterfall diagram

However, the price we've had to pay for this is that we now have taken over responsibility for some fundamental browser behaviour. Something the browser normally takes care of is now on your shoulders.

Whilst the default browser behaviour may not be the fastest it's also very safe, we're trading this safe behaviour for a speed increase.

For example, one of the things we have to watch for here is that the first paint now happens before the JavaScript has finished loading. This means that the site will appear on screen before the JavaScript is ready and you will have to make sure that you have put in place methods of dealing with this.

Resources

If you want to learn more about this then there are some good resources available.

The easiest place to start is in your browser's developer tools, the network tab will let you generate the the waterfall diagram for any page you visit.

If you want to learn more then Google's PageSpeed tools is a great resource. They have a tool that will examine your pages and give recommendations on how to make them faster. They also have a good documentation library that explains a lot of this in more detail.

If you really want to dive in at the deep end then WebPage test is the place to go. From there you can run tests on your pages in a number of browsers from various locations round the world and they'll give you full waterfall diagrams, timing breakdowns, film-strip views and a whole host of metrics.