Web App Performance

Performance in Context of the User

The RAIL Model

per·for·mance

/pərˈfôrməns/

noun

  1. The time it takes for actions to complete before the user's eyes

suc·cess

səkˈses/

noun

  1. 😁

Performance ROI

*Diagram courtesy of Google (Paul Lewis & Paul Irish), "Introduction to RAIL (Chrome Dev Summit 2015)"

⌛️⌛️⏳

😭

😁

The User's Perception

*Diagram courtesy of Google (Paul Lewis & Paul Irish), "Introduction to RAIL (Chrome Dev Summit 2015)"

RAIL Method: 

An implementation-agnostic, high level, user-centric performance model

What is too slow?

What does the user feel?

User-centric Performance Goals

*Diagram courtesy of Google (Paul Lewis & Paul Irish), "Introduction to RAIL (Chrome Dev Summit 2015)"

esponse

R

A

I

L

Response

Applicable to inputs:

  • buttons
  • toggling form controls
  • starting animations

Goal: Visual response to click/tap in < 100ms

Where the connection between action and reaction breaks.

Response

🛠 Implementation Strategies:

  • Decouple the work from the visual response
  • Provide feedback for actions > 500ms
  • Instant visual response
  • Feedback on status of the action

Response

🛠 Implementation Strategies:

  • Optimistic UI's
    • Assume Success
    • Only delayed notifications are on server error
    • Respond with errors within 2 seconds, while still in user's flow

Examples:

nimation

R

A

I

L

Animation

  • Reasons why:
    • A smooth experience
    • Users notice frame rate variations
    • The standard for most devices today

Goal: Each frame completes in less than 16ms (60 fps)

Animation

  • The browser takes 6ms per frame to paint
  • You get 10ms per frame

*Diagram courtesy of Google (Paul Lewis), "Rendering Performance" - developers.google.com

The Pixel Pipeline

Animation

🛠 Implementation Strategies:

**For animations experiencing sub-optimal performance:

 

 

  • Avoid Animations that change the layout of the page
  • Animate using transform and opacity
  • Use will-change property - keeps browser from having to consider layout render or painting
.moving-element {
  will-change: transform;
}
.moving-element {
  transform: translateZ(0);
}

⚠️ Don't overuse

Animation

GO

🛠 Implementation Strategies:

  • Profiling animation performance
  • Example of will-change and transform

GO

dle

R

A

I

L

Idle Time

  • Minimize data loading in pre-rendering stage
  • Use idle time to load what you can
  • Yield control back to main thread every 50ms, so the 100ms response goal can be met

Goal: Use idle time to do work, while still satisfying the Response goal

// Creating a new Web Worker
const myWorker = new Worker("./worker.js");

// Giving work to the worker thread
myWorker.postMessage(arbitraryObject);
console.log("Message posted to worker");

main.js

  • Allows web content to run scripts in background threads
  • Each script is a worker
  • Can be used for processing, fetching, and storage interactions 

Idle Time

Implementation Strategies (freeing up the main thread):

Web Workers

🛠

Idle Time

Web Workers

...

// Handling result event from worker thread
myWorker.onmessage = function(e) {
  console.log(`
    Message received from worker:
    ${JSON.stringify(e.data)}
  `);
}

main.js

onmessage = function(e) {
  console.log(`
    Received object from the main script:
    ${JSON.stringify(e.data)}
  `);
  const workerResult = doWork(e.data);
  // Posting a message back to main.js
  postMessage(workerResult);
}

worker.js

Idle Time

Other Implementation Strategies (freeing up the main thread):

  • Request Animation Frame (RAF)
  • Async script tags

🛠

oad

R

A

I

L

Load

  • User attention wanders after 1 second

Goal: First meaningful paint in < 1000ms

Load

🛠 Implementation Strategies

  • Lazy loading - React Example:
class View extends React.Component {

    ...

    // This method is invoked immediately after
    // a component is mounted to the DOM.
    componentDidMount() {
        fetch("my-endpoint")
            .then(data => data.json())
            .then(json => this.setState({ pageData: json }))
            .catch(e => this.setState({ pageError: e }));
    }
    
    render() {
        return (
            <MostMeaningfulContent />
            {this.state.pageData && <SupplementalContent />}
        )
    }
}

