-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
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
Optimize deleteModifier/addModifier #1659
Conversation
f9fb3c3
to
6394c11
Compare
479f4d4
to
9cd6c77
Compare
Alright friends, tests are passing and I think this is ready for review! |
After the optimizations I applied to generating modifiers in #1659, I noticed that about 50% of the time in componentWillReceiveProps is spent in isDayVisible() doing moment operations to generate the boundaries for the check. Since these boundaries remain very stable across each call, we can reduce most of the work done here to very little by adding some caching of these values. On top of my reduce optimizations, this brings down the time spent in componentWillReceiveProps to ~15ms. I'm not doing anything to limit the size of these Maps, so this will leak some memory, but I think the size of the cache will be really small in most cases, so I'm not sure it matters.
After the optimizations I applied to generating modifiers in #1659, I noticed that about 50% of the time in componentWillReceiveProps is spent in isDayVisible() doing moment operations to generate the boundaries for the check. Since these boundaries remain very stable across each call, we can reduce most of the work done here to very little by adding some caching of these values. On top of my reduce optimizations, this brings down the time spent in componentWillReceiveProps to ~15ms. I'm not doing anything to limit the size of these Maps, so this will leak some memory, but I think the size of the cache will be really small in most cases, so I'm not sure it matters.
After the optimizations I applied to generating modifiers in #1659, I noticed that about 50% of the time in componentWillReceiveProps is spent in isDayVisible() doing moment operations to generate the boundaries for the check. Since these boundaries remain very stable across each call, we can reduce most of the work done here to very little by adding some caching of these values. On top of my reduce optimizations, this brings down the time spent in componentWillReceiveProps to ~15ms. I'm not doing anything to limit the size of these Maps, so this will leak some memory, but I think the size of the cache will be really small in most cases, so I'm not sure it matters.
While profiling DayRangePickerController, I noticed that we spend a significant amount of time generating modifiers whenever anything is changed. A good amount of this cost comes from these reducers that unnecessarily create new objects and spread into them on every iteration. We can make this faster by mutating the accumulator while inside the reducer. In my profiling, this reduces the speed of deleteModifier from ~28ms to ~20ms, and I think it will reduce the memory usage, which should further help reduce garbage collection times.
We create this object in this function and then return it at the end, so it should be safe to just mutate it while we are in this function instead of rebuilding it immediately again. This reduces the amount of time spent in deleteModifier from ~20ms to ~16ms.
I noticed that we always create a new set and then delete from it even if the modifier is not in the set, or create a new set and then add it even it the modifier is already in the set. This optimization reduces the cost of DayPickerRangeController#componentWillReceiveProps from ~16ms to ~12ms.
[toISODateString(startOfMonth)]: [], | ||
[toISODateString(startOfMonth.clone().add(1, 'day'))]: [], | ||
[toISODateString(startOfMonth.clone().add(2, 'days'))]: [], | ||
[toISODateString(startOfMonth)]: new Set(), |
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 type change suggests an observable api change - is that the case, or am i missing context?
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 could be wrong but I think this is just a care of these fixtures being incorrect. Previously they were being converted to sets in the code by happenstance, but I think all of there are sets throughout the actual code.
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.
On phone now but I think these might be able to be passed in as props, so I might need to add a conversation step somewhere like in the constructor. I'll dig in more tomorrow.
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.
Nope, as far as I can tell, this is all component state. These tests are injecting it into the component by calling setState directly.
This is in the constructor where this variable begins its life: https://github.com/airbnb/react-dates/blob/0e2ed9a322dd0d8f9782614b40a362413406a76b/src/components/DayPickerRangeController.jsx#L217
And getStateForNewMonth uses getModifiers for visibleDates, which always uses Set for the set of modifiers: https://github.com/airbnb/react-dates/blob/0e2ed9a322dd0d8f9782614b40a362413406a76b/src/components/DayPickerRangeController.jsx#L965-L971
I'm pretty sure this is all internal stuff.
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 wonder if it’d be worth some kind of shared memoize abstraction to make this pattern easier to follow throughout the repo?
Yeah, it seems that there's a fair amount of duplicated code here, so abstracting something might make sense. |
After the optimizations I applied to generating modifiers in react-dates/react-dates#1659, I noticed that about 50% of the time in componentWillReceiveProps is spent in isDayVisible() doing moment operations to generate the boundaries for the check. Since these boundaries remain very stable across each call, we can reduce most of the work done here to very little by adding some caching of these values. On top of my reduce optimizations, this brings down the time spent in componentWillReceiveProps to ~15ms. I'm not doing anything to limit the size of these Maps, so this will leak some memory, but I think the size of the cache will be really small in most cases, so I'm not sure it matters.
After the optimizations I applied to generating modifiers in react-dates/react-dates#1659, I noticed that about 50% of the time in componentWillReceiveProps is spent in isDayVisible() doing moment operations to generate the boundaries for the check. Since these boundaries remain very stable across each call, we can reduce most of the work done here to very little by adding some caching of these values. On top of my reduce optimizations, this brings down the time spent in componentWillReceiveProps to ~15ms. I'm not doing anything to limit the size of these Maps, so this will leak some memory, but I think the size of the cache will be really small in most cases, so I'm not sure it matters.
While profiling DayRangePickerController, I noticed that we spend a
significant amount of time generating modifiers whenever anything is
changed. A good amount of this cost comes from these reducers that
unnecessarily create new objects and spread into them on every
iteration. We can make this faster by mutating the accumulator while
inside the reducer.
In my profiling, this reduces the speed of deleteModifier from ~28ms to
~20ms, and I think it will reduce the memory usage, which should further
help reduce garbage collection times.
Mutate updatedDaysAfterDeletion in deleteModifier/addModifier
We create this object in this function and then return it at the end, so
it should be safe to just mutate it while we are in this function
instead of rebuilding it immediately again.
This reduces the amount of time spent in deleteModifier from ~20ms to
~16ms.
Avoid creating new Sets when deleting/adding modifiers
I noticed that we always create a new set and then delete from it even
if the modifier is not in the set, or create a new set and then add it
even it the modifier is already in the set. This optimization reduces
the cost of DayPickerRangeController#componentWillReceiveProps from
~16ms to ~12ms.
Before:
![image](https://user-images.githubusercontent.com/195534/58668351-75febe80-82ed-11e9-9f0d-091f25eae995.png)
After:
![image](https://user-images.githubusercontent.com/195534/58668379-8adb5200-82ed-11e9-810d-29f82d16fe14.png)