-
Notifications
You must be signed in to change notification settings - Fork 135
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
Performance improvements #220
Conversation
47cc04b
to
cf6c06a
Compare
@@ -22,7 +38,7 @@ export interface KnownBaseTheme { | |||
spacing: { | |||
[key: string]: number | string; | |||
}; | |||
breakpoints: { | |||
breakpoints?: { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
breakpoints
are no longer required to be defined in the theme. If this value is not defined, we will skip computing responsive values.
3bd20b6
to
dd95404
Compare
export {getValueForScreenSize} from './getValueForScreenSize'; | ||
export {getResponsiveValue} from './getResponsiveValue'; | ||
export {isResponsiveObjectValue} from './isResponsiveObjectValue'; | ||
export {getThemeValue} from './getThemeValue'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I split the responsiveHelpers
file into multiple ones as it was getting too long
const props = omitPropertiesMap.variant | ||
? {variant: 'defaults', ...componentProps} | ||
: componentProps; | ||
return getKeys(props).reduce( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This reduce was extremely ineffective. filterRestyleProps
is called on every render of any restyle component – that is very often.
dd95404
to
a5d2e98
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tophat and code looks good!
0daffe3
to
e769b94
Compare
@@ -19,4 +19,4 @@ const theme = createTheme({ | |||
}); | |||
``` | |||
|
|||
See the [Responsive Values](/fundamentals/responsive-values) section to see how these can be used. | |||
See the [Responsive Values](/fundamentals/responsive-values) section to see how these can be used. Defining `breakpoints` is optional and we recommend defining it only if you plan to use them due to the small performance hit using responsive values incur. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it possible to mention how much slower it'll get? It'll make it easier to decide whether or not to use this feature
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added 👍
ResponsiveBaseTheme, | ||
} from '../types'; | ||
|
||
export const getValueForScreenSize = <Theme extends ResponsiveBaseTheme, TVal>({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be great if we can mention the purpose of some of the methods. It's a little difficult to understand the PR otherwise.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These are not new methods and I have only moved them to separate files. But yeah, I can add some descriptions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added 👍
e769b94
to
f929170
Compare
@naqvitalha, please, have another look and afterwards we can merge this. |
const options = {theme, dimensions}; | ||
// We know props won't have prototype properties | ||
// eslint-disable-next-line guard-for-in | ||
for (const key in props) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can this break if objects inherit some default properties?
For e.g, if object prototype includes some default values?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Technically, it might be possible, although I don't expect to happen. The extra if should be fine, added it
f929170
to
8d21898
Compare
8d21898
to
3e7f596
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM! Very excited about this!
Great work @fortmarek! |
I should just avoid using |
Description
restyle
is in its essence a runtime library to achieve better DX. Ideally,restyle
should have no overhead and no performance implications. Since, as things stand, we need to run code in the runtime to convert theme values to RN style, there will be some overhead.However, we should ensure that the overhead is as small as possible. We've already made some performance improvements in the past but we still found there's a noticeable performance drop when using restyle.
Using the fixture inside the
flash-list
repo in this branch, we found outrestyle
still incurs performance drop. Measuring on iPad Pro 2019, we found the FPS drops by ~7 % and list load time takes 12 % longer. Considering these numbers, we decided to investigate further whyrestyle
still has such an impact on performance.This PR includes multiple small performance improvements but the major ones have been:
reduce
tofor-loop
infilterRestyleProps
. Creating many objects on everyuseRestyle
render is expensive.for-loop
with updating objects in-place instead of copying and creating new ones is much more efficient.breakpoints
are now optional. Querying window dimensions and computing responsive values has additional performance cost. If developers are not usingbreakpoints
, we should not compute these values. In the future, we should consider creating responsive variants of restyle components, so the responsive computations can be done really only for those components that need them.useMemo
insideuseRestyle
to ensure a component does not need to recompute its style on every render. We decided to take this step further and memoize computed style inside thetheme
object. This means a style does not have to be recomputed if any other component has already computed the same theme value.Measurements
All the following measurements have been done in this flash-list branch on iPad Pro 2019:
FPS
Without restyle:
118.2, 116, 116.6, 117.1, 116.7, 115.7, 116.2, 115.7, 116.1, 115.3, 115.9
Avg: 116.32
With restyle:
109.7, 108.3, 106.8, 108.5, 110.4, 110.2, 108.0, 108.8, 107.0, 110.1
Avg: 108.78
Perf drop: 7 %
With this PR's optimizations:
115.7, 115.2, 116.1, 113.8, 115.1, 115.4, 115.0, 114.7, 111.1, 113.8, 113.1
Avg: 114.45
Perf drop: 1.5 %
Relative perf improvement: 79 %
List load time
Without restyle:
61, 60, 62, 59, 61, 59, 61, 64, 57, 61
Avg: 60.5
With restyle:
71, 67, 71, 69, 70, 64, 79, 61, 71, 65
Avg: 68.8
Performance drop: 12 %
With optimizations:
64, 65, 66, 60, 64, 64, 67, 63, 66, 64
Average: 64.3
Performance drop: 6 %
Relative performance improvement: 50 %
Tracer
We have created this helper class called
tracer
to get granular numbers on how much time JS spends on evaluatinguseRestyle
Time spent on useRestyle in ms for ~30000 calls:
Without optimizations:
563, 533, 515, 618, 583
Average: 562.4 ms
With optimizations:
270, 253, 242, 217, 262
Average: 248.8 ms
Performance improvement: 56 %
From this, more than 50 % is spent on
filterRestyleProps
method. I don't see how we can effectively get rid of that for loop since we need it to computed theserializedRestyleProps
that determine whether a style needs to be recomputed or not.Summary
The changes in this PR lead to large performance improvements where the FPS when scrolling in
flash-list
has improved by almost 80 %. In the FlashList fixture, using restyle still incurs ~1.5 % FPS drop. We expect this number to be smaller in more complicated lists as restyle would constitute smaller part of all the work done on every render.The total time spent on
useRestyle
has dropped by more than 50 %, the list load time has improved by 50 %.There is still some performance drop attached to
useRestyle
even with these new changes but the impact should be now minimal.Reviewers’ hat-rack 🎩
Checklist