Conrad Irwin
Conrad Irwin

Reputation: 1312

How does Chrome DevTools measure Response of interactions?

I'm trying to measure the user-perceived latency of an interaction in Chrome. This is the time from hitting a key to the screen being updated. In this example the we're re-rendering a large portion of the screen with new content. There is no network latency involved, we're just updating HTML.

In the below screenshot you can see the Chrome DevTools keydown event, it runs a lot of javascript, and then there's a bunch of painting, compositing etc., and then the keydown event "Response" span ends.

Assuming the horizontal gray lines are vsyncs (does anyone know if these lines are documented anywhere?) where Chrome wrote a new render to the GPU and thus the screen, it seems like the "Response" span the Devtools gives for the keydown event is a good approximation to what I'm trying to measure, as it measures the time from keydown until just after the first grey line after we've finished mutating the DOM.

I've tried various ways to approximate this time in javascript, both using requestAnimationFrame, requestIdleCallback, a setImmediate polyfill with message passing, and a few combinations of the above. It seems like all of them are longer than the pure Javascript time, but mostly they underestimate or overestimate the time to actually update the screen.

Does anyone know of a better way of measuring this time in production? How does the Chrome DevTools metric work? Should I be measuring something else entirely?

Chrome Timeline View

Upvotes: 3

Views: 1176

Answers (1)

Paul Irish
Paul Irish

Reputation: 49132

The instrumentation for these Input Latency events is pretty sophisticated and cool. These are great questions.

Input Latency events end time

Input Latency events end around the VSYNC, assuming that the work they triggered caused an invalidation that required a screen update. This is why, in your screenshot, Key Down extends much farther than Key Up. (Even if there were keyup listeners, they didn't invalidate styles/layout)

It's certainly possible there is an earlier paint & VSYNC that these events terminate at, which isn't really when the screen updates with the response to the input... but you'll have to make that call on your own.

Gray dotted lines, vsync, swap buffers

The gray dotted lines are frame boundaries, but it starts to get tricky here. In Chrome (and AFAIK, other browsers too) there are main thread frames and compositor thread frames. DevTools attempts to display a single timeline of frames to keep things simple, so it can't be perfect. For your purposes, I would look at the right edge of the screenshots in that Frames track. The right edge is where the screen updated with those contents. On Chrome we call this time swapBuffers. (VSYNC is technically when the monitor says "I'm ready for a new frame", which is slightly different)

Measuring this in JavaScript

Unfortunately, you don't have the perfect tools to pull this off. The old technique is a double-rAF, and sometimes subtract some time. At this point you definitely know there's been one new frame. Applying some of this knowledge, you could probably be pretty smart. But it wont be perfect. For example, long image decodes will delay the compositor in shipping the frame, but the main thread is now idle and VSYNC will cause it to begin a brand new frame (thus calling rAF again).

The Long Task API could help as it describes what sort of work is taking a while on the main thread. But I don't think it'll have the sort of precision we need to answer these questions. Frame Timing would have, but it died because of privacy reasons.

TDLR:

Use the timestamp from the input event, as it's taken when the input was received at the compositor. And then use some single-rAF, double-rAF mechanism, subtracting some amount of time, to approximate when the frame was shipped. And holler when you have something you're happy with; I'm interested.

Upvotes: 3

Related Questions