-
-
Notifications
You must be signed in to change notification settings - Fork 3.6k
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
Provide smoothed diagnostics readings #4983
Labels
A-Diagnostics
Logging, crash handling, error reporting and performance analysis
C-Feature
A new feature, making something new possible
Comments
CAD97
added
C-Feature
A new feature, making something new possible
S-Needs-Triage
This issue needs to be labelled
labels
Jun 11, 2022
Weibye
added
A-Diagnostics
Logging, crash handling, error reporting and performance analysis
and removed
S-Needs-Triage
This issue needs to be labelled
labels
Jun 11, 2022
james7132
pushed a commit
to james7132/bevy
that referenced
this issue
Oct 28, 2022
# Objective - Add Time-Adjusted Rolling EMA-based smoothing to diagnostics. - Closes bevyengine#4983; see that issue for more more information. ## Terms - EMA - [Exponential Moving Average](https://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average) - SMA - [Simple Moving Average](https://en.wikipedia.org/wiki/Moving_average#Simple_moving_average) ## Solution - We use a fairly standard approximation of a true EMA where $EMA_{\text{frame}} = EMA_{\text{previous}} + \alpha \left( x_{\text{frame}} - EMA_{\text{previous}} \right)$ where $\alpha = \Delta t / \tau$ and $\tau$ is an arbitrary smoothness factor. (See bevyengine#4983 for more discussion of the math.) - The smoothness factor is here defaulted to $2 / 21$; this was chosen fairly arbitrarily as supposedly related to the existing 20-bucket SMA. - The smoothness factor can be set on a per-diagnostic basis via `Diagnostic::with_smoothing_factor`. --- ## Changelog ### Added - `Diagnostic::smoothed` - provides an exponentially smoothed view of a recorded diagnostic, to e.g. reduce jitter in frametime readings. ### Changed - `LogDiagnosticsPlugin` now records the smoothed value rather than the raw value. - For diagnostics recorded less often than every 0.1 seconds, this change to defaults will have no visible effect. - For discrete diagnostics where this smoothing is not desirable, set a smoothing factor of 0 to disable smoothing. - The average of the recent history is still shown when available.
Pietrek14
pushed a commit
to Pietrek14/bevy
that referenced
this issue
Dec 17, 2022
# Objective - Add Time-Adjusted Rolling EMA-based smoothing to diagnostics. - Closes bevyengine#4983; see that issue for more more information. ## Terms - EMA - [Exponential Moving Average](https://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average) - SMA - [Simple Moving Average](https://en.wikipedia.org/wiki/Moving_average#Simple_moving_average) ## Solution - We use a fairly standard approximation of a true EMA where $EMA_{\text{frame}} = EMA_{\text{previous}} + \alpha \left( x_{\text{frame}} - EMA_{\text{previous}} \right)$ where $\alpha = \Delta t / \tau$ and $\tau$ is an arbitrary smoothness factor. (See bevyengine#4983 for more discussion of the math.) - The smoothness factor is here defaulted to $2 / 21$; this was chosen fairly arbitrarily as supposedly related to the existing 20-bucket SMA. - The smoothness factor can be set on a per-diagnostic basis via `Diagnostic::with_smoothing_factor`. --- ## Changelog ### Added - `Diagnostic::smoothed` - provides an exponentially smoothed view of a recorded diagnostic, to e.g. reduce jitter in frametime readings. ### Changed - `LogDiagnosticsPlugin` now records the smoothed value rather than the raw value. - For diagnostics recorded less often than every 0.1 seconds, this change to defaults will have no visible effect. - For discrete diagnostics where this smoothing is not desirable, set a smoothing factor of 0 to disable smoothing. - The average of the recent history is still shown when available.
ItsDoot
pushed a commit
to ItsDoot/bevy
that referenced
this issue
Feb 1, 2023
# Objective - Add Time-Adjusted Rolling EMA-based smoothing to diagnostics. - Closes bevyengine#4983; see that issue for more more information. ## Terms - EMA - [Exponential Moving Average](https://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average) - SMA - [Simple Moving Average](https://en.wikipedia.org/wiki/Moving_average#Simple_moving_average) ## Solution - We use a fairly standard approximation of a true EMA where $EMA_{\text{frame}} = EMA_{\text{previous}} + \alpha \left( x_{\text{frame}} - EMA_{\text{previous}} \right)$ where $\alpha = \Delta t / \tau$ and $\tau$ is an arbitrary smoothness factor. (See bevyengine#4983 for more discussion of the math.) - The smoothness factor is here defaulted to $2 / 21$; this was chosen fairly arbitrarily as supposedly related to the existing 20-bucket SMA. - The smoothness factor can be set on a per-diagnostic basis via `Diagnostic::with_smoothing_factor`. --- ## Changelog ### Added - `Diagnostic::smoothed` - provides an exponentially smoothed view of a recorded diagnostic, to e.g. reduce jitter in frametime readings. ### Changed - `LogDiagnosticsPlugin` now records the smoothed value rather than the raw value. - For diagnostics recorded less often than every 0.1 seconds, this change to defaults will have no visible effect. - For discrete diagnostics where this smoothing is not desirable, set a smoothing factor of 0 to disable smoothing. - The average of the recent history is still shown when available.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Labels
A-Diagnostics
Logging, crash handling, error reporting and performance analysis
C-Feature
A new feature, making something new possible
What problem does this solve or what need does it fill?
The current diagnostics collection offers two views into the data: getting the raw data, or a Simple Moving Average (the average of the last N data points). This is suboptimal, both because the duration of the last N samples depends on framerate, and because using SMA limits the visibility of a single frame spike (say, doing blocking IO on the main loop) to$1/N$ , as it will be averaged with the surrounding frame's good measurements. (And as the SMA of frame time is used to calculate the raw FPS, the FPS will only show $1/N^2$ of the impact.)
What solution would you like?
The solution is instead of applying SMA, to use some form of Exponential Moving Average / Exponential Smoothing.
Tracking the EMA is not expensive; it can be done in a rolling fashion by updating it in place each measurement:$$EMA_t = \alpha x_t + (1 - \alpha) EMA_{t-1} = EMA_{t - 1} + \alpha (x_t - EMA_{t - 1})$$ Since we're dealing with measurements that come in at a varying frequency, we need a Time Adjusted EMA, as the normal EMA formula assumes a constant sample rate. For this we have $$\alpha = 1 - e^{-\Delta t / \tau}$$ where $\tau$ is the time (in the same units as $t$ ) that it takes to reflect $1 - \frac{1}{e} \approx 63.2\%$ of a change in signal. When $\Delta t \ll \tau$ , $\alpha \approx \frac{\Delta t}{\tau}$ , so we can skip the exponentiation — in fact, when $\Delta t \not\ll \tau$ , the approximation results in reacting more to a change in source data, which in our case can be desirable!
Here are a few charts showing the different responses:
(There are axis and titles, GitHub is just refusing to not crop them for some reason... right click open in new tab to see them.)
The overshoot in the third is because I deliberately did not clamp$\alpha$ to $[0,1]$ when generating these plots. Doing the clamp is likely desirable in the real implementation. I've used a SMA width of 20 samples (as that is what is currently used for frametime/framerate), and a $\tau$ of $2 / 21$ for EMA, as this supposedly approximates a the "weight" of a 20-bucket SMA.
Simulation notebook: https://gist.github.com/CAD97/ad71b3d626050702a304ce74032b6a0b
I've also already implemented the change in the engine
Bevy diff
so here's the impact on bevymark:
BevyMark.2022-06-10.23-29-42.mp4
Of course, the
SMOOTHNESS_FACTOR
I've included (here2.0 / 21.0
) should a) be tweaked to get a desirable default response curve, and b) ideally be exposed as a plugin parameter so it can be adjusted on a game-by-game (and perhaps diagnostic-by-diagnostic) basis.What alternative(s) have you considered?
Additional context
Some miscellaneous references I used when re-researching EMA for this issue:
https://en.wikipedia.org/wiki/Moving_average
https://en.wikipedia.org/wiki/Exponential_smoothing
https://stackoverflow.com/a/1027808
http://www.eckner.com/papers/Algorithms%20for%20Unevenly%20Spaced%20Time%20Series.pdf
Others I'm sure, that I closed the tab on and are lost in my browser history
User impact
Currently with the above patch, just a new function
Diagnostic::smoothed(&self) -> Option<f64>
that provides an exponentially smoothed reading of the diagnostic.The text was updated successfully, but these errors were encountered: