-
Notifications
You must be signed in to change notification settings - Fork 113
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
Reduce CPU load by reducing view update frequency #231
Conversation
CADisplayLink is being used to perform view model state changes at 60 fps presumably to keep the progress ring animating at 60 fps. Nothing else in the view model needs to change that frequently
View model now knows when the next refresh for tokens should happen. Allows the TokenListViewController to animate the progress view from it's current progress without relying on CADisplayLink
TokenListViewController schedules a dispatch to refresh tokens when the next period commences
This allows the progress to be animatable with CoreAnimation
When the app comes back from the background, the progress animation needs to be added again.
Instead of relying on the side effect of a no-op action to trigger a view model update, instead have the AppController update the view directly using the latest view model.
When using a TimeInterval since "now" to control the progress ring and trigger the view model refresh, the timing becomes susceptible to skew based on differences between the time when the interval is calculated and the time when the interval is applied to a timer or animation.
I was considering using two My assumption is that it will cut the work in half since it only calculates one path per progress change instead of two:
|
@beaucollins That sounds like a good idea for optimizing the draw call. After commenting out the draw implementation, it does seem like some of the CPU load is caused just by the CAAnimation updating the progress property, so we may not be able to get this below 3-4%. Thank you for all your work on this! |
I gave the multiple layer idea a go and the CPU usage didn’t change noticeably. Probably can’t squeeze much more out of it. Changes look great. |
So it looks like using a couple of |
Codecov Report
@@ Coverage Diff @@
## develop #231 +/- ##
==========================================
+ Coverage 37.94% 38.4% +0.46%
==========================================
Files 36 36
Lines 2148 2182 +34
==========================================
+ Hits 815 838 +23
- Misses 1333 1344 +11
Continue to review full report at Codecov.
|
Based on the excellent work by @beaucollins in PR #224. My changes on top of that PR can be seen here.
Currently, the app animates the progress ring and keeps token passwords up-to-date by using
CADisplayLink
to regenerate the view model and update the view at 60 frames per second. This is needlessly inefficient, and causes excessive processor load. Instead of updating the view every frame, it is sufficient to generate a new view model only when a token password has updated, and to use Core Animation to drive the progress ring.Animatable Progress Ring
The drawing code for
OTPProgressRing
has been moved to a customCALayer
with an animatableprogress
property. TheTokenListViewModel
'sringProgress
property has been replaced with aProgressRingViewModel
which specifies the start and end times of the current cycle of the animation. These values are used to configure aCABasicAnimation
which drives the ring animation, removing the need for constant updates from aCADisplayLink
. The progress ring now only needs to be updated with a new view model when a new cycle of the animation should begin.View Model Expiration Date
The method which generates a view model for the token list (and for the root component) now returns a tuple containing both the view model and a
nextRefreshTime
. TheAppController
's display link has been replaced with a refresh timer, which is configured to fire at thenextRefreshTime
and trigger an update of the view with a new view model.The app also now triggers a view update on
applicationWillEnterForeground
, to ensure the UI is up-to-date when the app returns from the background.Future Performance Considerations
These changes reduce idle CPU usage from around 40% to less than 10%. The remaining CPU load is almost entirely caused by animating and redrawing the progress ring. Ideas for optimizing the drawing of the progress ring are discussed below.