Andy Davies

How the Browser Pre-loader Makes Pages Load Faster

The pre-loader (also known as the speculative or look-ahead pre-parser) may be the single biggest improvement ever made to browser performance.

During their implementation Mozilla reported a 19% improvement in load times, and in a test against the Alexa top 2,000 sites Google found around a 20% improvement.

It’s not a new browser feature but some seem to believe it’s Chrome only and yet others suggest it’s “the most destructive ‘performance enhancement’ there’s ever been”!

So what is the pre-loader and how does it improve performance?

How browsers used to load web pages

Web pages are full of dependencies – a page can’t start rendering until the relevant CSS has downloaded, then when a script is encountered the HTML parser pauses until the script has executed (of course if the script is external it needs to be downloaded too).

Let’s consider how a browser might load a page:

  • First the HTML is downloaded and the browser starts parsing it. It finds a reference to an external CSS resource and fires off a request to download it.

  • The browser can carry on parsing the HTML while the CSS is downloading but then it finds a script tag with an external URL, now (unless the script has async or defer attributes) it must wait until the script has downloaded and executed.

  • Once the script has downloaded and executed, the browser can continue parsing the HTML, when it finds non-blocking resources such as images it will request them and carry on parsing, but when it finds a script it must stop and wait for the script to be retrieved and executed.

Although a browser is capable of making multiple requests in parallel, one that behaved like this often wouldn’t be downloading any resources in parallel with a script.

This is how browsers used to behave and using Curzillion by Steve Sounders we can create a test page that demonstrates this in IE7.

The test page has two stylesheets followed by two scripts in the head, then in the body it has two images, a script and finally another image.

The waterfall makes it easy to see parallel downloads stop while a script is being downloaded.


Waterfall of Cuzillion generated test page in IE7

If browsers still worked like this then pages would be slower to load as every time a script was encountered the browser would need to wait for the script to be downloaded and executed before it could discover more resources.

How the pre-loader improves network utilisation

Internet Explorer, WebKit and Mozilla all implemented pre-loaders in 2008 as a way of overcoming the low network utilisation while waiting for scripts to download and execute.

When the browser is blocked on a script, a second lightweight parser scans the rest of the markup looking for other resources e.g. stylesheets, scripts, images etc., that also need to be retrieved.

The pre-loader then starts retrieving these resources in the background with the aim that by the time the main HTML parser reaches them they may have already been downloaded and so reduce blocking later in the page.

(Of course if the resource is already in the cache then the browser won’t need download it)

Repeating the previous test with IE8 shows other resources are now downloaded in parallel with scripts, delivering a huge performance improvement for this test case: 7s vs 14s.


Waterfall of Cuzillion generated test page in IE8

Pre-loader behaviour varies between browsers and is still an area of experimentation, some browsers seem to have naive implementations where they download the resources in order of discovery but other browsers prioritise the downloads, for example Safari gives stylesheets that don’t apply to the current viewport a low priority, Chrome schedules scripts (even those at the foot of a page) with a higher priority than most of the images on the page.

The prioritisation mechanisms aren’t well documented (you can read the source for some browsers!) but if you want to get a better understanding of what they can do, James Simonsen wrote some excellent notes about the approaches they’re trying in Chrome.

Pre-Loader Gotchas

Pre-loaders extract URLs from markup and don’t / cannot execute javascript so any URLs inserted using javascript aren’t visible to it and the download of these resources will be delayed until the HTML parser discovers and executes the javascript that loads them.

There are cases where inserting resources using javascript can also trip up some pre-loaders.

I came across an answer on Stack Overflow suggesting javascript should be used to insert a link to either a mobile or desktop stylesheet depending on browser width:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<html>
<head>
  <script>
      var file = window.innerWidth < 1000 ? "mobile.css" : "desktop.css";
      document.write('<link rel="stylesheet" type="text/css" href="css/' + file + '"/>');
  </script>
</head>
<body>
<img src="img/gallery-img1.jpg" />
<img src="img/gallery-img2.jpg" />
<img src="img/gallery-img3.jpg" />
<img src="img/gallery-img4.jpg" />
<img src="img/gallery-img5.jpg" />
<img src="img/gallery-img6.jpg" />
</body>
</html>

There are several reasons why I wouldn’t use this approach but even this simple example is enough to trip up IE9’s pre-loader – notice how the images grab all the connections and the CSS is delayed until one of the images completes and a connection becomes available.


Test page loaded in IE9

Some of the responsive image approaches use a fallback image and the pre-loader will often initiate the fallback image download before the javascript to select the appropriate image has executed leading to extra downloads.

Influencing the pre-loader

Currently there are limited ways we can influence the pre-loader’s priorities (hiding resources using javascript is one), but the W3C Resource Priorities spec proposes two attributes to help signal our intent.

lazyload : resource should not be downloaded until other resources that aren’t marked lazyload have started downloading

postpone : resource must not be downloaded until it’s visible to the user i.e. within the viewport and display is not none.

Although I’m not sure how easy it is to polyfill, perhaps postpone might enable a simple way of implementing responsive images?

Pre-loading vs Pre-fetching

Pre-fetching is a way of hinting to the browser about resources that are definitely going to or might be used in the future, some hints apply to the current page, others to possible future pages.

At the simplest level we can tell the browser to resolve the DNS for another hostname that we will access later on the page:

1
<link rel="dns-prefetch" href="other.hostname.com">

Chrome also allows us to hint that we’re going to use another resource later in the current page and so it should be downloaded as a high priority:

1
<link rel="subresource"  href="/some_other_resource.js">

(Chromium’s source code suggests it’s actually downloaded as a lower priority than stylesheets/scripts and fonts but at an equal or higher priority than images)

There are two more link types that allow us to speculatively hint about what comes next and will be downloaded at a lower priority than the resources on the current page.

Prefetch an individual resource that might be on the next page:

1
<link rel="prefetch"  href="/some_other_resource.jpeg">

Prefetch and render a whole page in a background tab:

1
<link rel="prerender"  href="//domain.com/next_page.html">

Ilya Grigorik’s Preconnect, prefetch, prerender… talk from WebPerfDays New York is a good place to start if you want to learn more about pre-fetching.

Summary

The pre-loader isn’t new, it delivers a significant performance boost and as authors we don’t need to do anything special to take advantage of it.

It’s widely implemented - I tested the following browsers to confirm they had a pre-loader:

  • IE8 / 9 / 10
  • Firefox
  • Chrome (inc Android)
  • Safari (inc iOS)
  • Android 2.3

Bruce Lawson also confirmed Opera Mini uses the Presto engine which has a pre-loader.

Resource Priorities (and perhaps <link rel=subresource...) will give us some ways to indicate our priorities to it.

If you spot any typos, or have and questions add them in the comments and I’ll do my best to fix and answer.

References / Further Reading:

If you’re interested in digging further here’s some presentations, posts, bug reports etc. I read while writing this:

The WebKit PreloadScanner

How a web page loads

Speculatively load referenced files while “real” parsing is blocked on a <script src=> load

Implement speculative preloading

Best Practice: Get your HEAD in order

Bugs in IE8’s Lookahead Downloader

W3C Resource Priorities

Chrome PLT Improvements Q1 2013

Chrome Pre-Loader Test

Preconnect, prefetch, prerender

Comments