Skip to content
This repository has been archived by the owner on Aug 14, 2024. It is now read-only.

Commit

Permalink
Add frames delay (#1153)
Browse files Browse the repository at this point in the history
Add docs for the performance metric frames delay.
  • Loading branch information
philipphofmann committed Feb 15, 2024
1 parent 930640a commit 58f8ded
Show file tree
Hide file tree
Showing 3 changed files with 163 additions and 0 deletions.
3 changes: 3 additions & 0 deletions src/components/sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,9 @@ export default () => {
<SidebarLink to="/sdk/performance/time-to-initial-full-display/">
Time to Initial/Full Display
</SidebarLink>
<SidebarLink to="/sdk/performance/frames-delay/">
Frames Delay
</SidebarLink>
<SidebarLink to="/sdk/performance/dynamic-sampling-context/">
Dynamic Sampling Context
</SidebarLink>
Expand Down
159 changes: 159 additions & 0 deletions src/docs/sdk/performance/frames-delay.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
---
title: "Frames Delay"
---

Frames Delay is the user-perceived delayed duration of rendered frames. SDKs can attach this
information to spans. We recommend implementing this feature for mobile and desktop SDKs.

## Background

This section explains why frames delay is a better metric than slow and frozen frames. If you
already know why, you can skip this section.

On Mobile, slow and frozen frames are well-established metrics for tracking the responsiveness of
applications. A phone or tablet typically renders 60 frames per second (fps). At 60 fps, every frame
has 16 or 16.67 ms to render:

* __Slow Frames__: Using 60 fps, slow frames are frames that take more than 16.67 ms to render.
* __Frozen Frames__: Frozen frames are frames that take longer than 700 ms to render.

The major downside is that they lack essential information on how long they lasted, which is a
significant problem when prioritizing which unresponsive application parts need improvement. The following
scenarios highlight the problem when prioritizing by looking at slow and frozen frames.

Scenario | Slow Frames | Frozen Frames |
-- | -- | -- | --
1 | 5 all 500ms | 0 |
2 | 10 all 20ms | 0 |
3 | 0 | 1 with 800ms |

When comparing the different scenarios, a user might want to fix scenario 3 first because it contains one frozen
frame. When comparing scenarios 1 and 2, they would pick scenario 2 cause it has more slow frames. While scenario
2 looks worse, the user experience is way worse for scenario 1 because the slow frames last longer.

That's where a new metric called __frames delay__ comes to the rescue. Frames delay represents the delayed frame
render duration of all frames. The frame delay for one frame is `actual frame duration - expected frame duration`.
For example, with 60 fps the expected frame duration is 16.67 ms. When a frame takes 20 ms to render, the frame
delay is `20ms - 16.67ms = 3.33ms`.

Let's calculate the frames delay for scenarios 1 - 3.

Scenario | Calculation | Frames Delay
-- | -- | --
1 | (500ms - 16.67ms) * 5 | 2416.65ms
2 | (20ms - 16.67 ms) * 10 | 33.3ms
3 | 800ms - 16.67ms | 783.33ms

Now, when prioritizing the user-perceived degraded responsiveness by frames delay, we get 1, 3, 2 instead of
3, 2, 1 when looking at slow and frozen frames.

## Specification

Frames delay represents the delayed frame render duration of all frames. The frame delay for one frame is
`actual frame duration - expected frame duration`. For example, with 60 fps the expected frame duration is 16.67 ms.
When a frame takes 20 ms to render, the frame delay is `20ms - 16.67ms = 3.33ms`. As the expected frame rate can
change at any time, SDKs must check the expected frame rate for every frame. The frame delay for a span is the sum
of frame delays for all slow and frozen frames during the start and end time of the span.

SDKs should attach frames delay in seconds to spans via <Link to="/sdk/performance/span-data-conventions/#mobile">span data</Link>
and __attach frames delay to all spans__, regardless of on which thread a span runs, and as well to concurrently running spans.
For transactions SDKs should send frames delay via the root span with span data.
While it makes sense to calculate frames delay in milliseconds, __SDKs must send frames delay in seconds__, as SDKs already
use seconds for most timestamps. For the specification here, we use milliseconds cause they are easier to read.

The specification is written in the [Gherkin syntax](https://cucumber.io/docs/gherkin/reference/).


```Gherkin
Scenario: Slow and frozen frames
Given a frame rate of 60 fps
And 2 frames with 300 ms
And 1 frame with 900 ms
And 10 frames with 16.76 ms
When calculating the frames delay
Then the frames delay is (300 - 16.67) * 2 + 900 - 16.67 = 1449.99 ms
Scenario: Frame rate changes
Given 1 frame with 200 ms with 60 fps
And 2 frame with 500 ms with 120 fps
And 10 frames with 8.33 ms with 120 fps
When calculating the frames delay
Then the frames delay is 200 - 16.67 + (500 - 8.33) * 2 = 1166.67 ms
Scenario: Ongoing delayed frame
"""
[nf][ ------- ?? ------ ] | nf = normal frame, ?? = No frame information
[---- span duration ----]
When there is an ongoing delayed frame, the logic doesn't know how long it
will last and, therefore, has no frame information.
"""
Given a frame rate of 60 fps
And 1 frame with 16.67 ms
And 200 ms pass after without any frame being drawn
And no recorded delayed frame
When calculating the frames delay
Then the frames delay 200 ms - 16.67 ms = 183.33 ms
Scenario: Delayed frame starts before span
"""
[---- delayed frame ---- ]
[- span duration -]
"""
Given a frame rate of 60 fps
And a recorded delayed frame with a duration of 300 ms
When calculating the frames delay for the span 100 ms to 300 ms
Then the frames delay is the full duration of the span of 200ms
Scenario: Delayed frame starts shortly before span
"""
[| e | delayed frame ---- ] e = the expected frame duration
[--- span duration --- ]
"""
Given a frame rate of 60 fps
And a recorded delayed frame with a duration of 300 ms
And the delayed frame started at 0 ms shortly before the span
When calculating the frames delay for the span 10 ms to 300 ms
Then the frames delay is 300 ms - 16.67 ms = 283.33 ms, which is only the delayed part
of the delayed frame
And the expected frame duration part of the delayed frame is not added as a frame delay
Scenario: Delayed frame starts and ends before the span
"""
[| e | delayed frame ------ ] e = the expected frame duration
[--- span duration --- ]
"""
Given a frame rate of 60 fps
And a recorded delayed frame with a duration of 310 ms
And the delayed frame started at 0 ms shortly before the span
When calculating the frames delay for the span duration of 10 ms to 300 ms
Then the frames delay is 300 ms - 6.67 ms = 293.33 ms, which is only the delayed part of
the delayed frame having an intersection with the span duration
And the expected frame duration part of the delayed frame is not added as a frame delay
Scenario: One delayed frame starts shortly before the end of a span
"""
[| e | delayed frame ][| e | delayed frame - ] e = the expected frame duration
[---- span duration ---- ]
"""
Given a frame rate of 60 fps
And a recorded delayed frame with a duration of 290 ms
And a recorded delayed frame with a duration of 200 ms
When calculating the frames delay for the span duration of 0 ms to 300 ms
Then the frames delay is 290 ms - 16.67 ms = 273.33 ms
And the expected frame duration part of the second delayed frame is not added as a frame delay
Scenario: Two concurrent spans
"""
[| e | --- delayed frame--- ] e = the expected frame duration
[----- span 1 -----]
[---- span 2 ----]
"""
Given a frame rate of 60 fps
And a recorded delayed frame with a duration of 300 ms
When calculating the frames delay
Then the frames delay for span 1 is it's full duration of 180 ms
Then the frames delay for span 2 is it's full duration of 160 ms
```
1 change: 1 addition & 0 deletions src/docs/sdk/performance/span-data-conventions.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ Below describes the conventions for the Span interface for the `data` field on t
| `frames.total` | int | The number of total frames rendered during the lifetime of the span. | `60` |
| `frames.slow` | int | The number of slow frames rendered during the lifetime of the span. | `2` |
| `frames.frozen` | int | The number of frozen frames rendered during the lifetime of the span. | `1` |
| `frames.delay` | number | The sum of all delayed frame durations in seconds during the lifetime of the span. For more information see <Link to="/sdk/performance/frames-delay/">frames delay</Link>. | `1.3246` |

## Browser

Expand Down

0 comments on commit 58f8ded

Please sign in to comment.