Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add an Engine method to get the 1% percentile low FPS #67136

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

Calinou
Copy link
Member

@Calinou Calinou commented Oct 9, 2022

Measuring performance by only looking at the average FPS can be misleading, as microstutters will not be represented in the final value. This can lead to confusion as a game might have an average FPS of over 60, yet it can feel very stuttery if its 1% low FPS are in the single digits.

This adds a Engine method and profiler readout for the 1% low FPS over the last 100 rendered frames. This is a moving metric by definition, but it still helps make these FPS drops more noticeable compared to an average readout.

This closes godotengine/godot-proposals#5459.

Testing project: test_fps_1_percent_low.zip

Preview

Performance monitor

2022-10-09_02 13 51

Video

FPS cap is engaged at the middle of the video. The recording has some stuttering, but it feels smooth in practice. The [Core] line is the metric introduced in this PR, while [Script] is a GDScript prototype I left in the testing project for comparison.

out.mp4

@Calinou Calinou requested review from a team as code owners October 9, 2022 14:29
@Calinou Calinou added this to the 4.0 milestone Oct 9, 2022
for (int i = 0; i < 100; i++) {
frametime_1_percent_low = MAX(frametime_1_percent_low, recent_frametimes[i]);
}
Engine::get_singleton()->_fps_1_percent_low = 1'000'000.0 / frametime_1_percent_low;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • There's an interesting comment on line 3173 about FPS reporting. Based on that, shouldn't this code be inside the IF from line 3173 (the if below)?
  • Suggestion: Since it is already printing the FPS below (L3178 and 3181), WDYT aboud add the 1% low FPT there too?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's an interesting comment on line 3173 about FPS reporting. Based on that, shouldn't this code be inside the IF from line 3173 (the if below)?

1% low FPS reporting is designed to be updated as often as possible, rather than just once per second. This makes the value less stable when you print out its value in _process, but it makes changes more reactive, which I think is better overall.

The editor performance monitor and --print-fps will still only update the displayed value once per second due to how they work (see the graph screenshot in OP).

In the long term, we could look into adding a smoothing algorithm like #63356, but I think this is better left for a future PR.

Suggestion: Since it is already printing the FPS below (L3178 and 3181), WDYT aboud add the 1% low FPT there too?

That's a good idea. I implemented this 🙂

main/main.cpp Outdated Show resolved Hide resolved
@Calinou Calinou force-pushed the engine-add-fps-1-percent-low branch 2 times, most recently from 957c0fb to c3edd33 Compare October 12, 2022 18:21
@connorshea
Copy link

Generally, when discussing performance (at least for web apps and APIs), the common thing to call this would be "99th percentile" (for the longest/slowest 1% of requests).

e.g. Sentry's performance tools, Google Cloud's latency metrics, Datadog's "percentile aggregations", etc.

To mirror that, should this be called "1st percentile FPS"? (or even just call it 99th percentile FPS, since that's still true here, it's just that in this case we want higher numbers rather than wanting lower numbers).

@Calinou
Copy link
Member Author

Calinou commented Oct 22, 2022

Generally, when discussing performance (at least for web apps and APIs), the common thing to call this would be "99th percentile" (for the longest/slowest 1% of requests).

See https://www.capframex.com/blog/post/Explanation%20of%20different%20performance%20metrics and https://www.capframex.com/blog/post/The%20challenge%20of%20displaying%20performance%20metrics%20as%20FPS. The "1% low" term generally seems more popular than "99th percentile" nowadays in gaming, though both are open to ambiguity as shown in the linked articles.

@akien-mga
Copy link
Member

We discussed this in a PR review meeting.

The feature looks nice and useful, but we have some concerns on the implementation:

  • Is this the one metric that will need? Or will there be more similar metrics that we'll want to add, each time requiring a new Engine property and adding more computation to Main::iteration?
  • Computing this in Main::iteration seems a bit wasteful performance-wise, especially if it's intended for debugging and not something that users actually need in production constantly (unlike computations done for the idle and physics frame). Maybe this can be handled in Performance itself in a way that would not affect production builds? Or in the editor profilers?

@Calinou
Copy link
Member Author

Calinou commented Nov 15, 2022

  • Is this the one metric that will need? Or will there be more similar metrics that we'll want to add, each time requiring a new Engine property and adding more computation to Main::iteration?

As I wrote in OP, there are other "% low" metrics out there, but 1% low is by far the most commonly used one.

  • Computing this in Main::iteration seems a bit wasteful performance-wise, especially if it's intended for debugging and not something that users actually need in production constantly (unlike computations done for the idle and physics frame). Maybe this can be handled in Performance itself in a way that would not affect production builds? Or in the editor profilers?

The computation could be disabled in release builds, but it only takes a few microseconds per frame so I'd prefer not. It should be possible for developers to profile fully optimized release exports after all.

@Calinou
Copy link
Member Author

Calinou commented Jul 23, 2024

Rebased and tested again, I'm noticing strange behavior from the 1% low FPS counter (look at the Core line):

fps_low.mp4

Most noticeably, when capping at 30 FPS, it never goes past 10 FPS (which is a FPS cap that is momentarily applied when I go to the 30 FPS cap, but it stays here even if I stay at a perfect 30 FPS for 10+ seconds).

Edit: Fixed thanks to @CaelusV's review comment below.

main/main.cpp Outdated Show resolved Hide resolved
@Calinou Calinou force-pushed the engine-add-fps-1-percent-low branch from e851733 to b2ce236 Compare July 24, 2024 15:52
@AThousandShips AThousandShips changed the title Add a Engine method to get the 1% percentile low FPS Add an Engine method to get the 1% percentile low FPS Jul 24, 2024
Measuring performance by only looking at the average FPS can be misleading,
as microstutters will not be represented in the final value.
This can lead to confusion as a game might have an average FPS of over 60,
yet it can feel very stuttery if its 1% low FPS are in the single digits.

This adds a Engine method and profiler readout for the 1% low FPS over
the last 100 rendered frames. This is a moving metric by definition,
but it still helps make these FPS drops more noticeable compared to an
average readout.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add an Engine method and Performance monitor to return the 1% low FPS ("99th percentile")
6 participants