Back to blog home

Improve rendering performance with dev tools

In the past, people have traditionally optimized their websites purely on a network only basis by reducing file size and using a number of techniques to get the page and content to the user as quickly as possible. However what happens once the page has loaded? Previously the tools to analyse this weren't available and the need for them wasn't recognised. However the way we access the web has completely changed over the last 5 years and 50% of global mobile phone users now use their mobile as either their primary or exclusive means of going online (from http://www.digitalbuzzblog.com/infographic-2013-mobile-growth-statistics/). The main issue with these devices is that the hardware specifications tend to be inferior to desktop and laptop computers and so website performance can deteriorate, causing your visitors to get frustrated, or worse, go elsewhere. As our websites and apps get more and more complex, we want to support better experiences by including smooth animation and scrolling performance. To do this, we need to achieve a frame rate of 60 FPS (more on that later) which closely matches the refresh rate of our devices - giving us the smoothest performance possible. With mobile devices and sometimes even desktops and laptops, this presents a unique challenge to get great performance across all devices. This article will show how you can approach this for your own sites.

We begin by using a profiler to identify bottlenecks and performance issues to find ways in which we can optimise our code to create a smoother experience and improve performance. In this article I'll discuss Chrome dev tools but you can also use your favourite browser of choice, as IE11, Firefox etc now all have rendering profilers.

To get started we first need to open up chrome dev tools (F12 on windows/*nix (or ctrl-shift-i) and Cmd-opt-I on a Mac). We now need to open the timeline tab within dev tools, which can be found across the top.

Once we are in the timeline section you will see three main tabs on the left, events, frames and memory. In this article I'll concentrate on using the frames section, as this is normally the most effective way of catching rendering performance issues and visually the best way of seeing FPS and component makeup (paint, reflow and so on, which we will explore more later) of each frame.

Record page performance

The simplest way to see how your page is performing is to measure the scrolling performance, along with any other scripts you have running. So to do this simply press the record icon (the greyed out circle icon in the top left) or press Ctrl-E.

This will then start recording your actions. Start by scrolling the page you are on and you will see the timeline start to populate. For good recordings I recommend you try to keep the recording as short as possible as it makes it much easier to find useful information in a smaller sample. Once you have finished your recording, simply click the recording button again. Let’s take a look at an example output and what it all means!

Interpret the recording

Each bar represents a frame, its height shows how long it took to render. There are four things the browser has to do in order to render the page and these are represented in different colours, both on the frame bars and in the records column to the left.

  • Loading Assets (Blue)
  • Scripting (Yellow)
  • Rendering / Reflow (Purple)
  • Painting (Green)

To zoom in on a portion of the output (as I have done in the screenshot below), simply click and drag on the area you want. Once this has been selected you can then use the handles to change your selection, if you want to reset the zoom just double click on same area.

You can see each of the events that have happened on the left hand side of the Dev tools panel as shown below.

They are colour-coordinated just the same as the bars in the graph view across the top. Clicking one of these items will give you a more detailed description about what happened during this event/action, including some great information for debugging. If it’s a Javascript action clicking on the filename to the right of the information will take you to the line of code which was executed.

If something took a long time to execute or there is an excessive amount of iterations in your Javascript code, Chrome will display a little warning icon (see the sections on reflow and layout) next to this Javascript function call to warn you something odd is going on. This is a great way to find areas in your scripts where you can optimise your code. You can then go back and refactor / debug your code and run the recording again to see if it has fixed the issue.

Improve my render performance

The target Frames per Second (FPS) for animations / scrolling and general all round performance should be around 60. The reason for this is that the refresh rates of most devices (the number of times the screen updates per second) is around 52-60Hz. Therefore the smoothest performance comes when the refresh rate matches the number of frames per second. In order for your app to run at 60 FPS your browser needs to render each frame in 16.66ms (1000ms / 60). So anything you can do to reduce the CPU and GPU footprint of your page will help towards reducing the time it takes to render each frame and hitting the ideal 60 FPS.

The two most expensive operations in terms of frame render time are layout (called "reflow") and paint. In fact, most sites that seem slow have issues with these two things. So let’s take a quick look at what they mean and see how they can affect frame render time.

UNDERSTAND PAGE REFLOW

When the browser wants to render the webpage, it needs to know where everything is positioned on the page. Working out where every element goes in relation to the next requires a lot of CPU power, so your goal is to reduce the number of reflows to an absolute minimum.

There are some "usual suspects" that will trigger a reflow:

  • Adding/removing nodes or otherwise updating the DOM (this includes hiding DOM nodes)
  • Adding a stylesheet
  • Moving DOM elements (e.g. during animation)
  • Resizing the window, changing font size, orscrolling

Browsers do their best to reduce the number of reflows which are needed, for instance they will queue up your script changes and run them in batches if possible, this way a number of operations are combined into one reflow which reduces the CPU load. However this is sometimes not possible as certain Javascript functions and scripts which request styling information need the most up to date information which means that the browser has to flush the queue and perform a reflow to see if anything has changed since you requested it last. Some of these functions / computed values are:

scrollTop/Left/Width/Height
clientTop/Left/Width/Height
offsetLeft, offsetTop, offsetHeight, offsetWidth
getComputedStyle()

A really basic example of this is avoiding accessing things like offsetWidth from within a loop.

So doing something like:

var elements = document.querySelectorAll('.blocks'),
i = elements.length,
sizer = document.querySelector(‘.sizer’);
while (i--) { 
     elements[i].style.width = sizer.offsetWidth + 'px'; 
}

 

For every iteration of this loop, the style.width of that element has changed, any write to an element will invalidate the previous read of sizer.offsetWidth. Therefore when the next iteration occurs, the browser needs to run a reflow in order to get the correct value for sizer.offsetWidth.

It’s much better to cache the value of sizer.offsetWidth. This way you can use the cached version to amend the style.width of your element to the sizer.offsetWidth value. Try this example instead:

var sizerWidth =  sizer.offsetWidth;
while (i--) { 
     elements[i].style.width = sizerWidth + 'px'; 
}

 

Taking this further, it’s also much better to perform your DOM manipulations on a cloned object using documentFragment or in jQuery, for example, you can use .clone() By working on a copy, you can hide the original element (one reflow), do all your calculations and amends to the cloned object, then reinsert it into the DOM and show the element again (second reflow). If you are dealing with hundreds of elements you need to change within an object, this can significantly increase performance by reducing the number of reflows and repaints that need to occur.

This will also apply for any Javascript on the page, regardless of the framework or library you are using. On a side note caching selectors whilst using jQuery will give performance increases, not only in speed of execution of your scripts but also in terms of rendering performance.

Improve paint performance

Painting the page is where the browser uses its rasterizer (in Chrome's case this is Skia) to take a vector representation of the page (the layout process) and convert it into a bitmap, which is the picture you see on your screen right now. This is an expensive process in terms of the CPU power needed so you want to reduce the number of repaints to as few as possible.

Chrome Dev Tools allows you to see whether repaints are causing performance issues. If you start a recording on your page, as we saw before, a number of bars appear which represent each frame. If you have a large green bar and it goes beyond the 30 FPS line then you have issues with painting.

You can see in the above screenshot each frame is taking well over 16.67ms to render. By hovering over the paint event in the records section it will highlight on the page exactly which area is being painted within that frame. In this case, by hovering over the paint record, I can see that it’s the entire page being repainted every time I scroll. The main cause of this, certainly in Chrome, is a fixed background image. Every time the page is scrolled the entire page needs to be repainted. Let’s see what happens if we remove the background and replace it with a single colour.

As you can see the paint time has gone from 80-90ms to basically nothing. If you notice there are a couple of the frames which have a large empty bar. This can sometimes be a bug in Dev Tools but if you want to read more about this problem you can do so here. However, in my experience it’s mostly caused by changing scrolling direction, and nothing to worry about.

Use layers to improve performance

Another great way to improve paint performance is to promote elements which have animations to a new layer (not z-index, although it's kind of similar). These elements can be anything from a slider to an animated menu. When a page is rendered it does so by going through the Render Tree, building up each element and rendering it into a single image. Layers work by separating the contents of the page into different Graphics Layers (which are then uploaded to the GPU) which are then composited together to produce a single rendered image bitmap which you see on screen.

So why does this help us? Well, if you move elements around on the page without using layers the whole page will have to re-render every time, as moving an object within the page will invalidate how it looks (as everything is on a single layer). If you promote an element to be rendered into its own layer, because this has now been uploaded to the GPU as a texture, you can move it / animate it as much as you like and the browser will not have to do a repaint. However if you modify the element(s) within that layer, for example you change the dimensions of an element or add something like padding, this will trigger the layer to perform a repaint; but only on itself and not the rest of the page. Greatly improving performance. If you want to view the layers on your page you can do so by clicking the console icon in the top right of Chrome Dev Tools (see the image below if you can't find this), then going to the rendering tab and clicking "Show Composited layer borders". Your layers will show up with a yellow border around them.

There is also a trick you can use to see if any of your animations are causing performance issues, either due to excessive repaints or something else, by activating the FPS meter and to also toggle “Show paint rectangles”. How to access these has changed in newer versions of Chrome; it can now be found in the console menu, as with the show / hide layer toggle as mentioned above.

You can access the console menu by clicking on the middle button, this shows you the console, click on the rendering tab and you should see a list of options as seen below.

With both of these activated you can see the areas in red which are being repainted, and also see the constant FPS of your page if you have an animation running. The FPS Meter won’t move if you don’t scroll or have a static site. Using the “Show paint rectangles” is a great tool to see if any of your elements could benefit from being promoted to a new layer in order to improve performance. The best way to move an element to a new layer is to add –webkit-transform: translate-z(0); to the element.

After making changes, run your recording again and check if your paint time has improved. Although using a new layer can really improve your rendering performance and FPS, using too many layers causes the same problem as having none at all. It’s a balancing act, as with most performance issues, if you use layers wisely they can give you great results.

Top tips for improving performance rendering

There are a few things you should do to increase the scrolling and animation performance on your site, which as a side effect means your CPU/GPU won’t have to work as hard which translates to happier users and lower battery consumption on mobile devices. We have already covered most of these in the article already, but here’s a recap and some smaller performance tweaks:

  • Avoid fixed background images. In Chrome (but not IE / Firefox) scrolling with a fixed background image causes your page to re-render when scrolling. This is bad news for performance and can make the difference between 5 FPS and 60 FPS on a mobile device
  • If you have sliders or animations on your page, as we discussed previously, promote them to a new layer (-webkit-transform: translate-z: (0), -ms-transform: translate-z(0)

) This causes the div or container and all its child elements to be promoted to a new layer. This can drastically reduce the number of repaints needed for sliders & animations etc, thus vastly improving performance.

  • If you have a lot of CSS3 effects on your page, for instance lots of rounded corners / linear gradients and shadows then consider removing them, or reducing the number of them. They are fine to use, but just remember there is a performance hit if you use large numbers of them!
  • Always prescale your images as the browser has to do more work. This is especially true with a responsive site. Although getting an all-encompassing responsive image solution is very difficult, if you can serve pre-scaled images on mobile devices, that can help a lot.
  • When animating, try and use requestAnimationFrame. Although I’ve not covered it in this article in detail, it really improves animation performance (and FPS), prevents dropped frames and doesn’t run animations in the background even if you don't have that browser tab focused! This improves battery life on mobile devices, which is never a bad thing.

 

Conclusion

Chrome Dev Tools gives us a great way of spotting and fixing performance related bugs on our websites; you can spot potential issues and really optimise your site to get the best performance out of it. As we mentioned at the start of the article, it’s now more important than ever to have a great performing site on all platforms, especially mobile, where there are extra challenges due to hardware limitations.

Using the frames section gives us a great overview of performance and for the majority of issues will be all you need to fix them. However if you are are still having issues or want to know more about Chrome Dev Tools in general please have a look on the official Google page for chrome Developer tools which is a great resource. If you want more info on how Chrome actually renders everything check out this great resource on chromium.org. Although we’ve looked at Chrome’s offering today, Firefox and IE11 also have great performance tools which will help you debug and improve performance on your sites. If there are any tips you use that you'd like to share, leave us a comment!