Rendering Performance Notes - Part 2

Β·

5 min read

Rendering Performance Notes - Part 2

If you haven't read the first part of my public notes on Rendering Performance, I highly recommend you check it out HERE before we move on.

Frameworks and Layout Thrashing

We only had to solve the problem of Layout Thrashing because we were keeping our state in the DOM.

So we have to go measure stuff before we can mutate it.

If we don't keep the data in the DOM anymore. By using frontend frameworks.

We keep things in state. Eg. React, Vue, Angular

Some key takeaways

  • Don't mix reading layout properties and writing them - you'll do unnecessary work.

  • If you can change the visual appearance of an element by adding CSS Class. Do that, you'll avoid accidental trashing.

  • Storing data in memory - as opposed to the DOM - means we don't have to check the DOM

  • Frameworks come with a certain amount of overhead.

  • You don't need to use a framework to take advantage of this.

  • You can do bad things even if you use a framework

  • You may not know you're layout thrashing - so, always measure!


Painting, Layers, the Profiling Thereof

  • Anytime you change something other than opacity or CSS transform ... you're going to trigger a paint. πŸ‘¨β€πŸŽ¨

  • When we do a painting, the browser tells every element on the page to draw a picture of itself.

  • It has all of this information when we constructed the render tree and did the layout.

  • Triggering a layout (reflows) will always trigger a PAINT.

BUT, if you're just changing colors - then you don't need to do a Reflow. Just a Repaint.

Rule of Thumb = PAINT as much as you need and as little as you can get away with it.


Nice Threads

An oversimplification is to say that a browser has 3 threads.

In fact, some browsers have more than this.

  • The UI Thread: Chrome itself. You don't have access to it. You can't touch it. The tab bar, etc

  • The Renderer Thread: is where we live most of the time. We usually call it MAIN Thread. This is where all JavaScript, parsing HTML and CSS, style calculation, Layout, and Painting happen. There is one of these per tab.

  • The Compositor Thread: Draws bitmaps to the screen via the GPU

πŸ‘‰ The Main Thread is CPU Intensive.

πŸ‘‰ The Compositor Thread is GPU Intensive. It can go off and work on some super hard JavaScript computation and the animations will still chug along.

πŸ‘‰ Anything that we can offload to another thread, that's doing GPU stuff, we should go ahead and do that.

πŸ‘‰ This is cool because it frees up the Main thread to do all of the work it's responsible for. The Main Thread has way more responsibilities than the Compositor Thread.

How can we do that?


Managing Layers

Painting is expensive.

How do we avoid or reduce painting?

Let's the Compositor Thread handle that stuff.

The Compositor Thread

  • When we paint, we create bitmaps for the elements, put them onto layers, and prepare shaders for animations if necessary.

  • After painting, the bitmaps are shared with a thread on the GPU to do the actual compositing.

  • The GPU process works with OpenGL to make magic happen on your screen.

Things the Compositor Thread is really good at

  • Drawing the SAME bitmaps over and over in different places.

  • Scaling and rotating bitmaps

  • Making bitmaps transparent.

  • Applying filters.

  • Mining Bitcoin. 🀣

If you want to be FAST, then OFFLOAD WHATEVER YOU CAN to the Less-busy thread.

🟒The browser did that.

It creates these things called Layers.

πŸ”΅ Layers are an optimization that the BROWSER DOES FOR YOU under the hood.

Disclaimer: Compositing is kind of a hack.

You don't have control over them.

But you can influence them.

What kind of stuff gets its own layer?

  • The root object of the page (which always gets its own layer) - makes sense, we need at least ONE layer.

  • Objects that have specific CSS positions (eg. nav bar fixed on the top)

  • Objects with CSS transforms.

  • Objects that have overflow.

  • (Other stuff...)

πŸ‘‰ Objects that don't fall under one of these reasons will be on the same element as the last one that did.

Good News

πŸ‘‰ You CAN give the Browser hints using the will-change property

πŸ‘‰ By doing this you're telling the browser - you're not wanna do all of that repainting over again.

πŸ‘‰ Can convince the browser to recommend that it become its own layer.

.sidebar {
    will-change: transform;
}

Using Layers is a Trade-off

  • Layers themselves aren't FREE.

  • Managing layers takes a certain amount of work on the browser's behalf.

  • Each layer needs to be kept in the shared memory between the Main thread and the Compositor thread.

If you want to go ahead and make everything a layer.

It's absolutely a terrible idea.

* {
    will-change: transform;
}
  • The browser is trying to help you out under the hood.

  • If you try to opt out of this algorithm, you make it useless.

  • If everything should be optimized, nothing is optimized.

Pro Tip: will-change is for things that WILL CHANGE. Not for things that are changing.


will-change is tricky

Because while it's a CSS property.

You'll typically access it using JavaScript.

For instance

element.addEventListener('mouseenter', () => {
    element.style.willChange = 'transform'
});

If it's something that the user is interacting with constantly, add it to the CSS

Otherwise do it with JavaScript.

Clean up after yourself

Remove will-change when it's not going to change anymore.

Otherwise, you will end up making a lot of layers.

element.addEventListener('transitionend', () => {
    element.style.willChange = 'auto'
});

_

Reference Source: Steve Kinney

Β