Common Performance Bottlenecks

There’s tons of information out there about performance. It can be easy to get overwhelmed. Let’s fight overwhelm and talk about the most common performance bottlenecks so you can concentrate on what’s most important.

Round Trip Time

We’ve talked before about how bandwidth isn’t usually the limiting factor in web browsing: latency is. This will dictate how quickly things will get to your user. You can’t fight the speed of light, nor can you upgrade your user’s ISP, so how can you reduce the round trip time to your user? You can move your content closer to them with a CDN (content delivery network). If you’ve got users spread around the world, inventing in a CDN can make their experience better.

Caching

You’ve heard the truism, “The fastest request is the one not made.” You can reduce the number of requests that a returning user needs to make to your site by caching resources. If you need your users to see your changes immediately after you deploy new code or content, add a version hash to the end of your files. That way, changed files will be requested and unchanged files will still be cached.

JavaScript

Javascript is a tricky beast. On one hand, you have tools (like async and delay) to control the loading more granularly. On the other, these same tools can deceive you into a false sense of performant pages.

Parse Time

All JS, whether it’s loaded async or not, needs to be parsed by the browser. By pushing out the loading of unessential files, you can get your critical render path shorter so your paints will come sooner. However, async is not free. The user’s browser has to parse it.

Why does it matter that all JS needs to be parsed? It locks down the thread so that the user can’t interact with the page. On machines with faster processors, it’s not that big a deal. On low end mobile devices and old tablets, it can significantly retard the user’s ability to use your page in a timely way.

Execution Time

This should go without saying, but make sure that the JS that you write follows best practices. Keep your code as efficient as possible so that it executes quickly.

Client-Side Rendering Considerations

Recently, I was looking at a React site that didn’t set up the image requests efficiently. The browser had to download the JS, then parse it, and only then did it see that it needed to get images, so then it downloaded them well after they should have been. Review the waterfall for your page and make sure that the necessary assets are being requested as soon as possible. Keep an eye out for strange things and consider if your client-side rending JS might have something to do with the issue.

Images

Images can make up a large part of your page weight, so making sure that they’re performant can lead to significant gains.

Correct Size

If you’re loading a high-resolution image and all you need it for is a small thumbnail, you’re loading more bytes than you need to. Make sure you’re following best practices for retna images and not loading higher resolution images than you need.

Optimized

Your images should be at a proper compression that fits with your user’s needs. Google recommends compressing at 85% for JPEG. This way, you’re shrinking the file a bit, but not introducing too many distracting artifacts into the image.

Only loaded if necessary

Again, don’t make unnecessary network requests. If there are images above the fold where the user will see them, load them as fast as possible. However, if they’re not in the user’s initial view, don’t. It may be that the user will just use the header bar to search for a product and you won’t need to load them at all. You can then lazy-load the images when they come into view. There are a bunch of tools out there to help you with this. If your product owner or client doesn’t want the blurrly to sharp experience on scroll, you can still delay the load the out-of-sight images to the onload event so that they won’t interfere with other needed assets on the page.

Progressivly loaded

User perception of speed is important. It’s preferable to load a bit at a time, so the user knows something is going on, rather than loading the whole page all at once at the end. Because of this, you want your images to load progressivly: rather than drawing from the top, they should load the whole image in a blurry state that gets more clear as the image finishes loading. Your users will probably think the image is completely loaded at 60-75%. Even if they’re more discerning, they’ll still percieve the page as being loaded faster. Progressivly load your images!

Modern formats

We’ve started exploring the different modern image formats you can use (article on WebP, article on JPEG XR). If you use them, they are generally more compact than a simliarly-compressed JPEG or PNG. The problem is is that the browser support is spotty. Your JPEG XR won’t work in Firefox, and your JPEG 2000 only works in Safari. This may change in the coming years, and in the meantime, you can use a <picture> tag to help the browser spot which image it should use. You’ve also got to host the extra assets, so you’ll need to calculate if the work of dealing with all that is worth the performance gains to you.

Unminified and compressed files

For your JS and CSS, you’ll want to minify them to remove excess characters (you don’t want your users to have to download your 30 line rant comment in your JS) and compress your text-based responses with something like gzip as well.

CSS

There are things that you can do to make your CSS better (not having too many stylesheets and don’t load unused CSS rules). However, CSS is rarely the bottleneck for performance. Get the other things great and then worry about your CSS.

Sources:
Designing for Performance by Lara Hogan
High Performance Browser Networking by Ilya Grigorik
Udacity Course on Web Performance with Ilya Grigorik
High Performance Images by Colin Bendell, Tim Kadlec, Yoav Weiss, Guy Podjarny, Nick Doyle & Mike McCall from O’Reilly Press

HTTP/1.x vs HTTP/2

One of the ongoing changes that is being made to the web is the transition from HTTP/1.x to HTTP/2. What is this change? What’s the difference between the two? How should assets be handled differently from one to the other to ensure good performance?

HTTP/1.x

