Performing in
Real World

Real User Measurement

Hi, I'm Rafal RUManek

doing frontend stuff at Codewise
event organizer for Angular Dragons
@RafalRumanek

I have a poor sense of humour
 

 (please, forgive me!)

1517 KB

 

is the average size of resources requested by a page

75 requests

 

 

is the average number of resources requested by a page

19 connections

 

is the average number of
TCP connections for a page

15.3s

is the average load time
for a mobile page

that takes longer than
3 seconds to load

53% of mobile site visits
leave a page

If you don't measure and optimize your app
 performance, eventually it will downgrade

If you don't measure and optimize your app
 performance, eventually it will downgrade

If you don't measure and optimize your app
 performance, eventually it will downgrade

RAIL model - industry standards?

1. Local tests

2. Synthetic tests
3. Real user tests 

(during development, devtools  + audits)

(pre-release)

(prod - Real User Measurement)

From click to load

image src & read more at:
https://www.w3.org/TR/navigation-timing

Good ol' friends


    const t0 = performance.now();
    doSomething();
    const t1 = performance.now();
    console.log("Call to doSomething took " + (t1 - t0) + " milliseconds.");

performance.now() - more accurate than Date.now()
and independant of the clock system;
almost a high-res timestamp
(Spectre, fingerprinting & other threats)

image src & read more at:
https://www.w3.org/TR/navigation-timing-2/

Navigation timing

Navigation timing

    
    const [perfData] = performance.getEntriesByType('navigation');
    // similar to window.performance.timing; 
    
    const pageLoadTime = perfData.loadEventEnd - perfData.startTime;
    const dnsLookupTime = perfData.domainLookupEnd - perfData.domainLookupStart;
    const renderTime = perfData.domComplete - perfData.domLoading;
    const timeTillFirstBye = perfData.responseStart - perfData.requestStart;

t = 3521ms

Resource timing

Resource timing


    // finding data for resources with versioning
    const [ringTimingData] = performance.getEntries()
        .filter(({name}) => name.includes('voluum_frontend_ring'));

    // choosing data by name
    const [resourceData] = performance
        .getEntriesByName('https://domain.com/smiesznypiesek.jpg');

    // retrieving script transfer size
    const scriptsResources = performance.getEntriesByType('resource')
        .filter(entry => entry.initiatorType === 'script');
    const scriptsTransferSize = scriptsResources
        .reduce((sum, {transferSize})=> sum + transferSize, 0);

Resource timing buffer 

  • Limited buffer size - overflowing entries are ignored

  • can be increased (setResourceTimingBufferSize) careful!

  • or cleared (clearResourceTiming)

  • onresourcetimingbufferfull

Performance observer


    const observer = new PerformanceObserver(obsrvr => {
       const entries = obsrvr.getEntries();
       // queue perf data to send
    });
    
    
    observer.observe({entryTypes: ["resource"]});
    
    // ...
 
    observer.disconnect();

Long Tasks

Long Tasks


    const observer = new PerformanceObserver(function(list) {
        const perfEntries = list.getEntries();
    
        // queue reporting for long tasks existance
    });
    
    observer.observe({entryTypes: ["longtask"]});
    
    // long task refers to occurrences of event loop tasks
    // or pauses caused by UI thread work
    // whose duration exceeds 50ms

Paint Timing


    // first-paint, first-contentful-paint

    const timingEntries = performance.getEntriesByType('paint');
    

Server Timing headers

Memory

Disclaimer

  • for sending gathered data, consider using Beacon API

  • make sure you separate background tab performance with Page Visibility API

When is your app really ready?

Perceived performance

Time till visually ready

  • First paint (if available) - Chrome's firstPaintTime, IE's msFirstPaint; not necessarily meaningful

  • domContentLoadedEventEnd - available through performance.timing; happens after domInteractive

  • hero image (if exists) - Resource Timing

  • framework ready - custom, user defined event

Time till interactive

  • TTVR

  • no long tasks (Long Tasks API)

  • FPS don't drop below 20 - can be measured with requestAnimationFrame

(hopefully not so obvious)
Performance tips

Compression

  • gzip is a standard - what about brotfli and zopfli?

  • keep in mind (de)compression times
    - doesn't mean brotli is slower

  • CDNs can do that for you!

Combine bundling & splitting

  • balance out your huuuge bundle with versioning of smaller pieces

  • use code splitting, lazy loading & preloading

  • is SSR + progressive enhancement a viable option?
     

Be lazy!

  • do lazy loading

  • do lazy rendering on boostrap

  • do lazy rendering for bigger UI elements

Use HTTP/2.0

  • headers compression (think of CSP header)

  • PUSH resources to users they will request (careful - PUSH vs. local cache)

  • better parallelism for resources from same domain

pre- hints

Cache all the things!

  • use CDN effectively!

  • cache-side cache, both for static resources and dynamic data: etag or

Data structures matter

simple things such as Map vs. Object
can make difference:
https://github.com/facebook/jest/pull/6960

Visualisations help

  • source-map-explorer

  • webpack bundle analyzer

  • request map generator

image soruce:
https://pexels.com

Being fast matters!

The longer you
neglect performance,
the more issues you will have

Being fast matters!

The longer you
neglect performance,
the more issues you will have

RUM @ meet.js krk 17.09

By Rafał Rumanek (truti)

RUM @ meet.js krk 17.09

  • 1,030