Browser Rendering Optimization

Using Chrome DevTools |                          May 2018

Deck: slides.com/hhkaos | Code: bit.ly/browser-optimization 

Raúl Jiménez Ortega | @hhkaos

Disclaimer: I'm not a performance expert

Disclaimer 2: Content and samples from this deck are based on this free MOOC

Quick survey: bit.ly/MadJSMay

CONTENT OVERVIEW

CORE CONCEPTS

Rendering process | Frames & pipeline | Web app lifecycle

BROWSER STEPS: GET THE HTML VIA HTTP REQUEST (1/6)

BROWSER STEPS: PARSE HTML -> DOM & CSS -> CSSOM (2/6)

BROWSER STEPS: COMBINE DOM + CSS -> RENDER TREE (3/6)

BROWSER STEPS: RENDER TREE -> LAYOUT (4/6)

BROWSER STEPS: LAYOUT -> PAINT (5/6)

BROWSER STEPS: COMPOSITE LAYER -> GPU (6/6)

Browser steps

Generating a Frame

  1. Get HTML via HTTP Request
  2. Parse HTML -> DOM & CSS -> CSSOM
  3. Combine DOM CSS -> Render Tree
  4. Render Tree -> Layout
  5. Layout -> Paint
  6. Composite layer -> GPU

That means ~10ms per frame

SMOOTH INTERFACES REQUIRES 60FPS (FRAMES PER SECOND)

RENDERING FRAME PIPELINE

You can trigger changes in the rendering pipeline modifying CSS properties using JavaScript, CSS and Web Animation APIs.

RENDERING FRAME PIPELINE

Each CSS property affects the pipelines differently

Weight Affects Properties
Light Composite Transform & opacity
Medium Paint + Composite Color, cursor, ...
Heavy Layout + Paint + Composite width, height, margin-*, borders-bottom-style, borders-bottom-width, bottom, left, ..., clear, display, font-size, flex-*, line-height, letter-spacing, overflow-*, padding-*, position,

LIAR Stages

STAGE THESHOLD NOTES
Load 1000ms First load text and basic functionality, afterwards during the idle load: images, videos, comments, ...
Idle 50ms Split in 50ms chunks to be able to quickly respond if user interact with the application
Animations 10ms On this stage the browser can run the layout, paint and compositing tasks
Response 100ms Split in 50ms chunks to avoid UX penalties (if not user will notice the lag)

WEB APPLICATION LIFECYCLE

Note: not all the app need to run at 60FPS, choose which stages you want to improve because in order to do that you might have to overload others.

Thresholds per task and stage

Clarification: "Blink" refers to the Chrome internal rendering engine

Response
100ms
Animation
10ms
Idle
"50ms"
Load
1000ms
Load / Parse Avoid Avoid Unknown 40%
Scripting 15% 30% Unknown 11%
Rendering 45% 60% Unknown 12.5%
Painting 45% 10% Unknown 36.5%

WEB APPLICATION LIFECYCLE

INTERACTIONS AND ANIMATIONS

Remember: almost every interaction and animation (except forms and theme changes) need to run at least at 60FPS

Chrome DevTools

Introduction to the Performance tab

     Tips when debugging perfomance

  • Close all apps except the browser

  • Use the incognito mode to avoid extensions inteference

  • Focus on finding what causes the Jank, not what is perceived.
    What is perceived may come from multiple bottlenecks coming from different parts of the code.

  • First thing to do before applying the improvements is to measure, if you don't measure before applying them you won't know if the improvements work or how much.

CHROME DEVTOOL - EXERCISES

JavaScript

Optimizing the scripting phase in the pipeline

Avoid micro-optimizations (or use them as your last resource):

 JUST IN TIME (JIT) COMPILER

JAVASCRIPT ANIMATIONS: requestAnimationFrame

The scripting phase in an animation stage should take ~3ms out of 10ms

Scripting ~30% | Rendering ~60% | Painting ~20%

Avoid JS interruptions in your flow:

WEB WORKERS

Web Workers documentation: MDN | HTML5Rocks

GARBAGE COLLECTOR

SCRIPTING PERFORMANCE CONSIDERATIONS

  • Avoid micro-optimizations
     
  • On animations: schedule your JS using requestAnimationFrame to avoid pipeline interruptions.
     
  • Use web workers for intensive JavaScript calculations avoiding main thread blocks.
     
  • Frameworks and libraries will probably add additional complexity to our performance issues because they need time to manage their own views, callbacks, etc.
     
  • It is not easy to know when the garbage collector is going to run and it will also depend on the frameworks and libraries been used but also how you structure your code.

SCRIPTING PERFORMANCE - EXERCISES

Style & Layout

Optimizing the rendering phase in the pipeline

STRATEGIES FOR KEEPING CSS SPECIFITY LOW

BEM strategy & other CSS strategies

FORCE SYNCHRONOUS LAYOUT (FSL)


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

Note: Reading a property that affects the layout (width, margin ....) forces you to enter that stage and doing so in a lopp will cause layout thrashing:

RENDERING PERFORMANCE CONSIDERATIONS

  • The cost of modifying style grows linearly, so modifying the style of 10 elements will cost x10 than 1
     
  • Keep CSS selectors simple
     
  • Most of the times CSS and JS animations are the same, the important thing are the properties you are changing
     
  • If possible, read CSS properties before entering a loop
     
  • During the animation phase avoid changing that triggers layout or paint
     
  • If you need to do an animation that requires changing those properties, try to do that during the Load Idle or Response phase

FLIP TECHNIQUE (FIRST LAST INVERT PLAY)

This is a technique Paul Lewis uses to create smooth animations when expensive calculation are needed.

RENDERING PERFORMANCE - EXERCISES

Paint & Composite

Optimizing the painting phase in the pipeline

HOW TO: ACTIVATE PAINT PROFILER

Use Cmd+Shift+P to "Show Rendering" and check the options available

LAYER MANAGEMENT TASKS

In the timeline you fill find two tasks related to layer management:

  • Update Layer Tree: runs when "Blink" finds out the layers it needs for the page.
  • Composite Layers: the browser is compositing and sending to the GPU.

Note: to achieve smooth animations keep each of this tasks <2ms

HOW TO: SUGGEST/FORCE TO CREATE A LAYER FOR AN ELEMENT


    /* Suggest to create a new layer */
    /* not supported in all browsers */
    .newer-browsers{ 
        will-change: transform/left/top/width/height/...;
    }

    /* No transform hack; force to create a new layer */
    .older-browsers{
        transform: translateZ(0); 
    }

Warning: do not promote every element to it's own layer, layer management also have a cost.

We will usually let the browser decide how to manage layers but in some cases we might want to suggest/force actions:

HOW TO: COUNT LAYERS

HOW TO: CHECK COMPOSITING REASON

Compositing reasons: will-change, overlap with composited content, 3D transform, accelerated canvas, animates a transform, etc.

PAINTING PERFORMANCE CONSIDERATIONS

  • Performance can be increased but also decreased interfering in the layer management
     
  • Forcing a layout we can creating by accident to other elements to have their own layer (for overlaping)
     
  • Mobile phones doesn't handle properly too many layers
     
  • We need to balance the time between compositing and layer management

PAINTING PERFORMANCE - EXERCISES

CREDITS

Paul Lewis @aerotwist

Design & Development | Visualization

Cameron Pittman @cwpittman

Full Stack Engineer @Udacity

Thanks for this awesome course!

Raúl Jiménez Ortega | @hhkaos | raul.jimenez@esri.es

I'm still not a performance expert, but at least now we know a little bit more             

THANK YOU ALL

Browser Rendering Optimization

By Raul Jimenez Ortega

Browser Rendering Optimization

Talk for MadridJS meetup May 2018. The content is based on this Udacity course: https://www.udacity.com/course/browser-rendering-optimization--ud860

  • 3,223