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

Optimize deleteModifier/addModifier #1659

Merged
merged 3 commits into from
May 31, 2019
Merged

Optimize deleteModifier/addModifier #1659

merged 3 commits into from
May 31, 2019

Conversation

lencioni
Copy link
Contributor

@lencioni lencioni commented May 30, 2019

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

After:
image

@lencioni lencioni added semver-patch: fixes/refactors/etc Anything that's not major or minor. performance labels May 30, 2019
@coveralls
Copy link

coveralls commented May 30, 2019

Coverage Status

Coverage decreased (-0.3%) to 84.314% when pulling 2e1dd23 on jdl-reduce into 0e2ed9a on master.

@lencioni lencioni force-pushed the jdl-reduce branch 2 times, most recently from f9fb3c3 to 6394c11 Compare May 30, 2019 18:36
@lencioni lencioni changed the title Use accumulator-mutating reducers Optimize deleteModifier May 30, 2019
@lencioni lencioni changed the title Optimize deleteModifier Optimize deleteModifier/addModifier May 30, 2019
@lencioni lencioni force-pushed the jdl-reduce branch 2 times, most recently from 479f4d4 to 9cd6c77 Compare May 30, 2019 21:44
@lencioni
Copy link
Contributor Author

Alright friends, tests are passing and I think this is ready for review!

lencioni added a commit that referenced this pull request May 30, 2019
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.
lencioni added a commit that referenced this pull request May 30, 2019
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.
lencioni added a commit that referenced this pull request May 30, 2019
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(),
Copy link
Member

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?

Copy link
Contributor Author

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.

Copy link
Contributor Author

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.

Copy link
Contributor Author

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.

Copy link
Member

@ljharb ljharb left a 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?

@lencioni lencioni merged commit 2e1dd23 into master May 31, 2019
@lencioni
Copy link
Contributor Author

Yeah, it seems that there's a fair amount of duplicated code here, so abstracting something might make sense.

@ljharb ljharb deleted the jdl-reduce branch May 31, 2019 15:45
devs-cloud pushed a commit to devs-cloud/react-date that referenced this pull request Dec 27, 2019
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.
MarcoAntonioAG pushed a commit to MarcoAntonioAG/React-dates that referenced this pull request Mar 31, 2022
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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
performance semver-patch: fixes/refactors/etc Anything that's not major or minor.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants