Fixing The Chrome DevTools

A tale about going from 6 to 60 frames per second 

Balázs Galambosi
5 min readMar 7, 2014

Today I got fed up with how awful the Network tab performed inside the Chrome DevTools. As much as I love this piece of jewel to hack all things imaginable, lately I noticed that it heats up my Air, and even all the cores of a decent desktop computer can’t keep up with the stress it puts them under. I decided to fix the DevTools by using the Devtools itself.

The following video shows how the animation can become smooth as butter:

https://www.youtube.com/watch?v=b3Ny_m7SJpY

(my computer was a bit slower when I was recording at 1080p and 60 fps, so it ‘only’ peaked at 45 here, for the full experience you’ll have to try it yourself :)

Left part is before, right part is after the optimization

The idea of the Network tab is simple: displaying a scrollable table with a dozen columns and a couple hundred rows. Sounds simple, right?

It is simple. But for some reason when I did a timeline recording I noticed things could get out of hand pretty quickly with 80 ms layout, another 65 ms style recalculations, plus 35 ms paint time while scrolling.

As most of you know, in order to get the animation fly at 60 frames per second you need to stay inside a 16 ms budget.

It turns out, the sum of those numbers above is well beyond what’s acceptable (180 ms). [1]

I already mentioned using the Timeline from the Chrome DevTools, so why don’t we go a step further and utilize the whole package to fix itself.

What’s making it slow then? The root causes can be traced back to:

  • Updating off-screen and on-screen rows triggers expensive layout and style recalculations
  • Tables have awful paint performance [2]
  • The time labels on the right hand side are not properly hidden

Let’s dive deeper in order to fix them. As our first step, if we look at the timeline data the big purple bars stand out immediately. These are responsible for the first bullet point about layout and restyle. Checking the ‘Capture stacks’ box on the top makes it possible to catch the criminals behind all this.

As you can see, the call history shows that the Element.enableStyleClass(c, e) method called from _updateOffscreenRows() is partly responsible for our sweating laptops. I hereby sentence them to death by code elimination.

Capturing the stack shows _updateOffscreenRows is to blame

Now you may ask why it is important to hide those rows in the first place. In theory, keeping them out of the layout tree with a display:none CSS style should improve performance.

However, as the numbers tell us, it ends up being a case of overengineering considering our needs. Changing the class of a few elements — the ones which have just scrolled out of our view get .offscreen, while the ones that have just scrolled into our view lose it — on every single scroll event is costly (a debouncing tecnique could help, but I wouldn’t stop there). [2]

Removing this code that dances with classes and defining every row to be visible by default already improves the frames per second to 20 (from the initial 6). You can kiss layout & restyle times goodbye. We’re getting there.

An interesting paradox, that after all the work that’s been put into hiding and displaying rows above and below the fold, we realize that there are other elements which are rarely seen, yet always present.

Yes, you guessed it—as I hinted earlier—the time labels on the right hand side never go away completely. They are given an opacity: 0, but that still causes them to feed those paint times to the state of being fat.

Time label, which only shows when you move your mouse over it

Adding a single line of CSS .network-graph-label { display:none }, and a couple more lines of Javascript [3] to toggle display during hover animation saves us all the 30 ms paint times, shooting us to submillisecond territory.

60. Frames. Per. Second.

We all know that Google has talented engineers working their hats off (sic.) to improve Chrome and specifically the DevTools to be a great gadget for our utility belt.

Don’t get me wrong, you may still be able use it to get from point A to point B as it is, but if all software would be measured by this rule, you’d never have experienced such delightful and responsive user interfaces that the current era of computing has given us.

We’re all guilty of not keeping our porches clean one time or another.

Or the shoemaker’s son always goes barefoot, as they used to say.

You can follow me on twitter here.

Appendix

[1] For the numbers in this article I used the latest Canary version, with a relatively huge, around 1920 pixels wide window. Smaller paint areas lead to smaller paint times, but I can get similar numbers on a laptop screen. Regardless, simple table scrolling should be smooth as butter, no matter how big the viewport is.

[2] I didn’t expand on this, but basically if you used simple block level elements (hello div), you could circumvent repainting the whole table when changing classes. In this case, only the new row entering the screen should be drawn. But this probably requires a more thorough rewrite. My short term solution of removing the off-screen class logic can be implemented in a matter of minutes.

[3] I’d leave this to the reader’s imagination, but one way to approach it is to call setTimeout to separate the display property change from the opacity transition. Otherwise the renderer will batch those changes and opacity will not be animated.

--

--

Balázs Galambosi

Create more value than you capture. Developing ‘Home - New Tab Page’ Chrome.