Before we get into what the change is, let’s talk about HTTP/1.x. This way of delivering information over a network has been around since 1997. Basically, after doing a DNS lookup (or getting it from cache), then a TCP connection, then a SSL negotiation (if it’s being served over HTTPS), the browser then makes a series of requests. HTTP/1.x is limited to six connections per origin: it must go through this process for each chunk of six assets that it wants to download. Thus, if you’ve got many different assets, like a bunch of stylesheets, it will take more time because for each chunk of assets, the client has to go through the negotiation process to get them.

Because with HTTP/1.x each asset on the page might be outside a chuck of six, best practice was to reduce the number of files on the critical render path as much as possible. This is why CSS is usually bundled in one file. This is why you have sprites with a bunch of different images in one file and CSS to point to one part of it. (What a headache!). JavaScript was put into all one file for this reason as well.

An example of a sprite by [Yug](https://commons.wikimedia.org/wiki/User:Yug)
An example of a sprite by Yug

The Change

You can see that the “best practices” that HTTP/1.x encouraged are not good:

  • Large bundled files

If you made one small change, you needed to cache bust and reload a large CSS or JavaScript file
– Couldn’t benefit as much by dividing files by section of site

If styles A and B are needed for the homepage and A and C needed for the product page, it’s still better to have A, B, and C lumped together.
– Sharded Domains
I mentioned above that with HTTP/1.x, you’re limited to loading six assets at a time from a given host. In order to get around this, you might load your code from origin.rei.com and your images from images.rei.com. That way, you can have 6 files loading from origin and six from images at the same time. The problem with this is that for every different host, you have to wait for a new negotiation.

  • Sprite Maintenance Nightmare

It’s not that big a deal to no longer use an icon that you no longer need in a sprite image, but if you need to add a new credit card type to a checkout sprite, now things need to be redone. Sure, there are tools that take some of the pain away: it’s still a headache.

There had to be a better way. In 2009, Google started experimenting with a new protocol called SPDY. Sites that started using this protocol saw up to a 50% reduction in page load times, a significant victory. The lessons learned from SPDY were incorporated into a new specification for HTTP, HTTP/2.

HTTP/2

HTTP/2 changes this. Now, instead of needing to shard domains and lump everything in the same file, HTTP/2 creates a two-way stream between the client and the server. The header is compressed, which reduces the weight, especially when sending many small files. Different streams can be prioritized, which leads to prioritized files ending up at their destination quicker.

Performance Implications

Surprisingly, even after 2.5 years of HTTP/2 being in the wild, the best practices for the front end aren’t firmly established. Backend implications are clear: don’t shard (have different origins)

Clear Front End Best Practices

There are definite gains that you can make on the front end:

  • Unbundle files

Because HTTP/2 is streaming and doesn’t have the six asset limit at a time, you can now be smarter about dividing and caching resources.

Unclear Front End Best Practices

Some implications aren’t as clear right now

  • Eliminate inlined styles and scripts?

Amazon Cloudflare ran an article in which they asserted that it would be best if developers stopped inlining styles, scripts and other resources (like SVGs). They point out that because these resources aren’t cached, users have to download them each time they visit. Not only that, it breaks the streaming priority. They assert that this will slow the time to first paint. Instead, they recommend removing inlined assets and use the push feature of HTTP/2.

I’m not sure that this is the best way to get time to first paint down. Theoretically, for first visit, you could deliver the HTML with things inlined, still download the assets in the background after load and cache them. Then, on a return visit (marked by a cookie), you could deliver the non-inlined version that refers to the files you’ve already cached on the client.

Amazon’s recommended approach sounds cleaner, but testing would need to be done to see if it’s faster for first paint. If you’ve seen any articles about time to first paint with HTTP/2, I’d love to hear about them.

As you can see, the front end best practices around HTTP/2 haven’t solidified yet. I’ll be running some experiments in the coming months to find out more and I’ll let you know the results. In the meantime, please pass along any articles you’ve seen about this topic so I find things I may have missed!

Sources:
Tim Kadlick’s web performance workshop at #perfmatters.
High Performance Browser Networking by Ilya Grigorik
New Relic article on HTTP/2
Cloudflare article on HTTP/2
HTTP/2 Spec

Modern Image Formats: WebP

One of the results you might see when you run a Chrome dev tools lighthouse audit on your site is that you need to use a modern image format on your site such as WebP, JPEG XR or JPEG2000. What are these formats? What’s the current state of browser support for them? Let’s dive in and take a look.

WebP

WebP is a type of image format developed by Google and released in September 2010. It was developed from the VP8 video encoding that was done within frames. WepP images are optimized for web performance and can reduce the size of JPEG and PNG files.

Google offers a command line tool, cwebp that can be used to convert images into WebP format. In two quick experiments I ran, I was able to take a 11.1 MB JPEG to a 2 MB WebP file, and a 1 MB PNG to a 46 KB WebP image. (Just over 4.5% of the original size!)

The compression strategy is to look a blocks of pixels and predict what their colors will be based on a previous block. Then it takes the difference between the prediction and the actual in order to reconstruct the block later.

WebP comes in three flavors:

  • Basic
    Single layer, lossy like JPEG, no transparency
  • Extended
    Support for transparency, lossless compression.
  • Animated
    Good replacement for GIFs.

Should you be using WebP?

Much like JPEG XR, browser support for WebP varies. Chrome (both desktop and Android) supports it. Opera supports it. Microsoft Edge is (developing support)[https://developer.microsoft.com/en-us/microsoft-edge/platform/status/webpimageformat/]. However, neither Safari nor Firefox supports WebP.

It can pay off to use WebP if you have the time and/or tooling to generate all the modern formats for the different browsers that you need. If you’re tight on time, just using a properly compressed progressive JPEG (85% compression is the recommend amount). It’s a wyas off, but now that Microsoft is going to support it, WebP may become the new standard image format in the web world.

Further reading:

https://en.wikipedia.org/wiki/WebP

https://developers.google.com/speed/webp/

https://blog.chromium.org/2010/09/webp-new-image-format-for-web.html

https://www.wired.com/2010/10/meet-webp-googles-new-image-format/

High Performance Images by Colin Bendell, Tim Kadlec, Yoav Weiss, Guy Podjarny, Nick Doyle & Mike McCall from O’Reilly Press

Critical Render Path: Implications

We’ve looked at the critical render path of the browser. Now, let’s talk about some of the basic implications of it for performance.

General principles

Every necessary file to rending the page is conisdered on the critical path. For a simple page, this might be the HTML file, the CSS file and a JavaScript file. Each network request on the critical path takes time. Minimize that time by minifying and compressing your files (with gzip, for instance). Considering eliminating files from the critical path by inlining crucial CSS on the first load. This will eliminate the need to block on waiting on the fetching of the file. JavaScript can also be inlined, but can bring other issues as it completely blocks parsing until it’s complete.

CSS

We talked about last time that CSS blocks rendering in the browser. Thus, the more CSS that the broswer has to parse, the longer the CSSOM will take to construct, and the longer the user has to wait to see any content. One way to limit the amount of CSS that’s parsed is by preventing the browser from requesting it by using media queries:

<link href="main.css" rel="stylesheet" />
<link href="mobile.css" rel="stylesheet" media="(max-width:720px)" />
<link href="print.css" rel="stylesheet" media="print" />

Here, we can see that for desktop computers with a wide enough viewport will only download the first stylesheet, saving time on downloading and parsing the other CSS. You can see what media queries are available to you over on MDN.

Be sure that you load the CSS that you need in the head, so that it can get started as soon as possible.

JavaScript

For your JavaScript, if you don’t need it to read things from the DOM or to change the layout, load it asynchronously. Careful, dispite what every third-party vendor might tell you, async isn’t free: the browser still has to download and parse even async JavaScript, which will slow time to interactive on devices that don’t have a lot of processing power. However, if you load it async, the browser can get the render treee done faster and get your content in front of the user. Scripts can be marked as async like this:

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

Analytics are a perfect use case for async. You can also defer a script until the page is completly finished loading with defer:

<script src="late-load.js" defer></script>

Remember, CSSOM construction blocks JavaScript execution and JS blocks the render tree. That’s why you’ll want to load your JavaScript at the bottom of the page and CSS at the top: you’ll want the CSSOM done so that your JS can execute.

Layout

It takes time for the browser to establish the correct layout. Avoid wasting time in multiple layout steps by batching updates.

Sources:
Designing for Performance by Lara Hogan
High Performance Browser Networking by Ilya Grigorik
Udacity Course on Web Performance with Ilya Grigorik

Critical Render Path

In order to understand front end performance, one of the key concepts to grasp is how the browser renders the page. They way it does this is going through steps called the critical render path. This articles will go through the nuts and bolts.

A diagram of the render path: HTML to DOM, on a parallel track CSS to CSSOM. JavaScript influences both DOM and CSSOM. The parallel tracks combine with the render tree, then the layout step and then finally the paint step

How it works

First, the browser establishes the connection to the server and get the HTML page. While parsing the HTML, it will request the related assets (CSS and JavaScript). CSS is render blocking: the browser won’t put anything in front of the user until it’s done downloading. This is because a partial CSS file can’t be parsed correctly (later CSS rules can overwrite earlier rules).

The browser will then construct the DOM (document object model) from the HTML and the CSSOM (CSS object model) from the CSS. After the CSSOM is finished, JavaScript can run (remember that CSS is render blocking). Javascript can change both the DOM and the CSSOM, so the browser waits for the JavaScript to finish changing the page before going on to the next step of the process.

After the CSSOM and DOM are complete and the synchronous JavaScript is finished, the browser combines the information from both parts and establishes the render tree, the thing that combines the CSS, HTML and JavaScript for all the elements that will be rendered on the page. Because the render tree only contains what’s on the page, DOM nodes with the CSS property display: none; will not be included as part of the render tree.

From the render tree, the browser will use the size of the viewport in conjunction with the hierarchy of the render tree and the CSS information in it to lay out the page in preparation for rendering. Every time the render tree updates or the viewport size changes (a user rotating their phone or resizing their browser some other way), the browser has to go though the layout process again.

After the layout is done, the browser can now paint what it has from the layout onto the page.

That’s the critical render path! You can use this knowledge to build performant pages. We’ll talk about the implications of the way the browser renders to performance next time.