@malchata

Demystifying

Performance timings


by Jeremy Wagner — @malchatajeremywagner.me

Hello,

I'm Jeremy!

Jeremy Wagner speaking at a conference.

Regarding

Testing methods

Synthetic testing

(or lab data)

A screenshot of Chrome's Lighthouse audit in action.

Lighthouse

The case for:

  • It's easy to do.
  • Great for spot checking performance in dev environments. (With network throttling, of course.)
  • Easy to measure the effect of code changes on performance due to consistent baselines.

The case against:

It doesn't necessarily reflect what real users are experiencing.

Enter RUM

A bottle of cheap rum.

(No, not this.)

Real User Metrics

(or field data)

The case for:

You're gathering metrics from real users who are using your site/app.

The case against:

It's not as convenient.

performance.getEntriesByType("navigation").forEach((navigation) => {
  console.dir(navigation);
});
A screenshot of a navigation timing entry in Chrome.
An animated image of a man crying.

Navigation

and

Resource Timing

Navigation timing

Gathers page metrics.

(Usually HTML documents.)

Resource timing

Gathers resource metrics.

(CSS, JavaScript, images, et cetera.)

Processing Model

Navigation timing processing model diagram.
w3.org/TR/navigation-timing-2/#processing-model

Unload

When the document unloads.

An animated image of a truck throwing stuff out the back using inertia.

Unload

Navigation timing processing model diagram with the unload phase highlighted.

Regarding

unload metrics

  • startTime is always 0.
  • unloadEventStart may be 0 if there is no previous document, or if a previous document (or redirect) is not from the same origin.
  • unloadEventEnd can overlap with other events (e.g., connect, redirect).

Redirect

When a navigation encounters one (or more) HTTP 300 level status codes.

An animated image of a cat chasing its tail.
A screenshot of redirects mapped out in Chrome dev tools.

Redirect*

Navigation timing processing model diagram with the redirect phase highlighted.
*Resource timing API processing model begins here.

Regarding

Redirect metrics

  • redirectStart and redirectEnd will be 0 if no redirects occur.
  • Redirects from and to other origins can still come up as 0 if the Timing-Allow-Origin header isn't set.

App Cache

When the browser begins to fetch an item.

An animated image of a person pushing a dog along the floor toward a ball.

App Cache

Navigation timing processing model diagram with the app cache phase highlighted.

Regarding

App Cache metrics

  • Regardless of caching, fetchStart begins about the same time.
  • If an item is cached, metrics that come after fetchStart (e.g., connectStart, et cetera) will occur very nearly after.

Cached vs. uncached

Differences in app cache performance timing phases for cached and uncached resources.

DNS

When DNS lookups occur.

Gandalf reading out of a giant book.
A screenshot of connection phases and times for a page resource in Chrome dev tools.

DNS

Navigation timing processing model diagram with the DNS phase highlighted.

Regarding

DNS metrics

  • DNS lookups can be cached at multiple levels:
    • Browser.
    • System.
    • Router.
    • ISP.
    • et al.

Connection

When connections are established.

Two men doing an awkward secret handshake.
A screenshot of connection phases and times for a page resource in Chrome dev tools.

Connection

Navigation timing processing model diagram with the TCP phase highlighted.

Regarding

Connection metrics

  • Connections can be reused.
  • secureConnectionStart will be 0 for non-secure sites.
  • secureConnectionStart can be 0 if TLS sessions are resumed.

Request/Response

When requests are made, and when responses begin/end.

Two men throwing cheeseballs in each others' mouths.
A screenshot of request and response metrics mapped out in Chrome dev tools.

Request/Response*

Navigation timing processing model diagram with the request and response phases highlighted.
*Resource timing API processing model ends here.

Regarding

Request/Response metrics

  • requestStart denotes the start of a request.
  • responseStart and responseEnd denote the start and end of a response, respectively.

Processing

When the DOM is being processed.

A time-lapse of cookies baking.

Processing

Navigation timing processing model diagram with the processing phase highlighted.

Regarding

Processing metrics

  • domInteractive marks when the browser has finished HTML parsing and DOM construction.
  • domContentLoadedEventStart and domContentLoadedEventEnd marks when the DOM is ready.
  • domComplete marks when everything has loaded.

Regarding

Processing metrics (continued)

  • domInteractive and domContentLoadedEventStart occur around the same time.
  • Depending on front end architecture complexity, domComplete often occurs some time after domContentLoadedEventEnd.
