The pre-loader (also known as the speculative or look-ahead pre-parser) may be the single biggest improvement ever made to browser performance.
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
deferattributes) 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.
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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
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
Influencing the pre-loader
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:
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:
(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:
Prefetch and render a whole page in a background tab:
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.
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
- 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: