Amiya Gupta (@amiyagupta) is a Web Performance Engineer at Microsoft. He has worked on making sites such as Microsoft.com and MSN.com load faster.
Introduction
Users of WebPageTest and other synthetic performance testing tools are familiar with the concepts of “First View” and “Repeat View” measurements. From the WebPageTest documentation:
First View:
The First View row is a test that was done with a browser that had its cache and cookies cleared out and represents what a first-time visitor to the page will experience.
Repeat View:
The Repeat View row is a test that was done immediately after the First View test without clearing out anything. The browser window is closed after the First View test and then a new browser is launched to do the Repeat View test. The Repeat View test represents what someone will see if they are coming back to the page some time after visiting it the first time (and is a best-case scenario since the test is re-visiting the page immediately).
These scenarios are intuitive and have worked well for many years. However, recent optimizations such as JS bytecode caching and Service Worker caching bring performance benefits that may not be fully captured by the First View and Repeat View scenarios.
Before Service Worker
Imagine a webpage contains a set of links to other pages and each link has an associated image. The page is built using standard server-side rendering and the content on the page changes four times a day. The page consists of the following resources:
- 1 HTML document (markup includes links)
- 1 CSS file
- 2 JS bundles: Vendor.js, App.js
- 10 images
Assumptions:
- Each cacheable resource has a unique/hashed URL ensuring no cache collisions
- The Vendor.js bundle contains libraries that do not change as frequently
- The App.js bundle is under active development and receives updates once a week
- Similarly the CSS bundle gets updated once a month.
Each resource’s configured cache lifetime and turnover rate (how often that resource is updated, invalidating cached versions) are listed below:
Resource | Cache Lifetime | Turnover Rate |
---|---|---|
HTML | 0 (never cached) | 6 hours |
CSS | 365 days | 30 days |
Vendor.js | 365 days | 90 days |
App.js | 365 days | 7 days |
Images | 30 days | 6 hours |
When a First View test runs the browser cache is empty and it needs to fetch all of these resources over the network. A Repeat View test would run within a few seconds to a minute and load only the HTML over the network; the rest of the resources would load from browser cache. As mentioned in the Repeat View definition above this is a best case scenario (with one notable exception).
All else being equal (browser, connection speed, device etc.) First View and Repeat View represent opposite ends of the performance spectrum for a given webpage. Performance will vary along that spectrum depending on the interval between visits:
Repeat Visit Interval | Resources Fetched over Network |
---|---|
First View | HTML + Images + App.js + CSS + Vendor.js |
Monthly | HTML + Images + App.js + CSS |
Weekly | HTML + Images + App.js |
Daily | HTML + Images |
Repeat View (~1 minute) | HTML |
Service Worker Based Architecture
Imagine a webpage with similar functionality that leverages Service Worker caching for its HTML. To ensure the links on the page are always fresh they are loaded via a new API call to the backend while the HTML functions as a shell for the content. The list of resources is:
- 1 HTML document (app shell, does not include links)
- 1 JSON API call containing links (called Links.json)
- 1 CSS file
- 2 JS bundles: Vendor.js, App.js
- 10 images
Resource | Cache Lifetime | Turnover Rate |
---|---|---|
HTML | 0 (browser cache), one-behind (SW cache) | 7 days |
Links.json | 0 (never cached) | 6 hours |
CSS | 365 days | 30 days |
Vendor.js | 365 days | 90 days |
App.js | 365 days | 7 days |
Images | 30 days | 6 hours |
Assumptions
- The HTML turnover rate matches the most volatile resource (App.js)
- The HTML is configured not to be cached on disk but is cached by the Service Worker
- Links.json is not set to cache (Service Worker could potentially be configured to load a stale version when the connection is offline)
In the non-Service Worker scenario the HTML was never cached. Frequent visitors to the Service Worker-enabled webpage will benefit from loading the HTML directly from cache and only needing to access the network for a much smaller payload (Links.json). Loading the HTML from cache shortens the critical rendering path.
The Problem
A Repeat View test as exists in WebPageTest today will not accurately model the benefit of the Service Worker cache and JS bytecode caching.
HTML caching only kicks in on the third visit:
- Install the Service Worker (typically done after page load)
- Add the HTML (and other resources) to the Service Worker cache
- (and onwards) Load HTML from Service Worker cache
Similarly the benefit of bytecode caching also kicks in on the third visit:
- When a JS file is first requested (i.e. a cold run), Chrome downloads it and gives it to V8 to compile. It also stores the file in the browser’s on-disk cache.
- When the JS file is requested a second time (i.e. a warm run), Chrome takes the file from the browser cache and once again gives it to V8 to compile. This time, however, the compiled code is serialized, and is attached to the cached script file as metadata.
- The third time (i.e. a hot run), Chrome takes both the file and the file’s metadata from the cache, and hands both to V8. V8 deserializes the metadata and can skip compilation.
Note that a JS bundle cached by a Service Worker will be bytecode cached from the second run onwards. However from a test automation perspective this still ends up being three steps because the Service Worker needs to be installed at the first step.
Conclusion
First View and Repeat View have been around for a long time and have traditionally done a good job demarcating the worst case and best case performance for a webpage under fixed browser, hardware and network conditions. First View is probably the single most important scenario for any website to focus on.
However, with the advent of advanced browser optimizations such as Bytecode caching and Service Worker caching, performance on the third visit onwards should be even better than what you see in Repeat View. If your web application gets a lot of repeat traffic it may be worth implementing a multi-step test using WebPageTest scripting to model your most frequent scenarios such as:
- Third view onwards: Leverage full benefits of Service Worker caching and JS bytecode caching
- Warm Service Worker cache + stale content: Modeling a daily visitor who benefits from having the HTML cached by the Service Worker but still needs to fetch fresh content over the network