A screenshot of redirects mapped out in Chrome dev tools.

Load

When the page and all of its resources are fully loaded.

A truck loading fail.

Load

Navigation timing processing model diagram with the load phase highlighted.

Regarding

Load metrics

  • loadEventStart usually occurs briefly after domComplete.
  • loadEventEnd signifies when the page has completely finished loading.

The little bits!

name

The URL of a document or resource.

transferSize

The size of a resource including headers.

An image of HTTP response headers.

encodedBodySize

The compressed size of a resource.

(Without headers.)

decodedBodySize

The decompressed size of a resource.

(Without headers.)

nextHopProtocol

The protocol used to transmit a resource.

nextHopProtocol values*:

  • h2 (HTTP/2)
  • hq (QUIC)
  • http/1.1

*This is probably not exhaustive!

Timing recipes!

TTFB (ms)


let ttfb = responseStart - requestStart; // 35.6001 ms

Resource fetch time (ms)


let fetchTime = responseEnd - fetchStart; // 512.3021 ms

Fetch speed (Mbps)


let fetchSpeed = (
  (transferSize / (2**20)) / ((responseEnd - responseStart) / 1000)
) * 8; // 9.0012 Mbps

Header size (bytes)


let headerSize = transferSize - encodedBodySize; // 169 bytes

DNS lookup time (ms)


let dnsTime = domainLookupEnd - domainLookupStart; // 171.2999 ms

TCP time (ms)


let tcpTime = connectEnd - connectStart; // 93.5005 ms

SSL time (ms)


let sslTime = 0;

if(secureConnectionStart > 0) {
  sslTime = connectEnd - secureConnectionStart; // 58.6002 ms
}

DOM processing time (ms)


let domTime = domComplete - domInteractive; // 276.6999 ms

Bonus materials!

Server Timing

Passes server side metrics to navigation timing.

w3c.github.io/server-timing

What Server-Timing
headers look like:

An image of the server timing header.

How Server-Timing
headers are visualized:

The visualization of server timing values in Chrome's dev tools.

Paint Timing API

Exposes first paint and first meaningful paint timings.

w3.org/TR/paint-timing

Try it in the console*:

performance.getEntriesByType("paint").forEach((paint) => {
  console.dir(paint);
});
*Currently only in Chrome

Paint timing metrics:

Paint timing metrics in the Chrome console.

Long Tasks API

Logs activity that blocks the main thread for a long period of time.

w3c.github.io/longtasks

What is a "long task"?
Long task refers to an event loop that exceeds 50ms.”
Long Tasks Spec, § 2.

var observer = new PerformanceObserver(function(list) {
  var perfEntries = list.getEntries();
  for (var i = 0; i < perfEntries.length; i++) {
    console.dir(perfEntries[i]);
  }
});

observer.observe({
  entryTypes: ["longtask"] // <-- The good stuff
});
*Will output nothing if no long tasks are detected after initializing the observer.

Long task metrics:

Long task metrics in the Chrome console.

Quick tips!

Don't record
computed metrics

(Unless you know what you're doing)

Always feature check:

// Check if we can do performance-y things
if ("performance" in window) {
  if (performance.getEntriesByType("navigation").length > 0) {
    // Do things with navigation timing
  }

  if (performance.getEntriesByType("resource").length > 0) {
    // Do things with resource timing
  }

  if (performance.getEntriesByType("paint").length > 0) {
    // Do things with paint timing
  }
}

Gather performance metrics with

PerformanceObserver

Using PerformanceObserver

var observer = new PerformanceObserver(function(list) {
  var perfEntries = list.getEntries();
  for (var i = 0; i < perfEntries.length; i++) {
    // Do stuff with whatever comes in
  }
});

observer.observe({
  // Add performance entry types you want to observe here:
  entryTypes: ["navigation", "resource", "paint", "longtask"]
});

Wrapping up!

With thanks to:

Estelle Weyl

Estelle Weyl
@estellevw

Ilya Grigorik

Ilya Grigorik
@igrigorik

Shubhie Panicker

Shubhie Panicker
@shubhie

Charles Vazac

Charles Vazac
@vazac

Jatinder Mann

Jatinder Mann
@jatindermann

Domenic Denicola

Domenic Denicola
@domenic

Tobin Titus

Tobin Titus
@tobint

Todd Reifsteck

Todd Reifsteck
@toddreifsteck

Paul Irish

Paul Irish
@paul_irish

Thanks!

Slides:
jlwagner.net/talks/perf-timings