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

Improve performance of Wrapper components (Chart, Group, Stack) #1494

Merged
merged 13 commits into from
Mar 9, 2020

Conversation

dmmulroy
Copy link
Contributor

This PR addresses issue #1376.

As soon as the first container component (i.e. Chart, Group, or Stack) renders calls getCalculatedProps we effectively have memoized values for calculatedProps on all subsequent/child getCalculatedProps calls (via the props passed in from a parent component) and we were additionally needlessly calling child.getChildren in each iteration of traverseChildren in each iteration of reduceChildren. Each getChildren then called reduceChildren (up to four times) and did the same pattern on its children and so on and so forth. So just defaulting the to current childs children (rather than calling getChildren) and using React.Children.toArray is enough since we will recurse the whole tree anyways.

For the example given in the codesandbox in #1376 I saw an improvement of rendering time from 4.47 seconds to 690 milliseconds and a decrease in calls to reduceChildren from 7234 to 21.

Benchmarks

Before:
Before flame chart

After:
After flame chart

@dmmulroy dmmulroy marked this pull request as ready for review February 25, 2020 23:38
@boygirl
Copy link
Contributor

boygirl commented Feb 27, 2020

@dmmulroy It looks like stacks are not working correctly after this change. You can check out the chromatic diffs here: https://www.chromaticqa.com/build?appId=5b4acf7c54c0490024d5980b&number=409

I'm going to dig into the code, too and see if there's something simple missing.

const nestedChildProps = assign({}, c.props, pick(childProps, sharedProps));
return React.cloneElement(c, nestedChildProps);
});
const nestedChildren = React.Children.toArray(child.props.children).map((c) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the issue for stacking. The domain won't be correctly calculated if VictoryStack getChildren isn't called when calculating the domain, because the y0 / y1 values wont be set on the children. I'm guessing removing this is also where we're getting most of our perf improvements, though. It's probably worth writing a specialized method for calculating a domain for VictoryStack.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for looking into this! I'll start poking around to see what I can figure out 👍

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm... we also have to make sure that VictoryChart and VictoryGroup actually use VictoryStack's getDomain method. Maybe it should be a static on that class the same way it is on components like VictoryBar

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'm actually looking into potentially having VictoryChart and VictoryGroup properly calculate y0 and y1 in their getCalculatedProps so we can bank on the those values being available/memoized on subsequent/child getCalculateProps calls.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I'm using the wrong terms... I mean _y0 and _y1. Values that get added to each data point when we stack values

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 haven't had a ton of time to spend on this since last week, but I did figure out a small win to fix the regression (I think, still need to check chromatic). It does decrease performance, from 690 milliseconds to about 1.07 seconds, and also increased the number of reduceChildren from 21 to 771, but that is significantly better than the original 7234 calls.

@dmmulroy dmmulroy requested a review from boygirl March 4, 2020 17:22
Copy link
Contributor

@narinluangrath narinluangrath left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One question about your changes to getChildren, otherwise LGTM. Maybe we should add props.scale || as well since it takes as long as getDomain (see flame chart for two stacked bars).

image

const nestedChildren =
child.type && isFunction(child.type.getChildren)
child.type && child.type.role === "stack" && isFunction(child.type.getChildren)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we get similar performance improvements without this change?

@boygirl
Copy link
Contributor

boygirl commented Mar 9, 2020

Maybe we should add props.scale || as well since it takes as long as getDomain

This is a good idea, but we'll have to be a little careful with that since the scale a user might provide ("time", "log") is not the same as a scale a victory component would provide to its children (a scale function with domain and range applied).

Copy link
Contributor

@boygirl boygirl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dmmulroy thank you for this improvement. I'm going to merge and release as a patch version today :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants