<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[reloading - Medium]]></title>
        <description><![CDATA[Ideas for efficient loading on the web - Medium]]></description>
        <link>https://medium.com/reloading?source=rss----df1be8001167---4</link>
        <image>
            <url>https://cdn-images-1.medium.com/proxy/1*TGH72Nnw24QL3iV9IOm4VA.png</url>
            <title>reloading - Medium</title>
            <link>https://medium.com/reloading?source=rss----df1be8001167---4</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sat, 06 Jun 2026 05:31:55 GMT</lastBuildDate>
        <atom:link href="https://medium.com/feed/reloading" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[JavaScript Start-up Performance]]></title>
            <link>https://medium.com/reloading/javascript-start-up-performance-69200f43b201?source=rss----df1be8001167---4</link>
            <guid isPermaLink="false">https://medium.com/p/69200f43b201</guid>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[performance]]></category>
            <category><![CDATA[web-development]]></category>
            <category><![CDATA[compile]]></category>
            <category><![CDATA[parse]]></category>
            <dc:creator><![CDATA[Addy Osmani]]></dc:creator>
            <pubDate>Sat, 08 Apr 2017 21:23:00 GMT</pubDate>
            <atom:updated>2019-07-16T16:26:38.846Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ZpwZLFDNYZodJDerr7-37A.png" /></figure><p><strong><em>Update: </em></strong><a href="https://v8.dev/blog/cost-of-javascript-2019"><strong><em>The Cost Of JavaScript In 2019</em></strong></a><strong><em> is now available to read.</em></strong></p><p>As web developers, we know how easy it is to end up with web page bloat. But <strong>loading </strong>a webpage is much more than shipping bytes down the wire. Once the browser has downloaded our page’s scripts it then has to parse, interpret &amp; run them. In this post, we’ll dive into this phase for JavaScript, <em>why</em> it might be slowing down your app’s start-up &amp; <em>how</em> you can fix it.</p><p>Historically, we just haven’t spent a lot of time optimizing for the JavaScript Parse/Compile step. We almost expect scripts to be immediately parsed and executed as soon as the parser hits a &lt;script&gt; tag. But this isn’t quite the case. <strong>Here’s a simplified breakdown of how V8 works</strong>:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*GuWInZljjvtDpdeT6O0emA.png" /><figcaption>A simplified view of how V8 works. This is our idealized pipeline that we’re working towards.</figcaption></figure><p>Let’s focus on some of the main phases.</p><h4><strong>What slows our web apps from booting up?</strong></h4><p>Parsing, Compiling and Executing scripts are things a JavaScript engine spends <strong>significant </strong>time in during start-up. This matters as if it takes a while, it can <strong>delay</strong> how soon users can <strong>interact</strong> with our site. Imagine if they can see a button but not click or touch it for multiple seconds. This can <strong>degrade</strong> the user experience.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*M94-AavlZjGoudZG." /><figcaption>Parse &amp; Compile times for a popular website using V8’s Runtime Call Stats in Chrome Canary. Notice how a slow Parse/Compile on desktop can take far longer on average mobile phones.</figcaption></figure><p>Start-up times matter for <strong>performance-sensitive</strong> code. In fact, V8 - Chrome’s JavaScript engine, spends a <strong>large</strong> amount of time parsing and compiling scripts on top sites like Facebook, Wikipedia and Reddit:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*XjHkzz0B7KlcDbLFD1JS8Q.png" /><figcaption>The pink area (JavaScript) represents time spent in V8 and Blink’s C++, while the orange and yellow represent parse and compile.</figcaption></figure><p>Parse and Compile have also been highlighted as a bottleneck by a <strong>number </strong>of large sites &amp; frameworks you may be using. Below are tweets from Facebook’s Sebastian Markbage and Google’s Rob Wormald:</p><h3>Sebastian Markbåge on Twitter</h3><p>@swannodette Parse/compile is a huge problem. I&#39;ll keep bugging our guys to share numbers. However, the disconnect is what size to measure.</p><h3>Rob Wormald on Twitter</h3><p>@aerotwist @github my read of the data currently is angular&#39;s main startup cost is before we start touching DOM at all, mainly in JS parse.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*nkJwMuE5PpgF_pE0e6RM6g.jpeg" /><figcaption>Sam Saccone calls out the cost of JS parse in ‘<a href="https://www.youtube.com/watch?v=RWLzUnESylc">Planning for Performance</a>’</figcaption></figure><p>As we move to an increasingly mobile world, it’s important that we understand the<strong> time spent in Parse/Compile can often be 2–5x as long on phones as on desktop</strong>. Higher-end phones (e.g the iPhone or Pixel) will perform very differently to a Moto G4. This highlights the importance of us testing on representative hardware (not just high-end!) so our users’ experiences don’t suffer.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*dnhO1M_zlmAhvtQY_7tZmA.jpeg" /><figcaption><a href="https://docs.google.com/spreadsheets/d/1wHcNNQea28LhwQ_amFamT33d5woVrJfJy53Z1k6V090/edit?usp=sharing">Parse times</a> for a 1MB bundle of JavaScript across desktop &amp; mobile devices of differing classes. Notice how close a high-end phone like an iPhone 7 is to perf on a Macbook Pro vs the performance as we go down the graph towards average mobile hardware.</figcaption></figure><p>If we’re shipping huge bundles for our app, this is where endorsing modern bundling techniques like<strong> </strong>code-splitting, tree-shaking and Service Worker caching can really make a huge difference. That said, <strong>even a small bundle, written poorly or with poor library choices can result in the main thread being pegged for a long time in compilation or function call times. </strong>It’s important to holistically measure and understand where our real bottlenecks are.</p><h3>What Are JavaScript Parse &amp; Compile bottlenecks for the average website?</h3><p>“Buuuut, I’m not Facebook”, I hear you say dear, reader. <strong>“How heavy are Parse &amp; Compile times for average sites out in the wild?”</strong>, you might be asking. Let’s science this out!</p><p>I spent two months <a href="https://github.com/GoogleChrome/discovery/issues/1">digging into</a> the performance of a large set of production sites (6000+) built with different libraries and frameworks — like React, Angular, Ember and Vue. Most of the tests were recently redone on WebPageTest so you can easily redo them yourself or dig into the numbers if you wish. Here are some insights.</p><p><strong>Apps became interactive in 8 seconds on desktop (using cable) and 16 seconds on mobile (Moto G4 over 3G)</strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*WC4zanI0DKAoSiJVU3VUeA.png" /></figure><p><strong>What contributed to this? Most apps spent an average of 4 seconds in start-up (Parse/Compile/Exec)..on desktop.</strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*NacL9cZJ1osZowPS6hbCsQ.jpeg" /></figure><p>On mobile, parse times were up to 36% higher than they were on desktop.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*uTRfB5pne06h8lp5jGtiIQ.jpeg" /></figure><p><strong>Was everyone shipping huge JS bundles? Not as large as I had guessed, but there’s room for improvement. </strong>At the median, developers shipped 410KB of gzipped JS for their pages. This is in line with the 420KB over ‘average JS per page’ reported by the HTTPArchive. The worst offenders were sending anywhere up to 10MB of script down the wire. Oof.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*GvwfE2GjKQyLBKPmmfRwuA.png" /><figcaption><a href="http://httparchive.org/trends.php?s=All&amp;minlabel=Nov+15+2015&amp;maxlabel=Nov+15+2016#bytesJS&amp;reqJS">HTTPArchive stat</a>: the average page ships down 420KB of JavaScript</figcaption></figure><p><strong>Script size is important, but it isn’t everything. Parse and Compile times don’t necessarily increase linearly when the script size increases. </strong>Smaller JavaScript bundles generally do result in a faster <strong>load </strong>time (regardless of our browser, device &amp; network connection) but 200KB of our JS !== 200KB of someone else’s and can have wildly different parse and compile numbers.</p><h3><strong>Measuring JavaScript Parse &amp; Compile today</strong></h3><p><strong>Chrome DevTools</strong></p><p>Timeline (Performance panel) &gt; Bottom-Up/Call Tree/Event Log will let us drill into the amount of time spent in Parse/Compile. For a more complete picture (like the time spent in Parsing, Preparsing or Lazy Compiling), we can turn on <strong>V8’s Runtime Call Stats</strong>. In Canary, this will be in Experiments &gt; V8 Runtime Call Stats on Timeline.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*rWkYJzc6Cp0r3Xkr." /></figure><p><strong>Chrome Tracing</strong></p><p><strong>about:tracing</strong> — Chrome’s lower-level Tracing tool allows us to use the `disabled-by-default-v8.runtime_stats` category to get deeper insights into where V8 spends its time. V8 have a <a href="https://docs.google.com/presentation/d/1Lq2DD28CGa7bxawVH_2OcmyiTiBn74dvC6vn2essroY/edit#slide=id.g1a504e63c9_2_84">step-by-step guide</a> on how to use this that was published just the other day.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*P-_pLIITtYJRikRN." /></figure><p><strong>WebPageTest</strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/756/1*y6x_vr7aOxK4jHG9blgseg.png" /></figure><p>WebPageTest’s “Processing Breakdown” page includes insights into V8 Compile, EvaluateScript and FunctionCall time when we do a trace with the Chrome &gt; Capture Dev Tools Timeline enabled.</p><p>We can now also get out the <strong>Runtime Call Stats</strong> by specifying `disabled-by-default-v8.runtime_stats` as a custom Trace category (Pat Meenan of WPT now does this by default!).</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*tV48evC-XzYkoHonyKGkOw.png" /></figure><p>For a guide on how to get the most out of this, see <a href="https://gist.github.com/addyosmani/45b135900a7e3296e22673148ae5165b">this gist</a> I wrote up.</p><p><strong>User Timing</strong></p><p>It’s possible to measure Parse times through the <a href="https://w3c.github.io/user-timing/#dom-performance-mark">User Timing API</a> as Nolan Lawson points out below:</p><h3>Nolan Lawson on Twitter</h3><p>Measuring this stuff is extremely tricky! In short, this is the error I made:</p><p>The third &lt;script&gt; here isn’t important, but it’s the first &lt;script&gt; being separate from the second (<em>performance.mark()</em> starting before the &lt;script&gt; has been reached) that is.</p><p><em>This approach can be affected on subsequent reloads by V8’s preparser. This could be worked around by appending a random string to the end of the script, something Nolan does in his optimize-js benchmarks.</em></p><p>I use a similar approach for measuring the impact of JavaScript Parse times using Google Analytics:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ziA8f9KhB1gOt-Mq07cRFw.jpeg" /><figcaption>A custom Google Analytics dimension for ‘parse’ allows me to measure JavaScript parse times from real users and devices hitting my pages in the wild.</figcaption></figure><p><strong>DeviceTiming</strong></p><p>Etsy’s <a href="https://github.com/danielmendel/DeviceTiming">DeviceTiming</a> tool can help measure parse &amp; execution times for scripts in a controlled environment. It works by wrapping local scripts with instrumentation code so that each time our pages are hit from different devices (e.g laptops, phones, tablets) we can locally compare parse/exec. Daniel Espeset’s <a href="http://talks.desp.in/unpacking-the-black-box">Benchmarking JS Parsing and Execution on Mobile Devices</a> goes into more detail on this tool.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*FFzrH2QUiQZFX2rlF5e2-g.jpeg" /></figure><h3><strong>What can we do to lower our JavaScript parse times today?</strong></h3><ul><li><strong>Ship less JavaScript</strong>. The less script that requires parsing, the lower our overall time spent in the parse &amp; compile phases will be.</li><li><strong>Use code-splitting to only ship the code a user needs for a route and lazy load the rest</strong>. This probably is going to help the most to avoid parsing too much JS. Patterns like <a href="https://developers.google.com/web/fundamentals/performance/prpl-pattern/">PRPL</a> encourage this type of route-based chunking, now used by Flipkart, Housing.com and Twitter.</li><li><strong>Script streaming: </strong>In the past, V8 have told developers to use `async/defer` to opt into <a href="https://blog.chromium.org/2015/03/new-javascript-techniques-for-rapid.html">script streaming</a> for parse-time improvements of between 10–20%. This allows the HTML parser to at least detect the resource early, push the work to the script streaming thread and not halt the document parsing. Now that this is done for parser-blocking scripts too, I don’t think there’s anything actionable we need to do here. V8 recommend <strong>loading larger bundles earlier on as there’s only one streamer thread</strong> (more on this later)</li><li><strong>Measure the parse cost of our dependencies</strong>, such as libraries and frameworks. Where possible, switch them out for dependencies with faster parse times (e.g switch React for Preact or Inferno, which require fewer bytes to bootup and have smaller parse/compile times). Paul Lewis covered <a href="https://aerotwist.com/blog/when-everything-is-important-nothing-is/">framework bootup</a> costs in a recent article. As Sebastian Markbage has also <a href="https://twitter.com/sebmarkbage/status/829733454119989248">noted</a>, <strong>a good way to measure start-up costs for frameworks is to first render a view, delete and then render again as this can tell you how it scales.</strong> The first render tends to warm up a bunch of lazily compiled code, which a larger tree can benefit from when it scales.</li></ul><p>If our JavaScript framework of choice supports an ahead-of-time compilation mode (AoT), this can also help heavily reduce the time spent in parse/compile. Angular apps benefit from this for example:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*sr4eb-cx3lq7hVrJGfDNaw.png" /><figcaption>Nolan Lawson’s ‘<a href="https://channel9.msdn.com/Blogs/msedgedev/nolanlaw-web-perf-crisis">Solving the Web Performance Crisis</a>’</figcaption></figure><h3><strong>What are <em>browsers</em> doing to improve Parse &amp; Compile times today?</strong></h3><p>Developers are not the only ones to still be catching up on real-world start-up times being an area for improvement. V8 discovered that Octane, one of our more historical benchmarks, was a poor proxy for real-world performance on the 25 popular sites we usually test. Octane can be a poor proxy for 1) <strong>JavaScript frameworks</strong> (typically code that isn’t mono/polymorphic) and 2) <strong>real-page app startup</strong> (where most code is cold). These two use-cases are pretty important for the web. That said, Octane isn’t unreasonable for all kinds of workloads.</p><p>The V8 team has been hard at work improving start-up time and we’ve already seem some wins here:</p><h3>Addy Osmani on Twitter</h3><p>V8 has had a ~25% improvement in JavaScript start-up performance year-on-year. Shifted to focusing more on better perf for real-world apps.</p><p>We also estimate a 25% improve on V8 parse times for many pages looking at our Octane-Codeload numbers:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*cE8uvuvb0-iZslygh2NCTQ.jpeg" /></figure><p>And we’re seeing wins in this area for Pinterest too. There are a number of other explorations V8 has started over the last few years to improve Parsing and Compile times.</p><p><strong>Code caching</strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/577/1*xChjWSbT1rCqgLMacOMotQ.png" /><figcaption>From <a href="https://www.nativescript.org/blog/using-v8-code-caching-to-minimize-app-load-time-on-android">using V8’s code caching</a></figcaption></figure><p>Chrome 42 introduced <a href="http://v8project.blogspot.com/2015/07/code-caching.html">code caching </a>— a way to store a local copy of compiled code so that when users returned to the page, steps like script fetching, parsing and compilation could all be skipped. At the time we noted that this change allowed Chrome to avoid about 40% of compilation time on future visits, but I want to provide a little more insight into this feature:</p><ul><li>Code caching triggers for scripts that are executed <strong>twice in 72 hours</strong>.</li><li>For scripts of Service Worker: Code caching triggers for scripts that are executed twice in 72 hours.</li><li>For scripts stored in Cache Storage via Service Worker: Code caching triggers for scripts in the <strong>first execution</strong>.</li></ul><p>So, yes. <strong>If our code is subject to caching V8 will skip parsing and compiling on the third load.</strong></p><p>We can play around with these in <em>chrome://flags/#v8-cache-strategies-for-cache-storage</em> to look at the difference. We can also run Chrome with — js-flags=profile-deserialization to see if items are being loaded from the code cache (these are presented as deserialization events in the log).</p><p>One caveat with code caching is that it only caches what’s being eagerly compiled. This is generally only the top-level code that’s run once to setup global values. Function definitions are usually lazily compiled and aren’t always cached. <strong>IIFEs</strong> (for users of optimize-js ;)) are also included in the V8 code cache as they are also eagerly compiled.</p><p><strong>Script Streaming</strong></p><p><a href="https://blog.chromium.org/2015/03/new-javascript-techniques-for-rapid.html">Script streaming</a> allows async or defer scripts to be parsed on a <strong>separate background thread</strong> once downloading begins and improves page loading times by up to 10%. As noted earlier, this now also works for <strong>sync</strong> scripts.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ooXJ0NES-gXEzteaGPL2nQ.png" /></figure><p>Since the feature was first introduced, V8 have switched over to allowing <strong>all scripts</strong>, <em>even</em> parser blocking &lt;script src=””&gt; to be parsed on a background thread so everyone should be seeing some wins here. The only caveat is that there’s only one streaming background thread and so it makes sense to put our large/critical scripts in here first. <em>It’s important to measure for any potential wins here.</em></p><p><strong>Practically, &lt;script defer&gt; in the &lt;head&gt; so we can discover the resource early and then parse it on the background thread.</strong></p><p>It’s also possible to check with DevTools Timeline whether the correct scripts get streamed — if there’s one big script that dominates the parse time, it would make sense to make sure it’s (usually) picked up by the streaming.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*FAvUG7DrVJUXCK3oweMSLQ.png" /></figure><p><strong>Better Parsing &amp; Compiling</strong></p><p>Work is ongoing for a slimmer and faster Parser that frees up memory and is more efficient with data structures. Today, the <strong>largest</strong> cause of main thread jank for V8 is the nonlinear parsing cost. Take a snippet of UMD:</p><p>(function (global, <strong>module</strong>) { … })(this, function <strong>module</strong>() { <em>my functions</em> })</p><p>V8 won’t know that <strong>module </strong>is definitely needed so we won’t compile it when the main script gets compiled. When we decide to compile <strong>module</strong>, we need to reparse all of the inner functions. This is what makes V8’s parse-times non-linear. Every function at n-th depth is parsed n times and causes jank.</p><p>V8 are already working on collecting info about inner functions during the initial compile, so any future compilations can <em>ignore</em> their inner functions. For <strong>module</strong>-style functions, this should result in a large perf improvement.</p><p>See ‘<a href="https://docs.google.com/presentation/d/1214p4CFjsF-NY4z9in0GEcJtjbyVQgU0A-UqEvovzCs/edit#slide=id.p">The V8 Parser(s) — Design, Challenges, and Parsing JavaScript Better</a>’ for the full story.</p><p>V8 are also exploring offloading parts of JavaScript compilation to the <strong>background</strong> during startup.</p><p><strong>Precompiling JavaScript?</strong></p><p>Every few years, it’s proposed engines offer a way to <em>precompile</em> scripts so we don’t waste time parsing or compiling code pops up. The idea is if instead, a build-time or server-side tool can just generate bytecode, we’d see a large win on start-up time. My opinion is shipping bytecode can increase your load-time (it’s larger) and you would likely need to sign the code and process it for security. V8’s position is for now we think exploring avoiding reparsing internally will help see a decent enough boost that precompilation may not offer too much more, but are always open to discussing ideas that can lead to faster startup times. That said, V8 are exploring being more aggressive at compiling and code-caching scripts when you update a site in a Service Worker and we hope to see some wins with this work.</p><p>We discussed precompilation at BlinkOn 7 with Facebook and Akamai and my notes can be found <a href="https://gist.github.com/addyosmani/4009ee1238c4b1ff6f2a2d8a5057c181">here</a>.</p><p><strong>The Optimize JS lazy-parsing parens ‘hack’</strong></p><p>JavaScript engines like V8 have a lazy parsing heuristic where they pre-parse most of the functions in our scripts before doing a complete round of parsing (e.g to check for syntax errors). This is based on the idea that most pages have JS functions that are lazily executed if at all.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*LMRg_jHJeP53vdy8aiTEJQ.png" /></figure><p>Pre-parsing can speed up startup times by only checking the minimal a browser needs to know about functions. This breaks down with IIFEs. Although engines try to skip pre-parsing for them, the heuristics aren’t always reliable and this is where tools like <a href="https://github.com/nolanlawson/optimize-js">optimize-js</a> can be useful.</p><p>optimize-js parses our scripts in advance, inserts parenthesis where it knows (or assumes via heuristics) functions will be immediately executed enabling <strong>faster execution</strong>. Some of the paren-hacked functions are sure bets (e.g IIFEs with !). Others are based on heuristics (e.g in a Browserify or Webpack bundle it’s assumed all modules are eagerly loaded which isn’t necessarily the case). Eventually, V8 hopes for such hacks to not be required but for now this is an optimization we can consider if we know what you’re doing.</p><p><em>V8 are also working on reducing the cost for cases where we guess wrong, and that should also reduce the need for the parens hack</em></p><h3>Conclusions</h3><p><strong>Start-up performance matters. </strong>A<strong> </strong>combination of slow parse, compile and execution times can be a real bottleneck for pages that wish to boot-up quickly. <strong>Measure</strong> how long your pages spend in this phase. Discover what you can do to make it faster.</p><p>We’ll keep working on improving V8 start-up performance from our end as much as we can. We promise ;) Happy perfing!</p><h3><strong>Read More</strong></h3><ul><li><a href="https://www.youtube.com/watch?v=RWLzUnESylc">Planning for Performance</a></li><li><a href="https://twitter.com/MSEdgeDev/status/819985530775404544">Solving the Web Performance Crisis by Nolan Lawson</a></li><li><a href="https://timkadlec.com/2014/09/js-parse-and-execution-time/">JS Parse and Execution Time</a></li><li><a href="http://carlos.bueno.org/2010/02/measuring-javascript-parse-and-load.html">Measuring Javascript Parse and Load</a></li><li><a href="https://www.safaribooksonline.com/library/view/velocity-conference-new/9781491900406/part78.html">Unpacking the Black Box: Benchmarking JS Parsing and Execution on Mobile Devices</a> (<a href="https://speakerdeck.com/desp/unpacking-the-black-box-benchmarking-js-parsing-and-execution-on-mobile-devices">slides</a>)</li><li><a href="https://aerotwist.com/blog/when-everything-is-important-nothing-is/">When everything’s important, nothing is!</a></li><li><a href="http://benediktmeurer.de/2016/12/16/the-truth-about-traditional-javascript-benchmarks/">The truth about traditional JavaScript benchmarks</a></li><li><a href="http://stackoverflow.com/questions/1096907/do-browsers-parse-javascript-on-every-page-load/">Do Browsers Parse JavaScript On Every Page Load</a></li></ul><p><em>With thanks to V8 (Toon Verwaest, Camillo Bruni, Benedikt Meurer, Marja Hölttä, Seth Thompson), Nolan Lawson (MS Edge), Malte Ubl (AMP), Tim Kadlec (Synk), Gray Norton (Chrome DX), Paul Lewis, Matt Gaunt and Rob Wormald (Angular) and for their reviews of this article.</em></p><p><strong>Update: </strong>Thanks to some awesome members of the community, this article is now available in <a href="https://mp.weixin.qq.com/s?__biz=MzIwNjQwMzUwMQ==&amp;mid=2247484987&amp;idx=1&amp;sn=7f20da20bc6baed62ca8ff115209942b&amp;chksm=972364f9a054edefccebc89bb4b39150328f84fc6a3da53dafa9563df7375fef00b3a1a4c483&amp;mpshare=1">Chinese</a> and <a href="https://habrahabr.ru/company/mailru/blog/321748/">Russian</a> too.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=69200f43b201" width="1" height="1" alt=""><hr><p><a href="https://medium.com/reloading/javascript-start-up-performance-69200f43b201">JavaScript Start-up Performance</a> was originally published in <a href="https://medium.com/reloading">reloading</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Preload, Prefetch And Priorities in Chrome]]></title>
            <link>https://medium.com/reloading/preload-prefetch-and-priorities-in-chrome-776165961bbf?source=rss----df1be8001167---4</link>
            <guid isPermaLink="false">https://medium.com/p/776165961bbf</guid>
            <category><![CDATA[web-development]]></category>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[chrome]]></category>
            <category><![CDATA[web-performance]]></category>
            <category><![CDATA[performance]]></category>
            <dc:creator><![CDATA[Addy Osmani]]></dc:creator>
            <pubDate>Wed, 29 Mar 2017 15:44:29 GMT</pubDate>
            <atom:updated>2021-02-04T20:15:42.612Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*W4_tAMHlFs6tunMxbXQjFA.png" /></figure><p>Today we’ll dive into insights from Chrome’s networking stack to provide clarity on how web loading primitives (like <a href="https://w3c.github.io/preload/"><strong>&lt;link rel=“preload”&gt;</strong></a> &amp; <a href="https://w3c.github.io/resource-hints/"><strong>&lt;link rel=“prefetch”&gt;</strong></a>) work behind the scenes so you can be more effective with them.</p><p>As covered well in <a href="https://www.smashingmagazine.com/2016/02/preload-what-is-it-good-for/">other articles</a>, <strong>preload is a declarative fetch, allowing you to force the browser to make a request for a resource without blocking the document’s </strong><a href="https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/onload"><strong>onload</strong></a><strong> event</strong>.</p><p><strong>Prefetch</strong> <strong>is a hint to the browser that a resource might be needed</strong>, but delegates deciding whether and when loading it is a good idea or not to the browser.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*PSMeFcC3AXDUmdNf5l19Ug.jpeg" /><figcaption>Preload can decouple the load event from script parse time. If you haven’t used it before, read ‘<a href="https://www.smashingmagazine.com/2016/02/preload-what-is-it-good-for/">Preload: What is it Good For?</a>’ by Yoav Weiss</figcaption></figure><h3>Preload success stories in production sites</h3><p>Before we dive into the details, here’s a quick summary of some positive impact to loading metrics that have been observed using preload in the last year:</p><p>Housing.com saw a <a href="https://twitter.com/HousingEngg/status/844169796891508737"><strong>~10% improvement in Time to Interactive</strong></a> when they switched to preloading key late-discovered scripts for their Progressive Web App:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*fZH0GKzI42x7IgxKfiaddA.png" /></figure><p>Shopify’s switch to <a href="https://www.bramstein.com/writing/preload-hints-for-web-fonts.html">preloading Web Fonts</a> saw a<strong> </strong><a href="https://twitter.com/ShopifyEng/status/844245243948163072"><strong>50%</strong></a><strong> (1.2 second) improvement in time-to-text-paint</strong> on Chrome desktop (cable). This removed their flash-of-invisible text completely.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*rDnsYXceRwO-xxSZ." /><figcaption>Left: with preload, Right: without (<a href="https://video.twimg.com/tweet_video/C7dcmxaUwAAUhPX.mp4">video</a>)</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*r2RiRVrghz5iDUnhBX8W1Q.png" /><figcaption>Web Font loading using &lt;link rel=”preload”&gt;</figcaption></figure><p>Treebo, one of India’s largest hotel chains <strong>shaved </strong><a href="https://twitter.com/__lakshya/status/844429211867791361"><strong>1 second</strong></a><strong> off both time to First Paint and Time to Interactive</strong> for their desktop experience over 3G, by preloading their header image and key Webpack bundles:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*SKYdHNpGldFFUPZBDZQgSQ.png" /></figure><p>Similarly, by switching to preloading their key bundles, Flipkart <a href="https://twitter.com/adityapunjani/status/844250835802619905"><strong>shaved</strong></a><strong> a great deal of main thread idle</strong> before route chunks get evaluated on their PWA (trace from a low-end phone over 3G):</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*QL0ztXPZ1wUXpRKX." /><figcaption>Top: without preload, Bottom: with preload</figcaption></figure><p>And the Chrome Data Saver team saw <strong>time to first contentful paint improvements of </strong><a href="https://medium.com/reloading/a-link-rel-preload-analysis-from-the-chrome-data-saver-team-5edf54b08715#.bgj9qkqfr"><strong>12% on average</strong></a><strong> </strong>for pages that could use preload on scripts and CSS stylesheets.</p><p>As for prefetch, it’s widely used and at Google we still use it in <a href="https://plus.google.com/+IlyaGrigorik/posts/ahSpGgohSDo">Search results pages</a> to prefetch critical resources that can speed up rendering destination pages.</p><p>Preload is used in production by large sites for a number of use-cases and you can find more of them later on in the article. Before that, let’s dive into how the network stack actually treats preload vs prefetch.</p><h3>When should you &lt;link rel=”preload”&gt; vs &lt;link rel=”prefetch”&gt;?</h3><p><strong>Tip: Preload resources you have high-confidence will be used in the current page. Prefetch resources likely to be used for future navigations across multiple navigation boundaries.</strong></p><p>Preload is an early fetch instruction to the browser to request a resource <em>needed</em> for a page (key scripts, Web Fonts, hero images).</p><p>Prefetch serves a slightly different use case — a future navigation by the user (e.g between views or pages) where fetched resources and requests need to persist across navigations. If Page A initiates a prefetch request for critical resources needed for Page B, the critical resource and navigation requests can be completed in parallel. If we used preload for this use case, it would be immediately cancelled on Page A’s unload.</p><p>Between preload and prefetch, we get solutions for loading critical resources for the current navigation _or_ a future navigation.</p><h3>What is the caching behavior for &lt;link rel=”preload”&gt; and &lt;link rel=”prefetch”&gt;?</h3><p><a href="https://calendar.perfplanet.com/2016/a-tale-of-four-caches/">Chrome has four caches</a>: the HTTP cache, memory cache, Service Worker cache &amp; Push cache. Both preload and prefetched resources are stored in the <strong>HTTP cache.</strong></p><p>When a resource is <strong>preloaded or prefetched</strong> it travels up from the net stack through to the HTTP cache and into the renderer’s memory cache. If the resource can be cached (e.g there’s a valid <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control">cache-control</a> with valid max-age), it is stored in the HTTP cache and is available for <strong>current and future sessions</strong>. If the resource is <strong>not cacheable</strong>, it does not get stored in the HTTP cache. Instead, it goes up to the memory cache and stays there until it gets used.</p><h3>How does Chrome’s network prioritisation handle preload and prefetch?</h3><p>Here’s a break-down (<a href="https://docs.google.com/document/d/1bCDuq9H1ih9iNjgzyAL0gpwNFiEP4TZS-YLRp_RuMlc/edit#">courtesy</a> of Pat Meenan) showing how different resources are prioritized in Blink as of Chrome 46 and beyond:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*BTi3YhvCAYiJYRpjNQft9Q.jpeg" /><figcaption><em>* Preload using “as” or fetch using “type” use the priority of the type they are requesting. (e.g. preload as=style will use Highest priority). With no “as” they will behave like an XHR. ** “Early” is defined as being requested before any non-preloaded images have been requested (“late” is after). Thanks to Paul Irish for updating this table with the DevTools priorities mapping to the Net and Blink priorities.</em></figcaption></figure><p>Let’s talk about this table for a moment.</p><p><strong>Scripts get different priorities based on where they are in the document and whether they are async, defer or blocking:</strong></p><ul><li>Blocking scripts requested before the first image (an image early in the document) are Net:Medium</li><li>Blocking scripts requested after the first image is fetched are Net:Low</li><li>Async/defer/injected scripts (regardless of where they are in the document) are Net:Lowest</li></ul><p><strong>Images (that are visible and in the viewport) have a higher priority (Net:Medium) than those that are not in the viewport (Net:Lowest)</strong>, so to some extent Chrome will do it’s best to pseudo-lazy-load those images for you. Images start off with a lower priority and after layout is done and they are discovered to be in the viewport, will get a priority boost (but note that images already in flight when layout completes won’t be reprioritized).</p><p>Preloaded resources using the “as” attribute will have the <strong>same resource priority </strong>as the <strong>type</strong> of resource they are requesting. For example, preload as=“style” will get the highest priority while as=”script” will get a low or medium priority. These resources are <strong>also subject to the same CSP policies</strong> (e.g script is subject to script-src).</p><p>Preloaded resources without an “as” will otherwise be requested with the same priority as async XHR (so High).</p><p>If you’re interested in understanding what priority a resource was loaded with, this information is exposed in DevTools via both the Network section of Timeline/Performance:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*5QsDQsYJ4ts-4Tl0_1dZwQ.png" /></figure><p>and in the Network panel behind the “Priority” column:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*26d5UlWhql2NZ0Eg." /></figure><h3>What happens when a page tries to preload a resource that has already been cached in the Service Worker cache, the HTTP cache or both?</h3><p>This is going to be a large “it depends” but generally, something good should almost always happen in this case — the resource won’t be refetched from the network unless it has expired from the HTTP cache or the Service Worker intentionally refetches it.</p><p>If the resource is in the HTTP cache (between the SW Cache &amp; the network) then preload should get a cache hit from the same resource.</p><h3>Are there risks with these primitives of wasting a user’s bandwidth?</h3><p><strong>With “preload” or “prefetch”, you’re running some risk of wasting a user’s bandwidth, especially if the resource is not cacheable.</strong></p><p>Unused preloads trigger a console warning in Chrome, ~3 seconds after <em>onload:</em></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*Um55iV_tEBO3eXEs." /></figure><p>The reason for this warning is you’re probably using preload to try warming the cache for other resources you need to improve performance but if these preloaded resources aren’t being used, you’re doing extra work for no reason. On mobile, this sums up to wasting a user’s data plans, so be mindful of what you’re preloading.</p><h3>What can cause double fetches?</h3><p>Preload and prefetch are blunt tools and it isn’t hard to find yourself <a href="https://bugs.chromium.org/p/chromium/issues/list?can=2&amp;q=preload%20double%20owner%3Ayoav%40yoav.ws">double-fetching</a> if you aren’t careful.</p><p><strong>Don’t use “prefetch” as a fallback for “preload”</strong>. They’re again, used for different purposes and often end up causing <a href="https://twitter.com/yoavweiss/status/824957889991303168">double fetches</a> while this probably isn’t your intention. Use preload if it’s supported for warming the cache for current sessions otherwise prefetch for future sessions. Don’t use one in place of the other.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*KKong0kz69LOteD3." /></figure><p><strong>Don’t rely on fetch() working with “preload”… just yet.</strong> In Chrome if you try to use preload with the fetch() API you will end up triggering a double download. This doesn’t currently occur with XHR and we have an <a href="https://bugs.chromium.org/p/chromium/issues/detail?id=652228">open bug</a> to try addressing it.</p><p><strong>Supply an “as” when preloading or you’ll negate any benefits!</strong></p><p>If you don’t supply a valid “as” when specifying what to preload, for example, scripts, you will end up <a href="https://twitter.com/DasSurma/status/808791438171537408">fetching twice</a>.</p><p><strong>Preloaded fonts without crossorigin will double fetch! </strong>Ensure you’re adding a <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_settings_attributes">crossorigin attribute</a> when fetching fonts using preload otherwise they will be double downloaded. They’re requested using anonymous mode CORS. This advice applies even if fonts are on the same origin as the page. This is applicable to other anonymous fetches too (e.g XHR by default).</p><p><strong>Resources with an integrity attribute can’t reuse preloaded resources (for now) and can also cause double fetches.</strong> The `<a href="https://bugs.chromium.org/p/chromium/issues/detail?id=677022">integrity</a>` attribute for link elements has not yet been implemented and there’s an open <a href="https://github.com/w3c/webappsec-subresource-integrity/issues/26">spec issue</a> about it. This means the presence of any integrity metadata will currently discard preloaded resources. In the wild, it can also result in duplicate requests where you have to make a trade-off between security and performance.</p><p>Finally, although it won’t cause double fetches, this is generally good advice:</p><p><strong>Don’t try preloading absolutely everything! </strong>Instead, select specific late discovered resources that you want to load earlier and use preload to tell the browser about them.</p><h3>Should I just preload all the assets that my page requests in the head? Is there a recommended limit like “only preload ~6 things”?</h3><p>This is a good example of <strong>Tools, not rules. </strong>How much you preload may well factor in how much network contention you’re going to have with other resources also being loaded on your page, your user’s available bandwidth and other network conditions.</p><p>Preload resources that are likely to be discovered late in your page, but are otherwise important to fetch as early as possible. With scripts, preloading your key bundles is good as it separates fetching from execution in a way that just using say, &lt;script async&gt; wouldn’t as it blocks the window’s onload event. You can preload images, styles, fonts, media. Most things — what’s important is that you’re in better control of early-fetching what you as a page author knows is definitely needed by your page sooner rather than later.</p><h3>Does prefetch have any magical properties you should be aware of? Well, yes.</h3><p>In Chrome, if a user navigates away from a page while prefetch requests for other pages are still in flight, these requests will not get terminated.</p><p>Furthermore, prefetch requests are maintained in the unspecified net-stack cache for at least 5 minutes regardless of the cachability of the resource.</p><h3>I’m using a custom “preload” implementation written in JS. How does this differ from rel=”preload” or Preload headers?</h3><p>Preload decouples fetching a resource from JS processing and execution. As such, preloads declared in markup are optimized in Chrome by the preload scanner. This means that in many cases, the preload will be fetched (with the indicated priority) before the HTML parser has even reached the tag. This makes it a lot more powerful than a custom preload implementation.</p><h3>Wait. Shouldn’t we be using HTTP/2 Server Push instead of Preload?</h3><p><strong>Use Push when you know the precise loading order for resources and have a service worker to intercept requests that would cause cached resources to be pushed again. Use preload to move the start download time of an asset closer to the initial request — it’s useful for both first and third-party resources.</strong></p><p>Again, this is going to be an “<a href="https://docs.google.com/document/d/1K0NykTXBbbbTlv60t5MyJvXjqKGsCVNYHyLEXIxYMv0/edit">it depends</a>”. Let’s imagine we’re working on a cart for the Google Play store. For a given request to play.google.com/cart:</p><p>Using Preload to load key modules for the page requires the browser to wait for the play.google.com/cart payload in order for the preload scanner to detect dependencies, but after this contains sufficient information to saturate a network pipe with requests for the site’s assets. This might not be the most optimal at cold-boot but is very cache and bandwidth friendly for subsequent requests.</p><p>Using H/2 Server Push, we can saturate the network pipe right away on the request for play.google.com/cart but can waste bandwidth if the resources being pushed are already in the HTTP or Service Worker cache. There are always going to be trade-offs for these two approaches.</p><p>Although Push is invaluable, it doesn’t enable all the same use-cases as Preload does.</p><p>Preload has the benefit of decoupling download from execution. Thanks to support for document onload events you can control scripting if, how and when a resource gets applied. This can be powerful for say, fetching JS bundles and executing them in idle blocks or fetching CSS and applying them at the right point in time.</p><p>Push can’t be used by third-party hosted content. By sending resources down immediately, it also effectively short-circuits the browser’s own resource prioritization logic. In cases where you know exactly what you’re doing, this can yield performance wins, but in cases where you don’t you could actually harm performance significantly.</p><h3>What is the Link preload header? How does it compare to the preload link tag? And how does it relate to HTTP/2 Server Push?</h3><p>As with other types of links, a preload link can be specified using either an HTML tag or an HTTP header (a <a href="https://w3c.github.io/preload/#server-push-http-2">Link preload header</a>). In either case, a preload link directs the browser to begin loading a resource into the memory cache, indicating that the page expects with high confidence to use the resource and doesn’t want to wait for the preload scanner or the parser to discover it.</p><p><strong>How does a preload tag and Link header differ?</strong>&lt;link rel=preload&gt; tags are initiated after the browser receives a document and the preload scanner discovers these tags. Preloading via headers _may_ offer a very negligible improvement, but require the document to be committed before processing happens.</p><p>When the Financial Times introduced a Link preload header to their site, they<strong> shaved </strong><a href="https://twitter.com/wheresrhys/status/843252599902167040"><strong>1 second</strong></a><strong> off the time it took to display the masthead image:</strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*QGUllBDRLMjdy1uawXG8EQ.jpeg" /><figcaption>Bottom: with preload, Top: without. <strong>Comparison for Moto G4 over 3G: </strong>Before: <a href="https://www.webpagetest.org/result/170319_Z2_GFR/">https://www.webpagetest.org/result/170319_Z2_GFR/</a>, After: <a href="https://www.webpagetest.org/result/170319_R8_G4Q/">https://www.webpagetest.org/result/170319_R8_G4Q/</a></figcaption></figure><p>You can provide preload links in either form, but there is one important difference you should understand: as allowed by the spec, many servers initiate an HTTP/2 Server Push when they encounter a preload link in HTTP header form. The performance implications of H/2 Server Push are different from those of preloading (see below), so you should make sure you don’t unintentionally trigger pushes.</p><p>You can avoid unwanted pushes by using preload link tags instead of headers, or by including the ‘nopush’ attribute in your headers.</p><h3>How can I feature detect support for link rel=preload?</h3><p>Feature detecting for &lt;link rel=”preload”&gt;can be accomplished using the following snippet:</p><pre>const preloadSupported = () =&gt; {<br>  const link = document.createElement(&#39;link&#39;);<br>  const relList = link.relList;<br>  if (!relList || !relList.supports)<br>    return false;<br>  return relList.supports(&#39;preload&#39;);<br>};</pre><p>The FilamentGroup also have a <a href="https://github.com/filamentgroup/loadCSS/blob/master/src/cssrelpreload.js#L8-L14">preload check</a> they use as part of their async CSS loading library, <a href="https://github.com/filamentgroup/loadCSS">loadCSS</a>.</p><h3>Can you immediately apply preloaded CSS stylesheets?</h3><p>Absolutely. Preload support markup based asynchronous loading. Stylesheets loaded using &lt;link rel=”preload”&gt; can be immediately applied to the current document using the `onload` event as follows:</p><pre>&lt;link rel=&quot;preload&quot; href=&quot;style.css&quot; onload=&quot;this.rel=stylesheet&quot;&gt;</pre><p>For more examples like this, see <em>Use Cases</em> in this great Yoav Weiss <a href="http://yoavweiss.github.io/link_htmlspecial_16/#53">deck</a>.</p><h3>What else is Preload being used for in the wild?</h3><p><strong>According to the HTTPArchive, </strong><a href="https://twitter.com/addyosmani/status/843254667316465664"><strong>most</strong></a><strong> sites using &lt;link rel=”preload”&gt; use it to </strong><a href="https://www.zachleat.com/web/preload/"><strong>preload Web Fonts</strong></a><strong>, including Teen Vogue and as mentioned earlier, Shopify:</strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*osYEtZ6gZnmstK4fpcJTrg.png" /></figure><p><strong><em>While </em></strong><a href="https://twitter.com/addyosmani/status/843258951110074368"><strong><em>other</em></strong></a><strong><em> popular sites like LifeHacker and JCPenny use it to asynchronously load CSS (via the FilamentGroup’s </em></strong><a href="https://github.com/filamentgroup/loadCSS"><strong><em>loadCSS</em></strong></a><strong><em>):</em></strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*BxecU2LjN-uGAW_uQgDTdw.png" /></figure><p><strong>And then there are a growing breed of Progressive Web Apps (like Twitter.com mobile, Flipkart and Housing) using it to preload scripts that are needed for the current navigation using patterns like </strong><a href="https://developers.google.com/web/fundamentals/performance/prpl-pattern/"><strong>PRPL</strong></a><strong>:</strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*rppoHbaTTJQNVZBO4j_NAQ.png" /></figure><p><em>The basic idea there is to maintain artifacts at high-granularity (as opposed to monolithic bundles) so any facet of the app can on demand load it’s dependencies or preload those that are likely to be needed next to warm up the cache.</em></p><h3>What is the current browser support for Preload and Prefetch?</h3><p>&lt;link rel=”preload”&gt; is available to <a href="http://caniuse.com/#feat=link-rel-preload">~50% </a>of the global population according to CanIUse and is implemented in the <a href="https://developer.apple.com/safari/technology-preview/release-notes/">Safari Tech Preview</a>. &lt;link rel=”prefetch”&gt; is available to <a href="http://caniuse.com/#search=prefetch">71%</a> of global users.</p><h3>Further insights you may find helpful:</h3><ul><li>Yoav Weiss landed a <a href="https://twitter.com/yoavweiss/status/843810722383630337 ">recent change</a> in Chrome that avoids preload contending with CSS &amp; blocking scripts.</li><li>He also recently <a href="https://groups.google.com/a/chromium.org/forum/#!topic/blink-dev/BN6tqGLBmuI">split</a> the ability to preload media into three distinct types: video, audio and track.</li><li>Domenic Denicola is <a href="https://github.com/whatwg/html/pull/2383">exploring</a> a spec change to add support for preloading ES6 Modules.</li><li>Yoav also recently shipped support for <a href="https://groups.google.com/a/chromium.org/forum/#!msg/blink-dev/8Zo2HiNEs94/h8mDVkx0EwAJ">Link header support for “prefetch”</a> allowing easier additional of resource hints needed for the next navigation.</li></ul><h3>Further reading on these loading primitives:</h3><ul><li><a href="https://www.smashingmagazine.com/2016/02/preload-what-is-it-good-for/">Preload — what is it good for?</a> — Yoav Weiss</li><li><a href="https://twitter.com/ChromiumDev/status/837715866078752768">A &lt;link rel=”preload”&gt; study</a> by the Chrome Data Saver team</li><li><a href="https://www.youtube.com/watch?v=RWLzUnESylc">Planning for performance</a> — Sam Saccone</li><li><a href="https://github.com/googlechrome/preload-webpack-plugin">Webpack plugin</a> for auto-wiring up &lt;link rel=”preload”&gt;</li><li><a href="https://www.keycdn.com/blog/resource-hints/">What is preload, prefetch and preconnect?</a> — KeyCDN</li><li><a href="https://www.zachleat.com/web/preload/">Web Fonts preloaded</a> by Zach Leat</li><li><a href="https://www.google.com/url?q=https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/http-caching%23cache-control&amp;sa=D&amp;ust=1490641457910000&amp;usg=AFQjCNEb6fMArN_ahD7ySMICPF1Obf4rsw">HTTP Caching: cache-control</a> by Ilya Grigorik</li></ul><p><em>With thanks to @ShopifyEng, @AdityaPunjani from Flipkart, @HousingEngg, @adgad and @wheresrhys at the FT and @__lakshya from Treebo for sharing their before/after preload stats.</em></p><p><strong><em>With many thanks for their technical reviews &amp; suggestions: Ilya Grigorik, Gray Norton, Yoav Weiss, Pat Meenan, Kenji Baheux, Surma, Sam Saccone, Charles Harrison, Paul Irish, Matt Gaunt, Dru Knox, Scott Jehl.</em></strong></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=776165961bbf" width="1" height="1" alt=""><hr><p><a href="https://medium.com/reloading/preload-prefetch-and-priorities-in-chrome-776165961bbf">Preload, Prefetch And Priorities in Chrome</a> was originally published in <a href="https://medium.com/reloading">reloading</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[A Link: rel=preload Analysis From the Chrome Data Saver Team]]></title>
            <link>https://medium.com/reloading/a-link-rel-preload-analysis-from-the-chrome-data-saver-team-5edf54b08715?source=rss----df1be8001167---4</link>
            <guid isPermaLink="false">https://medium.com/p/5edf54b08715</guid>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[performance]]></category>
            <category><![CDATA[web-performance]]></category>
            <category><![CDATA[web-development]]></category>
            <category><![CDATA[chrome]]></category>
            <dc:creator><![CDATA[Simon Pelchat]]></dc:creator>
            <pubDate>Wed, 01 Mar 2017 16:54:49 GMT</pubDate>
            <atom:updated>2017-03-03T21:30:06.181Z</atom:updated>
            <content:encoded><![CDATA[<p>Some of us in the <a href="https://developer.chrome.com/multidevice/data-compression">Chrome Data Saver</a> (<a href="https://www.usenix.org/system/files/conference/nsdi15/nsdi15-paper-agababov.pdf">Flywheel</a>) team at Google (<a href="https://medium.com/u/84d6883f0aee">Simon Pelchat</a>, Michael Buettner and Tom Bergan) experimented with the <a href="https://www.smashingmagazine.com/2016/02/preload-what-is-it-good-for/">new preload directives</a> as a means to improve page load performance for Chrome Data Saver users. We found our initial assumptions about preload to be wrong and decided to share our experiences in the hope they might benefit others. This post summarizes our findings.</p><p>For those who don’t have time to read through this rather long post, our conclusion is that:</p><p>Preload can greatly improve performance on some pages. We saw time to <a href="https://github.com/WICG/paint-timing">first contentful paint</a> (TTFCP) improvements of up to 20% in our experiments. However, in some cases preload can degrade performance. We came up with three rules of thumb to use as a starting point when experimenting with preload:</p><ol><li>Preload usually works well for doc.written resources.</li><li>Preload works better earlier in the page load.</li><li>Use the link tag and place it after the resource that will request the preloaded resource.</li></ol><h3>What is Preload?</h3><p>The <a href="https://www.w3.org/TR/preload/">preload</a> keyword (note that this is still a draft RFC) is being added to the <a href="https://tools.ietf.org/html/draft-nottingham-rfc5988bis-04">Link HTTP header</a> and link HTML element. To quote the spec:</p><p><em>This keyword provides a declarative fetch primitive that initiates an early fetch and separates fetching from resource execution.</em></p><p>The main use cases mentioned by the spec are:</p><ol><li><strong>Early fetch of critical resources:</strong><br>Some resources are not discoverable by the browser’s <a href="https://andydavies.me/blog/2013/10/22/how-the-browser-pre-loader-makes-pages-load-faster/">preload scanner</a>. The browser may only learn about them when executing Javascript or loading CSS. At this point, the browser usually needs the resource right away, but will have to wait one round trip time (RTT) before receiving the response. Preload can be used to let the browser know a resource will be needed before it is discovered.</li><li><strong>Early fetch and application-defined execution:</strong><br>The preload link element provides async-like semantics for non-script elements. This lets the web page trigger fetches for resources early and apply the resource at a later (application-defined) time.</li></ol><h3>When Do Preloads Happen?</h3><p>The <a href="https://www.w3.org/TR/preload/">spec</a> states that, unlike <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Link_prefetching_FAQ">prefetch</a>, preload is a high-priority fetch that is necessary for the current navigation: the browser is required to fetch the resource. However, the spec leaves the prioritization specifics undefined. In particular, there is no mention of how preload requests should be prioritized relative to other requests the browser will make. This is unfortunate, but understandable, given that request prioritization within a page load is already unspecified, and the prioritization mechanism available varies on the browser and the network protocol used (e.g. HTTP/2 has multiplexing dependency trees, whereas HTTP/1.1 has no multiplexing and a limit of six connections per origin).</p><p>Nonetheless, this makes it difficult to use preload. We want to preload resources to promote them earlier in the fetch order, but if we don’t know where they will be promoted to, it is hard to reason about if they should be promoted at all. Since the spec leaves this undefined, we must assume some definition.</p><p>We try to use a definition that is as general as possible for most of our examples, so that our analysis remains valid even if browsers change their implementation. For this reason, as well as for simplicity, we mostly limit ourselves to analyzing pages containing only scripts. While this is not realistic, we show that even in this restricted case, preload is hard to reason about and has some non-trivial implications. We have seen these issues on real pages and distilled them to the simplest examples we could think of. Adding more resource types complicates the analysis with browser-specific implementation details.</p><p>Our definition is:</p><ul><li>preloads from the link HTML element are prioritized so that they will be fetched immediately after the preceding resource in the HTML</li><li>preloads from the Link header are prioritized as if they came first in the HTML</li></ul><p>We think this definition is reasonable because it seems to be intuitively (though not explicitly) stated by the spec. It also coincides with Chrome’s implementation for the pages that we’re analyzing.</p><p>Moreover, we assume resources are fetched sequentially, in priority order. This coincides with what Chrome does when using HTTP/2 over a single origin. Apart from certain resource types like progressive images and videos, this is an optimal bandwidth allocation since resources are only useful once they have fully been fetched.</p><p>Finally, although the spec makes no distinction between the Link header and the link element, there is a key difference between them. The latter has a position in the document, which means that it implicitly contains more information than the Link header; i.e., its relative ordering with respect to other resources in the document. However, when enabling preload from a web server or web proxy, the Link header has the advantage of not requiring the server to parse and modify the page’s HTML, which could break the page.</p><h3>How Chrome Prioritizes Preloads</h3><p>We note here some details on how a real browser implements preload prioritization. These details will matter on real pages with different resource types when deciding when to use preload.</p><p>For resource prioritization, Chrome uses a few priority buckets that are mostly separated by resource type (for example, CSS has a higher priority than images). Among requests in a given bucket, Chrome prioritizes them in the order they are discovered. These buckets are the only difference between Chrome’s implementation and the definition we used above.</p><p>When using HTTP/2 over a single origin, Chrome will fetch resources sequentially, from the most important resource (first discovered resource of the most important bucket) to the least important (last discovered resource of the least important bucket).</p><p>Preloads from the Link header are discovered in the order that they appear in the header, before any resource included in the HTML. Preloaded resources are otherwise treated similarly to non-preloaded resource and use the same prioritization scheme as mentioned above. Given that preloads are meant for high-priority fetches, this implementation is reasonable.</p><h3>Page Load Metrics</h3><p>In this document, we focus on using preload to improve TTFCP. For the most part, our discussion is not specific to improving TTFCP: one could imagine using preload to improve a different metric. However, as we’ll see later, preload is mostly useful early on in the page load, which makes TTFCP a natural candidate to optimize.</p><h3><strong>Example Where Preload Improves TTFCP</strong></h3><p>Assume you have a simple HTML page:</p><pre>&lt;html&gt;<br>  &lt;head&gt;&lt;script src=”a.js”&gt;&lt;/script&gt;&lt;/head&gt;<br>  &lt;body&gt;Hello World!&lt;/body&gt;<br>&lt;/html&gt;</pre><p>With a.js:</p><pre>document.write(&#39;&lt;script src=”b.js”&gt;&lt;/script&gt;&#39;);</pre><p>If we add the header Link: rel=preload; b.js; as=script to the HTML response, then preload works as expected and we save one RTT when fetching b.js. The page load waterfall will look something like the following (the blue bar represents TTFCP, filled green space is download time and yellow space is idle network time):</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/417/1*_5VnOSuOqgOITk11alHnEA.png" /><figcaption>Preload</figcaption></figure><p>Instead of:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/577/1*jVuGxN17DvQfKbVj9_t_SA.png" /><figcaption>No preload</figcaption></figure><h3>Bad Preload Orders Can Degrade TTFCP</h3><p>But if instead you have:</p><pre>&lt;html&gt;<br>  &lt;head&gt;&lt;/head&gt;<br>  &lt;body&gt;<br>    &lt;script src=”c.js”&gt;&lt;/script&gt;<br>    &lt;script src=”a.js”&gt;&lt;/script&gt;<br>  &lt;/body&gt;<br>&lt;/html&gt;</pre><p>a.js:</p><pre>document.write(‘&lt;script src=”b.js”&gt;&lt;/script&gt;’);</pre><p>c.js:</p><pre>document.write(‘&lt;b&gt;Hello World!&lt;/b&gt;’);</pre><p>If we do as before and add the header Link: rel=preload; b.js; as=script to the HTML response, then preload actually increases the TTFCP because b.js is fetched before c.js and c.js is the only resource required to trigger first contentful paint.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/487/1*hy2TEIDMBsow9hEijBTJ8A.png" /><figcaption>Preload with Link header</figcaption></figure><p>If instead we had not used preload, the waterfall would have been:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/625/1*HEqMlh-6rjlvU_a7As30SA.png" /><figcaption>No preload</figcaption></figure><p>While we did save 1 RTT in the time to fetch all resources, we delayed TTFCP.</p><p>The issue here is that while b.js is indeed needed, the browser does not know how to prioritize the request for b.js relative to other requests. From the HTML, it can tell that c.js is needed before a.js, but it has no idea when b.js will be needed.</p><h4><strong>The Preload Link Element Improves Resource Ordering</strong></h4><p>Note that this particular problem can be fixed by using the link element instead of the Link header:</p><pre>&lt;html&gt;<br>  &lt;head&gt;&lt;/head&gt;<br>  &lt;body&gt;<br>    &lt;script src=”c.js”&gt;&lt;/script&gt;<br>    &lt;script src=”a.js”&gt;&lt;/script&gt;<br>    &lt;link rel=”preload” href=”b.js” as=”script”/&gt;<br>  &lt;/body&gt;<br>&lt;/html&gt;</pre><p>In this case, the waterfall will look like:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/518/1*S3qUKJJ_6D3xu9MdSiVPQg.png" /><figcaption>Preload with link HTML element</figcaption></figure><p>This is optimal. The ordering of the link HTML element relative to other resources determines its priority. In many cases, this prioritization is key. Indeed, if we limit ourselves to resources inserted synchronously and executed synchronously, such as doc.written resources, then inserting a link tag after the resource that will need the preloaded resource will always yield an optimal fetch schedule.</p><h3><strong>Example With Asynchronous Resources</strong></h3><p>In reality, page loads are much more complex than the above toy examples. Images do not block rendering (i.e. they are “executed” asynchronously), many scripts are asynchronous, and CSS blocks Javascript execution, but not HTML parsing. This is great from a performance point of view, but makes reasoning about the page load process very difficult.</p><p>Let’s assume we have the following page:</p><pre>&lt;html&gt;<br>  &lt;head&gt;<br>    &lt;script async=”true” src=”a.js”&gt;&lt;/script&gt;<br>    &lt;script async=”true” src=”b.js”&gt;&lt;/script&gt;<br>  &lt;/head&gt;<br>  &lt;body&gt;&lt;/body&gt;<br>&lt;/html&gt;</pre><p>Both a.js and b.js insert a script (a-hidden.js and b-hidden.js respectively) in the document. In turn, a-hidden.js and b-hidden.js both insert text in the body. Assume that a.js and b.js are small (0.5 RTT to download), and a-hidden.js and b-hidden.js are bigger (1 RTT to download). The waterfall will look something like this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/626/1*z0BkbdrDVCPwg2rI_JP4hA.png" /><figcaption>No preload</figcaption></figure><p>We can see there is 0.5 RTT where the network is idle when waiting for a-hidden.js. It seems like a good opportunity to use preload. So we preload a-hidden.js and b-hidden.js:</p><pre>&lt;html&gt;<br>  &lt;head&gt;<br>    &lt;script async=”true” src=”a.js”&gt;&lt;/script&gt;<br>    &lt;link rel=”preload” href=”a-hidden.js” as=”script”&gt;&lt;/link&gt;<br>    &lt;script async=”true” src=”b.js”&gt;&lt;/script&gt;<br>    &lt;link rel=”preload” href=”b-hidden.js” as=”script”&gt;&lt;/link&gt;<br>  &lt;/head&gt;<br>  &lt;body&gt;&lt;/body&gt;<br>&lt;/html&gt;</pre><p>Now the waterfall looks like:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/626/1*G0b_LfVv51jz_jo4KfVYDg.png" /><figcaption>Preload</figcaption></figure><p>This is great, we saved 1 RTT on first contentful paint!</p><p>But what if the user had b-hidden.js cached? We ignore the disk cache latency for this example (assuming it is much faster than the network). The original (without preload) waterfall would have been:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/613/1*rhCEGa9iA1EIJNHRS0AhoQ.png" /><figcaption>No preload, b-hidden.js cached</figcaption></figure><p>The waterfall with preload will be:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/625/1*qvE4sEFujF0TKBWC_gdcpQ.png" /><figcaption>Preload, b-hidden.js cached</figcaption></figure><p>Preloading here will actually increase the TTFCP by 0.5 RTT.</p><p>The reason why prioritizing preload requests over other requests can cause performance regressions is that when the rendering process is not fully synchronous and linear, the critical path of a metric is highly unstable. We will explain what we mean by critical path in the next section.</p><h3>Critical Path</h3><p>In the context of first contentful paint, we can loosely view the critical path as the set of resources required to reach first contentful paint. This is a simplification of the critical path concept, but this is good enough for our discussion; see the <a href="http://homes.cs.washington.edu/~arvind/papers/wprof.pdf">WProf paper</a> for a more precise definition.</p><p>The critical path varies based on:</p><ul><li>cache state</li><li>network conditions: assuming one path has three RTTs, but has fewer bytes, and the other has 2 RTTs but more bytes, the critical path will depend on the relative latency and bandwidth of the connection</li><li>performance characteristics: if one path is CPU-bound, but the other is IO-bound (either disk or network), then the critical path will vary depending on the specs of the device or even what other processes are running on the device</li><li>scheduling in the browser: the scheduling of requests/rendering/Javascript execution in the browser is nondeterministic and can affect the critical path</li></ul><p>In the previous example, a.js and a-hidden.js are the critical path in the case where the cache is empty (a-hidden.js is what triggers first contentful paint and a.js is needed to load a-hidden.js). However, when b-hidden.js is in the cache, then the critical path consists of b.js and b-hidden.js (b-hidden.js is what triggers first paint).</p><p>The fact that the critical path is unstable is what makes it difficult to correctly position preload link tags. In our example, we have to guess as to which of a-hidden.js or b-hidden.js will execute first, to decide which path we want to optimize with preload (optimizing one is usually at the cost of the other given that bandwidth is limited). For fully synchronous page loads, the critical path does not change (all resources are always evaluated in the same order), so this is not a problem.</p><p>Note that images also result in unstable critical paths because they are asynchronous in their execution. Suppose we have the page</p><pre>&lt;html&gt;<br>  &lt;head&gt;<br>    &lt;script src=a.js&gt;&lt;/script&gt;<br>  &lt;/head&gt;<br>  &lt;body&gt;<br>    &lt;img src=”small.jpg”&gt;&lt;/img&gt;<br>  &lt;body&gt;<br>&lt;/html&gt;</pre><p>where a.js inserts big.jpg in the body. The critical path for this page is usually a.js and small.jpg. However, by preloading big.jpg after a.js, one would change the critical path to be a.js and big.jpg, which makes performance worse.</p><h3>Low-Priority Preloads</h3><p>One way to use preload in such a way that it will never make page loads worse is to use the lowest possible priority for preload requests, until the browser discovers the corresponding resource through conventional means, at which point it can set the correct priority. Note that this implies using a different definition of preload that the one we have been using so far and requires modifying the preload implementation in browsers. This implementation is a middle ground between the current preload implementation, which gives high priority to preloaded resources, and Link rel=prefetch, which is meant for resources that may be needed in a future page load. Unfortunately, there are several issues with such an implementation of preload.</p><h4>Lower Potential Gains</h4><p>By only using idle time to fetch preloaded resources, we significantly limit the potential gain from using preload. If the network is busy downloading low-priority resources during the RTT between two important requests, then preload will have no benefit. For example, if a page full of images includes a synchronous script in its head which calls document.write to include another script, then preloading the doc.written script will have no effect on this page: the first script will be fetched, it will trigger a request for the second script, but 1 RTT will be spent downloading images before the response for the second script arrives.</p><h4>Bufferbloat</h4><p>While the possible gains are lower, it still seems tempting to implement “low-priority” preloads, since the in theory they cannot negatively affect performance. However, in practice they can negatively affect performance because of <a href="https://en.wikipedia.org/wiki/Bufferbloat">bufferbloat</a>.</p><p>When there is buffering between the server and the client (this could be a kernel send buffer in the server, or queues in the network), the effective RTT of the connection increases significantly: the time between the moment a server writes a byte and the client receives it is the time it takes to flush the buffers plus 0.5 * RTT.</p><p>This means client-directed scheduling is less responsive, and sending low-priority bytes on otherwise idle network time can delay later requests because the server will fill the buffers with low-priority bytes.</p><p>For example, while we imagine preloading a low-priority image (preloaded.jpg) would fill RTT 4 as below:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/625/1*X0a4yND85JpBbIZTY42ahQ.png" /><figcaption>Preload without bufferbloat</figcaption></figure><p>In practice, we see something like:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/625/1*6sp2tPzAF0Ue4XH4te_tqw.png" /><figcaption>Preload with bufferbloat</figcaption></figure><p>The server fills the buffers with the response for preloaded.jpg. When it gets the request for doc-written.js, there is 0.5 RTT worth of buffers to flush. Had preloaded.jpg not been requested, the buffers would have been empty and the client would have received the response for doc-written.js sooner.</p><h3>Bufferbloat with High-Priority Preload</h3><p>Note that even if we are not using low-priority preload, bufferbloat is still very relevant to preload.</p><p>For example, it is tempting to preload all low-priority requests for which we know the browser will correctly assign a low-priority (e.g. hidden images).</p><p>However, in practice, the server may take more time to generate responses for some requests. For example, the server may be a CDN, where images are cached at the CDN and therefore images responses will be ready faster than, for example, a higher-priority Javascript request that needs to be dynamically generated by the origin. This means the server will start filling the network buffers with image bytes until the higher-priority response has been generated, at which point the network buffers will already be full with low-priority bytes.</p><p>This problem is not specific to preload, but preloading low-priority resources increases the number of low-priority requests sent to the server, which increases the chances that the server will have a low-priority response ready sooner that high-priority responses.</p><p>Somewhat counter-intuitively, preload can also mitigate bufferbloat. Let’s ignore the previous issue we mentioned and assume the server can generate all response immediately. Suppose we have:</p><pre>&lt;html&gt;<br>  &lt;head&gt;&lt;/head&gt;<br>  &lt;body&gt;<br>    &lt;script src=”a.js”&gt;&lt;/script&gt;<br>    &lt;script src=”c.js” async=&quot;true&quot;&gt;&lt;/script&gt;<br>  &lt;/body&gt;<br>&lt;/html&gt;</pre><p>a.js:</p><pre>document.write(‘&lt;script src=”b.js”&gt;&lt;/script&gt;’);</pre><p>b.js:</p><pre>document.write(‘&lt;b&gt;Hello World!&lt;/b&gt;’);</pre><p>c.js:</p><pre>// Analytics code, no user-visible impact.</pre><p>In the absence of preload, we would get the following waterfall:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/626/1*0BE5JAdwmzOHSoT5xNICMQ.png" /><figcaption>Bufferbloat, without preload</figcaption></figure><p>Here, the browser requests a.js and c.js first. It then discovers b.js , which is higher-priority than c.js because the latter is asynchronous. This is a Chrome implementation detail: for this example, we are deviating from our simple model where resources are prioritized strictly in discovery order. Unfortunately, the network buffers have been filled with bytes from c.js so it takes some time for the server to switch to serving b.js .</p><p>Using preload, we get:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/626/1*xHlLNZktofy-izMzCehFKw.png" /><figcaption>Bufferbloat, with preload</figcaption></figure><p>Here, since the browser knows about all three scripts right away and fetches the resources with the correct priority, bufferbloat no longer affects the page load. Therefore, preload saves 1.5 RTT: 0.5 RTT more than is expected in the absence of bufferbloat.</p><p>The takeaway here is that bufferbloat can affect page loads in non-intuitive ways. This makes experimenting in realistic conditions all the more important.</p><p>A promising solution to the bufferbloat problem is to use a bandwidth-aware congestion control protocol, like the new <a href="https://research.google.com/pubs/pub45646.html">BBR congestion-control algorithm</a>, which could be used in web servers to avoid filling intermediate buffers in the network.</p><h3>Resources Inserted by Event Handlers</h3><p>So far we’ve focused on resources inserted during the page load process through initial script execution or by CSS @imports.</p><p>One important use case we’ve ignored is resources inserted by event handlers such as onload and onclick. The issue here is that if one preloads these resources by including link tags in the initial HTML, there is no logical place to put them. It’s not clear how we can tell the browser that a resource will be needed after a certain event handler is triggered.</p><p>The best alternative is to either include the link tags at the end of the HTML, or to dynamically insert link tags later on in the page load, but (hopefully) before the event of interest fires. Including the link tag at the end of the HTML can be problematic for certain content types like CSS because, as mentioned earlier, Chrome considers CSS to be high-priority and will fetch the lazy-loaded CSS before most other resource types.</p><h3><strong>What Should We Preload in Practice?</strong></h3><p>We condensed our findings into three rules of thumb for using preload:</p><ol><li><strong>Preload usually works well for doc.written resources:<br></strong>The benefit of preloading such resources is usually so great that it dwarfs other potential downsides. However, it’s not safe in general to preload doc.written resources that appear late in the page load. For example, they might be at the end of the HTML, where they don’t actually block rendering and they might use bandwidth that would otherwise be used for above-the-fold images.</li><li><strong>Preload works better earlier in the page load when things are more synchronous:</strong><br>Early in the page load, browsers are requesting mostly synchronous render-blocking resources, such as CSS and synchronous scripts. At this point, the critical path is more stable. It’s therefore easier to reason about the page load process and it’s easier to use preload without having a negative effect on performance. As the page load progresses, there are more asynchronous requests and the order of loading events is less deterministic, which makes the critical path less stable. At this point it becomes very hard to reason about the page load process and to know what to preload.</li><li><strong>Use the link tag and place it after the resource that will insert the preloaded resource:<br></strong>This is a blunt way to give the browser some information on how to prioritize the preloaded request.</li></ol><p>Given the complexity of page loads, our rules of thumb are only general guidelines. There are cases where those rules can be violated and also cases where following those rules will degrade performance. When in doubt, measure performance in realistic scenarios, such as with a partially warm cache, and varying network conditions. Even better, measure on live traffic with real users.</p><h3>Live Experiment</h3><h4>Strategy</h4><p>We tried automatically inserting Link preload headers to page loads going through Chrome Data Saver. We used the Link header (contrary to our suggestion), because automatically modifying HTML is difficult and can break pages (e.g. a script might assume that the element following it is a &lt;div&gt;, so inserting a link element there might break the page). Instead, to preserve the ordering of the resources to preload relative to non-preloaded resources, our Link header lists resources in the order they are evaluated by Chrome. Chome orders preload fetches in the order they are specified in the Link header. This is roughly equivalent to using the preload HTML element as we have defined it.</p><p>Given the conclusions above, we preloaded only hidden render-blocking resources (HRBRs) that block first contentful paint. HRBRs are resources that cannot be discovered by the preload scanner and that block rendering, such as scripts and CSS inserted using document.write and CSS @imports. We discovered HRBRs on pages by cloud rendering and instrumenting them.</p><h4>Results</h4><p>We found that 90% of page loads going through Chrome Data Saver correspond to pages that do not have HRBRs blocking first contentful paint. Of the pages with HRBRs, about 65% of the page loads included a request for an HRBR. This means that 35% of the time, all HRBRs are cached at the client (for popular sites, this figure was significantly higher). Therefore, at most 6.5% of page loads going through Chrome Data Saver can possibly benefit from preload. This made it difficult to find a substantial set of pages for which we had enough data to know if preload had an impact.</p><p>We selected a set of popular pages that contain HRBRs. Of those pages, there were 22 which had a significant number of loads that requested at least one HRBR per experiment arm (control vs preload). Of these pages, 8 had significant (<a href="https://en.wikipedia.org/wiki/Mann%E2%80%93Whitney_U_test">Mann-Whitney test</a> with p=0.005) improvements in TTFCP (12% on average, from 6% to 16%), and none had a significant degradation in TTFCP.</p><h3>Conclusion</h3><p>Preload is a great new tool in developers’ toolboxes. It has a lot of potential for addressing common performance issues. That said, it should not be used indiscriminately. The key to using preload well is to carefully analyze the page load, see which (if any) resources it makes sense to preload, and measure the impact in realistic conditions. Good candidates for preload are important resources that are not discoverable by the preload scanner that will be needed early on in the page load.</p><p><em>Reviewed by Addy, Gray, Yoav and others passionate about loading on the web.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=5edf54b08715" width="1" height="1" alt=""><hr><p><a href="https://medium.com/reloading/a-link-rel-preload-analysis-from-the-chrome-data-saver-team-5edf54b08715">A Link: rel=preload Analysis From the Chrome Data Saver Team</a> was originally published in <a href="https://medium.com/reloading">reloading</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Toward Sustainable Loading]]></title>
            <link>https://medium.com/reloading/toward-sustainable-loading-4760957ee46f?source=rss----df1be8001167---4</link>
            <guid isPermaLink="false">https://medium.com/p/4760957ee46f</guid>
            <category><![CDATA[web-development]]></category>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[performance]]></category>
            <category><![CDATA[web-performance]]></category>
            <category><![CDATA[chrome]]></category>
            <dc:creator><![CDATA[Gray Norton]]></dc:creator>
            <pubDate>Wed, 22 Feb 2017 21:42:35 GMT</pubDate>
            <atom:updated>2017-02-22T21:42:35.336Z</atom:updated>
            <content:encoded><![CDATA[<p>Welcome to <strong>re:loading</strong>, a new Medium publication dedicated to understanding and improving the state of loading on the web.</p><p>Why, you ask?</p><p>In short, poor loading performance is holding the web back, especially on mobile devices where the world is now doing most of its computing. DoubleClick <a href="https://www.doubleclickbygoogle.com/articles/mobile-speed-matters/">reported last year</a> that <strong>77% of mobile sites take longer than 10 seconds to load</strong>, and the average load time is a staggering 19 seconds. Meanwhile, <strong>53% of mobile visits are abandoned when a site takes longer than 3 seconds to load</strong>. You do the math — this is not sustainable.</p><p>But <strong>the causes of poor loading performance are many and varied</strong>, and the solutions are not always obvious. Is your site simply too heavy? Are your resources optimally bundled and compressed? Is your JavaScript <a href="https://medium.com/dev-channel/javascript-start-up-performance-69200f43b201">taking too long to parse, compile and execute</a>? Are you effectively using the browser’s caching mechanisms? Are third-party ads and analytics slowing you down? And the list goes on…</p><p>To make things more complicated, <strong>the answers to questions like these are not one-size-fits-all</strong>. The recipe for optimal loading may be different if you’re building a content-heavy site, versus a feature-rich app. Your target markets and business requirements will dictate the degree to which you need to optimize for extremely slow or flaky connections. Likewise, your best path forward probably depends on whether you’re starting a new project or looking to improve a large legacy app.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*J5WMvG24Ea-rIp0hNDRMdg.png" /><figcaption>Sustainable Loading features we’ll be tracking by <a href="https://medium.com/u/2508e4c7a8ec">Addy Osmani</a></figcaption></figure><p>Finally, you’re chasing a moving target. <strong>Best practices for loading will likely change significantly over the next several years</strong>, as browser vendors continue to optimize various parts of the browser stack and introduce new features to help you manage the loading process.</p><p>Solving the web’s loading problems will take an ongoing <strong>Sustainable Loading</strong> movement, with commitment and coordinated effort from many groups, including business stakeholders, web app developers, framework and library developers, tool developers, browser vendors, CDNs and hosting providers.</p><p>Please join us by following re:loading. Even better, let us know if you’d like to add your voice. This initiative grew out of the Chrome team. We bring a particular perspective to the table, and we won’t be shy about sharing it — but we don’t have all the answers. <strong>We see re:loading as a public space</strong> where people from all of the groups above can advance our common cause by sharing news, data, analysis, case studies, opinions and advice.</p><p><strong>Are you in?</strong></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=4760957ee46f" width="1" height="1" alt=""><hr><p><a href="https://medium.com/reloading/toward-sustainable-loading-4760957ee46f">Toward Sustainable Loading</a> was originally published in <a href="https://medium.com/reloading">reloading</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>