Load

🛠 Implementation Strategies

  • Server-side rendering and caching
    • In-house options
    • Community options
  • Conditional loading:
    • Load on scroll
    • Load on event

🎊 The End 🎉

React Performance Techniques

Virtual DOM Defficiencies

shouldComponentUpdate()

React.PureComponent
shouldComponentUpdate(nextProps, nextState) {
    return true;
}

shouldComponentUpdate()

Only needed if render() is a known bottleneck:
- Requires hardcoding at the prop/state level
- Has its own performance cost
- Complicates the workflow (immutability)

DOM

Virtual DOM

<App />

<Header />

<Body />

New Props or State

render()

<Title />

<TileList />

<Tile />

<Tile />

<Tile />

render()
render()
render()
render()
render()
render()
render()
render()

Diff?

Update DOM()

Example

class Greeting extends React.Component {

  shouldComponentUpdate(nextProps, nextState) {
    const { props, state } = this;
    if(
      nextProps.name !== props.name
      || nextProps.hasVisited !== props.hasVisited
    ) {
      // Update the component if a prop has changed
      return true;
    } else {
      return false;
    }
  }

  render() { // After initial render, only called if
             // shouldComponentUpdate returns true
    const { name, hasVisited } = this.props;
    return <h1>Welcome {hasVisited && "back "}{name}!</h1>;
  }
}
 
 import Perf from 'react-addons-perf';

Demo with React Profilers

https://my-app.com?react_perf   ➡

class Thing extends React.PureComponent {

    ...

}

React.PureComponent

class Thing extends React.PureComponent {
    ...
}

React.PureComponent

Only needed if render() is a known bottleneck:
- Requires hardcoding at the prop level
- Has its own performance cost
- Complicates the workflow (immutability)
class Thing extends React.PureComponent {
    ...
}

React.PureComponent

===

class Thing extends React.Component {

    ...
    
    shouldComponentUpdate(nextProps, nextState) {

        let shouldUpdate = false;

        Object.keys(nextProps).map(prop => {
            if (nextProps[prop] !== this.props[prop]) {
                // A prop has shallowly changed
                shouldUpdate = true;
            }
        });

        Object.keys(nextState).map(element => {
            if (nextState[element] !== this.state[element]) {
                // An element of state has shallowly changed
                shouldUpdate = true;
            }
        });
        return shouldUpdate;
    }
}

Roadblocks

  • Inline object literals as props

Roadblocks

  • React.PureComponent for stateless components: 'recompose'

Classical Techniques

...Javascriptified

O(n^2) ➡ 🏌 O(nlogn) 🏌 O(n)

Time Complexity

Hashing Arrays By ID

// Turning an Array of objects into a hash for O(1) operations.
// Illustrated below: If the property being hashed by ('id' here)
// is not unique (like a primary key), previous records with the same
// id will be overwritten.

const myArr = [
  {id: 1, data: 'a'},
  {id: 1, data: 'b'},
  {id: 2, data: 'c'}
];

const objectOfObjects = myArr.reduce((newHash, record) => {
  newHash[record.id] = record;
  return newHash;
}, {}) // <- {} is the 'newHash' seed

console.log(objectOfObjects);

Sources

  • RAIL Model

    • https://developers.google.com/web/fundamentals/performance/rail

    • https://www.smashingmagazine.com/2015/10/rail-user-centric-model-performance/

  • Web App Performance High Level Strategies

    • https://developers.google.com/web/fundamentals/performance/rendering/

    • https://www.smashingmagazine.com/2016/11/true-lies-of-optimistic-user-interfaces/

  • Web App Performance Techniques

    • https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers

  • Optimizing Web App Animations

    • https://developers.google.com/web/fundamentals/design-and-ux/animations/

    • https://developers.google.com/web/fundamentals/design-and-ux/animations/animations-and-performance

    • https://medium.com/outsystems-experts/how-to-achieve-60-fps-animations-with-css3-db7b98610108

    • https://developers.google.com/web/fundamentals/performance/rendering/stick-to-compositor-only-properties-and-manage-layer-count

  • React Performance Techniques


Web App Performance Extended

By Evan Peterson

Web App Performance Extended

  • 702